parse method
dynamic
parse(])
override
Implementation
@override
parse(json, [String? path, Function? onLoad, Function? onError]) async {
var state = ParserState();
if (json.indexOf('\r\n') != -1) {
// This is faster than String.split with regex that splits on both
json = json.replaceAll(RegExp("\r\n", multiLine: true), '\n');
}
if (json.indexOf('\\\n') != -1) {
// join lines separated by a line continuation character (\)
json = json.replaceAll(RegExp("\\\n"), '');
}
var lines = json.split('\n');
var line = '', lineFirstChar = '';
var lineLength = 0;
var result = [];
// Faster to just trim left side of the line. Use if available.
for (var i = 0, l = lines.length; i < l; i++) {
line = lines[i];
line = line.trimLeft();
// print("i: ${i} line: ${line} ");
lineLength = line.length;
if (lineLength == 0) continue;
lineFirstChar = line[0];
// @todo invoke passed in handler if any
if (lineFirstChar == '#') continue;
if (lineFirstChar == 'v') {
var data = line.split(RegExp(r"\s+"));
switch (data[0]) {
case 'v':
state.vertices.addAll([parseFloat(data[1]), parseFloat(data[2]), parseFloat(data[3])]);
if (data.length >= 7) {
state.colors.addAll([parseFloat(data[4]), parseFloat(data[5]), parseFloat(data[6])]);
} else {
// if no colors are defined, add placeholders so color and vertex indices match
state.colors.addAll([]);
}
break;
case 'vn':
state.normals.addAll([parseFloat(data[1]), parseFloat(data[2]), parseFloat(data[3])]);
break;
case 'vt':
state.uvs.addAll([parseFloat(data[1]), parseFloat(data[2])]);
break;
}
} else if (lineFirstChar == 'f') {
var lineData = line.substring(1).trim();
var vertexData = lineData.split(RegExp(r"\s+"));
List<List> faceVertices = [];
// Parse the face vertex data into an easy to work with format
// print(" lineFirstChar is f .................. ");
// print(vertexData);
for (var j = 0, jl = vertexData.length; j < jl; j++) {
var vertex = vertexData[j];
if (vertex.isNotEmpty) {
var vertexParts = vertex.split('/');
faceVertices.add(vertexParts);
}
}
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
var v1 = faceVertices[0];
for (var j = 1, jl = faceVertices.length - 1; j < jl; j++) {
var v2 = faceVertices[j];
var v3 = faceVertices[j + 1];
state.addFace(
v1[0],
v2[0],
v3[0],
v1.length > 1 ? v1[1] : null,
v2.length > 1 ? v2[1] : null,
v3.length > 1 ? v3[1] : null,
v1.length > 2 ? v1[2] : null,
v2.length > 2 ? v2[2] : null,
v3.length > 2 ? v3[2] : null);
}
} else if (lineFirstChar == 'l') {
var lineParts = line.substring(1).trim().split(' ');
var lineVertices = [];
var lineUVs = [];
if (!line.contains('/')) {
lineVertices = lineParts;
} else {
for (var li = 0, llen = lineParts.length; li < llen; li++) {
var parts = lineParts[li].split('/');
if (parts[0] != '') lineVertices.add(parts[0]);
if (parts[1] != '') lineUVs.add(parts[1]);
}
}
state.addLineGeometry(lineVertices, lineUVs);
} else if (lineFirstChar == 'p') {
var lineData = line.substring(1).trim();
var pointData = lineData.split(' ');
state.addPointGeometry(pointData);
} else if (_objectPattern.hasMatch(line)) {
result = _objectPattern.allMatches(line).toList();
// o object_name
// or
// g group_name
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
// var name = result[ 0 ].substr( 1 ).trim();
var name = ' ${result[0].group(0).substring(1).trim()}'.substring(1);
state.startObject(name, null);
} else if (_materialUsePattern.hasMatch(line)) {
// material
state.object!.startMaterial(line.substring(7).trim(), state.materialLibraries);
} else if (_materialLibraryPattern.hasMatch(line)) {
// mtl file
state.materialLibraries.add(line.substring(7).trim());
} else if (_mapUsePattern.hasMatch(line)) {
// the line is parsed but ignored since the loader assumes textures are defined MTL files
// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
print('THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.');
} else if (lineFirstChar == 's') {
result = line.split(' ');
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
/*
* http://paulbourke.net/dataformats/obj/
* or
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/
if (result.length > 1) {
var value = result[1].trim().toLowerCase();
state.object!.smooth = (value != '0' && value != 'off');
} else {
// ZBrush can produce "s" lines #11707
state.object!.smooth = true;
}
var material = state.object!.currentMaterial();
if (material != null) material.smooth = state.object!.smooth;
} else {
// Handle null terminated files without exception
if (line == '0') continue;
print('THREE.OBJLoader: Unexpected line: "$line"');
}
}
state.finalize();
var container = Group();
// container.materialLibraries = [].concat( state.materialLibraries );
var hasPrimitives = !(state.objects.length == 1 && state.objects[0].geometry["vertices"].length == 0);
if (hasPrimitives == true) {
for (var i = 0, l = state.objects.length; i < l; i++) {
var object = state.objects[i];
var geometry = object.geometry;
var materials = object.materials;
var isLine = (geometry["type"] == 'Line');
var isPoints = (geometry["type"] == 'Points');
var hasVertexColors = false;
// Skip o/g line declarations that did not follow with any faces
if (geometry["vertices"].length == 0) continue;
var buffergeometry = BufferGeometry();
buffergeometry.setAttribute(
'position', Float32BufferAttribute(Float32Array.fromList(List<double>.from(geometry["vertices"])), 3));
if (geometry["normals"].length > 0) {
buffergeometry.setAttribute(
'normal', Float32BufferAttribute(Float32Array.fromList(List<double>.from(geometry["normals"])), 3));
}
if (geometry["colors"].length > 0) {
hasVertexColors = true;
buffergeometry.setAttribute(
'color', Float32BufferAttribute(Float32Array.fromList(List<double>.from(geometry["colors"])), 3));
}
if (geometry["hasUVIndices"] == true) {
buffergeometry.setAttribute(
'uv', Float32BufferAttribute(Float32Array.fromList(List<double>.from(geometry["uvs"])), 2));
}
// Create materials
var createdMaterials = [];
for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
var sourceMaterial = materials[mi];
var materialHash = sourceMaterial.name + '_' + "${sourceMaterial.smooth}" + '_' + "$hasVertexColors";
var material = state.materials[materialHash];
if (this.materials != null) {
material = await this.materials!.create(sourceMaterial.name);
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if (isLine && material && material is! LineBasicMaterial) {
var materialLine = LineBasicMaterial({});
materialLine.copy(material);
// Material.prototype.copy.call( materialLine, material );
materialLine.color.copy(material.color);
material = materialLine;
} else if (isPoints && material && material is! PointsMaterial) {
var materialPoints = PointsMaterial({"size": 10, "sizeAttenuation": false});
// Material.prototype.copy.call( materialPoints, material );
materialPoints.copy(material);
materialPoints.color.copy(material.color);
materialPoints.map = material.map;
material = materialPoints;
}
}
if (material == null) {
if (isLine) {
material = LineBasicMaterial({});
} else if (isPoints) {
material = PointsMaterial({"size": 1, "sizeAttenuation": false});
} else {
material = MeshPhongMaterial();
}
material.name = sourceMaterial.name;
material.flatShading = sourceMaterial.smooth ? false : true;
material.vertexColors = hasVertexColors;
state.materials[materialHash] = material;
}
createdMaterials.add(material);
}
// Create mesh
var mesh;
if (createdMaterials.length > 1) {
for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
var sourceMaterial = materials[mi];
buffergeometry.addGroup(sourceMaterial.groupStart.toInt(), sourceMaterial.groupCount.toInt(), mi);
}
if (isLine) {
mesh = LineSegments(buffergeometry, createdMaterials);
} else if (isPoints) {
mesh = Points(buffergeometry, createdMaterials);
} else {
mesh = Mesh(buffergeometry, createdMaterials);
}
} else {
if (isLine) {
mesh = LineSegments(buffergeometry, createdMaterials[0]);
} else if (isPoints) {
mesh = Points(buffergeometry, createdMaterials[0]);
} else {
mesh = Mesh(buffergeometry, createdMaterials[0]);
}
}
mesh.name = object.name;
container.add(mesh);
}
} else {
// if there is only the default parser state object with no geometry data, interpret data as point cloud
if (state.vertices.isNotEmpty) {
var material = PointsMaterial({"size": 1, "sizeAttenuation": false});
var buffergeometry = BufferGeometry();
buffergeometry.setAttribute('position', Float32BufferAttribute(Float32Array.fromList(state.vertices), 3));
if (state.colors.isNotEmpty) {
buffergeometry.setAttribute('color', Float32BufferAttribute(Float32Array.fromList(state.colors), 3));
material.vertexColors = true;
}
var points = Points(buffergeometry, material);
container.add(points);
}
}
return container;
}