Files
leocad/common/lc_mesh.cpp
T
Leonardo Zide 98c67f3705 Align texture acquisition and scoped ownership
Texture lookup previously acquired references implicitly, and mesh sections and
loader requests paired raw texture pointers with manual release paths. Separate
borrowed lookup from scoped acquisition and explicit load requests/waits to match
the piece data ownership API.

Store lcTextureRef in mesh sections and loader requests, retain references on
section copies, and reset worker-shared request handles on the UI thread before
completion, cancellation, or shutdown. Keep reference primitives internal and use
scoped acquisitions for merge validation. Remove redundant section initialization
and synth-notification resets, and document the resulting ownership contracts.

Validation: macOS Debug build, diff whitespace checks, and 30 offscreen lifecycle
checks covering reference copies/moves, texture dependencies, cancellation,
shutdown, merge conflicts, and existing piece ownership paths. Successful GPU
upload/rendering is not covered by the offscreen checks.
2026-10-19 10:38:51 -07:00

642 lines
22 KiB
C++

#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<typename IndexType>
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<GLushort>(Start, End, MinDist, HitPlane);
else
return MinIntersectDist<GLuint>(Start, End, MinDist, HitPlane);
}
template<typename IndexType>
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<GLushort>(Planes);
else
return IntersectsPlanes<GLuint>(Planes);
}
template<typename IndexType>
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<GLushort>(File, MeshName, ColorTable);
else
ExportPOVRay<GLuint>(File, MeshName, ColorTable);
}
template<typename IndexType>
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<GLushort>(File, DefaultColorIndex, VertexOffset);
else
ExportWavefrontIndices<GLuint>(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<lcMeshFlags>(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<quint64>(VertexCount) * sizeof(lcVertex) + static_cast<quint64>(TexturedVertexCount) * sizeof(lcVertexTextured) + static_cast<quint64>(ConditionalVertexCount) * sizeof(lcVertexConditional);
const quint64 IndexElementSize = VertexCount < 0x10000 && TexturedVertexCount < 0x10000 ? sizeof(GLushort) : sizeof(GLuint);
const quint64 IndexDataSize = static_cast<quint64>(IndexCount) * IndexElementSize;
constexpr quint64 MinimumSectionSize = 3 * sizeof(quint32) + sizeof(quint16) + 7 * sizeof(float) + sizeof(quint16);
const quint64 MinimumSectionsSize = static_cast<quint64>(NumSections[LC_MESH_LOD_HIGH] + NumSections[LC_MESH_LOD_LOW]) * MinimumSectionSize;
const long Position = File.GetPosition();
if (VertexDataSize > std::numeric_limits<uint>::max() || IndexDataSize > std::numeric_limits<uint>::max() || Position < 0 || static_cast<size_t>(Position) > File.GetLength() ||
MinimumSectionsSize + VertexDataSize + IndexDataSize > File.GetLength() - static_cast<size_t>(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<int>(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<size_t>(mVertexDataSize))
return false;
if (mIndexType == GL_UNSIGNED_SHORT)
{
if (File.ReadU16((quint16*)mIndexData, mIndexDataSize / 2) != static_cast<size_t>(mIndexDataSize / 2))
return false;
}
else
{
if (File.ReadU32((quint32*)mIndexData, mIndexDataSize / 4) != static_cast<size_t>(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<quint32>(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;
}