1 namespace gfx3D::models;
5 #if defined(__EMSCRIPTEN__)
11 #if (defined(__ANDROID__) || defined(__ODROID__)) && !defined(_GLES)
15 #if !defined(_GLES) && !defined(_GLES2)
16 #define USE_32_BIT_INDICES true
17 #define indicesMember indices32
18 #define uintindex uint32
20 #define USE_32_BIT_INDICES false
21 #define indicesMember indices
22 #define uintindex uint16
27 static enum ChunkID3DS : uint16
32 RGB_BYTE_GAMMA = 0x0012,
33 RGB_FLOAT_GAMMA = 0x0013,
40 EDIT_AMBIENT = 0x2100,
41 EDIT_MATERIAL = 0xAFFF,
48 // Triangular Mesh Chunks
51 TRI_MATERIAL = 0x4130,
52 TRI_MAPPINGCOORS = 0x4140,
53 TRI_SMOOTHING = 0x4150,
59 LIT_ATTENUATION = 0x4625,
62 LIT_MULTIPLIER = 0x465B,
72 MAT_SPECULAR = 0xA030,
73 MAT_SHININESS = 0xA040,
74 MAT_SHINSTRENGTH = 0xA041,
76 MAT_TRANSPARENCY = 0xA050,
79 MAT_SELFILLUM = 0xA080,
80 MAT_DOUBLESIDED = 0xA081,
81 MAT_ADDITIVE = 0xA083,
82 MAT_SELFILPCT = 0xA084,
84 MAT_SUPERSMP = 0xA086,
85 MAT_WIRETHICKNESS = 0xA087,
87 MAT_XPFALLIN = 0xA08A,
91 MAT_MAPTEXTURE1 = 0xA200,
92 MAT_SPECULARMAP = 0xA204,
93 MAT_MAPOPACITY = 0xA210,
94 MAT_REFLECTIONMAP = 0xA220,
98 MAP_FILENAME = 0xA300,
100 MAT_SHININESSMAP = 0xA33C,
101 MAT_EMISSIVEMAP = 0xA33D,
103 MAP_OPTIONS = 0xA351,
104 MAP_1_U_SCALE = 0xA354,
105 MAP_1_V_SCALE = 0xA356,
106 MAP_U_OFFSET = 0xA358,
107 MAP_V_OFFSET = 0xA35A,
108 MAP_ROTATION = 0xA35C,
110 MAP_FILTERBLUR = 0xA353,
111 MAP_1_U_SCALE = 0xA354,
112 MAP_1_V_SCALE = 0xA356,
113 MAP_U_OFFSET = 0xA358,
114 MAP_V_OFFSET = 0xA35A,
115 MAP_ROTATION = 0xA35C,
117 MAP_LUMTINT1 = 0xA360,
118 MAP_LUMTINT2 = 0xA362,
125 KEYFRAME3DS = 0xB000,
126 FRM_AMBIENT = 0xB001,
127 FRM_MESHINFO = 0xB002,
129 FRM_CAMERATARGET = 0xB004,
130 FRM_OMNILIGHT = 0xB005,
131 FRM_SPOTLIGHTTARGET = 0xB006,
132 FRM_SPOTLIGHT = 0xB007,
135 FRM_DUMMYNAME = 0xB011,
137 FRM_TRACKPOS = 0xB020,
138 FRM_TRACKROT = 0xB021,
139 FRM_TRACKSCALE = 0xB022,
140 FRM_TRACKFOV = 0xB023,
141 FRM_TRACKROLL = 0xB024,
142 FRM_TRACKCOLOR = 0xB025,
143 FRM_TRACKMORPH = 0xB026, // What does this do?
144 FRM_TRACKHOTSPOT = 0xB027,
145 FRM_TRACKFALLOFF = 0xB028,
146 FRM_TRACKHIDE = 0xB029,
147 FRM_HIERARCHY = 0xB030
150 static class MapOptions : uint32
152 bool decal:1; // (related to dontTile)
156 bool summedFiltering:1;
158 bool lumOrAlphaTint:1;
163 typedef struct FileInfo FileInfo;
176 static struct FileInfo
179 DisplaySystem displaySystem;
181 const String fileName;
189 char textureDirectory[MAX_DIRECTORY];
190 Map<uintptr, Array<int>> matFaces;
193 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
194 | (((unsigned short)(word) & 0xff00) >> 8)
196 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
197 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
198 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
199 | (((unsigned int)(dword) & 0xff000000) >> 24))
201 #ifndef __BIG_ENDIAN__
202 #define BIGENDSWAP_WORD(word)
203 #define BIGENDSWAP_DWORD(dword)
205 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
206 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
209 // Zero Terminated String
210 static int ReadASCIIZ(File f, char ** string)
212 // *** Read String ***
214 char temp[1024] = "";
218 if(!temp[c++]) break;
220 *string = new char[c];
222 strcpy(*string, temp);
226 static float ReadFloat(File f)
228 float floatValue = 0;
229 f.Read(&floatValue, sizeof(float), 1);
230 BIGENDSWAP_DWORD(floatValue);
234 static uint16 ReadWORD(File f)
236 uint16 wordValue = 0;
237 f.Read(&wordValue, sizeof(uint16), 1);
238 BIGENDSWAP_WORD(wordValue);
242 static uint ReadDWORD(File f)
245 f.Read(&dwordValue, sizeof(uint), 1);
246 BIGENDSWAP_DWORD(dwordValue);
251 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
253 for(;info->pos < info->end;)
255 FileInfo childInfo = *info;
258 childInfo.parent = info;
260 info->f.Seek(info->pos, start);
261 childInfo.chunkId = (ChunkID3DS)ReadWORD(info->f);
262 length = ReadDWORD(info->f);
264 childInfo.pos += sizeof(uint16) + sizeof(uint);
265 childInfo.end = info->pos + length;
267 if(!chunkParser(&childInfo, data))
270 info->pos = childInfo.end;
276 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
278 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
281 info->f.Getc((char *)&value); rgb->r = value / 255.0f;
282 info->f.Getc((char *)&value); rgb->g = value / 255.0f;
283 info->f.Getc((char *)&value); rgb->b = value / 255.0f;
285 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
287 rgb->r = ReadFloat(info->f);
288 rgb->g = ReadFloat(info->f);
289 rgb->b = ReadFloat(info->f);
294 static bool Read3DVertex(File f, Vector3Df vertex)
296 vertex.x = ReadFloat(f);
297 vertex.y = ReadFloat(f);
298 vertex.z = ReadFloat(f);
302 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
304 if(info->chunkId == AMOUNT_OF)
305 *amountOf = ReadWORD(info->f);
309 #define WELD_TRESHOLD 0.000001
310 #define SMOOTH_CUTOFF 0 // 45
312 struct SharedSourceVertexInfo
319 int OnCompare(SharedSourceVertexInfo b)
321 if(unique < b.unique) return -1;
322 if(unique > b.unique) return 1;
326 if(face < b.face) return -1;
327 if(face > b.face) return 1;
329 if(index == b.index) return 0;
330 if(WELD_TRESHOLD > 0)
332 if(value.x < b.value.x - WELD_TRESHOLD) return -1;
333 if(value.x > b.value.x + WELD_TRESHOLD) return 1;
334 if(value.y < b.value.y - WELD_TRESHOLD) return -1;
335 if(value.y > b.value.y + WELD_TRESHOLD) return 1;
336 if(value.z < b.value.z - WELD_TRESHOLD) return -1;
337 if(value.z > b.value.z + WELD_TRESHOLD) return 1;
341 if(index < b.index) return -1;
342 if(index > b.index) return 1;
348 class SharedDestVertexInfo
350 Array<int> faces { };
353 struct SourceVertexInfo
355 SharedSourceVertexInfo * shared;
359 int OnCompare(SourceVertexInfo b)
361 int r = (*shared).OnCompare(*b.shared);
362 if(!r) r = texCoord.OnCompare(b.texCoord);
363 if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
370 int index, copyFromIndex;
371 Vector3Df normal, tangent1, tangent2;
374 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
377 Face * faces = info->faces;
378 int nVertices = mesh.nVertices;
381 Vector3Df * mVertices;
382 double cutOff = cos(Degrees { SMOOTH_CUTOFF });
383 bool hasBumpMap = false;
385 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
386 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
387 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
389 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
390 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
392 nNewVertices = nVertices;
393 mVertices = mesh->vertices;
395 for(c = 0; c<info->nFaces; c++)
397 Face * face = &faces[c];
399 uint * indices = face->indices;
400 Vector3D edges[3], rEdges[3];
401 computeNormalWeights(3, mVertices, indices, true, 0, face->dots, edges, rEdges);
403 plane.FromPointsf(mVertices[indices[2]], mVertices[indices[1]], mVertices[indices[0]]);
404 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
407 for(c = 0; c < info->nFaces; c++)
409 Face * face = &faces[c];
411 if(face->material && face->material.bumpMap)
415 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
416 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
421 SharedSourceVertexInfo * source;
422 SharedDestVertexInfo svInfo;
423 DestVertexInfo vInfo;
425 index = face->indices[i];
427 if(face->smoothGroups)
428 itShared.Index({ index = index, mVertices[index], face = face }, true);
430 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
431 svInfo = itShared.data;
432 if(!svInfo) itShared.data = svInfo = { };
435 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
436 // TODO: Allow obtaining address of MapIterator::key
437 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
438 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
444 vInfo.copyFromIndex = index;
449 vertices[index] = (void *)it.pointer;
450 else if(vertices[index] != it.pointer)
452 // If it's a different smoothing group, we'll need extra vertices
453 index = vertices.size;
455 vertices.Add((void *)it.pointer);
458 face->indices[i] = vInfo.index;
462 for(index = 0; index < nNewVertices; index++)
464 it.pointer = vertices[index];
469 DestVertexInfo vInfo = it.data;
471 Vector3D tangent1 { };
472 Vector3D tangent2 { };
473 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
474 uint smoothing = inf->smoothGroups;
476 SharedSourceVertexInfo * shared = inf->shared;
477 SharedDestVertexInfo svInfo = sharedVertices[*shared];
479 if(!svInfo || vInfo.index != index)
482 for(i : svInfo.faces)
484 Face * face = &info->faces[i];
486 if(smoothing & face->smoothGroups)
487 smoothing |= face->smoothGroups;
490 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
491 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
493 for(i : svInfo.faces)
495 Face * face = &info->faces[i];
496 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
499 for(j = 0; j < 3; j++)
501 if(face->indices[j] == vInfo.index)
503 origIndex = face->origIndices[j];
505 normal.x += face->normal.x;
506 normal.y += face->normal.y;
507 normal.z += face->normal.z;
520 for(i : svInfo.faces)
522 Face * face = &info->faces[i];
523 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
527 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
532 for(k = 0; k < 3; k++)
534 if(face->indices[k] == vInfo.index)
536 origIndexB = face->origIndices[k];
540 valid = origIndex == origIndexB;
543 for(j : svInfo.faces)
545 if(info->faces[j].done)
547 double dot = info->faces[j].normal.DotProduct(face->normal);
548 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
549 valid = fabs(dot) > cutOff;
558 uint * indices = face->origIndices;
559 uint ix = vInfo.copyFromIndex;
560 uint ix0 = indices[0], ix1 = indices[1], ix2 = indices[2];
561 double dot = ix == ix0 ? face->dots[0] : ix == ix1 ? face->dots[1] : ix == ix2 ? face->dots[2] : 1;
562 if(ix != indices[0] && ix != indices[1] && ix != indices[2])
564 if(!mVertices[ix0].OnCompare(mVertices[ix])) dot = face->dots[0];
565 else if(!mVertices[ix1].OnCompare(mVertices[ix])) dot = face->dots[1];
566 else if(!mVertices[ix2].OnCompare(mVertices[ix])) dot = face->dots[2];
568 normal.x += face->normal.x * dot;
569 normal.y += face->normal.y * dot;
570 normal.z += face->normal.z * dot;
574 Vector3Df * p0 = &mVertices[ix0], * p1 = &mVertices[ix1], * p2 = &mVertices[ix2];
575 Pointf * t0 = &mesh->texCoords[ix0], * t1 = &mesh->texCoords[ix1], * t2 = &mesh->texCoords[ix2];
576 Vector3D v01 { (double)p1->x - (double)p0->x, (double)p1->y - (double)p0->y, (double)p1->z - (double)p0->z };
577 Vector3D v02 { (double)p2->x - (double)p0->x, (double)p2->y - (double)p0->y, (double)p2->z - (double)p0->z };
579 Vector3D t01 { (double)t1->x - (double)t0->x, (double)t1->y - (double)t0->y };
580 Vector3D t02 { (double)t2->x - (double)t0->x, (double)t2->y - (double)t0->y };
581 double f = dot / (t01.x * t02.y - t02.x * t01.y);
583 tangent1.x += f * (v01.x * t02.y - v02.x * t01.y);
584 tangent1.y += f * (v01.y * t02.y - v02.y * t01.y);
585 tangent1.z += f * (v01.z * t02.y - v02.z * t01.y);
587 tangent2.x += f * (v02.x * t01.x - v01.x * t02.x);
588 tangent2.y += f * (v02.y * t01.x - v01.y * t02.x);
589 tangent2.z += f * (v02.z * t01.x - v01.z * t02.x);
599 if(!SMOOTH_CUTOFF || !(WELD_TRESHOLD > 0)) break;
601 // normal.Scale(normal, 1.0f / numShared);
602 normal.Scale(normal, 1.0 / dotSum);
603 normal.Normalize(normal);
605 //tangent1.Scale(tangent1, 1.0 / dotSum);
606 tangent1.Normalize(tangent1);
608 //tangent2.Scale(tangent2, 1.0 / dotSum);
609 tangent2.Normalize(tangent2);
611 if(vInfo.index == index)
613 vInfo.normal = { (float)normal.x, (float)normal.y, (float)normal.z };
614 vInfo.tangent1 = { (float)tangent1.x, (float)tangent1.y, (float)tangent1.z };
615 vInfo.tangent2 = { (float)tangent2.x, (float)tangent2.y, (float)tangent2.z };
618 // Auto welding/smoothing requires extra vertices because angle is too steep
619 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
621 SharedDestVertexInfo newSharedInfo = null;
623 for(i : svInfo.faces)
625 Face * face = &info->faces[i];
626 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
629 for(j = 0; j < 3; j++)
631 if(face->indices[j] == vInfo.index)
635 DestVertexInfo newVert;
636 SharedSourceVertexInfo * source;
638 index = nNewVertices++;
639 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
640 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
641 itShared.data = newSharedInfo = { };
643 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
646 newVert.copyFromIndex = vInfo.copyFromIndex;
647 newVert.index = index;
649 vertices.Add((void *)it.pointer);
651 face->indices[j] = index;
652 newSharedInfo.faces.Add(i);
662 // Allocate some extra vertices
664 Vector3Df * oldVertices = mesh.vertices;
665 Pointf * oldTexCoords = mesh.texCoords;
667 // TODO: Support reallocation?
668 *((void **)&mesh.vertices) = null;
669 *((void **)&mesh.texCoords) = null;
670 *((int *)&mesh.nVertices) = 0;
672 mesh.Allocate( { vertices = true, normals = true, tangents = oldTexCoords ? true : false, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
674 // Fill in the new vertices
675 for(index = 0; index < nNewVertices; index++)
677 DestVertexInfo vInfo;
678 it.pointer = vertices[index];
682 mesh.normals[index] = vInfo ? vInfo.normal : { };
683 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
686 mesh.tangents[2*index+0] = vInfo ? vInfo.tangent1 : { };
687 mesh.tangents[2*index+1] = vInfo ? vInfo.tangent2 : { };
690 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
699 for(i = 0; i < info->nFaces; i++)
700 info->faces[i].done = false;
703 mesh.Unlock({ normals = true, tangents = true });
704 object.flags.computeLightVectors = hasBumpMap;
706 // Free all the temporary stuff
711 sharedVertices.Free();
712 delete sharedVertices;
716 static bool ReadSmoothing(FileInfo * info, Object object)
718 switch(info->chunkId)
723 for(c = 0; c<info->nFaces; c++)
724 info->faces[c].smoothGroups = ReadDWORD(info->f);
731 static bool ReadFacesListChunks(FileInfo * info, Object object)
733 DisplaySystem displaySystem = info->displaySystem;
734 switch(info->chunkId)
743 char matName[MAX_LOCATION + 100];
745 strcpy(matName, info->fileName);
746 ReadASCIIZ(info->f, &name);
747 count = ReadWORD(info->f);
748 strcat(matName, name);
750 mat = displaySystem.GetMaterial(matName);
751 faces = info->matFaces[(uintptr)mat];
753 info->matFaces[(uintptr)mat] = faces = { };
757 for(c = 0; c<count; c++)
759 uint16 face = ReadWORD(info->f);
761 info->faces[face].material = mat;
770 static bool ReadTriMesh(FileInfo * info, Object object)
772 Mesh mesh = object.mesh;
773 switch(info->chunkId)
778 uint16 nVertices = ReadWORD(info->f);
779 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
780 *((int *) &mesh.nVertices) = nVertices;
781 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
784 for(c = 0; c<mesh.nVertices; c++)
787 Read3DVertex(info->f, vertex);
788 mesh.vertices[c].x = vertex.x;
789 mesh.vertices[c].y =-vertex.z;
790 mesh.vertices[c].z = vertex.y;
797 case TRI_MAPPINGCOORS:
800 uint16 count = ReadWORD(info->f);
801 count = (uint16)Min(mesh.nVertices, count);
803 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
804 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
805 mesh.flags.texCoords1 = true;
808 for(c = 0; c<count; c++)
810 mesh.texCoords[c].x = ReadFloat(info->f);
811 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
825 info->nFaces = nFaces = ReadWORD(info->f);
826 info->pos += sizeof(uint16);
828 info->faces = new0 Face[nFaces];
829 for(c = 0; c<nFaces; c++)
833 info->faces[c].origIndices[i] =
834 info->faces[c].indices[i] = ReadWORD(info->f);
836 info->pos += 4*sizeof(uint16);
839 ReadChunks(ReadSmoothing, info, object);
843 info->matFaces.Free();
844 info->matFaces = { };
846 ReadChunks(ReadFacesListChunks, info, object);
848 ComputeNormals(mesh, info, object);
851 for(m : info->matFaces)
853 Material mat = (Material)&m;
854 Array<int> faces = m;
855 if(mat.flags.translucent)
857 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
860 Face * face = &info->faces[i];
861 PrimitiveSingle * triangle;
863 triangle = &mesh.primitives[mesh.nPrimitives++];
864 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
866 triangle->indicesMember[0] = (uintindex)face->indices[0];
867 triangle->indicesMember[1] = (uintindex)face->indices[1];
868 triangle->indicesMember[2] = (uintindex)face->indices[2];
869 triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
870 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
871 triangle->plane.FromPointsf(
872 mesh.vertices[triangle->indicesMember[2]],
873 mesh.vertices[triangle->indicesMember[1]],
874 mesh.vertices[triangle->indicesMember[0]]);
876 mesh.UnlockPrimitive(triangle);
878 triangle->middle.x /= 3;
879 triangle->middle.y /= 3;
880 triangle->middle.z /= 3;
882 triangle->material = mat;
885 object.flags.translucent = true;
890 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
894 group.material = mat;
897 Face * face = &info->faces[i];
899 if(object.flags.flipWindings)
901 group.indicesMember[c*3] = (uintindex)face->indices[2];
902 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
903 group.indicesMember[c*3+2] = (uintindex)face->indices[0];
907 group.indicesMember[c*3] = (uintindex)face->indices[0];
908 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
909 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
914 mesh.UnlockPrimitiveGroup(group);
919 // Add faces without a material all together
921 for(c = 0; c<nFaces; c++)
922 if(!info->faces[c].done)
926 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
929 for(c = 0; c<nFaces; c++)
931 Face * face = &info->faces[c];
934 group.indicesMember[c*3] = (uintindex)face->indices[0];
935 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
936 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
939 mesh.UnlockPrimitiveGroup(group);
946 info->matFaces.Free();
947 delete info->matFaces;
954 Vector3Df xAxis, yAxis, zAxis, center;
957 Matrix inverse/*, source = { 0 }*/;
960 Read3DVertex(info->f, xAxis);
961 Read3DVertex(info->f, yAxis);
962 Read3DVertex(info->f, zAxis);
963 Read3DVertex(info->f, center);
965 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
966 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
967 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
969 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
972 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
973 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
974 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
975 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
977 inverse.Inverse(source);
980 object.flags.flipWindings = false;
982 xAxis.Normalize(xAxis);
983 yAxis.Normalize(yAxis);
984 zAxis.Normalize(zAxis);
986 orth.CrossProduct(yAxis, zAxis);
987 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
988 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
989 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
991 object.flags.flipWindings ^= true;
995 orth.CrossProduct(zAxis, xAxis);
996 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
997 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
998 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
1000 object.flags.flipWindings ^= true;
1004 orth.CrossProduct(xAxis, yAxis);
1005 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
1006 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
1007 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
1009 object.flags.flipWindings ^= true;
1016 rotation.m[0][0] = xAxis.x;
1017 rotation.m[0][1] =-xAxis.z;
1018 rotation.m[0][2] = xAxis.y;
1019 rotation.m[0][3] = 0;
1020 rotation.m[1][0] =-zAxis.x;
1021 rotation.m[1][1] = zAxis.z;
1022 rotation.m[1][2] =-zAxis.y;
1023 rotation.m[1][3] = 0;
1024 rotation.m[2][0] = yAxis.x;
1025 rotation.m[2][1] =-yAxis.z;
1026 rotation.m[2][2] = yAxis.y;
1027 rotation.m[2][3] = 0;
1028 rotation.m[3][0] = 0;
1029 rotation.m[3][1] = 0;
1030 rotation.m[3][2] = 0;
1031 rotation.m[3][3] = 1;
1035 inverse.Transpose(rotation);
1037 temp.Translate(-center.x, center.z, -center.y);
1038 temp2.Multiply(temp, inverse);
1039 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
1043 object.transform.scaling = scaling;
1044 // TODO: Improve language to support deep properties on non constant functions
1045 // object.transform.orientation.RotationMatrix(rotation);
1047 Quaternion orientation;
1048 orientation.RotationMatrix(rotation);
1049 object.transform.orientation = orientation;
1051 object.transform.position = { center.x, -center.z, center.y };
1054 // Localize All Vertices
1055 for(c = 0; c<mesh.nVertices; c++)
1057 Vector3Df vertex = mesh.vertices[c];
1059 mesh.vertices[c].MultMatrix(vertex, inverse);
1061 mesh.vertices[c].x -= object.pivot.x;
1062 mesh.vertices[c].y -= object.pivot.y;
1063 mesh.vertices[c].z -= object.pivot.z;
1072 static bool ReadMap(FileInfo * info, Material mat)
1074 DisplaySystem displaySystem = info->displaySystem;
1075 switch(info->chunkId)
1080 char location[MAX_LOCATION];
1081 char bumpName[MAX_LOCATION+5];
1083 ReadASCIIZ(info->f, &name);
1085 strcpy(location, info->textureDirectory);
1086 PathCat(location, name);
1087 if(!FileExists(location))
1089 // Attempt all lowercase if original case does not exist
1091 strcpy(location, info->textureDirectory);
1092 PathCat(location, name);
1094 strcpy(bumpName, "BUMP:");
1095 strcat(bumpName, location);
1097 if(info->parent->chunkId == MAT_BUMPMAP)
1099 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1100 if(displaySystem.GetTexture(location))
1101 mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1102 else if(!mat.bumpMap)
1104 mat.bumpMap = displaySystem.GetTexture(bumpName);
1107 mat.bumpMap = Bitmap { };
1108 if(!mat.bumpMap.Load(location, null, null) ||
1109 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1110 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1114 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1115 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1118 for(y = 0; y < bh; y++)
1119 for(x = 0; x < bw; x++)
1121 uint bc = y * bw + x;
1122 Color color = picture[bc].color;
1123 picture[bc] = { 255, { color.r, color.g, color.b } };
1129 else if(info->parent->chunkId == MAT_SPECULARMAP)
1131 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1132 char specName[MAX_LOCATION+5];
1133 strcpy(specName, "SPEC:");
1134 strcat(specName, location);
1135 if(!mat.specularMap)
1137 mat.specularMap = displaySystem.GetTexture(specName);
1138 if(!mat.specularMap)
1140 mat.specularMap = Bitmap { };
1141 if(!mat.specularMap.Load(location, null, null) ||
1142 !mat.specularMap.Convert(null, pixelFormat888, null) ||
1143 !displaySystem.AddTexture(specName, mat.specularMap))
1144 delete mat.specularMap;
1147 ColorAlpha * picture = (ColorAlpha *)mat.specularMap.picture;
1148 int bw = mat.specularMap.width, bh = mat.specularMap.height;
1151 for(y = 0; y < bh; y++)
1152 for(x = 0; x < bw; x++)
1154 uint bc = y * bw + x;
1155 Color color = picture[bc].color;
1156 picture[bc] = { 255, { color.r, color.g, color.b } };
1164 Bitmap opacityMap = null;
1165 bool alphaOnly = true;
1166 bool translucent = false;
1169 mat.baseMap = displaySystem.GetTexture(location);
1172 mat.baseMap = Bitmap { };
1173 if(displaySystem.GetTexture(bumpName))
1175 mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1177 if(!mat.baseMap.Load(location, null, null) ||
1178 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1179 !displaySystem.AddTexture(location, mat.baseMap))
1183 opacityMap = mat.baseMap;
1186 else if(info->parent->chunkId == MAT_MAPOPACITY)
1188 opacityMap = Bitmap { };
1189 if(opacityMap.Load(location, null, null))
1191 if(opacityMap.pixelFormat == pixelFormatRGBA)
1193 if(!opacityMap.Convert(null, pixelFormat888, null))
1200 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1202 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1203 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1205 int ow = opacityMap.width, oh = opacityMap.height;
1206 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1208 for(y = 0; y < bh; y++)
1209 for(x = 0; x < bw; x++)
1211 int bc = ((y % bh) * bw + (x % bw));
1212 int oc = ((y % oh) * bw + (x % ow));
1213 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1214 if(alpha && alpha < 255)
1216 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1220 mat.flags.translucent = true;
1221 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1222 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1224 if(opacityMap != mat.baseMap)
1232 MapOptions options = (MapOptions)ReadWORD(info->f);
1233 if(!options.dontTile) mat.flags.tile = true;
1237 mat.uScale = ReadFloat(info->f);
1240 mat.vScale = ReadFloat(info->f);
1246 PrintLn("Unhandled Map block");
1253 static bool ReadMaterial(FileInfo * info, Material mat)
1255 switch(info->chunkId)
1260 char matName[MAX_LOCATION + 100];
1261 strcpy(matName, info->fileName);
1262 ReadASCIIZ(info->f, &name);
1263 strcat(matName, name);
1264 mat.name = CopyString(matName);
1268 case MAT_TRANSPARENCY:
1270 uint16 transparency;
1271 ReadChunks(ReadAmountOf, info, &transparency);
1272 mat.opacity = 1.0f - transparency / 100.0f;
1273 if(mat.opacity < 1.0)
1274 mat.flags.translucent = true;
1279 ReadChunks(ReadRGB, info, &mat.diffuse);
1280 ReadChunks(ReadRGB, info, &mat.diffuse);
1285 ReadChunks(ReadRGB, info, &mat.ambient);
1286 ReadChunks(ReadRGB, info, &mat.ambient);
1291 ReadChunks(ReadRGB, info, &mat.specular);
1292 ReadChunks(ReadRGB, info, &mat.specular);
1298 ReadChunks(ReadAmountOf, info, &emissive);
1299 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1300 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1301 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1304 case MAT_SHINSTRENGTH:
1307 ReadChunks(ReadAmountOf, info, &shininess);
1308 mat.reflectivity = shininess / 100.0f;
1310 mat.specular.r *= shininess / 100.0f;
1311 mat.specular.g *= shininess / 100.0f;
1312 mat.specular.b *= shininess / 100.0f;
1319 ReadChunks(ReadAmountOf, info, &power);
1323 case MAT_MAPTEXTURE1:
1324 ReadChunks(ReadMap, info, mat);
1326 case MAT_MAPOPACITY:
1327 ReadChunks(ReadMap, info, mat);
1329 case MAT_DOUBLESIDED:
1330 mat.flags.doubleSided = true;
1333 ReadChunks(ReadMap, info, mat);
1337 PrintLn("Unhandled MAT type ID", info->chunkId);
1344 static bool ReadLight(FileInfo * info, Object object)
1346 Light * light = &object.light;
1347 switch(info->chunkId)
1350 case RGB_BYTE_GAMMA:
1352 case RGB_FLOAT_GAMMA:
1353 ReadRGB(info, &light->diffuse);
1354 light->specular = light->diffuse;
1359 char targetName[MAXNAMELEN];
1361 strcpy(targetName, object.name);
1362 strcat(targetName, ".target");
1364 light->flags.omni = false;
1365 light->flags.spot = true;
1367 target = Object { };
1368 target.name = CopyString(targetName);
1369 info->rootObject.children.AddName(target);
1370 target.parent = info->rootObject;
1372 light->target = target;
1374 target.transform.position.x = ReadFloat(info->f);
1375 target.transform.position.z = ReadFloat(info->f);
1376 target.transform.position.y =-ReadFloat(info->f);
1378 light->hotSpot = ReadFloat(info->f);
1379 light->fallOff = ReadFloat(info->f);
1384 light->flags.off = true;
1387 case LIT_ATTENUATION:
1391 d = 300, small = 0.001,
1393 d * (Kl + Kq * d) = (1 / small) - Kc
1395 { Kc, Kl, Kq, small, d });
1399 #define MINLIGHT 0.08
1400 light->Kq = 1/(light->end*light->end*MINLIGHT);
1403 #define MINLIGHT 0.15f
1404 // #define MINLIGHT 0.1
1405 //light->Kl = (float)(1/(light->end*MINLIGHT));
1407 float c = 1.0, l = 0.005, q = 0.0005, small = 500, end = light->end;
1408 light->Kc = (c + l * end + q * end * end) / small;
1409 light->Kl = light->Kc * l/c;
1410 light->Kq = light->Kc * q/c;
1411 light->flags.attenuation = true;
1416 light->start = ReadFloat(info->f);
1421 light->end = ReadFloat(info->f);
1424 case LIT_MULTIPLIER:
1426 light->multiplier = ReadFloat(info->f);
1434 static bool ReadCamera(FileInfo * info, Object object)
1436 switch(info->chunkId)
1444 //Camera camera = object.camera;
1445 /*float nearRange = */ReadFloat(info->f);
1446 /*float farRange = */ReadFloat(info->f);
1448 camera.zMin = Max(0.1, nearRange);
1449 camera.zMax = farRange;
1458 static bool ReadEditObject(FileInfo * info, char * name)
1460 DisplaySystem displaySystem = info->displaySystem;
1461 switch(info->chunkId)
1465 Object object = info->rootObject.Find(name);
1468 object = Object { };
1469 object.name = CopyString(name);
1470 info->rootObject.children.AddName(object);
1471 object.parent = info->rootObject;
1473 object.InitializeMesh(displaySystem);
1474 ReadChunks(ReadTriMesh, info, object);
1475 object.flags.mesh = true;
1476 object.mesh.Unlock(0);
1481 Object object = info->rootObject.Find(name);
1487 object = Object { };
1488 object.name = CopyString(name);
1489 info->rootObject.children.AddName(object);
1490 object.parent = info->rootObject;
1492 object.flags.light = true;
1494 light = &object.light;
1495 light->lightObject = object;
1496 light->flags.omni = true;
1497 light->multiplier = 1.0f;
1499 // This is not used?
1500 Read3DVertex(info->f, position);
1501 light->direction = { position.x, position.y, position.z };
1502 info->pos += sizeof(float) * 3;
1504 ReadChunks(ReadLight, info, object);
1509 char targetName[MAXNAMELEN];
1510 Object object = info->rootObject.Find(name);
1513 float /*bankAngle, */focus;
1516 strcpy(targetName, name);
1517 strcat(targetName, ".target");
1518 target = info->rootObject.Find(targetName);
1522 object = Object { };
1523 object.name = CopyString(name);
1524 info->rootObject.children.AddName(object);
1526 object.parent = info->rootObject;
1527 object.camera = Camera { };
1528 object.camera.type = lookAtObject;
1533 target = Object { };
1534 target.name = CopyString(targetName);
1535 info->rootObject.children.AddName(target);
1536 target.parent = info->rootObject;
1539 object.flags.camera = true;
1540 object.cameraTarget = target;
1542 camera = object.camera;
1543 camera.cameraObject = object;
1544 camera.target = target;
1546 //Read3DVertex(info->f, camera.position);
1547 object.transform.position.x = ReadFloat(info->f);
1548 object.transform.position.z = ReadFloat(info->f);
1549 object.transform.position.y =-ReadFloat(info->f);
1551 info->pos += sizeof(float) * 3;
1552 //Read3DVertex(info->f, object.cameraTarget.position);
1553 target.transform.position.x = ReadFloat(info->f);
1554 target.transform.position.z = ReadFloat(info->f);
1555 target.transform.position.y =-ReadFloat(info->f);
1557 info->pos += sizeof(float) * 3;
1558 /*bankAngle = */ReadFloat(info->f);
1559 info->pos += sizeof(float);
1560 focus = ReadFloat(info->f);
1561 info->pos += sizeof(float);
1563 mm = (focus - 5.05659508373109) / 1.13613250717301;
1564 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1566 ReadChunks(ReadCamera, info, object);
1569 case OBJ_HIDDEN: break;
1574 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1576 static bool ReadEditChunks(FileInfo * info, void * data)
1578 switch(info->chunkId)
1582 // Read the ambient color
1583 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1588 Material material { /*flags = { singleSideLight = true }*/ };
1590 ReadChunks(ReadMaterial, info, material);
1592 mat = info->displaySystem.AddNamedMaterial(material.name);
1595 if(material.baseMap)
1596 material.baseMap.MakeMipMaps(info->displaySystem);
1597 if(material.bumpMap)
1598 material.bumpMap.MakeMipMaps(info->displaySystem);
1599 if(material.specularMap)
1600 material.specularMap.MakeMipMaps(info->displaySystem);
1602 // COPY_NITEM(mat, material);
1603 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1607 delete material.baseMap;
1609 delete material.name;
1616 info->pos += ReadASCIIZ(info->f, &name);
1617 ReadChunks(ReadEditObject, info, name);
1625 struct ObjectInfoBlock
1635 // Key Framer Chunks
1636 static class AccelerationFlags : uint32
1638 bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1641 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1643 switch(info->chunkId)
1647 //uint16 flags1, flags2;
1648 ReadASCIIZ(info->f, &block->name);
1649 /*flags1 = */ReadWORD(info->f);
1650 /*flags2 = */ReadWORD(info->f);
1651 block->parent = ReadWORD(info->f);
1655 ReadASCIIZ(info->f, &block->dummyName);
1658 Read3DVertex(info->f, block->pivot);
1661 block->hierarchy = ReadWORD(info->f);
1665 case FRM_TRACKSCALE:
1668 case FRM_TRACKCOLOR:
1669 case FRM_TRACKHOTSPOT:
1670 case FRM_TRACKFALLOFF:
1672 FrameTrack track { };
1679 block->tracks.Add(track);
1681 flags = ReadWORD(info->f);
1683 info->f.Read(unknown, sizeof(unknown), 1);
1685 track.numKeys = ReadDWORD(info->f);
1687 switch(info->chunkId)
1689 case FRM_TRACKPOS: track.type.type = position; break;
1690 case FRM_TRACKROT: track.type.type = rotation; break;
1691 case FRM_TRACKSCALE: track.type.type = scaling; break;
1692 case FRM_TRACKROLL: track.type.type = roll; break;
1693 case FRM_TRACKFOV: track.type.type = fov; break;
1694 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1695 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1696 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1698 if((flags & 0x0003) == 3)
1699 track.type.loop = true;
1701 track.keys = new0 FrameKey[track.numKeys];
1702 for(c = 0; c<track.numKeys; c++)
1704 AccelerationFlags accelerationFlags;
1705 FrameKey * key = track.keys + c;
1707 key->frame = ReadDWORD(info->f);
1708 accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1710 if(accelerationFlags.tension)
1711 key->tension = ReadFloat(info->f);
1712 if(accelerationFlags.continuity)
1713 key->continuity = ReadFloat(info->f);
1714 if(accelerationFlags.bias)
1715 key->bias = ReadFloat(info->f);
1716 if(accelerationFlags.easeTo)
1717 key->easeTo = ReadFloat(info->f);
1718 if(accelerationFlags.easeFrom)
1719 key->easeFrom = ReadFloat(info->f);
1721 switch(info->chunkId)
1726 Read3DVertex(info->f, position);
1727 key->position = { position.x, -position.z, position.y };
1733 Angle angle = ReadFloat(info->f);
1736 Read3DVertex(info->f, axis);
1737 fixedAxis = { axis.x, -axis.z, axis.y };
1741 Quaternion rotation;
1742 rotation.RotationAxis(fixedAxis, angle);
1743 key->orientation.Multiply((key - 1)->orientation, rotation);
1746 key->orientation.RotationAxis(fixedAxis, angle);
1749 case FRM_TRACKSCALE:
1752 Read3DVertex(info->f, scaling);
1753 key->scaling = { scaling.x, scaling.z, scaling.y };
1758 key->fov = ReadFloat(info->f);
1763 key->roll = -ReadFloat(info->f);
1766 case FRM_TRACKCOLOR:
1768 FileInfo childInfo = *info;
1769 childInfo.chunkId = RGB_FLOAT;
1770 ReadRGB(&childInfo, &key->color);
1773 case FRM_TRACKHOTSPOT:
1775 key->hotSpot = ReadFloat(info->f);
1778 case FRM_TRACKFALLOFF:
1780 key->fallOff = ReadFloat(info->f);
1792 static Object FindObjectID(Object object, int id)
1794 Object result = null;
1796 for(child = object.children.first; child; child = child.next)
1798 if(child.flags.hierarchy == (uint16) id)
1805 result = FindObjectID(child, id);
1812 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1814 switch(info->chunkId)
1818 ObjectInfoBlock block { };
1819 Object object = null;
1821 ReadChunks(ReadFrameInfoBlock, info, &block);
1823 if(block.dummyName && block.dummyName[0])
1825 if(!strcmp(block.name, "$$$DUMMY"))
1827 object = Object { };
1828 object.name = block.dummyName;
1829 info->rootObject.children.AddName(object);
1830 object.transform.scaling = { 1,1,1 };
1834 Object model = info->rootObject.Find(block.name);
1837 object = Object { };
1838 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1839 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1840 object.flags = model.flags;
1841 object.flags.ownMesh = false;
1842 object.mesh = model.mesh;
1844 object.min = model.min;
1845 object.max = model.max;
1846 object.radius = model.radius;
1848 object.transform = model.transform;
1849 info->rootObject.children.AddName(object);
1851 delete block.dummyName;
1854 object.parent = info->rootObject;
1857 object = info->rootObject.Find(block.name);
1861 Mesh mesh = object.mesh;
1862 object.flags.hierarchy = block.hierarchy + 1;
1863 if(block.parent != -1)
1865 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1868 object.parent.children.Remove(object);
1869 parent.children.AddName(object);
1870 object.parent = parent;
1873 object.pivot.x = block.pivot.x;
1874 object.pivot.y =-block.pivot.z;
1875 object.pivot.z = block.pivot.y;
1877 if(mesh && object.flags.ownMesh)
1879 if(mesh.Lock({ vertices = true }))
1882 // Take pivot into account
1883 for(c = 0; c<mesh.nVertices; c++)
1885 mesh.vertices[c].x -= object.pivot.x;
1886 mesh.vertices[c].y -= object.pivot.y;
1887 mesh.vertices[c].z -= object.pivot.z;
1889 mesh.Unlock({ vertices = true });
1893 object.tracks = block.tracks;
1894 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1895 object.endFrame = info->rootObject.endFrame;
1898 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1904 ObjectInfoBlock block { };
1905 Object object = null;
1907 ReadChunks(ReadFrameInfoBlock, info, &block);
1909 if(block.dummyName && block.dummyName[0])
1911 if(!strcmp(block.name, "$$$DUMMY"))
1913 object = Object { };
1914 object.name = block.dummyName;
1915 info->rootObject.children.AddName(object);
1916 object.transform.scaling = { 1, 1, 1 };
1917 object.flags.camera = true;
1921 Object model = info->rootObject.Find(block.name);
1924 object = Object { };
1925 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1926 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1927 object.flags = model.flags;
1928 object.flags.ownMesh = false;
1929 object.camera = model.camera;
1930 object.flags.camera = true;
1931 info->rootObject.children.AddName(object);
1933 delete block.dummyName;
1936 object.parent = info->rootObject;
1939 object = info->rootObject.Find(block.name);
1943 object.flags.hierarchy = block.hierarchy + 1;
1944 if(block.parent != -1)
1946 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1949 object.parent.children.Remove(object);
1950 parent.children.AddName(object);
1951 object.parent = parent;
1954 object.pivot.x = block.pivot.x;
1955 object.pivot.y =-block.pivot.z;
1956 object.pivot.z = block.pivot.y;
1958 object.tracks = block.tracks;
1959 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1960 object.endFrame = info->rootObject.endFrame;
1963 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1967 case FRM_CAMERATARGET:
1969 ObjectInfoBlock block { };
1970 Object object = null;
1971 char targetName[MAXNAMELEN];
1973 ReadChunks(ReadFrameInfoBlock, info, &block);
1975 strcpy(targetName, block.name);
1976 strcat(targetName, ".target");
1978 if(block.dummyName && block.dummyName[0])
1980 if(!strcmp(block.name, "$$$DUMMY"))
1982 object = Object { };
1983 object.name = block.dummyName;
1984 info->rootObject.children.AddName(object);
1985 object.transform.scaling = { 1,1,1 };
1989 Object model = info->rootObject.Find(targetName);
1992 object = Object { };
1993 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1994 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1995 object.flags = model.flags;
1996 object.flags.ownMesh = false;
1997 object.camera = model.camera;
1998 info->rootObject.children.AddName(object);
2000 delete block.dummyName;
2003 object.parent = info->rootObject;
2006 object = info->rootObject.Find(targetName);
2010 object.flags.hierarchy = block.hierarchy + 1;
2011 if(block.parent != -1)
2013 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2016 object.parent.children.Remove(object);
2017 parent.children.AddName(object);
2018 object.parent = parent;
2021 object.pivot.x = block.pivot.x;
2022 object.pivot.y =-block.pivot.z;
2023 object.pivot.z = block.pivot.y;
2025 object.tracks = block.tracks;
2026 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2027 object.endFrame = info->rootObject.endFrame;
2030 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2036 ObjectInfoBlock block { };
2038 ReadChunks(ReadFrameInfoBlock, info, &block);
2040 info->rootObject.tracks = block.tracks;
2046 ObjectInfoBlock block { };
2047 Object object = null;
2049 ReadChunks(ReadFrameInfoBlock, info, &block);
2051 if(block.dummyName && block.dummyName[0])
2053 if(!strcmp(block.name, "$$$DUMMY"))
2055 object = Object { };
2056 object.name = block.dummyName;
2057 info->rootObject.children.AddName(object);
2058 object.transform.scaling = { 1, 1, 1 };
2059 object.flags.light = true;
2063 Object model = info->rootObject.Find(block.name);
2066 object = Object { };
2067 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2068 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2069 object.flags = model.flags;
2070 object.flags.ownMesh = false;
2071 object.light = model.light;
2072 object.flags.light = true;
2073 info->rootObject.children.AddName(object);
2075 delete block.dummyName;
2078 object.parent = info->rootObject;
2081 object = info->rootObject.Find(block.name);
2085 object.flags.hierarchy = block.hierarchy + 1;
2086 if(block.parent != -1)
2088 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2091 object.parent.children.Remove(object);
2092 parent.children.AddName(object);
2093 object.parent = parent;
2096 object.pivot.x = block.pivot.x;
2097 object.pivot.y =-block.pivot.z;
2098 object.pivot.z = block.pivot.y;
2100 object.tracks = block.tracks;
2101 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2102 object.endFrame = info->rootObject.endFrame;
2105 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2109 case FRM_SPOTLIGHTTARGET:
2111 ObjectInfoBlock block { };
2112 Object object = null;
2113 char targetName[MAXNAMELEN];
2115 ReadChunks(ReadFrameInfoBlock, info, &block);
2117 strcpy(targetName, block.name);
2118 strcat(targetName, ".target");
2120 if(block.dummyName && block.dummyName[0])
2122 if(!strcmp(block.name, "$$$DUMMY"))
2124 object = Object { };
2125 object.name = block.dummyName;
2126 info->rootObject.children.AddName(object);
2127 object.transform.scaling = { 1,1,1 };
2131 Object model = info->rootObject.Find(targetName);
2134 object = Object { };
2135 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2136 // TODO: When passing a String to a const String, use member if property is const String but member is String
2137 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2138 object.flags = model.flags;
2139 object.flags.ownMesh = false;
2140 object.light = model.light;
2141 info->rootObject.children.AddName(object);
2143 delete block.dummyName;
2146 object.parent = info->rootObject;
2149 object = info->rootObject.Find(targetName);
2153 object.flags.hierarchy = block.hierarchy + 1;
2154 if(block.parent != -1)
2156 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2159 object.parent.children.Remove(object);
2160 parent.children.AddName(object);
2161 object.parent = parent;
2164 object.pivot.x = block.pivot.x;
2165 object.pivot.y =-block.pivot.z;
2166 object.pivot.z = block.pivot.y;
2168 object.tracks = block.tracks;
2169 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2170 object.endFrame = info->rootObject.endFrame;
2173 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2179 info->rootObject.startFrame = ReadDWORD(info->f);
2180 info->rootObject.endFrame = ReadDWORD(info->f);
2181 *&(info->rootObject.frame) = info->rootObject.startFrame;
2189 static bool ReadMainChunks(FileInfo * info, void * data)
2191 switch(info->chunkId)
2194 ReadChunks(ReadEditChunks, info, null);
2198 Object object = data;
2199 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2201 ReadChunks(ReadKeyFrameChunks, info, null);
2202 object.flags.keysLoaded = true;
2210 static bool ReadMain(FileInfo * info, void * data)
2212 switch(info->chunkId)
2215 ReadChunks(ReadMainChunks, info, data);
2221 class Object3DSFormat : ObjectFormat
2223 class_property(extension) = "3ds";
2225 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2227 bool result = false;
2230 FileInfo info = {0};
2231 info.rootObject = object;
2232 info.displaySystem = displaySystem;
2235 info.fileName = fileName;
2236 StripLastDirectory(fileName, info.textureDirectory);
2237 info.f = FileOpen(fileName, read);
2240 // TOFIX: eC reorders that badly
2241 // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2242 if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2244 object.flags.root = true;
2245 object.SetMinMaxRadius(true);
2246 object._Animate(object.frame);
2247 object.UpdateTransform();
2253 info.matFaces.Free();
2254 delete info.matFaces;
2257 object.Free(displaySystem);