#include "lc_global.h" #include "lc_meshloader.h" #include "lc_file.h" #include "lc_colors.h" #include "lc_library.h" #include "lc_application.h" #include "lc_texture.h" static void lcCheckTexCoordsWrap(const lcVector4& Plane2, const lcVector3 (&Positions)[3], lcVector2 (&TexCoords)[3]) { lcVector2& TexCoords1 = TexCoords[0]; lcVector2& TexCoords2 = TexCoords[1]; lcVector2& TexCoords3 = TexCoords[2]; const float u12 = fabsf(TexCoords1.x - TexCoords2.x); const float u13 = fabsf(TexCoords1.x - TexCoords3.x); const float u23 = fabsf(TexCoords2.x - TexCoords3.x); if (u12 < 0.5f && u13 < 0.5f && u23 < 0.5f) return; const float Dot1 = fabsf(lcDot(Plane2, lcVector4(Positions[0], 1.0f))); const float Dot2 = fabsf(lcDot(Plane2, lcVector4(Positions[1], 1.0f))); const float Dot3 = fabsf(lcDot(Plane2, lcVector4(Positions[2], 1.0f))); if (Dot1 > Dot2) { if (Dot1 > Dot3) { if (u12 > 0.5f) TexCoords2.x += TexCoords2.x < 0.5f ? 1.0f : -1.0f; if (u13 > 0.5f) TexCoords3.x += TexCoords3.x < 0.5f ? 1.0f : -1.0f; } else { if (u13 > 0.5f) TexCoords1.x += TexCoords1.x < 0.5f ? 1.0f : -1.0f; if (u23 > 0.5f) TexCoords2.x += TexCoords2.x < 0.5f ? 1.0f : -1.0f; } } else { if (Dot2 > Dot3) { if (u12 > 0.5f) TexCoords1.x += TexCoords1.x < 0.5f ? 1.0f : -1.0f; if (u23 > 0.5f) TexCoords3.x += TexCoords3.x < 0.5f ? 1.0f : -1.0f; } else { if (u13 > 0.5f) TexCoords1.x += TexCoords1.x < 0.5f ? 1.0f : -1.0f; if (u23 > 0.5f) TexCoords2.x += TexCoords2.x < 0.5f ? 1.0f : -1.0f; } } } static void lcCheckTexCoordsPole(const lcVector4& FrontPlane, const lcVector4& Plane2, const lcVector3(&Positions)[3], lcVector2(&TexCoords)[3]) { int PoleIndex; int EdgeIndex1, EdgeIndex2; if (fabsf(lcDot(lcVector4(Positions[0], 1.0f), FrontPlane)) < 0.01f && fabsf(lcDot(lcVector4(Positions[0], 1.0f), Plane2)) < 0.01f) { PoleIndex = 0; EdgeIndex1 = 1; EdgeIndex2 = 2; } else if (fabsf(lcDot(lcVector4(Positions[1], 1.0f), FrontPlane)) < 0.01f && fabsf(lcDot(lcVector4(Positions[1], 1.0f), Plane2)) < 0.01f) { PoleIndex = 1; EdgeIndex1 = 0; EdgeIndex2 = 2; } else if (fabsf(lcDot(lcVector4(Positions[2], 1.0f), FrontPlane)) < 0.01f && fabsf(lcDot(lcVector4(Positions[2], 1.0f), Plane2)) < 0.01f) { PoleIndex = 2; EdgeIndex1 = 0; EdgeIndex2 = 1; } else return; const lcVector3 OppositeEdge = Positions[EdgeIndex2] - Positions[EdgeIndex1]; const lcVector3 SideEdge = Positions[PoleIndex] - Positions[EdgeIndex1]; const float OppositeLength = lcLength(OppositeEdge); const float Projection = lcDot(OppositeEdge, SideEdge) / (OppositeLength * OppositeLength); TexCoords[PoleIndex].x = TexCoords[EdgeIndex1].x + (TexCoords[EdgeIndex2].x - TexCoords[EdgeIndex1].x) * Projection; } static void lcResequenceQuad(int* Indices, int a, int b, int c, int d) { Indices[0] = a; Indices[1] = b; Indices[2] = c; Indices[3] = d; } static void lcTestQuad(int (&TriangleIndices)[2][3], const lcVector3 (&Vertices)[4]) { const lcVector3 v01 = Vertices[1] - Vertices[0]; const lcVector3 v02 = Vertices[2] - Vertices[0]; const lcVector3 v03 = Vertices[3] - Vertices[0]; const lcVector3 cp1 = lcCross(v01, v02); const lcVector3 cp2 = lcCross(v02, v03); if (lcDot(cp1, cp2) > 0.0f) return; const lcVector3 v12 = Vertices[2] - Vertices[1]; const lcVector3 v13 = Vertices[3] - Vertices[1]; const lcVector3 v23 = Vertices[3] - Vertices[2]; int QuadIndices[4] = { 0, 1, 2, 3 }; if (lcDot(lcCross(v12, v01), lcCross(v01, v13)) > 0.0f) { if (-lcDot(lcCross(v02, v12), lcCross(v12, v23)) > 0.0f) lcResequenceQuad(QuadIndices, 1, 2, 3, 0); else lcResequenceQuad(QuadIndices, 0, 3, 1, 2); } else { if (-lcDot(lcCross(v02, v12), lcCross(v12, v23)) > 0.0f) lcResequenceQuad(QuadIndices, 0, 1, 3, 2); else lcResequenceQuad(QuadIndices, 1, 2, 3, 0); } TriangleIndices[0][0] = QuadIndices[0]; TriangleIndices[0][1] = QuadIndices[1]; TriangleIndices[0][2] = QuadIndices[2]; TriangleIndices[1][0] = QuadIndices[2]; TriangleIndices[1][1] = QuadIndices[3]; TriangleIndices[1][2] = QuadIndices[0]; } constexpr float lcDistanceEpsilon = 0.01f; // Maximum value for 50591.dat static bool lcCompareVertices(const lcVector3& Position1, const lcVector3& Position2) { return fabsf(Position1.x - Position2.x) < lcDistanceEpsilon && fabsf(Position1.y - Position2.y) < lcDistanceEpsilon && fabsf(Position1.z - Position2.z) < lcDistanceEpsilon; } lcMeshLoaderSection* lcMeshLoaderTypeData::AddSection(lcMeshPrimitiveType PrimitiveType, lcMeshLoaderMaterial* Material) { for (const std::unique_ptr& Section : mSections) if (Section->mMaterial == Material && Section->mPrimitiveType == PrimitiveType) return Section.get(); mSections.emplace_back(new lcMeshLoaderSection(PrimitiveType, Material)); return mSections.back().get(); } quint32 lcMeshLoaderTypeData::AddVertex(const lcVector3& Position, bool Optimize) { if (Optimize) { for (int VertexIdx = static_cast(mVertices.size()) - 1; VertexIdx >= 0; VertexIdx--) { const lcMeshLoaderVertex& Vertex = mVertices[VertexIdx]; if (lcCompareVertices(Position, Vertex.Position)) return VertexIdx; } } lcMeshLoaderVertex& Vertex = mVertices.emplace_back(); Vertex.Position = Position; Vertex.Normal = lcVector3(0.0f, 0.0f, 0.0f); Vertex.NormalWeight = 0.0f; return static_cast(mVertices.size()) - 1; } quint32 lcMeshLoaderTypeData::AddVertex(const lcVector3& Position, const lcVector3& Normal, float NormalWeight, bool Optimize) { if (Optimize) { for (int VertexIdx = static_cast(mVertices.size()) - 1; VertexIdx >= 0; VertexIdx--) { lcMeshLoaderVertex& Vertex = mVertices[VertexIdx]; if (lcCompareVertices(Position, Vertex.Position)) { if (Vertex.NormalWeight == 0.0f) { Vertex.Normal = Normal; Vertex.NormalWeight = NormalWeight; return VertexIdx; } else if (lcDot(Normal, Vertex.Normal) > 0.71f) { Vertex.Normal = lcNormalize(Vertex.Normal * Vertex.NormalWeight + Normal * NormalWeight); Vertex.NormalWeight += NormalWeight; return VertexIdx; } } } } lcMeshLoaderVertex& Vertex = mVertices.emplace_back(); Vertex.Position = Position; Vertex.Normal = Normal; Vertex.NormalWeight = 1.0f; return static_cast(mVertices.size()) - 1; } quint32 lcMeshLoaderTypeData::AddConditionalVertex(const lcVector3(&Position)[4]) { lcMeshLoaderConditionalVertex& Vertex = mConditionalVertices.emplace_back(); Vertex.Position[0] = Position[0]; Vertex.Position[1] = Position[1]; Vertex.Position[2] = Position[2]; Vertex.Position[3] = Position[3]; return static_cast(mConditionalVertices.size()) - 1; } void lcMeshLoaderTypeData::ProcessLine(int LineType, lcMeshLoaderMaterial* Material, bool WindingCCW, lcVector3 (&Vertices)[4], bool Optimize) { constexpr lcMeshPrimitiveType PrimitiveTypes[4] = { LC_MESH_LINES, LC_MESH_TRIANGLES, LC_MESH_TRIANGLES, LC_MESH_CONDITIONAL_LINES }; lcMeshPrimitiveType PrimitiveType = PrimitiveTypes[LineType - 2]; if (Material->Type != lcMeshLoaderMaterialType::Solid && PrimitiveType == LC_MESH_TRIANGLES) PrimitiveType = LC_MESH_TEXTURED_TRIANGLES; lcMeshLoaderSection* Section = AddSection(PrimitiveType, Material); if (LineType == 3 || LineType == 4) { int TriangleIndices[2][3] = { { 0, 1, 2 }, { 2, 3, 0 } }; if (LineType == 4) lcTestQuad(TriangleIndices, Vertices); lcVector3 Normal = lcNormalize(lcCross(Vertices[1] - Vertices[0], Vertices[2] - Vertices[0])); if (!WindingCCW) Normal = -Normal; for (int TriangleIndex = 0; TriangleIndex < LineType - 2; TriangleIndex++) { const lcVector3& Vertex1 = Vertices[TriangleIndices[TriangleIndex][0]]; const lcVector3& Vertex2 = Vertices[TriangleIndices[TriangleIndex][1]]; const lcVector3& Vertex3 = Vertices[TriangleIndices[TriangleIndex][2]]; const lcVector3 Edge1 = lcNormalize(Vertex2 - Vertex1); const lcVector3 Edge2 = lcNormalize(Vertex3 - Vertex1); const lcVector3 Edge3 = lcNormalize(Vertex3 - Vertex2); const float Angle1 = acosf(lcDot(Edge1, Edge2)); const float Angle2 = acosf(lcDot(-Edge1, Edge3)); const float Angle3 = LC_PI - Angle1 - Angle2; int Indices[3]; Indices[0] = AddVertex(Vertex1, Normal, Angle1, Optimize); Indices[1] = AddVertex(Vertex2, Normal, Angle2, Optimize); Indices[2] = AddVertex(Vertex3, Normal, Angle3, Optimize); if (Indices[0] != Indices[1] && Indices[0] != Indices[2] && Indices[1] != Indices[2]) { if (WindingCCW) { Section->mIndices.emplace_back(Indices[0]); Section->mIndices.emplace_back(Indices[1]); Section->mIndices.emplace_back(Indices[2]); } else { Section->mIndices.emplace_back(Indices[2]); Section->mIndices.emplace_back(Indices[1]); Section->mIndices.emplace_back(Indices[0]); } } } } else if (LineType == 2) { int Indices[2]; Indices[0] = AddVertex(Vertices[0], Optimize); Indices[1] = AddVertex(Vertices[1], Optimize); if (Indices[0] != Indices[1]) { Section->mIndices.emplace_back(Indices[0]); Section->mIndices.emplace_back(Indices[1]); } } else if (LineType == 5) { int Indices[2]; Indices[0] = AddConditionalVertex(Vertices); Section->mIndices.emplace_back(Indices[0]); std::swap(Vertices[0], Vertices[1]); Indices[1] = AddConditionalVertex(Vertices); Section->mIndices.emplace_back(Indices[1]); } } void lcMeshLoaderTypeData::AddMeshData(const lcMeshLoaderTypeData& Data, const lcMatrix44& Transform, quint32 CurrentColorCode, bool InvertWinding, bool InvertNormals, lcMeshLoaderTextureMap* TextureMap) { const std::vector& DataVertices = Data.mVertices; std::vector IndexRemap; const lcMatrix33 NormalTransform = lcMatrix33Transpose(lcMatrix33(lcMatrix44Inverse(Transform))); IndexRemap.reserve(DataVertices.size()); mVertices.reserve(mVertices.size() + DataVertices.size()); for (const lcMeshLoaderVertex& DataVertex : DataVertices) { const lcVector3 Position = lcMul31(DataVertex.Position, Transform); int Index; if (DataVertex.NormalWeight == 0.0f) Index = AddVertex(Position, true); else { lcVector3 Normal = lcNormalize(lcMul(DataVertex.Normal, NormalTransform)); if (InvertNormals) Normal = -Normal; Index = AddVertex(Position, Normal, DataVertex.NormalWeight, true); } IndexRemap.emplace_back(Index); } mConditionalVertices.reserve(mConditionalVertices.size() + Data.mConditionalVertices.size()); std::vector ConditionalRemap(Data.mConditionalVertices.size()); for (const lcMeshLoaderConditionalVertex& DataVertex : Data.mConditionalVertices) { lcVector3 Position[4]; Position[0] = lcMul31(DataVertex.Position[0], Transform); Position[1] = lcMul31(DataVertex.Position[1], Transform); Position[2] = lcMul31(DataVertex.Position[2], Transform); Position[3] = lcMul31(DataVertex.Position[3], Transform); const int Index = AddConditionalVertex(Position); ConditionalRemap.emplace_back(Index); } for (const std::unique_ptr& SrcSection : Data.mSections) { const quint32 ColorCode = SrcSection->mMaterial->Color == 16 ? CurrentColorCode : SrcSection->mMaterial->Color; lcMeshPrimitiveType PrimitiveType = SrcSection->mPrimitiveType; lcMeshLoaderSection* DstSection; if (SrcSection->mMaterial->Type != lcMeshLoaderMaterialType::Solid) { lcMeshLoaderTextureMap DstTextureMap = *TextureMap; for (lcVector3& Point : DstTextureMap.Points) Point = lcMul31(Point, Transform); DstSection = AddSection(PrimitiveType, mMeshData->GetTexturedMaterial(ColorCode, DstTextureMap)); } else if (TextureMap && SrcSection->mPrimitiveType == LC_MESH_TRIANGLES) { PrimitiveType = LC_MESH_TEXTURED_TRIANGLES; DstSection = AddSection(PrimitiveType, mMeshData->GetTexturedMaterial(ColorCode, *TextureMap)); } else { DstSection = AddSection(PrimitiveType, mMeshData->GetMaterial(ColorCode)); } DstSection->mIndices.reserve(DstSection->mIndices.size() + SrcSection->mIndices.size()); if (PrimitiveType == LC_MESH_CONDITIONAL_LINES) { for (const quint32 Index : SrcSection->mIndices) DstSection->mIndices.emplace_back(ConditionalRemap[Index]); } else if (!InvertWinding || (PrimitiveType == LC_MESH_LINES)) { for (const quint32 Index : SrcSection->mIndices) DstSection->mIndices.emplace_back(IndexRemap[Index]); } else { for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx += 3) { DstSection->mIndices.emplace_back(IndexRemap[SrcSection->mIndices[IndexIdx + 2]]); DstSection->mIndices.emplace_back(IndexRemap[SrcSection->mIndices[IndexIdx + 1]]); DstSection->mIndices.emplace_back(IndexRemap[SrcSection->mIndices[IndexIdx + 0]]); } } } } void lcMeshLoaderTypeData::AddMeshDataNoDuplicateCheck(const lcMeshLoaderTypeData& Data, const lcMatrix44& Transform, quint32 CurrentColorCode, bool InvertWinding, bool InvertNormals, lcMeshLoaderTextureMap* TextureMap) { const std::vector& DataVertices = Data.mVertices; quint32 BaseIndex; const lcMatrix33 NormalTransform = lcMatrix33Transpose(lcMatrix33(lcMatrix44Inverse(Transform))); BaseIndex = static_cast(mVertices.size()); mVertices.reserve(mVertices.size() + DataVertices.size()); for (const lcMeshLoaderVertex& SrcVertex : DataVertices) { lcMeshLoaderVertex& DstVertex = mVertices.emplace_back(); DstVertex.Position = lcMul31(SrcVertex.Position, Transform); DstVertex.Normal = lcNormalize(lcMul(SrcVertex.Normal, NormalTransform)); if (InvertNormals) DstVertex.Normal = -DstVertex.Normal; DstVertex.NormalWeight = SrcVertex.NormalWeight; } mConditionalVertices.reserve(mConditionalVertices.size() + Data.mConditionalVertices.size()); const quint32 BaseConditional = static_cast(mConditionalVertices.size()); for (const lcMeshLoaderConditionalVertex& DataVertex : Data.mConditionalVertices) { lcMeshLoaderConditionalVertex& Vertex = mConditionalVertices.emplace_back(); Vertex.Position[0] = lcMul31(DataVertex.Position[0], Transform); Vertex.Position[1] = lcMul31(DataVertex.Position[1], Transform); Vertex.Position[2] = lcMul31(DataVertex.Position[2], Transform); Vertex.Position[3] = lcMul31(DataVertex.Position[3], Transform); } for (const std::unique_ptr& SrcSection : Data.mSections) { const quint32 ColorCode = SrcSection->mMaterial->Color == 16 ? CurrentColorCode : SrcSection->mMaterial->Color; lcMeshPrimitiveType PrimitiveType = SrcSection->mPrimitiveType; lcMeshLoaderSection* DstSection; if (SrcSection->mMaterial->Type != lcMeshLoaderMaterialType::Solid) { lcMeshLoaderTextureMap DstTextureMap = *TextureMap; for (lcVector3& Point : DstTextureMap.Points) Point = lcMul31(Point, Transform); DstSection = AddSection(PrimitiveType, mMeshData->GetTexturedMaterial(ColorCode, DstTextureMap)); } else if (TextureMap && SrcSection->mPrimitiveType == LC_MESH_TRIANGLES) { PrimitiveType = LC_MESH_TEXTURED_TRIANGLES; DstSection = AddSection(PrimitiveType, mMeshData->GetTexturedMaterial(ColorCode, *TextureMap)); } else { DstSection = AddSection(PrimitiveType, mMeshData->GetMaterial(ColorCode)); } DstSection->mIndices.reserve(DstSection->mIndices.size() + SrcSection->mIndices.size()); if (PrimitiveType == LC_MESH_CONDITIONAL_LINES) { for (const quint32 Index : SrcSection->mIndices) DstSection->mIndices.emplace_back(BaseConditional + Index); } else if (!InvertWinding || (PrimitiveType == LC_MESH_LINES)) { for (const quint32 Index : SrcSection->mIndices) DstSection->mIndices.emplace_back(BaseIndex + Index); } else { for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx += 3) { DstSection->mIndices.emplace_back(BaseIndex + SrcSection->mIndices[IndexIdx + 2]); DstSection->mIndices.emplace_back(BaseIndex + SrcSection->mIndices[IndexIdx + 1]); DstSection->mIndices.emplace_back(BaseIndex + SrcSection->mIndices[IndexIdx + 0]); } } } } void lcLibraryMeshData::AddVertices(lcMeshDataType MeshDataType, size_t VertexCount, int* BaseVertex, lcMeshLoaderVertex** VertexBuffer) { std::vector& Vertices = mData[MeshDataType].mVertices; int CurrentSize = static_cast(Vertices.size()); Vertices.resize(CurrentSize + VertexCount); *BaseVertex = CurrentSize; *VertexBuffer = &Vertices[CurrentSize]; } void lcLibraryMeshData::AddIndices(lcMeshDataType MeshDataType, lcMeshPrimitiveType PrimitiveType, quint32 ColorCode, size_t IndexCount, quint32** IndexBuffer) { lcMeshLoaderSection* Section = mData[MeshDataType].AddSection(PrimitiveType, GetMaterial(ColorCode)); std::vector& Indices = Section->mIndices; const size_t CurrentSize = Indices.size(); Indices.resize(CurrentSize + IndexCount); *IndexBuffer = &Indices[CurrentSize]; } void lcLibraryMeshData::AddMeshData(const lcLibraryMeshData& Data, const lcMatrix44& Transform, quint32 CurrentColorCode, bool InvertWinding, bool InvertNormals, lcMeshLoaderTextureMap* TextureMap, lcMeshDataType OverrideDestIndex) { for (int MeshDataIdx = 0; MeshDataIdx < LC_NUM_MESHDATA_TYPES; MeshDataIdx++) { const int DestIndex = OverrideDestIndex == LC_MESHDATA_SHARED ? MeshDataIdx : OverrideDestIndex; mData[DestIndex].AddMeshData(Data.mData[MeshDataIdx], Transform, CurrentColorCode, InvertWinding, InvertNormals, TextureMap); } mHasTextures |= (Data.mHasTextures || TextureMap); } void lcLibraryMeshData::AddMeshDataNoDuplicateCheck(const lcLibraryMeshData& Data, const lcMatrix44& Transform, quint32 CurrentColorCode, bool InvertWinding, bool InvertNormals, lcMeshLoaderTextureMap* TextureMap, lcMeshDataType OverrideDestIndex) { for (int MeshDataIdx = 0; MeshDataIdx < LC_NUM_MESHDATA_TYPES; MeshDataIdx++) { const int DestIndex = OverrideDestIndex == LC_MESHDATA_SHARED ? MeshDataIdx : OverrideDestIndex; mData[DestIndex].AddMeshDataNoDuplicateCheck(Data.mData[MeshDataIdx], Transform, CurrentColorCode, InvertWinding, InvertNormals, TextureMap); } mHasTextures |= (Data.mHasTextures || TextureMap); } lcMeshLoaderMaterial* lcLibraryMeshData::GetMaterial(quint32 ColorCode) { for (const std::unique_ptr& Material : mMaterials) if (Material->Type == lcMeshLoaderMaterialType::Solid && Material->Color == ColorCode) return Material.get(); lcMeshLoaderMaterial* Material = new lcMeshLoaderMaterial(); mMaterials.emplace_back(Material); Material->Type = lcMeshLoaderMaterialType::Solid; Material->Color = ColorCode; return Material; } lcMeshLoaderMaterial* lcLibraryMeshData::GetTexturedMaterial(quint32 ColorCode, const lcMeshLoaderTextureMap& TextureMap) { for (const std::unique_ptr& Material : mMaterials) { if (Material->Type != TextureMap.Type || Material->Color != ColorCode) continue; if (strcmp(Material->Name, TextureMap.Name)) continue; if (Material->Points[0] != TextureMap.Points[0] || Material->Points[1] != TextureMap.Points[1] || Material->Points[2] != TextureMap.Points[2]) continue; if (Material->Type == lcMeshLoaderMaterialType::Cylindrical) { if (Material->Angles[0] != TextureMap.Angles[0]) continue; } else if (Material->Type == lcMeshLoaderMaterialType::Spherical) { if (Material->Angles[0] != TextureMap.Angles[0] || Material->Angles[1] != TextureMap.Angles[1]) continue; } return Material.get(); } lcMeshLoaderMaterial* Material = new lcMeshLoaderMaterial(); mMaterials.emplace_back(Material); Material->Type = TextureMap.Type; Material->Color = ColorCode; Material->Points[0] = TextureMap.Points[0]; Material->Points[1] = TextureMap.Points[1]; Material->Points[2] = TextureMap.Points[2]; Material->Angles[0] = TextureMap.Angles[0]; Material->Angles[1] = TextureMap.Angles[1]; strcpy(Material->Name, TextureMap.Name); return Material; } static bool lcMeshLoaderFinalSectionCompare(const lcMeshLoaderFinalSection& a, const lcMeshLoaderFinalSection& b) { if (a.PrimitiveType != b.PrimitiveType) { const lcMeshPrimitiveType PrimitiveOrder[LC_MESH_NUM_PRIMITIVE_TYPES] = { LC_MESH_TRIANGLES, LC_MESH_TEXTURED_TRIANGLES, LC_MESH_LINES, LC_MESH_CONDITIONAL_LINES }; for (int PrimitiveType = 0; PrimitiveType < LC_MESH_NUM_PRIMITIVE_TYPES; PrimitiveType++) { const lcMeshPrimitiveType Primitive = PrimitiveOrder[PrimitiveType]; if (a.PrimitiveType == Primitive) return true; if (b.PrimitiveType == Primitive) return false; } } const bool TranslucentA = lcIsColorTranslucent(a.Color); const bool TranslucentB = lcIsColorTranslucent(b.Color); if (TranslucentA != TranslucentB) return !TranslucentA; return a.Color > b.Color; } quint32 lcLibraryMeshData::AddTexturedVertex(const lcVector3& Position, const lcVector3& Normal, const lcVector2& TexCoords) { for (int VertexIndex = static_cast(mTexturedVertices.size()) - 1; VertexIndex >= 0; VertexIndex--) { const lcMeshLoaderTexturedVertex& Vertex = mTexturedVertices[VertexIndex]; if (Vertex.Position == Position && Vertex.Normal == Normal && Vertex.TexCoords == TexCoords) return VertexIndex; } lcMeshLoaderTexturedVertex& Vertex = mTexturedVertices.emplace_back(); Vertex.Position = Position; Vertex.Normal = Normal; Vertex.TexCoords = TexCoords; return static_cast(mTexturedVertices.size()) - 1; } void lcLibraryMeshData::GeneratePlanarTexcoords(lcMeshLoaderSection* Section, const lcMeshLoaderTypeData& Data) { const lcMeshLoaderMaterial* Material = Section->mMaterial; lcVector4 Planes[2]; for (int EdgeIdx = 0; EdgeIdx < 2; EdgeIdx++) { lcVector3 Normal = Material->Points[EdgeIdx + 1] - Material->Points[0]; const float Length = lcLength(Normal); Normal /= Length; Planes[EdgeIdx].x = Normal.x / Length; Planes[EdgeIdx].y = Normal.y / Length; Planes[EdgeIdx].z = Normal.z / Length; Planes[EdgeIdx].w = -lcDot(Normal, Material->Points[0]) / Length; } for (quint32& Index : Section->mIndices) { const lcMeshLoaderVertex& SrcVertex = Data.mVertices[Index]; const lcVector2 TexCoords(lcDot3(SrcVertex.Position, Planes[0]) + Planes[0].w, lcDot3(SrcVertex.Position, Planes[1]) + Planes[1].w); Index = AddTexturedVertex(SrcVertex.Position, SrcVertex.Normal, TexCoords); } } void lcLibraryMeshData::GenerateCylindricalTexcoords(lcMeshLoaderSection* Section, const lcMeshLoaderTypeData& Data) { const lcMeshLoaderMaterial* Material = Section->mMaterial; const lcVector3 Up = Material->Points[0] - Material->Points[1]; const float UpLength = lcLength(Up); const lcVector3 Front = lcNormalize(Material->Points[2] - Material->Points[1]); const lcVector3 Plane1Normal = Up / UpLength; const lcVector3 Plane2Normal = lcNormalize(lcCross(Front, Up)); const lcVector4 FrontPlane = lcVector4(Front, -lcDot(Front, Material->Points[1])); const lcVector4 Plane1 = lcVector4(Plane1Normal, -lcDot(Plane1Normal, Material->Points[1])); const lcVector4 Plane2 = lcVector4(Plane2Normal, -lcDot(Plane2Normal, Material->Points[1])); const float Angle = 360.0f / Material->Angles[0]; for (size_t TriangleIndex = 0; TriangleIndex < Section->mIndices.size(); TriangleIndex += 3) { const lcVector3 Positions[3] = { Data.mVertices[Section->mIndices[TriangleIndex + 0]].Position, Data.mVertices[Section->mIndices[TriangleIndex + 1]].Position, Data.mVertices[Section->mIndices[TriangleIndex + 2]].Position }; lcVector2 TexCoords[3]; for (int CornerIndex = 0; CornerIndex < 3; CornerIndex++) { const float DotPlane1 = lcDot(lcVector4(Positions[CornerIndex], 1.0f), Plane1); const lcVector3 PointInPlane1 = Positions[CornerIndex] - lcVector3(Plane1) * DotPlane1; const float DotFrontPlane = lcDot(lcVector4(PointInPlane1, 1.0f), FrontPlane); const float DotPlane2 = lcDot(lcVector4(PointInPlane1, 1.0f), Plane2); const float Angle1 = atan2f(DotPlane2, DotFrontPlane) / LC_PI * Angle; TexCoords[CornerIndex].x = lcClamp(0.5f + 0.5f * Angle1, 0.0f, 1.0f); TexCoords[CornerIndex].y = DotPlane1 / UpLength; } lcCheckTexCoordsWrap(Plane2, Positions, TexCoords); for (int CornerIndex = 0; CornerIndex < 3; CornerIndex++) Section->mIndices[TriangleIndex + CornerIndex] = AddTexturedVertex(Positions[CornerIndex], Data.mVertices[Section->mIndices[TriangleIndex + CornerIndex]].Normal, TexCoords[CornerIndex]); } } void lcLibraryMeshData::GenerateSphericalTexcoords(lcMeshLoaderSection* Section, const lcMeshLoaderTypeData& Data) { const lcMeshLoaderMaterial* Material = Section->mMaterial; const lcVector3 Front = lcNormalize(Material->Points[1] - Material->Points[0]); const lcVector3 Plane1Normal = lcNormalize(lcCross(Front, Material->Points[2] - Material->Points[0])); const lcVector3 Plane2Normal = lcNormalize(lcCross(Plane1Normal, Front)); const lcVector4 FrontPlane = lcVector4(Front, -lcDot(Front, Material->Points[0])); const lcVector3 Center = Material->Points[0]; const lcVector4 Plane1 = lcVector4(Plane1Normal, -lcDot(Plane1Normal, Material->Points[0])); const lcVector4 Plane2 = lcVector4(Plane2Normal, -lcDot(Plane2Normal, Material->Points[0])); const float Angle1 = 360.0f / Material->Angles[0]; const float Angle2 = 180.0f / Material->Angles[1]; for (size_t TriangleIndex = 0; TriangleIndex < Section->mIndices.size(); TriangleIndex += 3) { const lcVector3 Positions[3] = { Data.mVertices[Section->mIndices[TriangleIndex + 0]].Position, Data.mVertices[Section->mIndices[TriangleIndex + 1]].Position, Data.mVertices[Section->mIndices[TriangleIndex + 2]].Position }; lcVector2 TexCoords[3]; for (int CornerIndex = 0; CornerIndex < 3; CornerIndex++) { const lcVector3 VertexDir = Positions[CornerIndex] - Center; const float DotPlane1 = lcDot(lcVector4(Positions[CornerIndex], 1.0f), Plane1); const lcVector3 PointInPlane1 = Positions[CornerIndex] - lcVector3(Plane1) * DotPlane1; const float DotFrontPlane = lcDot(lcVector4(PointInPlane1, 1.0f), FrontPlane); const float DotPlane2 = lcDot(lcVector4(PointInPlane1, 1.0f), Plane2); const float AngleX = atan2f(DotPlane2, DotFrontPlane) / LC_PI * Angle1; TexCoords[CornerIndex].x = 0.5f + 0.5f * AngleX; const float AngleY = asinf(DotPlane1 / lcLength(VertexDir)) / LC_PI * Angle2; TexCoords[CornerIndex].y = 0.5f - AngleY; } lcCheckTexCoordsWrap(Plane2, Positions, TexCoords); lcCheckTexCoordsPole(FrontPlane, Plane2, Positions, TexCoords); for (int CornerIndex = 0; CornerIndex < 3; CornerIndex++) Section->mIndices[TriangleIndex + CornerIndex] = AddTexturedVertex(Positions[CornerIndex], Data.mVertices[Section->mIndices[TriangleIndex + CornerIndex]].Normal, TexCoords[CornerIndex]); } } void lcLibraryMeshData::GenerateTexturedVertices() { for (lcMeshLoaderTypeData& Data : mData) { for (const std::unique_ptr& Section : Data.mSections) { switch (Section->mMaterial->Type) { case lcMeshLoaderMaterialType::Solid: break; case lcMeshLoaderMaterialType::Planar: GeneratePlanarTexcoords(Section.get(), Data); break; case lcMeshLoaderMaterialType::Cylindrical: GenerateCylindricalTexcoords(Section.get(), Data); break; case lcMeshLoaderMaterialType::Spherical: GenerateSphericalTexcoords(Section.get(), Data); break; } } } } lcMesh* lcLibraryMeshData::CreateMesh() { lcMesh* Mesh = new lcMesh(); int BaseVertices[LC_NUM_MESHDATA_TYPES]; int BaseConditionalVertices[LC_NUM_MESHDATA_TYPES]; int NumVertices = 0; int ConditionalVertexCount = 0; for (const std::unique_ptr& Material : mMaterials) Material->Color = lcGetColorIndex(Material->Color); for (int MeshDataIdx = 0; MeshDataIdx < LC_NUM_MESHDATA_TYPES; MeshDataIdx++) { BaseVertices[MeshDataIdx] = NumVertices; NumVertices += static_cast(mData[MeshDataIdx].mVertices.size()); BaseConditionalVertices[MeshDataIdx] = ConditionalVertexCount; ConditionalVertexCount += static_cast(mData[MeshDataIdx].mConditionalVertices.size()); } if (mHasTextures) GenerateTexturedVertices(); quint16 NumSections[LC_NUM_MESH_LODS]; int NumIndices = 0; std::vector FinalSections[LC_NUM_MESH_LODS]; for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { auto AddFinalSection = [](lcMeshLoaderSection* Section, std::vector& FinalSections) { for (const lcMeshLoaderFinalSection& FinalSection : FinalSections) if (FinalSection.PrimitiveType == Section->mPrimitiveType && FinalSection.Color == Section->mMaterial->Color && !strcmp(FinalSection.Name, Section->mMaterial->Name)) return; lcMeshLoaderFinalSection& FinalSection = FinalSections.emplace_back(); FinalSection.PrimitiveType = Section->mPrimitiveType; FinalSection.Color = Section->mMaterial->Color; strcpy(FinalSection.Name, Section->mMaterial->Name); }; for (const std::unique_ptr& Section : mData[LC_MESHDATA_SHARED].mSections) { NumIndices += static_cast(Section->mIndices.size()); AddFinalSection(Section.get(), FinalSections[LodIdx]); } for (const std::unique_ptr& Section : mData[LodIdx].mSections) { NumIndices += static_cast(Section->mIndices.size()); AddFinalSection(Section.get(), FinalSections[LodIdx]); } NumSections[LodIdx] = static_cast(FinalSections[LodIdx].size()); std::sort(FinalSections[LodIdx].begin(), FinalSections[LodIdx].end(), lcMeshLoaderFinalSectionCompare); } Mesh->Create(NumSections, NumVertices, static_cast(mTexturedVertices.size()), ConditionalVertexCount, NumIndices); lcVertex* DstVerts = (lcVertex*)Mesh->mVertexData; for (const lcMeshLoaderTypeData& Data : mData) { for (const lcMeshLoaderVertex& SrcVertex : Data.mVertices) { lcVertex& DstVertex = *DstVerts++; DstVertex.Position = lcVector3LDrawToLeoCAD(SrcVertex.Position); DstVertex.Normal = lcPackNormal(lcVector3LDrawToLeoCAD(SrcVertex.Normal)); } } lcVertexTextured* DstTexturedVerts = (lcVertexTextured*)DstVerts; if (mHasTextures) { for (const lcMeshLoaderTexturedVertex& SrcVertex : mTexturedVertices) { lcVertexTextured& DstVertex = *DstTexturedVerts++; DstVertex.Position = lcVector3LDrawToLeoCAD(SrcVertex.Position); DstVertex.Normal = lcPackNormal(lcVector3LDrawToLeoCAD(SrcVertex.Normal)); DstVertex.TexCoord = SrcVertex.TexCoords; } } lcVertexConditional* DstConditionalVerts = (lcVertexConditional*)DstTexturedVerts; for (const lcMeshLoaderTypeData& Data : mData) { for (const lcMeshLoaderConditionalVertex& SrcVertex : Data.mConditionalVertices) { lcVertexConditional& DstVertex = *DstConditionalVerts++; DstVertex.Position1 = lcVector3LDrawToLeoCAD(SrcVertex.Position[0]); DstVertex.Position2 = lcVector3LDrawToLeoCAD(SrcVertex.Position[1]); DstVertex.Position3 = lcVector3LDrawToLeoCAD(SrcVertex.Position[2]); DstVertex.Position4 = lcVector3LDrawToLeoCAD(SrcVertex.Position[3]); } } if (Mesh->mIndexType == GL_UNSIGNED_SHORT) WriteSections(Mesh, FinalSections, BaseVertices, BaseConditionalVertices); else WriteSections(Mesh, FinalSections, BaseVertices, BaseConditionalVertices); if (mHasStyleStud) Mesh->mFlags |= lcMeshFlag::HasStyleStud; UpdateMeshBoundingBox(Mesh); return Mesh; } template void lcLibraryMeshData::WriteSections(lcMesh* Mesh, const std::vector (&FinalSections)[LC_NUM_MESH_LODS], int(&BaseVertices)[LC_NUM_MESHDATA_TYPES], int(&BaseConditionalVertices)[LC_NUM_MESHDATA_TYPES]) { int NumIndices = 0; for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { for (size_t SectionIndex = 0; SectionIndex < FinalSections[LodIdx].size(); SectionIndex++) { const lcMeshLoaderFinalSection& FinalSection = FinalSections[LodIdx][SectionIndex]; lcMeshSection& DstSection = Mesh->mLods[LodIdx].Sections[SectionIndex]; DstSection.ColorIndex = FinalSection.Color; DstSection.PrimitiveType = FinalSection.PrimitiveType; DstSection.NumIndices = 0; if (!FinalSection.Name[0]) DstSection.Texture = nullptr; else { if (mMeshLoader) DstSection.Texture = lcGetPiecesLibrary()->FindTexture(FinalSection.Name, mMeshLoader->mCurrentProject, mMeshLoader->mSearchProjectFolder); else DstSection.Texture = lcGetPiecesLibrary()->FindTexture(FinalSection.Name, nullptr, false); if (DstSection.Texture) DstSection.Texture->AddRef(); } DstSection.IndexOffset = NumIndices * sizeof(IndexType); IndexType* Index = (IndexType*)Mesh->mIndexData + NumIndices; const auto AddSection = [&DstSection, &Index, &BaseVertices, &BaseConditionalVertices](lcMeshLoaderSection* SrcSection, lcMeshDataType SrcDataType) { switch (DstSection.PrimitiveType) { case LC_MESH_LINES: case LC_MESH_TRIANGLES: { const IndexType BaseVertex = BaseVertices[SrcDataType]; for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx++) *Index++ = BaseVertex + SrcSection->mIndices[IndexIdx]; } break; case LC_MESH_CONDITIONAL_LINES: { const IndexType BaseVertex = BaseConditionalVertices[SrcDataType]; for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx++) *Index++ = BaseVertex + SrcSection->mIndices[IndexIdx]; } break; case LC_MESH_TEXTURED_TRIANGLES: { for (size_t IndexIdx = 0; IndexIdx < SrcSection->mIndices.size(); IndexIdx++) *Index++ = SrcSection->mIndices[IndexIdx]; } break; case LC_MESH_NUM_PRIMITIVE_TYPES: break; } DstSection.NumIndices += static_cast(SrcSection->mIndices.size()); }; for (const std::unique_ptr& Section : mData[LC_MESHDATA_SHARED].mSections) if (FinalSection.PrimitiveType == Section->mPrimitiveType && FinalSection.Color == Section->mMaterial->Color && !strcmp(FinalSection.Name, Section->mMaterial->Name)) AddSection(Section.get(), LC_MESHDATA_SHARED); const lcMeshDataType MeshDataType = (LodIdx == LC_MESH_LOD_LOW) ? LC_MESHDATA_LOW : LC_MESHDATA_HIGH; for (const std::unique_ptr& Section : mData[MeshDataType].mSections) if (FinalSection.PrimitiveType == Section->mPrimitiveType && FinalSection.Color == Section->mMaterial->Color && !strcmp(FinalSection.Name, Section->mMaterial->Name)) AddSection(Section.get(), MeshDataType); if (DstSection.PrimitiveType == LC_MESH_TRIANGLES || DstSection.PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) { if (DstSection.ColorIndex == gDefaultColor) Mesh->mFlags |= lcMeshFlag::HasDefault; else { if (lcIsColorTranslucent(DstSection.ColorIndex)) Mesh->mFlags |= lcMeshFlag::HasTranslucent; else Mesh->mFlags |= lcMeshFlag::HasSolid; } } else Mesh->mFlags |= lcMeshFlag::HasLines; if (DstSection.PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) Mesh->mFlags |= lcMeshFlag::HasTexture; NumIndices += DstSection.NumIndices; } } } void lcLibraryMeshData::UpdateMeshBoundingBox(lcMesh* Mesh) { lcVector3 MeshMin(FLT_MAX, FLT_MAX, FLT_MAX), MeshMax(-FLT_MAX, -FLT_MAX, -FLT_MAX); bool UpdatedBoundingBox = false; for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { lcMeshLod& Lod = Mesh->mLods[LodIdx]; for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++) { lcMeshSection& Section = Lod.Sections[SectionIdx]; lcVector3 SectionMin(FLT_MAX, FLT_MAX, FLT_MAX), SectionMax(-FLT_MAX, -FLT_MAX, -FLT_MAX); if (Mesh->mIndexType == GL_UNSIGNED_SHORT) UpdateMeshSectionBoundingBox(Mesh, Section, SectionMin, SectionMax); else UpdateMeshSectionBoundingBox(Mesh, Section, SectionMin, SectionMax); Section.BoundingBox.Max = SectionMax; Section.BoundingBox.Min = SectionMin; Section.Radius = lcLength((SectionMax - SectionMin) / 2.0f); if (Section.PrimitiveType == LC_MESH_TRIANGLES || Section.PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) { UpdatedBoundingBox = true; MeshMin = lcMin(SectionMin, MeshMin); MeshMax = lcMax(SectionMax, MeshMax); } } } if (!UpdatedBoundingBox) MeshMin = MeshMax = lcVector3(0.0f, 0.0f, 0.0f); Mesh->mBoundingBox.Max = MeshMax; Mesh->mBoundingBox.Min = MeshMin; Mesh->mRadius = lcLength((MeshMax - MeshMin) / 2.0f); } template void lcLibraryMeshData::UpdateMeshSectionBoundingBox(const lcMesh* Mesh, const lcMeshSection& Section, lcVector3& SectionMin, lcVector3& SectionMax) { const IndexType* IndexBuffer = reinterpret_cast(static_cast(Mesh->mIndexData) + Section.IndexOffset); switch (Section.PrimitiveType) { case LC_MESH_LINES: case LC_MESH_TRIANGLES: { const lcVertex* VertexBuffer = Mesh->GetVertexData(); for (int Index = 0; Index < Section.NumIndices; Index++) { const lcVector3& Position = VertexBuffer[IndexBuffer[Index]].Position; SectionMin = lcMin(SectionMin, Position); SectionMax = lcMax(SectionMax, Position); } } break; case LC_MESH_CONDITIONAL_LINES: { const lcVertexConditional* VertexBuffer = Mesh->GetConditionalVertexData(); for (int Index = 0; Index < Section.NumIndices; Index++) { const lcVector3& Position = VertexBuffer[IndexBuffer[Index]].Position1; SectionMin = lcMin(SectionMin, Position); SectionMax = lcMax(SectionMax, Position); } } break; case LC_MESH_TEXTURED_TRIANGLES: { const lcVertexTextured* VertexBuffer = Mesh->GetTexturedVertexData(); for (int Index = 0; Index < Section.NumIndices; Index++) { const lcVector3& Position = VertexBuffer[IndexBuffer[Index]].Position; SectionMin = lcMin(SectionMin, Position); SectionMax = lcMax(SectionMax, Position); } } break; case LC_MESH_NUM_PRIMITIVE_TYPES: break; } } lcMeshLoader::lcMeshLoader(lcLibraryMeshData& MeshData, bool Optimize, Project* CurrentProject, bool SearchProjectFolder) : mCurrentProject(CurrentProject), mSearchProjectFolder(SearchProjectFolder), mMeshData(MeshData), mOptimize(Optimize) { MeshData.SetMeshLoader(this); } bool lcMeshLoader::LoadMesh(lcFile& File, lcMeshDataType MeshDataType) { return ReadMeshData(File, lcMatrix44Identity(), 16, false, MeshDataType); } bool lcMeshLoader::ReadMeshData(lcFile& File, const lcMatrix44& CurrentTransform, quint32 CurrentColorCode, bool InvertWinding, lcMeshDataType MeshDataType) { char Buffer[1024]; char* Line; bool InvertNext = false; bool WindingCCW = !InvertWinding; lcPiecesLibrary* Library = lcGetPiecesLibrary(); while (File.ReadLine(Buffer, sizeof(Buffer))) { if (Library->ShouldCancelLoading()) return false; quint32 ColorCode, ColorCodeHex; bool LastToken = false; int LineType; Line = Buffer; if (sscanf(Line, "%d", &LineType) != 1) continue; if (LineType == 0) { char* Token = Line; while (*Token && *Token <= 32) Token++; Token++; while (*Token && *Token <= 32) Token++; char* End = Token; while (*End && *End > 32) End++; LastToken = (*End == 0); *End = 0; if (!strcmp(Token, "!TEXMAP")) { Token += 8; while (*Token && *Token <= 32) Token++; End = Token; while (*End && *End > 32) End++; *End = 0; bool Start = false; bool Next = false; if (!strcmp(Token, "START")) { Token += 6; Start = true; } else if (!strcmp(Token, "NEXT")) { Token += 5; Next = true; } if (Start || Next) { while (*Token && *Token <= 32) Token++; End = Token; while (*End && *End > 32) End++; *End = 0; auto CleanTextureName = [](char* FileName) { char* Ch; for (Ch = FileName; *Ch; Ch++) { if (*Ch >= 'a' && *Ch <= 'z') *Ch = *Ch + 'A' - 'a'; else if (*Ch == '\\') *Ch = '/'; } if (Ch - FileName > 4) { Ch -= 4; if (!memcmp(Ch, ".PNG", 4)) *Ch = 0; } }; if (!strcmp(Token, "PLANAR")) { Token += 7; mTextureStack.emplace_back(); lcMeshLoaderTextureMap& Map = mTextureStack.back(); Map.Type = lcMeshLoaderMaterialType::Planar; lcVector3 (&Points)[3] = Map.Points; sscanf(Token, "%f %f %f %f %f %f %f %f %f %s", &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z, Map.Name); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); CleanTextureName(Map.Name); } else if (!strcmp(Token, "CYLINDRICAL")) { Token += 12; mTextureStack.emplace_back(); lcMeshLoaderTextureMap& Map = mTextureStack.back(); Map.Type = lcMeshLoaderMaterialType::Cylindrical; lcVector3 (&Points)[3] = Map.Points; float& Angle = Map.Angles[0]; sscanf(Token, "%f %f %f %f %f %f %f %f %f %f %s", &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z, &Angle, Map.Name); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); CleanTextureName(Map.Name); } else if (!strcmp(Token, "SPHERICAL")) { Token += 10; mTextureStack.emplace_back(); lcMeshLoaderTextureMap& Map = mTextureStack.back(); Map.Type = lcMeshLoaderMaterialType::Spherical; lcVector3(&Points)[3] = Map.Points; float& Angle1 = Map.Angles[0]; float& Angle2 = Map.Angles[1]; sscanf(Token, "%f %f %f %f %f %f %f %f %f %f %f %s", &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z, &Angle1, &Angle2, Map.Name); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); CleanTextureName(Map.Name); } } else if (!strcmp(Token, "FALLBACK")) { if (!mTextureStack.empty()) mTextureStack.back().Fallback = true; } else if (!strcmp(Token, "END")) { if (!mTextureStack.empty()) mTextureStack.pop_back(); } continue; } else if (!strcmp(Token, "BFC")) { while (!LastToken) { Token = End + 1; while (*Token && *Token <= 32) Token++; End = Token; while (*End && *End > 32) End++; LastToken = (*End == 0); *End = 0; if (!strcmp(Token, "INVERTNEXT")) InvertNext = true; else if (!strcmp(Token, "CCW")) WindingCCW = !InvertWinding; else if (!strcmp(Token, "CW")) WindingCCW = InvertWinding; } } else if (!strcmp(Token, "!:")) { Token += 3; Line = Token; if (mTextureStack.empty()) continue; } else continue; } if (sscanf(Line, "%d %d", &LineType, &ColorCode) != 2) continue; if (LineType < 1 || LineType > 5) continue; if (ColorCode == 0) { sscanf(Line, "%d %i", &LineType, &ColorCodeHex); if (ColorCode != ColorCodeHex) ColorCode = ColorCodeHex | LC_COLOR_DIRECT; } if (ColorCode == 16) ColorCode = CurrentColorCode; lcMeshLoaderTextureMap* TextureMap = nullptr; if (!mTextureStack.empty()) { TextureMap = &mTextureStack.back(); // TODO: think about a way to handle the texture fallback // if (TextureMap->Texture) { if (TextureMap->Fallback) continue; } // else // { // if (!TextureMap->Fallback) // continue; // // TextureMap = nullptr; // } } int Dummy; lcVector3 Points[4]; switch (LineType) { case 1: { char OriginalFileName[LC_MAXPATH]; float fm[12]; sscanf(Line, "%d %i %f %f %f %f %f %f %f %f %f %f %f %f %s", &LineType, &Dummy, &fm[0], &fm[1], &fm[2], &fm[3], &fm[4], &fm[5], &fm[6], &fm[7], &fm[8], &fm[9], &fm[10], &fm[11], OriginalFileName); char FileName[LC_MAXPATH]; strcpy(FileName, OriginalFileName); char* Ch; for (Ch = FileName; *Ch; Ch++) { if (*Ch >= 'a' && *Ch <= 'z') *Ch = *Ch + 'A' - 'a'; else if (*Ch == '\\') *Ch = '/'; } lcLibraryPrimitive* Primitive = !TextureMap ? Library->FindPrimitive(FileName) : nullptr; lcMatrix44 IncludeTransform(lcVector4(fm[3], fm[6], fm[9], 0.0f), lcVector4(fm[4], fm[7], fm[10], 0.0f), lcVector4(fm[5], fm[8], fm[11], 0.0f), lcVector4(fm[0], fm[1], fm[2], 1.0f)); IncludeTransform = lcMul(IncludeTransform, CurrentTransform); bool Mirror = IncludeTransform.Determinant() < 0.0f; const auto FileCallback = [this, &IncludeTransform, &ColorCode, &Mirror, &InvertNext, &MeshDataType](lcFile& File) { ReadMeshData(File, IncludeTransform, ColorCode, Mirror ^ InvertNext, MeshDataType); }; if (Primitive) { if (Primitive->mState != lcPrimitiveState::Loaded && !Library->LoadPrimitive(Primitive)) break; if (Primitive->mStud) mMeshData.AddMeshDataNoDuplicateCheck(Primitive->mMeshData, IncludeTransform, ColorCode, Mirror ^ InvertNext, InvertNext, TextureMap, MeshDataType); else if (!Primitive->mSubFile) { if (mOptimize) mMeshData.AddMeshData(Primitive->mMeshData, IncludeTransform, ColorCode, Mirror ^ InvertNext, InvertNext, TextureMap, MeshDataType); else mMeshData.AddMeshDataNoDuplicateCheck(Primitive->mMeshData, IncludeTransform, ColorCode, Mirror ^ InvertNext, InvertNext, TextureMap, MeshDataType); } else Library->GetPrimitiveFile(Primitive, FileCallback); mMeshData.mHasStyleStud |= Primitive->mStudStyle | Primitive->mMeshData.mHasStyleStud; } else Library->GetPieceFile(FileName, FileCallback); } break; case 2: sscanf(Line, "%d %i %f %f %f %f %f %f", &LineType, &Dummy, &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetMaterial(ColorCode), WindingCCW, Points, mOptimize); break; case 3: sscanf(Line, "%d %i %f %f %f %f %f %f %f %f %f", &LineType, &Dummy, &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); if (!TextureMap) mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetMaterial(ColorCode), WindingCCW, Points, mOptimize); else { mMeshData.mHasTextures = true; mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetTexturedMaterial(ColorCode, *TextureMap), WindingCCW, Points, mOptimize); if (TextureMap->Next) mTextureStack.pop_back(); } break; case 4: sscanf(Line, "%d %i %f %f %f %f %f %f %f %f %f %f %f %f", &LineType, &Dummy, &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z, &Points[3].x, &Points[3].y, &Points[3].z); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); Points[3] = lcMul31(Points[3], CurrentTransform); if (!TextureMap) mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetMaterial(ColorCode), WindingCCW, Points, mOptimize); else { mMeshData.mHasTextures = true; mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetTexturedMaterial(ColorCode, *TextureMap), WindingCCW, Points, mOptimize); if (TextureMap->Next) mTextureStack.pop_back(); } break; case 5: sscanf(Line, "%d %i %f %f %f %f %f %f %f %f %f %f %f %f", &LineType, &Dummy, &Points[0].x, &Points[0].y, &Points[0].z, &Points[1].x, &Points[1].y, &Points[1].z, &Points[2].x, &Points[2].y, &Points[2].z, &Points[3].x, &Points[3].y, &Points[3].z); Points[0] = lcMul31(Points[0], CurrentTransform); Points[1] = lcMul31(Points[1], CurrentTransform); Points[2] = lcMul31(Points[2], CurrentTransform); Points[3] = lcMul31(Points[3], CurrentTransform); mMeshData.mData[MeshDataType].ProcessLine(LineType, mMeshData.GetMaterial(ColorCode), WindingCCW, Points, mOptimize); break; } InvertNext = false; } return true; }