void RawModel::CreateMaterialModels()

in src/raw/RawModel.cpp [476:641]


void RawModel::CreateMaterialModels(
    std::vector<RawModel>& materialModels,
    bool shortIndices,
    const int keepAttribs,
    const bool forceDiscrete) const {
  // Sort all triangles based on material first, then surface, then first vertex index.
  std::vector<RawTriangle> sortedTriangles;

  bool invertedTransparencySort = true;
  if (invertedTransparencySort) {
    // Split the triangles into opaque and transparent triangles.
    std::vector<RawTriangle> opaqueTriangles;
    std::vector<RawTriangle> transparentTriangles;
    for (const auto& triangle : triangles) {
      const int materialIndex = triangle.materialIndex;
      if (materialIndex < 0) {
        opaqueTriangles.push_back(triangle);
        continue;
      }
      const int textureIndex = materials[materialIndex].textures[RAW_TEXTURE_USAGE_DIFFUSE];
      if (textureIndex < 0) {
        if (vertices[triangle.verts[0]].color.w < 1.0f ||
            vertices[triangle.verts[1]].color.w < 1.0f ||
            vertices[triangle.verts[2]].color.w < 1.0f) {
          transparentTriangles.push_back(triangle);
          continue;
        }
        opaqueTriangles.push_back(triangle);
        continue;
      }
      if (textures[textureIndex].occlusion == RAW_TEXTURE_OCCLUSION_TRANSPARENT) {
        transparentTriangles.push_back(triangle);
      } else {
        opaqueTriangles.push_back(triangle);
      }
    }

    // Sort the opaque triangles.
    std::sort(opaqueTriangles.begin(), opaqueTriangles.end(), TriangleModelSortPos::Compare);

    // Sort the transparent triangles in the reverse direction.
    std::sort(
        transparentTriangles.begin(), transparentTriangles.end(), TriangleModelSortNeg::Compare);

    // Add the triangles to the sorted list.
    for (const auto& opaqueTriangle : opaqueTriangles) {
      sortedTriangles.push_back(opaqueTriangle);
    }
    for (const auto& transparentTriangle : transparentTriangles) {
      sortedTriangles.push_back(transparentTriangle);
    }
  } else {
    sortedTriangles = triangles;
    std::sort(sortedTriangles.begin(), sortedTriangles.end(), TriangleModelSortPos::Compare);
  }

  // Overestimate the number of models that will be created to avoid massive reallocation.
  int discreteCount = 0;
  for (const auto& surface : surfaces) {
    discreteCount += surface.discrete ? 1 : 0;
  }

  materialModels.clear();
  materialModels.reserve(materials.size() + discreteCount);

  const RawVertex defaultVertex;

  // Create a separate model for each material.
  RawModel* model;
  for (size_t i = 0; i < sortedTriangles.size(); i++) {
    if (sortedTriangles[i].materialIndex < 0 || sortedTriangles[i].surfaceIndex < 0) {
      continue;
    }

    if (i == 0 || (shortIndices && model->GetVertexCount() >= 0xFFFE) ||
        sortedTriangles[i].materialIndex != sortedTriangles[i - 1].materialIndex ||
        (sortedTriangles[i].surfaceIndex != sortedTriangles[i - 1].surfaceIndex &&
         (forceDiscrete || surfaces[sortedTriangles[i].surfaceIndex].discrete ||
          surfaces[sortedTriangles[i - 1].surfaceIndex].discrete))) {
      materialModels.resize(materialModels.size() + 1);
      model = &materialModels[materialModels.size() - 1];
    }

    // FIXME: will have to unlink from the nodes, transform both surfaces into a
    // common space, and reparent to a new node with appropriate transform.

    const int prevSurfaceCount = model->GetSurfaceCount();
    const int materialIndex = model->AddMaterial(materials[sortedTriangles[i].materialIndex]);
    const int surfaceIndex = model->AddSurface(surfaces[sortedTriangles[i].surfaceIndex]);
    RawSurface& rawSurface = model->GetSurface(surfaceIndex);

    if (model->GetSurfaceCount() > prevSurfaceCount) {
      const std::vector<long>& jointIds = surfaces[sortedTriangles[i].surfaceIndex].jointIds;
      for (const auto& jointId : jointIds) {
        const int nodeIndex = GetNodeById(jointId);
        assert(nodeIndex != -1);
        model->AddNode(GetNode(nodeIndex));
      }
      rawSurface.bounds.Clear();
    }

    int verts[3];
    for (int j = 0; j < 3; j++) {
      RawVertex vertex = vertices[sortedTriangles[i].verts[j]];

      if (keepAttribs != -1) {
        int keep = keepAttribs;
        if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) {
          keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
        }
        if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) {
          keep |= RAW_VERTEX_ATTRIBUTE_POSITION;

          const RawMaterial& mat = model->GetMaterial(materialIndex);
          if (mat.textures[RAW_TEXTURE_USAGE_DIFFUSE] != -1) {
            keep |= RAW_VERTEX_ATTRIBUTE_UV0;
          }
          if (mat.textures[RAW_TEXTURE_USAGE_NORMAL] != -1) {
            keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | RAW_VERTEX_ATTRIBUTE_TANGENT |
                RAW_VERTEX_ATTRIBUTE_BINORMAL | RAW_VERTEX_ATTRIBUTE_UV0;
          }
          if (mat.textures[RAW_TEXTURE_USAGE_SPECULAR] != -1) {
            keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | RAW_VERTEX_ATTRIBUTE_UV0;
          }
          if (mat.textures[RAW_TEXTURE_USAGE_EMISSIVE] != -1) {
            keep |= RAW_VERTEX_ATTRIBUTE_UV1;
          }
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_POSITION) == 0) {
          vertex.position = defaultVertex.position;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_NORMAL) == 0) {
          vertex.normal = defaultVertex.normal;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_TANGENT) == 0) {
          vertex.tangent = defaultVertex.tangent;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_BINORMAL) == 0) {
          vertex.binormal = defaultVertex.binormal;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_COLOR) == 0) {
          vertex.color = defaultVertex.color;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_UV0) == 0) {
          vertex.uv0 = defaultVertex.uv0;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) {
          vertex.uv1 = defaultVertex.uv1;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) {
          vertex.jointIndices = defaultVertex.jointIndices;
        }
        if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) {
          vertex.jointWeights = defaultVertex.jointWeights;
        }
      }

      verts[j] = model->AddVertex(vertex);
      model->vertexAttributes |= vertex.Difference(defaultVertex);

      rawSurface.bounds.AddPoint(vertex.position);
    }

    model->AddTriangle(verts[0], verts[1], verts[2], materialIndex, surfaceIndex);
  }
}