loadObj function

Future<List<Mesh>> loadObj(
  1. String fileName,
  2. bool normalized, {
  3. bool isAsset = true,
})

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;
}