1 #include "lc_global.h"
2 #include "lc_mesh.h"
3 #include "lc_colors.h"
4 #include "lc_texture.h"
5 #include "lc_file.h"
6 #include "lc_math.h"
7 #include "lc_application.h"
8 #include "lc_library.h"
9 
10 #define LC_MESH_FILE_ID      LC_FOURCC('M', 'E', 'S', 'H')
11 #define LC_MESH_FILE_VERSION 0x0120
12 
13 lcMesh* gPlaceholderMesh;
14 
lcMesh()15 lcMesh::lcMesh()
16 {
17 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
18 	{
19 		mLods[LodIdx].Sections = nullptr;
20 		mLods[LodIdx].NumSections = 0;
21 	}
22 
23 	mNumVertices = 0;
24 	mNumTexturedVertices = 0;
25 	mIndexType = 0;
26 	mVertexData = nullptr;
27 	mVertexDataSize = 0;
28 	mIndexData = nullptr;
29 	mIndexDataSize = 0;
30 	mVertexCacheOffset = -1;
31 	mIndexCacheOffset = -1;
32 }
33 
~lcMesh()34 lcMesh::~lcMesh()
35 {
36 	free(mVertexData);
37 	free(mIndexData);
38 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
39 		delete[] mLods[LodIdx].Sections;
40 }
41 
Create(quint16 (& NumSections)[LC_NUM_MESH_LODS],int VertexCount,int TexturedVertexCount,int ConditionalVertexCount,int IndexCount)42 void lcMesh::Create(quint16(&NumSections)[LC_NUM_MESH_LODS], int VertexCount, int TexturedVertexCount, int ConditionalVertexCount, int IndexCount)
43 {
44 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
45 	{
46 		if (NumSections[LodIdx])
47 			mLods[LodIdx].Sections = new lcMeshSection[NumSections[LodIdx]];
48 		mLods[LodIdx].NumSections = NumSections[LodIdx];
49 	}
50 
51 	mNumVertices = VertexCount;
52 	mNumTexturedVertices = TexturedVertexCount;
53 	mConditionalVertexCount = ConditionalVertexCount;
54 	mVertexDataSize = VertexCount * sizeof(lcVertex) + TexturedVertexCount * sizeof(lcVertexTextured) + ConditionalVertexCount * sizeof(lcVertexConditional);
55 	mVertexData = malloc(mVertexDataSize);
56 
57 	if (VertexCount < 0x10000 && TexturedVertexCount < 0x10000 && ConditionalVertexCount < 0x10000)
58 	{
59 		mIndexType = GL_UNSIGNED_SHORT;
60 		mIndexDataSize = IndexCount * sizeof(GLushort);
61 	}
62 	else
63 	{
64 		mIndexType = GL_UNSIGNED_INT;
65 		mIndexDataSize = IndexCount * sizeof(GLuint);
66 	}
67 
68 	mIndexData = malloc(mIndexDataSize);
69 }
70 
CreateBox()71 void lcMesh::CreateBox()
72 {
73 	quint16 NumSections[LC_NUM_MESH_LODS];
74 	memset(NumSections, 0, sizeof(NumSections));
75 	NumSections[LC_MESH_LOD_HIGH] = 2;
76 
77 	Create(NumSections, 24, 0, 0, 36 + 24);
78 
79 	lcVector3 Min(-10.0f, -10.0f, -24.0f);
80 	lcVector3 Max(10.0f, 10.0f, 4.0f);
81 	mRadius = lcLength(Max - Min) / 2.0f;
82 	mBoundingBox.Min = Min;
83 	mBoundingBox.Max = Max;
84 	mFlags |= lcMeshFlag::HasDefault | lcMeshFlag::HasLines;
85 
86 	lcVertex* Verts = (lcVertex*)mVertexData;
87 	quint16* Indices = (quint16*)mIndexData;
88 
89 	Verts[0].Position = lcVector3(Min[0], Min[1], Min[2]);
90 	Verts[0].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f));
91 	Verts[1].Position = lcVector3(Min[0], Max[1], Min[2]);
92 	Verts[1].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f));
93 	Verts[2].Position = lcVector3(Max[0], Max[1], Min[2]);
94 	Verts[2].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f));
95 	Verts[3].Position = lcVector3(Max[0], Min[1], Min[2]);
96 	Verts[3].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, -1.0f));
97 	Verts[4].Position = lcVector3(Min[0], Min[1], Max[2]);
98 	Verts[4].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f));
99 	Verts[5].Position = lcVector3(Min[0], Max[1], Max[2]);
100 	Verts[5].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f));
101 	Verts[6].Position = lcVector3(Max[0], Max[1], Max[2]);
102 	Verts[6].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f));
103 	Verts[7].Position = lcVector3(Max[0], Min[1], Max[2]);
104 	Verts[7].Normal = lcPackNormal(lcVector3(0.0f, 0.0f, 1.0f));
105 
106 	Verts[8].Position = lcVector3(Min[0], Min[1], Min[2]);
107 	Verts[8].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f));
108 	Verts[9].Position = lcVector3(Min[0], Min[1], Max[2]);
109 	Verts[9].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f));
110 	Verts[10].Position = lcVector3(Min[0], Max[1], Max[2]);
111 	Verts[10].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f));
112 	Verts[11].Position = lcVector3(Min[0], Max[1], Min[2]);
113 	Verts[11].Normal = lcPackNormal(lcVector3(-1.0f, 0.0f, 0.0f));
114 	Verts[12].Position = lcVector3(Max[0], Min[1], Min[2]);
115 	Verts[12].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f));
116 	Verts[13].Position = lcVector3(Max[0], Min[1], Max[2]);
117 	Verts[13].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f));
118 	Verts[14].Position = lcVector3(Max[0], Max[1], Max[2]);
119 	Verts[14].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f));
120 	Verts[15].Position = lcVector3(Max[0], Max[1], Min[2]);
121 	Verts[15].Normal = lcPackNormal(lcVector3(1.0f, 0.0f, 0.0f));
122 
123 	Verts[16].Position = lcVector3(Min[0], Min[1], Min[2]);
124 	Verts[16].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f));
125 	Verts[17].Position = lcVector3(Min[0], Min[1], Max[2]);
126 	Verts[17].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f));
127 	Verts[18].Position = lcVector3(Max[0], Min[1], Max[2]);
128 	Verts[18].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f));
129 	Verts[19].Position = lcVector3(Max[0], Min[1], Min[2]);
130 	Verts[19].Normal = lcPackNormal(lcVector3(0.0f, -1.0f, 0.0f));
131 	Verts[20].Position = lcVector3(Min[0], Max[1], Min[2]);
132 	Verts[20].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f));
133 	Verts[21].Position = lcVector3(Min[0], Max[1], Max[2]);
134 	Verts[21].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f));
135 	Verts[22].Position = lcVector3(Max[0], Max[1], Max[2]);
136 	Verts[22].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f));
137 	Verts[23].Position = lcVector3(Max[0], Max[1], Min[2]);
138 	Verts[23].Normal = lcPackNormal(lcVector3(0.0f, 1.0f, 0.0f));
139 
140 	lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[0];
141 	Section->ColorIndex = gDefaultColor;
142 	Section->IndexOffset = 0;
143 	Section->NumIndices = 36;
144 	Section->PrimitiveType = LC_MESH_TRIANGLES;
145 	Section->Texture = nullptr;
146 	Section->BoundingBox = mBoundingBox;
147 	Section->Radius = mRadius;
148 
149 	*Indices++ = 0; *Indices++ = 1; *Indices++ = 2;
150 	*Indices++ = 0; *Indices++ = 2; *Indices++ = 3;
151 
152 	*Indices++ = 7; *Indices++ = 6; *Indices++ = 5;
153 	*Indices++ = 7; *Indices++ = 5; *Indices++ = 4;
154 
155 	*Indices++ = 8; *Indices++ = 9; *Indices++ = 10;
156 	*Indices++ = 8; *Indices++ = 10; *Indices++ = 11;
157 
158 	*Indices++ = 15; *Indices++ = 14; *Indices++ = 13;
159 	*Indices++ = 15; *Indices++ = 13; *Indices++ = 12;
160 
161 	*Indices++ = 16; *Indices++ = 17; *Indices++ = 18;
162 	*Indices++ = 16; *Indices++ = 18; *Indices++ = 19;
163 
164 	*Indices++ = 23; *Indices++ = 22; *Indices++ = 21;
165 	*Indices++ = 23; *Indices++ = 21; *Indices++ = 20;
166 
167 
168 	Section = &mLods[LC_MESH_LOD_HIGH].Sections[1];
169 	Section->ColorIndex = gEdgeColor;
170 	Section->IndexOffset = 36 * 2;
171 	Section->NumIndices = 24;
172 	Section->PrimitiveType = LC_MESH_LINES;
173 	Section->Texture = nullptr;
174 	Section->BoundingBox = mBoundingBox;
175 	Section->Radius = mRadius;
176 
177 	*Indices++ = 0; *Indices++ = 1; *Indices++ = 1; *Indices++ = 2;
178 	*Indices++ = 2; *Indices++ = 3; *Indices++ = 3; *Indices++ = 0;
179 
180 	*Indices++ = 4; *Indices++ = 5; *Indices++ = 5; *Indices++ = 6;
181 	*Indices++ = 6; *Indices++ = 7; *Indices++ = 7; *Indices++ = 4;
182 
183 	*Indices++ = 0; *Indices++ = 4; *Indices++ = 1; *Indices++ = 5;
184 	*Indices++ = 2; *Indices++ = 6; *Indices++ = 3; *Indices++ = 7;
185 }
186 
187 template<typename IndexType>
MinIntersectDist(const lcVector3 & Start,const lcVector3 & End,float & MinDistance)188 bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDistance)
189 {
190 	float Distance;
191 	if (!lcBoundingBoxRayIntersectDistance(mBoundingBox.Min, mBoundingBox.Max, Start, End, &Distance, nullptr) || (Distance >= MinDistance))
192 		return false;
193 
194 	lcVertex* const Verts = (lcVertex*)mVertexData;
195 	bool Hit = false;
196 	lcVector3 Intersection;
197 
198 	for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
199 	{
200 		lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
201 
202 		if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
203 			continue;
204 
205 		IndexType* Indices = (IndexType*)mIndexData + Section->IndexOffset / sizeof(IndexType);
206 
207 		for (int Idx = 0; Idx < Section->NumIndices; Idx += 3)
208 		{
209 			const lcVector3& v1 = Verts[Indices[Idx]].Position;
210 			const lcVector3& v2 = Verts[Indices[Idx + 1]].Position;
211 			const lcVector3& v3 = Verts[Indices[Idx + 2]].Position;
212 
213 			if (lcLineTriangleMinIntersection(v1, v2, v3, Start, End, &MinDistance, &Intersection))
214 				Hit = true;
215 		}
216 	}
217 
218 	return Hit;
219 }
220 
MinIntersectDist(const lcVector3 & Start,const lcVector3 & End,float & MinDist)221 bool lcMesh::MinIntersectDist(const lcVector3& Start, const lcVector3& End, float& MinDist)
222 {
223 	if (mIndexType == GL_UNSIGNED_SHORT)
224 		return MinIntersectDist<GLushort>(Start, End, MinDist);
225 	else
226 		return MinIntersectDist<GLuint>(Start, End, MinDist);
227 }
228 
229 template<typename IndexType>
IntersectsPlanes(const lcVector4 (& Planes)[6])230 bool lcMesh::IntersectsPlanes(const lcVector4 (&Planes)[6])
231 {
232 	lcVertex* Verts = (lcVertex*)mVertexData;
233 
234 	for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
235 	{
236 		lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
237 
238 		if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
239 			continue;
240 
241 		IndexType* Indices = (IndexType*)mIndexData + Section->IndexOffset / sizeof(IndexType);
242 
243 		for (int Idx = 0; Idx < Section->NumIndices; Idx += 3)
244 			if (lcTriangleIntersectsPlanes(Verts[Indices[Idx]].Position, Verts[Indices[Idx+1]].Position, Verts[Indices[Idx+2]].Position, Planes))
245 				return true;
246 	}
247 
248 	return false;
249 }
250 
IntersectsPlanes(const lcVector4 (& Planes)[6])251 bool lcMesh::IntersectsPlanes(const lcVector4 (&Planes)[6])
252 {
253 	if (mIndexType == GL_UNSIGNED_SHORT)
254 		return IntersectsPlanes<GLushort>(Planes);
255 	else
256 		return IntersectsPlanes<GLuint>(Planes);
257 }
258 
259 template<typename IndexType>
ExportPOVRay(lcFile & File,const char * MeshName,const char ** ColorTable)260 void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char** ColorTable)
261 {
262 	char Line[1024];
263 
264 	int NumSections = 0;
265 
266 	for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
267 	{
268 		const lcMeshSection* const Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
269 
270 		if (Section->PrimitiveType == LC_MESH_TRIANGLES || Section->PrimitiveType == LC_MESH_TEXTURED_TRIANGLES)
271 			NumSections++;
272 	}
273 
274 	if (NumSections > 1)
275 		sprintf(Line, "#declare lc_%s = union {\n", MeshName);
276 	else
277 		sprintf(Line, "#declare lc_%s = mesh {\n", MeshName);
278 	File.WriteLine(Line);
279 
280 	const lcVertex* const Verts = (lcVertex*)mVertexData;
281 
282 	for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
283 	{
284 		lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
285 
286 		if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
287 			continue;
288 
289 		IndexType* Indices = (IndexType*)mIndexData + Section->IndexOffset / sizeof(IndexType);
290 
291 		if (NumSections > 1)
292 			File.WriteLine(" mesh {\n");
293 
294 		for (int Idx = 0; Idx < Section->NumIndices; Idx += 3)
295 		{
296 			const lcVector3 v1 = Verts[Indices[Idx]].Position / 25.0f;
297 			const lcVector3 v2 = Verts[Indices[Idx + 1]].Position / 25.0f;
298 			const lcVector3 v3 = Verts[Indices[Idx + 2]].Position / 25.0f;
299 			const lcVector3 n1 = lcUnpackNormal(Verts[Indices[Idx]].Normal);
300 			const lcVector3 n2 = lcUnpackNormal(Verts[Indices[Idx + 1]].Normal);
301 			const lcVector3 n3 = lcUnpackNormal(Verts[Indices[Idx + 2]].Normal);
302 
303 			sprintf(Line, "  smooth_triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n",
304 			        -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);
305 			File.WriteLine(Line);
306 		}
307 
308 		if (Section->ColorIndex != gDefaultColor)
309 		{
310 			sprintf(Line, "material { texture { %s normal { bumps 0.1 scale 2 } } }", ColorTable[Section->ColorIndex]);
311 			File.WriteLine(Line);
312 		}
313 
314 		if (NumSections > 1)
315 			File.WriteLine(" }\n");
316 	}
317 
318 	File.WriteLine("}\n\n");
319 }
320 
ExportPOVRay(lcFile & File,const char * MeshName,const char ** ColorTable)321 void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char** ColorTable)
322 {
323 	if (mIndexType == GL_UNSIGNED_SHORT)
324 		ExportPOVRay<GLushort>(File, MeshName, ColorTable);
325 	else
326 		ExportPOVRay<GLuint>(File, MeshName, ColorTable);
327 }
328 
329 template<typename IndexType>
ExportWavefrontIndices(lcFile & File,int DefaultColorIndex,int VertexOffset)330 void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset)
331 {
332 	char Line[1024];
333 
334 	for (int SectionIdx = 0; SectionIdx < mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
335 	{
336 		lcMeshSection* Section = &mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
337 
338 		if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
339 			continue;
340 
341 		IndexType* Indices = (IndexType*)mIndexData + Section->IndexOffset / sizeof(IndexType);
342 
343 		if (Section->ColorIndex == gDefaultColor)
344 			sprintf(Line, "usemtl %s\n", gColorList[DefaultColorIndex].SafeName);
345 		else
346 			sprintf(Line, "usemtl %s\n", gColorList[Section->ColorIndex].SafeName);
347 		File.WriteLine(Line);
348 
349 		for (int Idx = 0; Idx < Section->NumIndices; Idx += 3)
350 		{
351 			const long int idx1 = Indices[Idx + 0] + VertexOffset;
352 			const long int idx2 = Indices[Idx + 1] + VertexOffset;
353 			const long int idx3 = Indices[Idx + 2] + VertexOffset;
354 
355 			if (idx1 != idx2 && idx1 != idx3 && idx2 != idx3)
356 				sprintf(Line, "f %ld//%ld %ld//%ld %ld//%ld\n", idx1, idx1, idx2, idx2, idx3, idx3);
357 			File.WriteLine(Line);
358 		}
359 	}
360 
361 	File.WriteLine("\n");
362 }
363 
ExportWavefrontIndices(lcFile & File,int DefaultColorIndex,int VertexOffset)364 void lcMesh::ExportWavefrontIndices(lcFile& File, int DefaultColorIndex, int VertexOffset)
365 {
366 	if (mIndexType == GL_UNSIGNED_SHORT)
367 		ExportWavefrontIndices<GLushort>(File, DefaultColorIndex, VertexOffset);
368 	else
369 		ExportWavefrontIndices<GLuint>(File, DefaultColorIndex, VertexOffset);
370 }
371 
FileLoad(lcMemFile & File)372 bool lcMesh::FileLoad(lcMemFile& File)
373 {
374 	if (File.ReadU32() != LC_MESH_FILE_ID || File.ReadU32() != LC_MESH_FILE_VERSION)
375 		return false;
376 
377 	mFlags = static_cast<lcMeshFlags>(File.ReadU32());
378 	mBoundingBox.Min = File.ReadVector3();
379 	mBoundingBox.Max = File.ReadVector3();
380 	mRadius = File.ReadFloat();
381 
382 	quint32 VertexCount, TexturedVertexCount, ConditionalVertexCount, IndexCount;
383 	quint16 NumLods, NumSections[LC_NUM_MESH_LODS];
384 
385 	if (!File.ReadU32(&VertexCount, 1) || !File.ReadU32(&TexturedVertexCount, 1) || !File.ReadU32(&ConditionalVertexCount, 1) || !File.ReadU32(&IndexCount, 1))
386 		return false;
387 
388 	if (!File.ReadU16(&NumLods, 1) || NumLods != LC_NUM_MESH_LODS || !File.ReadU16(NumSections, LC_NUM_MESH_LODS))
389 		return false;
390 
391 	Create(NumSections, VertexCount, TexturedVertexCount, ConditionalVertexCount, IndexCount);
392 
393 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
394 	{
395 		for (int SectionIdx = 0; SectionIdx < mLods[LodIdx].NumSections; SectionIdx++)
396 		{
397 			lcMeshSection& Section = mLods[LodIdx].Sections[SectionIdx];
398 
399 			quint32 ColorCode, IndexOffset;
400 			quint16 PrimtiveType, Length;
401 
402 			if (!File.ReadU32(&ColorCode, 1) || !File.ReadU32(&IndexOffset, 1) || !File.ReadU32(&IndexCount, 1) || !File.ReadU16(&PrimtiveType, 1))
403 				return false;
404 
405 			Section.ColorIndex = lcGetColorIndex(ColorCode);
406 			Section.IndexOffset = IndexOffset;
407 			Section.NumIndices = IndexCount;
408 			Section.PrimitiveType = (lcMeshPrimitiveType)PrimtiveType;
409 			Section.BoundingBox.Min = File.ReadVector3();
410 			Section.BoundingBox.Max = File.ReadVector3();
411 			Section.Radius = File.ReadFloat();
412 
413 			if (!File.ReadU16(&Length, 1))
414 				return false;
415 
416 			if (Length)
417 			{
418 				if (Length >= LC_TEXTURE_NAME_LEN)
419 					return false;
420 
421 				char FileName[LC_TEXTURE_NAME_LEN];
422 
423 				File.ReadBuffer(FileName, Length);
424 				FileName[Length] = 0;
425 
426 				Section.Texture = lcGetPiecesLibrary()->FindTexture(FileName, nullptr, false);
427 			}
428 			else
429 				Section.Texture = nullptr;
430 		}
431 	}
432 
433 	File.ReadBuffer(mVertexData, mNumVertices * sizeof(lcVertex) + mNumTexturedVertices * sizeof(lcVertexTextured) + mConditionalVertexCount * sizeof(lcVertexConditional));
434 
435 	if (mIndexType == GL_UNSIGNED_SHORT)
436 		File.ReadU16((quint16*)mIndexData, mIndexDataSize / 2);
437 	else
438 		File.ReadU32((quint32*)mIndexData, mIndexDataSize / 4);
439 
440 	return true;
441 }
442 
FileSave(lcMemFile & File)443 bool lcMesh::FileSave(lcMemFile& File)
444 {
445 	File.WriteU32(LC_MESH_FILE_ID);
446 	File.WriteU32(LC_MESH_FILE_VERSION);
447 
448 	File.WriteU32(mFlags);
449 	File.WriteVector3(mBoundingBox.Min);
450 	File.WriteVector3(mBoundingBox.Max);
451 	File.WriteFloat(mRadius);
452 
453 	File.WriteU32(mNumVertices);
454 	File.WriteU32(mNumTexturedVertices);
455 	File.WriteU32(mConditionalVertexCount);
456 	File.WriteU32(mIndexDataSize / (mIndexType == GL_UNSIGNED_SHORT ? 2 : 4));
457 
458 	File.WriteU16(LC_NUM_MESH_LODS);
459 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
460 		File.WriteU16(mLods[LodIdx].NumSections);
461 
462 	for (int LodIdx = 0; LodIdx < LC_NUM_MESH_LODS; LodIdx++)
463 	{
464 		for (int SectionIdx = 0; SectionIdx < mLods[LodIdx].NumSections; SectionIdx++)
465 		{
466 			const lcMeshSection& Section = mLods[LodIdx].Sections[SectionIdx];
467 
468 			File.WriteU32(lcGetColorCode(Section.ColorIndex));
469 			File.WriteU32(Section.IndexOffset);
470 			File.WriteU32(Section.NumIndices);
471 			File.WriteU16(Section.PrimitiveType);
472 			File.WriteVector3(Section.BoundingBox.Min);
473 			File.WriteVector3(Section.BoundingBox.Max);
474 			File.WriteFloat(Section.Radius);
475 
476 			if (Section.Texture)
477 			{
478 				const quint16 Length = (quint16)strlen(Section.Texture->mName);
479 				File.WriteU16(Length);
480 				File.WriteBuffer(Section.Texture->mName, Length);
481 			}
482 			else
483 				File.WriteU16(0);
484 		}
485 	}
486 
487 	File.WriteBuffer(mVertexData, mNumVertices * sizeof(lcVertex) + mNumTexturedVertices * sizeof(lcVertexTextured) + mConditionalVertexCount * sizeof(lcVertexConditional));
488 
489 	if (mIndexType == GL_UNSIGNED_SHORT)
490 		File.WriteU16((quint16*)mIndexData, mIndexDataSize / 2);
491 	else
492 		File.WriteU32((quint32*)mIndexData, mIndexDataSize / 4);
493 
494 	return true;
495 }
496 
GetLodIndex(float Distance) const497 int lcMesh::GetLodIndex(float Distance) const
498 {
499 	if (lcGetPiecesLibrary()->GetStudStyle() != lcStudStyle::Plain) // todo: support low lod studs
500 		return LC_MESH_LOD_HIGH;
501 
502 	if (mLods[LC_MESH_LOD_LOW].NumSections && (Distance > mRadius))
503 		return LC_MESH_LOD_LOW;
504 	else
505 		return LC_MESH_LOD_HIGH;
506 }
507