#include "lc_global.h" #include "lc_mesh.h" #include "lc_colors.h" #include "lc_texture.h" #include "lc_file.h" #include "lc_math.h" #include "lc_application.h" #include "lc_library.h" #define LC_MESH_FILE_ID LC_FOURCC('M', 'E', 'S', 'H') #define LC_MESH_FILE_VERSION 0x0123 lcMesh::lcMesh() { } lcMesh::~lcMesh() { free(mVertexData); free(mIndexData); for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { delete[] mLods[LodIdx].Sections; } } void lcMesh::Create(quint16(&NumSections)[LC_NUM_MESH_LODS], int VertexCount, int TexturedVertexCount, int ConditionalVertexCount, int IndexCount) { for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { if (NumSections[LodIdx]) mLods[LodIdx].Sections = new lcMeshSection[NumSections[LodIdx]]; mLods[LodIdx].NumSections = NumSections[LodIdx]; } mNumVertices = VertexCount; mNumTexturedVertices = TexturedVertexCount; mConditionalVertexCount = ConditionalVertexCount; mVertexDataSize = VertexCount * sizeof(lcVertex) + TexturedVertexCount * sizeof(lcVertexTextured) + ConditionalVertexCount * sizeof(lcVertexConditional); mVertexData = malloc(mVertexDataSize); if (VertexCount < 0x10000 && TexturedVertexCount < 0x10000) { mIndexType = GL_UNSIGNED_SHORT; mIndexDataSize = IndexCount * sizeof(GLushort); } else { mIndexType = GL_UNSIGNED_INT; mIndexDataSize = IndexCount * sizeof(GLuint); } mIndexData = mIndexDataSize ? malloc(mIndexDataSize) : nullptr; } void lcMesh::CreateBox() { quint16 NumSections[LC_NUM_MESH_LODS]; memset(NumSections, 0, sizeof(NumSections)); NumSections[LC_MESH_LOD_HIGH] = 2; Create(NumSections, 24, 0, 0, 36 + 24); lcVector3 Min(-10.0f, -10.0f, -24.0f); lcVector3 Max(10.0f, 10.0f, 4.0f); mRadius = lcLength(Max - Min) / 2.0f; mBoundingBox.Min = Min; mBoundingBox.Max = Max; mFlags |= lcMeshFlag::HasDefault | lcMeshFlag::HasLines; lcVertex* Verts = (lcVertex*)mVertexData; quint16* Indices = (quint16*)mIndexData; Verts[0].Position = lcVector3(Min[0], Min[1], Min[2]); Verts[0].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f)); Verts[1].Position = lcVector3(Min[0], Max[1], Min[2]); Verts[1].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f)); Verts[2].Position = lcVector3(Max[0], Max[1], Min[2]); Verts[2].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f)); Verts[3].Position = lcVector3(Max[0], Min[1], Min[2]); Verts[3].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f)); Verts[4].Position = lcVector3(Min[0], Min[1], Max[2]); Verts[4].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f)); Verts[5].Position = lcVector3(Min[0], Max[1], Max[2]); Verts[5].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f)); Verts[6].Position = lcVector3(Max[0], Max[1], Max[2]); Verts[6].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f)); Verts[7].Position = lcVector3(Max[0], Min[1], Max[2]); Verts[7].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f)); Verts[8].Position = lcVector3(Min[0], Min[1], Min[2]); Verts[8].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f)); Verts[9].Position = lcVector3(Min[0], Min[1], Max[2]); Verts[9].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f)); Verts[10].Position = lcVector3(Min[0], Max[1], Max[2]); Verts[10].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f)); Verts[11].Position = lcVector3(Min[0], Max[1], Min[2]); Verts[11].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f)); Verts[12].Position = lcVector3(Max[0], Min[1], Min[2]); Verts[12].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f)); Verts[13].Position = lcVector3(Max[0], Min[1], Max[2]); Verts[13].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f)); Verts[14].Position = lcVector3(Max[0], Max[1], Max[2]); Verts[14].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f)); Verts[15].Position = lcVector3(Max[0], Max[1], Min[2]); Verts[15].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f)); Verts[16].Position = lcVector3(Min[0], Min[1], Min[2]); Verts[16].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f)); Verts[17].Position = lcVector3(Min[0], Min[1], Max[2]); Verts[17].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f)); Verts[18].Position = lcVector3(Max[0], Min[1], Max[2]); Verts[18].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f)); Verts[19].Position = lcVector3(Max[0], Min[1], Min[2]); Verts[19].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f)); Verts[20].Position = lcVector3(Min[0], Max[1], Min[2]); Verts[20].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f)); Verts[21].Position = lcVector3(Min[0], Max[1], Max[2]); Verts[21].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f)); Verts[22].Position = lcVector3(Max[0], Max[1], Max[2]); Verts[22].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f)); Verts[23].Position = lcVector3(Max[0], Max[1], Min[2]); Verts[23].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f)); lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[0]; Section->ColorIndex = gDefaultColor; Section->DrawOffset = 0; Section->DrawCount = 36; Section->PrimitiveType = LC_MESH_TRIANGLES; Section->BoundingBox = mBoundingBox; Section->Radius = mRadius; *Indices++ = 0; *Indices++ = 1; *Indices++ = 2; *Indices++ = 0; *Indices++ = 2; *Indices++ = 3; *Indices++ = 7; *Indices++ = 6; *Indices++ = 5; *Indices++ = 7; *Indices++ = 5; *Indices++ = 4; *Indices++ = 8; *Indices++ = 9; *Indices++ = 10; *Indices++ = 8; *Indices++ = 10; *Indices++ = 11; *Indices++ = 15; *Indices++ = 14; *Indices++ = 13; *Indices++ = 15; *Indices++ = 13; *Indices++ = 12; *Indices++ = 16; *Indices++ = 17; *Indices++ = 18; *Indices++ = 16; *Indices++ = 18; *Indices++ = 19; *Indices++ = 23; *Indices++ = 22; *Indices++ = 21; *Indices++ = 23; *Indices++ = 21; *Indices++ = 20; Section = &mLods[LC_MESH_LOD_HIGH].Sections[1]; Section->ColorIndex = gEdgeColor; Section->DrawOffset = 36 * 2; Section->DrawCount = 24; Section->PrimitiveType = LC_MESH_LINES; Section->BoundingBox = mBoundingBox; Section->Radius = mRadius; *Indices++ = 0; *Indices++ = 1; *Indices++ = 1; *Indices++ = 2; *Indices++ = 2; *Indices++ = 3; *Indices++ = 3; *Indices++ = 0; *Indices++ = 4; *Indices++ = 5; *Indices++ = 5; *Indices++ = 6; *Indices++ = 6; *Indices++ = 7; *Indices++ = 7; *Indices++ = 4; *Indices++ = 0; *Indices++ = 4; *Indices++ = 1; *Indices++ = 5; *Indices++ = 2; *Indices++ = 6; *Indices++ = 3; *Indices++ = 7; } template bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDistance, lcVector3& HitPlane) { float Distance; lcVector3 IntersectionPlane; if (!lcBoundingBoxRayIntersectDistance(mBoundingBox.Min, mBoundingBox.Max, Start, End, &Distance, nullptr, &IntersectionPlane) || (Distance >= MinDistance)) return false; bool Hit = false; lcVector3 Intersection; for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++) { const lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx]; if (Section->PrimitiveType == LC_MESH_TRIANGLES) { const lcVertex* Verts = GetVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const lcVector3& v1 = Verts[Indices[Idx]].Position; const lcVector3& v2 = Verts[Indices[Idx + 1]].Position; const lcVector3& v3 = Verts[Indices[Idx + 2]].Position; if (lcLineTriangleMinIntersection(v1, v2, v3, Start, End, &MinDistance, &Intersection)) Hit = true; } } else if (Section->PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) { const lcVertexTextured* Verts = GetTexturedVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const lcVector3& v1 = Verts[Indices[Idx]].Position; const lcVector3& v2 = Verts[Indices[Idx + 1]].Position; const lcVector3& v3 = Verts[Indices[Idx + 2]].Position; if (lcLineTriangleMinIntersection(v1, v2, v3, Start, End, &MinDistance, &Intersection)) Hit = true; } } } if (Hit) HitPlane = IntersectionPlane; return Hit; } bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDist, lcVector3& HitPlane) { if (mIndexType == GL_UNSIGNED_SHORT) return MinIntersectDist(Start, End, MinDist, HitPlane); else return MinIntersectDist(Start, End, MinDist, HitPlane); } template bool lcMesh::IntersectsPlanes(const lcVector4 (&Planes)[6]) { for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++) { const lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx]; if (Section->PrimitiveType == LC_MESH_TRIANGLES) { const lcVertex* Verts = GetVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) if (lcTriangleIntersectsPlanes(Verts[Indices[Idx]].Position, Verts[Indices[Idx+1]].Position, Verts[Indices[Idx+2]].Position, Planes)) return true; } else if (Section->PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) { const lcVertexTextured* Verts = GetTexturedVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) if (lcTriangleIntersectsPlanes(Verts[Indices[Idx]].Position, Verts[Indices[Idx+1]].Position, Verts[Indices[Idx+2]].Position, Planes)) return true; } } return false; } bool lcMesh::IntersectsPlanes(const lcVector4 (&Planes)[6]) { if (mIndexType == GL_UNSIGNED_SHORT) return IntersectsPlanes(Planes); else return IntersectsPlanes(Planes); } template void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char** ColorTable) { char Line[1024]; int NumSections = 0; for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++) { const lcMeshSection* const Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx]; if (Section->PrimitiveType == LC_MESH_TRIANGLES || Section->PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) NumSections++; } if (NumSections > 1) snprintf(Line, sizeof(Line), "#declare lc_%s = union {\n", MeshName); else snprintf(Line, sizeof(Line), "#declare lc_%s = mesh {\n", MeshName); File.WriteLine(Line); for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++) { lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx]; if (Section->PrimitiveType == LC_MESH_TRIANGLES) { const lcVertex* Verts = GetVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); if (NumSections > 1) File.WriteLine(" mesh {\n"); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const lcVector3 v1 = Verts[Indices[Idx]].Position / 25.0f; const lcVector3 v2 = Verts[Indices[Idx + 1]].Position / 25.0f; const lcVector3 v3 = Verts[Indices[Idx + 2]].Position / 25.0f; const lcVector3 n1 = lcUnpackNormal(Verts[Indices[Idx]].Normal); const lcVector3 n2 = lcUnpackNormal(Verts[Indices[Idx + 1]].Normal); const lcVector3 n3 = lcUnpackNormal(Verts[Indices[Idx + 2]].Normal); snprintf(Line, sizeof(Line), " smooth_triangle { <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g> }\n", -v1.y, -v1.x, v1.z, -n1.y, -n1.x, n1.z, -v2.y, -v2.x, v2.z, -n2.y, -n2.x, n2.z, -v3.y, -v3.x, v3.z, -n3.y, -n3.x, n3.z); File.WriteLine(Line); } } else if (Section->PrimitiveType == LC_MESH_TEXTURED_TRIANGLES) { const lcVertexTextured* Verts = GetTexturedVertexData(); const IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); if (NumSections > 1) File.WriteLine(" mesh {\n"); for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const lcVector3 v1 = Verts[Indices[Idx]].Position / 25.0f; const lcVector3 v2 = Verts[Indices[Idx + 1]].Position / 25.0f; const lcVector3 v3 = Verts[Indices[Idx + 2]].Position / 25.0f; const lcVector3 n1 = lcUnpackNormal(Verts[Indices[Idx]].Normal); const lcVector3 n2 = lcUnpackNormal(Verts[Indices[Idx + 1]].Normal); const lcVector3 n3 = lcUnpackNormal(Verts[Indices[Idx + 2]].Normal); snprintf(Line, sizeof(Line), " smooth_triangle { <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g> }\n", -v1.y, -v1.x, v1.z, -n1.y, -n1.x, n1.z, -v2.y, -v2.x, v2.z, -n2.y, -n2.x, n2.z, -v3.y, -v3.x, v3.z, -n3.y, -n3.x, n3.z); File.WriteLine(Line); } } else continue; if (Section->ColorIndex != gDefaultColor) { snprintf(Line, sizeof(Line), " material { texture { %s normal { bumps 0.1 scale 2 } } }", ColorTable[Section->ColorIndex]); File.WriteLine(Line); } if (NumSections > 1) File.WriteLine(" }\n"); } File.WriteLine("}\n\n"); } void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char** ColorTable) { if (mIndexType == GL_UNSIGNED_SHORT) ExportPOVRay(File, MeshName, ColorTable); else ExportPOVRay(File, MeshName, ColorTable); } template void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset) { char Line[1024]; for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++) { lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx]; if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES) continue; IndexType* Indices = (IndexType*)mIndexData + Section->DrawOffset / sizeof(IndexType); if (Section->ColorIndex == gDefaultColor) snprintf(Line, sizeof(Line), "usemtl %s\n", gColorList[DefaultColorIndex].SafeName); else snprintf(Line, sizeof(Line), "usemtl %s\n", gColorList[Section->ColorIndex].SafeName); File.WriteLine(Line); if (Section->PrimitiveType == LC_MESH_TRIANGLES) { for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const long int idx1 = Indices[Idx + 0] + VertexOffset; const long int idx2 = Indices[Idx + 1] + VertexOffset; const long int idx3 = Indices[Idx + 2] + VertexOffset; if (idx1 != idx2 && idx1 != idx3 && idx2 != idx3) { snprintf(Line, sizeof(Line), "f %ld//%ld %ld//%ld %ld//%ld\n", idx1, idx1, idx2, idx2, idx3, idx3); File.WriteLine(Line); } } } else { for (int Idx = 0; Idx < Section->DrawCount; Idx += 3) { const long int idx1 = Indices[Idx + 0] + VertexOffset; const long int idx2 = Indices[Idx + 1] + VertexOffset; const long int idx3 = Indices[Idx + 2] + VertexOffset; if (idx1 != idx2 && idx1 != idx3 && idx2 != idx3) { snprintf(Line, sizeof(Line), "f %ld//%ld %ld//%ld %ld//%ld\n", idx1 + mNumVertices, idx1 + mNumVertices, idx2 + mNumVertices, idx2 + mNumVertices, idx3 + mNumVertices, idx3 + mNumVertices); File.WriteLine(Line); } } } } File.WriteLine("\n"); } void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset) { if (mIndexType == GL_UNSIGNED_SHORT) ExportWavefrontIndices(File, DefaultColorIndex, VertexOffset); else ExportWavefrontIndices(File, DefaultColorIndex, VertexOffset); } bool lcMesh::FileLoad(lcMemFile& File) { quint32 FileId, FileVersion, Flags; if (File.ReadU32(&FileId, 1) != 1 || FileId != LC_MESH_FILE_ID || File.ReadU32(&FileVersion, 1) != 1 || FileVersion != LC_MESH_FILE_VERSION || File.ReadU32(&Flags, 1) != 1) return false; mFlags = static_cast(Flags); if (File.ReadFloats(mBoundingBox.Min.GetFloats(), 3) != 3 || File.ReadFloats(mBoundingBox.Max.GetFloats(), 3) != 3 || File.ReadFloats(&mRadius, 1) != 1) return false; quint32 VertexCount, TexturedVertexCount, ConditionalVertexCount, IndexCount; quint16 NumLods, NumSections[LC_NUM_MESH_LODS]; if (File.ReadU32(&VertexCount, 1) != 1 || File.ReadU32(&TexturedVertexCount, 1) != 1 || File.ReadU32(&ConditionalVertexCount, 1) != 1 || File.ReadU32(&IndexCount, 1) != 1) return false; if (File.ReadU16(&NumLods, 1) != 1 || NumLods != LC_NUM_MESH_LODS || File.ReadU16(NumSections, LC_NUM_MESH_LODS) != LC_NUM_MESH_LODS) return false; const quint64 VertexDataSize = static_cast(VertexCount) * sizeof(lcVertex) + static_cast(TexturedVertexCount) * sizeof(lcVertexTextured) + static_cast(ConditionalVertexCount) * sizeof(lcVertexConditional); const quint64 IndexElementSize = VertexCount < 0x10000 && TexturedVertexCount < 0x10000 ? sizeof(GLushort) : sizeof(GLuint); const quint64 IndexDataSize = static_cast(IndexCount) * IndexElementSize; constexpr quint64 MinimumSectionSize = 3 * sizeof(quint32) + sizeof(quint16) + 7 * sizeof(float) + sizeof(quint16); const quint64 MinimumSectionsSize = static_cast(NumSections[LC_MESH_LOD_HIGH] + NumSections[LC_MESH_LOD_LOW]) * MinimumSectionSize; const long Position = File.GetPosition(); if (VertexDataSize > std::numeric_limits::max() || IndexDataSize > std::numeric_limits::max() || Position < 0 || static_cast(Position) > File.GetLength() || MinimumSectionsSize + VertexDataSize + IndexDataSize > File.GetLength() - static_cast(Position)) return false; Create(NumSections, VertexCount, TexturedVertexCount, ConditionalVertexCount, IndexCount); if ((mVertexDataSize && !mVertexData) || (mIndexDataSize && !mIndexData)) return false; for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { for (int SectionIdx = 0; SectionIdx < mLods[LodIdx].NumSections; SectionIdx++) { lcMeshSection& Section = mLods[LodIdx].Sections[SectionIdx]; quint32 ColorCode, DrawOffset, DrawCount; quint16 PrimtiveType, Length; if (File.ReadU32(&ColorCode, 1) != 1 || File.ReadU32(&DrawOffset, 1) != 1 || File.ReadU32(&DrawCount, 1) != 1 || File.ReadU16(&PrimtiveType, 1) != 1) return false; Section.ColorIndex = static_cast(ColorCode); Section.DrawOffset = DrawOffset; Section.DrawCount = DrawCount; Section.PrimitiveType = (lcMeshPrimitiveType)PrimtiveType; if (File.ReadFloats(Section.BoundingBox.Min.GetFloats(), 3) != 3 || File.ReadFloats(Section.BoundingBox.Max.GetFloats(), 3) != 3 || File.ReadFloats(&Section.Radius, 1) != 1) return false; if (File.ReadU16(&Length, 1) != 1) return false; if (Length) { if (Length >= LC_TEXTURE_NAME_LEN) return false; char FileName[LC_TEXTURE_NAME_LEN]; if (File.ReadBuffer(FileName, Length) != Length) return false; FileName[Length] = 0; Section.TextureName = QString::fromLatin1(FileName); } } } if (File.ReadBuffer(mVertexData, mVertexDataSize) != static_cast(mVertexDataSize)) return false; if (mIndexType == GL_UNSIGNED_SHORT) { if (File.ReadU16((quint16*)mIndexData, mIndexDataSize / 2) != static_cast(mIndexDataSize / 2)) return false; } else { if (File.ReadU32((quint32*)mIndexData, mIndexDataSize / 4) != static_cast(mIndexDataSize / 4)) return false; } return true; } void lcMesh::FinalizeSections() { mFlags &= ~(lcMeshFlag::HasDefault | lcMeshFlag::HasSolid | lcMeshFlag::HasTranslucent); for (lcMeshLod& Lod : mLods) { for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++) { lcMeshSection& Section = Lod.Sections[SectionIdx]; Section.ColorIndex = lcGetColorIndex(static_cast(Section.ColorIndex)); if (Section.PrimitiveType != LC_MESH_TRIANGLES && Section.PrimitiveType != LC_MESH_TEXTURED_TRIANGLES) continue; if (Section.ColorIndex == gDefaultColor) mFlags |= lcMeshFlag::HasDefault; else if (lcIsColorTranslucent(Section.ColorIndex)) mFlags |= lcMeshFlag::HasTranslucent; else mFlags |= lcMeshFlag::HasSolid; } if (Lod.NumSections > 1) std::sort(Lod.Sections, Lod.Sections + Lod.NumSections, [](const lcMeshSection& Left, const lcMeshSection& Right) { if (Left.PrimitiveType != Right.PrimitiveType) { const lcMeshPrimitiveType PrimitiveOrder[] = { LC_MESH_TRIANGLES, LC_MESH_TEXTURED_TRIANGLES, LC_MESH_LINES, LC_MESH_CONDITIONAL_LINES }; return std::find(std::begin(PrimitiveOrder), std::end(PrimitiveOrder), Left.PrimitiveType) < std::find(std::begin(PrimitiveOrder), std::end(PrimitiveOrder), Right.PrimitiveType); } const bool LeftTranslucent = lcIsColorTranslucent(Left.ColorIndex); const bool RightTranslucent = lcIsColorTranslucent(Right.ColorIndex); if (LeftTranslucent != RightTranslucent) return !LeftTranslucent; return Left.ColorIndex > Right.ColorIndex; }); } } bool lcMesh::FileSave(lcMemFile& File) { File.WriteU32(LC_MESH_FILE_ID); File.WriteU32(LC_MESH_FILE_VERSION); File.WriteU32(mFlags); File.WriteVector3(mBoundingBox.Min); File.WriteVector3(mBoundingBox.Max); File.WriteFloat(mRadius); File.WriteU32(mNumVertices); File.WriteU32(mNumTexturedVertices); File.WriteU32(mConditionalVertexCount); File.WriteU32(mIndexDataSize / (mIndexType == GL_UNSIGNED_SHORT ? 2 : 4)); File.WriteU16(LC_NUM_MESH_LODS); for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) File.WriteU16(mLods[LodIdx].NumSections); for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++) { for (int SectionIdx = 0; SectionIdx < mLods[LodIdx].NumSections; SectionIdx++) { const lcMeshSection& Section = mLods[LodIdx].Sections[SectionIdx]; File.WriteU32(lcGetColorCode(Section.ColorIndex)); File.WriteU32(Section.DrawOffset); File.WriteU32(Section.DrawCount); File.WriteU16(Section.PrimitiveType); File.WriteVector3(Section.BoundingBox.Min); File.WriteVector3(Section.BoundingBox.Max); File.WriteFloat(Section.Radius); const QByteArray TextureName = Section.TextureName.isEmpty() && Section.Texture ? QByteArray(Section.Texture->mName) : Section.TextureName.toLatin1(); if (!TextureName.isEmpty()) { const quint16 Length = (quint16)TextureName.size(); File.WriteU16(Length); File.WriteBuffer(TextureName.constData(), Length); } else File.WriteU16(0); } } File.WriteBuffer(mVertexData, mNumVertices * sizeof(lcVertex) + mNumTexturedVertices * sizeof(lcVertexTextured) + mConditionalVertexCount * sizeof(lcVertexConditional)); if (mIndexType == GL_UNSIGNED_SHORT) File.WriteU16((quint16*)mIndexData, mIndexDataSize / 2); else File.WriteU32((quint32*)mIndexData, mIndexDataSize / 4); return true; } int lcMesh::GetLodIndex(float Distance) const { if (lcGetPiecesLibrary()->GetStudStyle() != lcStudStyle::Plain) // todo: support low lod studs return LC_MESH_LOD_HIGH; if (mLods[LC_MESH_LOD_LOW].NumSections && (Distance > mRadius)) return LC_MESH_LOD_LOW; else return LC_MESH_LOD_HIGH; }