loadObj function
Loading mesh from Wavefront's object file (.obj). Reference:http://paulbourke.net/dataformats/obj/
Implementation
Future<List<Mesh>> loadObj(String fileName, bool normalized, {bool isAsset = true}) async {
Map<String, Material>? materials;
List<Vector3> vertices = <Vector3>[];
List<Offset> texcoords = <Offset>[];
List<Polygon> vertexIndices = <Polygon>[];
List<Polygon> textureIndices = <Polygon>[];
List<String> elementNames = <String>[];
List<String> elementMaterials = <String>[];
List<int> elementOffsets = <int>[];
String? materialName;
String? objectlName;
String? groupName;
String basePath = path.dirname(fileName);
var data;
if (isAsset) {
// load obj data from asset.
data = await rootBundle.loadString(fileName);
} else {
// load obj data from file.
data = await File(fileName).readAsString();
}
final lines = data.split('\n');
for (var line in lines) {
List<String> parts = line.trim().split(RegExp(r"\s+"));
switch (parts[0]) {
case 'mtllib':
// load material library file. eg: mtllib master.mtl
final mtlFileName = path.join(basePath, parts[1]);
materials = await loadMtl(mtlFileName, isAsset: isAsset);
break;
case 'usemtl':
// material name from material library. eg: usemtl red
if (parts.length >= 2) materialName = parts[1];
// create a new mesh element
final String elementName = objectlName ?? groupName ?? materialName ?? '';
elementNames.add(elementName);
elementMaterials.add(materialName ?? '');
elementOffsets.add(vertexIndices.length);
break;
case 'g':
// the name for the group. eg: g front cube
if (parts.length >= 2) groupName = parts[1];
break;
case 'o':
// the user-defined object name. eg: o cube
if (parts.length >= 2) objectlName = parts[1];
break;
case 'v':
// a geometric vertex and its x y z coordinates. eg: v 0.000000 2.000000 0.000000
if (parts.length >= 4) {
final v = Vector3(double.parse(parts[1]), double.parse(parts[2]), double.parse(parts[3]));
vertices.add(v);
}
break;
case 'vt':
// eg: vt 0.000000 0.000000
if (parts.length >= 3) {
double x = double.parse(parts[1]);
double y = double.parse(parts[2]);
if (x < 0 || x > 1.0) x %= 1.0;
if (y < 0 || y > 1.0) y %= 1.0;
final vt = Offset(x, y);
texcoords.add(vt);
}
break;
case 'f':
if (parts.length >= 4) {
// eg: f 1/1 2/2 3/3
final List<String> p1 = parts[1].split('/');
final List<String> p2 = parts[2].split('/');
final List<String> p3 = parts[3].split('/');
Polygon vi = Polygon(_getVertexIndex(p1[0]), _getVertexIndex(p2[0]), _getVertexIndex(p3[0]));
vertexIndices.add(vi);
Polygon ti = Polygon(0, 0, 0);
if ((p1.length >= 2 && p1[1] != '') && (p2.length >= 2 && p2[1] != '') && (p3.length >= 2 && p3[1] != '')) {
ti = Polygon(_getVertexIndex(p1[1]), _getVertexIndex(p2[1]), _getVertexIndex(p3[1]));
textureIndices.add(ti);
}
// polygon to triangle. eg: f 1/1 2/2 3/3 4/4 ==> f 1/1 2/2 3/3 + f 1/1 3/3 4/4
for (int i = 4; i < parts.length; i++) {
final List<String> p3 = parts[i].split('/');
vi = Polygon(vi.vertex0, vi.vertex2, _getVertexIndex(p3[0]));
vertexIndices.add(vi);
if (p3.length >= 2 && p3[1] != '') {
ti = Polygon(ti.vertex0, ti.vertex2, _getVertexIndex(p3[1]));
textureIndices.add(ti);
}
}
}
break;
default:
}
}
final meshes = await _buildMesh(
vertices,
texcoords,
vertexIndices,
textureIndices,
materials,
elementNames,
elementMaterials,
elementOffsets,
basePath,
isAsset,
);
return normalized ? normalizeMesh(meshes) : meshes;
}