vertex.flags always holds pre-encoded ClipFlags regardless of source format: ODOL: SaveOptimized() pre-computes ClipFlags (materials 200-203, fog, land, decal); ODOLLoader stores them directly in vertex.flags. MLOD: MLODLoader converts raw POINT_* flags (data3d.h) to ClipFlags, encodes materials into ClipUserMask, then stores in vertex.flags. Both paths are identical here — always cast vertex.flag
| 79 | } |
| 80 | |
| 81 | // Texture clamping (values match between FaceFlags and old special flags) |
| 82 | if (raw & 0x2000) |
| 83 | spec |= 0x2000; // NoClamp |
| 84 | if (raw & 0x4000) |
| 85 | spec |= 0x4000; // ClampU |
| 86 | if (raw & 0x8000) |
| 87 | spec |= 0x8000; // ClampV |
| 88 | |
| 89 | // Texture merging |
| 90 | if (raw & static_cast<uint32_t>(FaceFlags::DisableTexMerge)) |
| 91 | { |
| 92 | spec |= 0x1000000; |
| 93 | } |
| 94 | |
| 95 | return spec; |
| 96 | } |
| 97 | |
| 98 | // vertex.flags always holds pre-encoded ClipFlags regardless of source format: |
| 99 | // ODOL: SaveOptimized() pre-computes ClipFlags (materials 200-203, fog, land, decal); |
| 100 | // ODOLLoader stores them directly in vertex.flags. |
| 101 | // MLOD: MLODLoader converts raw POINT_* flags (data3d.h) to ClipFlags, encodes |
| 102 | // materials into ClipUserMask, then stores in vertex.flags. |
| 103 | // Both paths are identical here — always cast vertex.flags to ClipFlags directly. |
| 104 | LODShapeWithShadow* convertToLODShape(const Poseidon::Model::Model& model, bool reversed) |
| 105 | { |
| 106 | auto shape = new LODShapeWithShadow(); |
| 107 | |
| 108 | // Must match the lowered name used in ShapeBank::New |
| 109 | shape->_name = model.sourcePath.c_str(); |
| 110 | |
| 111 | int lodCount = static_cast<int>(model.lodLevels.size()); |
| 112 | shape->_nLods = static_cast<signed char>(lodCount); |
| 113 | |
| 114 | for (int i = 0; i < lodCount; ++i) |
| 115 | { |
| 116 | const LODLevel& lodLevel = model.lodLevels[i]; |
| 117 | const Mesh& mesh = lodLevel.mesh; |
| 118 | int vertexCount = static_cast<int>(mesh.vertices.size()); |
| 119 | int triangleCount = static_cast<int>(mesh.triangles.size()); |
| 120 | int quadCount = static_cast<int>(mesh.quads.size()); |
| 121 | bool isODOL = (model.sourceFormat == "ODOL"); |
| 122 | |
| 123 | shape->_resolutions[i] = lodLevel.resolution; |
| 124 | shape->_lods[i] = new Shape(); |
| 125 | shape->_lods[i]->Init(vertexCount); |
| 126 | |
| 127 | if (isODOL && !mesh.edges.mlodIndices.empty()) |
| 128 | { |
| 129 | int nPoints = static_cast<int>(mesh.edges.mlodIndices.size()); |
| 130 | int nVerts = static_cast<int>(mesh.edges.vertexIndices.size()); |
| 131 | shape->_lods[i]->_pointToVertex.Resize(nPoints); |
| 132 | for (int v = 0; v < nPoints; ++v) |
| 133 | shape->_lods[i]->_pointToVertex[v] = static_cast<VertexIndex>(mesh.edges.mlodIndices[v]); |
| 134 | shape->_lods[i]->_vertexToPoint.Resize(nVerts); |
| 135 | for (int v = 0; v < nVerts; ++v) |
| 136 | shape->_lods[i]->_vertexToPoint[v] = static_cast<VertexIndex>(mesh.edges.vertexIndices[v]); |
| 137 | } |
| 138 | else |
no test coverage detected