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 });
384 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
385 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
386 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
388 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
389 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
391 nNewVertices = nVertices;
392 mVertices = mesh->vertices;
394 for(c = 0; c<info->nFaces; c++)
396 Face * face = &faces[c];
398 uint * indices = face->indices;
399 Vector3D edges[3], rEdges[3];
400 computeNormalWeights(3, mVertices, indices, true, 0, face->dots, edges, rEdges);
402 plane.FromPointsf(mVertices[indices[2]], mVertices[indices[1]], mVertices[indices[0]]);
403 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
406 for(c = 0; c < info->nFaces; c++)
408 Face * face = &faces[c];
412 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
413 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
418 SharedSourceVertexInfo * source;
419 SharedDestVertexInfo svInfo;
420 DestVertexInfo vInfo;
422 index = face->indices[i];
424 if(face->smoothGroups)
425 itShared.Index({ index = index, mVertices[index], face = face }, true);
427 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
428 svInfo = itShared.data;
429 if(!svInfo) itShared.data = svInfo = { };
432 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
433 // TODO: Allow obtaining address of MapIterator::key
434 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
435 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
441 vInfo.copyFromIndex = index;
446 vertices[index] = (void *)it.pointer;
447 else if(vertices[index] != it.pointer)
449 // If it's a different smoothing group, we'll need extra vertices
450 index = vertices.size;
452 vertices.Add((void *)it.pointer);
455 face->indices[i] = vInfo.index;
459 for(index = 0; index < nNewVertices; index++)
461 it.pointer = vertices[index];
466 DestVertexInfo vInfo = it.data;
468 Vector3D tangent1 { };
469 Vector3D tangent2 { };
470 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
471 uint smoothing = inf->smoothGroups;
473 SharedSourceVertexInfo * shared = inf->shared;
474 SharedDestVertexInfo svInfo = sharedVertices[*shared];
476 if(!svInfo || vInfo.index != index)
479 for(i : svInfo.faces)
481 Face * face = &info->faces[i];
483 if(smoothing & face->smoothGroups)
484 smoothing |= face->smoothGroups;
487 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
488 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
490 for(i : svInfo.faces)
492 Face * face = &info->faces[i];
493 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
496 for(j = 0; j < 3; j++)
498 if(face->indices[j] == vInfo.index)
500 origIndex = face->origIndices[j];
502 normal.x += face->normal.x;
503 normal.y += face->normal.y;
504 normal.z += face->normal.z;
517 for(i : svInfo.faces)
519 Face * face = &info->faces[i];
520 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
524 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
529 for(k = 0; k < 3; k++)
531 if(face->indices[k] == vInfo.index)
533 origIndexB = face->origIndices[k];
537 valid = origIndex == origIndexB;
540 for(j : svInfo.faces)
542 if(info->faces[j].done)
544 double dot = info->faces[j].normal.DotProduct(face->normal);
545 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
546 valid = fabs(dot) > cutOff;
555 uint * indices = face->origIndices;
556 uint ix = vInfo.copyFromIndex;
557 uint ix0 = indices[0], ix1 = indices[1], ix2 = indices[2];
558 double dot = ix == ix0 ? face->dots[0] : ix == ix1 ? face->dots[1] : ix == ix2 ? face->dots[2] : 1;
559 if(ix != indices[0] && ix != indices[1] && ix != indices[2])
561 if(!mVertices[ix0].OnCompare(mVertices[ix])) dot = face->dots[0];
562 else if(!mVertices[ix1].OnCompare(mVertices[ix])) dot = face->dots[1];
563 else if(!mVertices[ix2].OnCompare(mVertices[ix])) dot = face->dots[2];
565 normal.x += face->normal.x * dot;
566 normal.y += face->normal.y * dot;
567 normal.z += face->normal.z * dot;
571 Vector3Df * p0 = &mVertices[ix0], * p1 = &mVertices[ix1], * p2 = &mVertices[ix2];
572 Pointf * t0 = &mesh->texCoords[ix0], * t1 = &mesh->texCoords[ix1], * t2 = &mesh->texCoords[ix2];
573 Vector3D v01 { (double)p1->x - (double)p0->x, (double)p1->y - (double)p0->y, (double)p1->z - (double)p0->z };
574 Vector3D v02 { (double)p2->x - (double)p0->x, (double)p2->y - (double)p0->y, (double)p2->z - (double)p0->z };
576 Vector3D t01 { (double)t1->x - (double)t0->x, (double)t1->y - (double)t0->y };
577 Vector3D t02 { (double)t2->x - (double)t0->x, (double)t2->y - (double)t0->y };
578 double f = dot / (t01.x * t02.y - t02.x * t01.y);
580 tangent1.x += f * (v01.x * t02.y - v02.x * t01.y);
581 tangent1.y += f * (v01.y * t02.y - v02.y * t01.y);
582 tangent1.z += f * (v01.z * t02.y - v02.z * t01.y);
584 tangent2.x += f * (v02.x * t01.x - v01.x * t02.x);
585 tangent2.y += f * (v02.y * t01.x - v01.y * t02.x);
586 tangent2.z += f * (v02.z * t01.x - v01.z * t02.x);
596 if(!SMOOTH_CUTOFF || !(WELD_TRESHOLD > 0)) break;
598 // normal.Scale(normal, 1.0f / numShared);
599 normal.Scale(normal, 1.0 / dotSum);
600 normal.Normalize(normal);
602 //tangent1.Scale(tangent1, 1.0 / dotSum);
603 tangent1.Normalize(tangent1);
605 //tangent2.Scale(tangent2, 1.0 / dotSum);
606 tangent2.Normalize(tangent2);
608 if(vInfo.index == index)
610 vInfo.normal = { (float)normal.x, (float)normal.y, (float)normal.z };
611 vInfo.tangent1 = { (float)tangent1.x, (float)tangent1.y, (float)tangent1.z };
612 vInfo.tangent2 = { (float)tangent2.x, (float)tangent2.y, (float)tangent2.z };
615 // Auto welding/smoothing requires extra vertices because angle is too steep
616 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
618 SharedDestVertexInfo newSharedInfo = null;
620 for(i : svInfo.faces)
622 Face * face = &info->faces[i];
623 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
626 for(j = 0; j < 3; j++)
628 if(face->indices[j] == vInfo.index)
632 DestVertexInfo newVert;
633 SharedSourceVertexInfo * source;
635 index = nNewVertices++;
636 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
637 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
638 itShared.data = newSharedInfo = { };
640 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
643 newVert.copyFromIndex = vInfo.copyFromIndex;
644 newVert.index = index;
646 vertices.Add((void *)it.pointer);
648 face->indices[j] = index;
649 newSharedInfo.faces.Add(i);
659 // Allocate some extra vertices
661 Vector3Df * oldVertices = mesh.vertices;
662 Pointf * oldTexCoords = mesh.texCoords;
664 // TODO: Support reallocation?
665 *((void **)&mesh.vertices) = null;
666 *((void **)&mesh.texCoords) = null;
667 *((int *)&mesh.nVertices) = 0;
669 mesh.Allocate( { vertices = true, normals = true, tangents = oldTexCoords ? true : false, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
671 // Fill in the new vertices
672 for(index = 0; index < nNewVertices; index++)
674 DestVertexInfo vInfo;
675 it.pointer = vertices[index];
679 mesh.normals[index] = vInfo ? vInfo.normal : { };
680 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
683 mesh.tangents[2*index+0] = vInfo ? vInfo.tangent1 : { };
684 mesh.tangents[2*index+1] = vInfo ? vInfo.tangent2 : { };
687 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
696 for(i = 0; i < info->nFaces; i++)
697 info->faces[i].done = false;
700 mesh.Unlock({ normals = true, tangents = true });
702 // Free all the temporary stuff
707 sharedVertices.Free();
708 delete sharedVertices;
712 static bool ReadSmoothing(FileInfo * info, Object object)
714 switch(info->chunkId)
719 for(c = 0; c<info->nFaces; c++)
720 info->faces[c].smoothGroups = ReadDWORD(info->f);
727 static bool ReadFacesListChunks(FileInfo * info, Object object)
729 DisplaySystem displaySystem = info->displaySystem;
730 switch(info->chunkId)
739 char matName[MAX_LOCATION + 100];
741 strcpy(matName, info->fileName);
742 ReadASCIIZ(info->f, &name);
743 count = ReadWORD(info->f);
744 strcat(matName, name);
746 mat = displaySystem.GetMaterial(matName);
747 faces = info->matFaces[(uintptr)mat];
749 info->matFaces[(uintptr)mat] = faces = { };
753 for(c = 0; c<count; c++)
755 uint16 face = ReadWORD(info->f);
757 info->faces[face].material = mat;
766 static bool ReadTriMesh(FileInfo * info, Object object)
768 Mesh mesh = object.mesh;
769 switch(info->chunkId)
774 uint16 nVertices = ReadWORD(info->f);
775 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
776 *((int *) &mesh.nVertices) = nVertices;
777 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
780 for(c = 0; c<mesh.nVertices; c++)
783 Read3DVertex(info->f, vertex);
784 mesh.vertices[c].x = vertex.x;
785 mesh.vertices[c].y =-vertex.z;
786 mesh.vertices[c].z = vertex.y;
793 case TRI_MAPPINGCOORS:
796 uint16 count = ReadWORD(info->f);
797 count = (uint16)Min(mesh.nVertices, count);
799 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
800 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
801 mesh.flags.texCoords1 = true;
804 for(c = 0; c<count; c++)
806 mesh.texCoords[c].x = ReadFloat(info->f);
807 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
821 info->nFaces = nFaces = ReadWORD(info->f);
822 info->pos += sizeof(uint16);
824 info->faces = new0 Face[nFaces];
825 for(c = 0; c<nFaces; c++)
829 info->faces[c].origIndices[i] =
830 info->faces[c].indices[i] = ReadWORD(info->f);
832 info->pos += 4*sizeof(uint16);
835 ReadChunks(ReadSmoothing, info, object);
839 info->matFaces.Free();
840 info->matFaces = { };
842 ReadChunks(ReadFacesListChunks, info, object);
844 ComputeNormals(mesh, info, object);
847 for(m : info->matFaces)
849 Material mat = (Material)&m;
850 Array<int> faces = m;
851 if(mat.flags.translucent)
853 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
856 Face * face = &info->faces[i];
857 PrimitiveSingle * triangle;
859 triangle = &mesh.primitives[mesh.nPrimitives++];
860 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
862 triangle->indicesMember[0] = (uintindex)face->indices[0];
863 triangle->indicesMember[1] = (uintindex)face->indices[1];
864 triangle->indicesMember[2] = (uintindex)face->indices[2];
865 triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
866 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
867 triangle->plane.FromPointsf(
868 mesh.vertices[triangle->indicesMember[2]],
869 mesh.vertices[triangle->indicesMember[1]],
870 mesh.vertices[triangle->indicesMember[0]]);
872 mesh.UnlockPrimitive(triangle);
874 triangle->middle.x /= 3;
875 triangle->middle.y /= 3;
876 triangle->middle.z /= 3;
878 triangle->material = mat;
881 object.flags.translucent = true;
886 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
890 group.material = mat;
893 Face * face = &info->faces[i];
895 if(object.flags.flipWindings)
897 group.indicesMember[c*3] = (uintindex)face->indices[2];
898 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
899 group.indicesMember[c*3+2] = (uintindex)face->indices[0];
903 group.indicesMember[c*3] = (uintindex)face->indices[0];
904 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
905 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
910 mesh.UnlockPrimitiveGroup(group);
915 // Add faces without a material all together
917 for(c = 0; c<nFaces; c++)
918 if(!info->faces[c].done)
922 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
925 for(c = 0; c<nFaces; c++)
927 Face * face = &info->faces[c];
930 group.indicesMember[c*3] = (uintindex)face->indices[0];
931 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
932 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
935 mesh.UnlockPrimitiveGroup(group);
942 info->matFaces.Free();
943 delete info->matFaces;
950 Vector3Df xAxis, yAxis, zAxis, center;
953 Matrix inverse/*, source = { 0 }*/;
956 Read3DVertex(info->f, xAxis);
957 Read3DVertex(info->f, yAxis);
958 Read3DVertex(info->f, zAxis);
959 Read3DVertex(info->f, center);
961 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
962 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
963 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
965 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
968 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
969 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
970 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
971 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
973 inverse.Inverse(source);
976 object.flags.flipWindings = false;
978 xAxis.Normalize(xAxis);
979 yAxis.Normalize(yAxis);
980 zAxis.Normalize(zAxis);
982 orth.CrossProduct(yAxis, zAxis);
983 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
984 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
985 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
987 object.flags.flipWindings ^= true;
991 orth.CrossProduct(zAxis, xAxis);
992 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
993 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
994 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
996 object.flags.flipWindings ^= true;
1000 orth.CrossProduct(xAxis, yAxis);
1001 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
1002 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
1003 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
1005 object.flags.flipWindings ^= true;
1012 rotation.m[0][0] = xAxis.x;
1013 rotation.m[0][1] =-xAxis.z;
1014 rotation.m[0][2] = xAxis.y;
1015 rotation.m[0][3] = 0;
1016 rotation.m[1][0] =-zAxis.x;
1017 rotation.m[1][1] = zAxis.z;
1018 rotation.m[1][2] =-zAxis.y;
1019 rotation.m[1][3] = 0;
1020 rotation.m[2][0] = yAxis.x;
1021 rotation.m[2][1] =-yAxis.z;
1022 rotation.m[2][2] = yAxis.y;
1023 rotation.m[2][3] = 0;
1024 rotation.m[3][0] = 0;
1025 rotation.m[3][1] = 0;
1026 rotation.m[3][2] = 0;
1027 rotation.m[3][3] = 1;
1031 inverse.Transpose(rotation);
1033 temp.Translate(-center.x, center.z, -center.y);
1034 temp2.Multiply(temp, inverse);
1035 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
1039 object.transform.scaling = scaling;
1040 // TODO: Improve language to support deep properties on non constant functions
1041 // object.transform.orientation.RotationMatrix(rotation);
1043 Quaternion orientation;
1044 orientation.RotationMatrix(rotation);
1045 object.transform.orientation = orientation;
1047 object.transform.position = { center.x, -center.z, center.y };
1050 // Localize All Vertices
1051 for(c = 0; c<mesh.nVertices; c++)
1053 Vector3Df vertex = mesh.vertices[c];
1055 mesh.vertices[c].MultMatrix(vertex, inverse);
1057 mesh.vertices[c].x -= object.pivot.x;
1058 mesh.vertices[c].y -= object.pivot.y;
1059 mesh.vertices[c].z -= object.pivot.z;
1068 static bool ReadMap(FileInfo * info, Material mat)
1070 DisplaySystem displaySystem = info->displaySystem;
1071 switch(info->chunkId)
1076 char location[MAX_LOCATION];
1077 char bumpName[MAX_LOCATION+5];
1079 ReadASCIIZ(info->f, &name);
1081 strcpy(location, info->textureDirectory);
1082 PathCat(location, name);
1083 if(!FileExists(location))
1085 // Attempt all lowercase if original case does not exist
1087 strcpy(location, info->textureDirectory);
1088 PathCat(location, name);
1090 strcpy(bumpName, "BUMP:");
1091 strcat(bumpName, location);
1093 if(info->parent->chunkId == MAT_BUMPMAP)
1095 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1096 if(displaySystem.GetTexture(location))
1097 mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1098 else if(!mat.bumpMap)
1100 mat.bumpMap = displaySystem.GetTexture(bumpName);
1103 mat.bumpMap = Bitmap { };
1104 if(!mat.bumpMap.Load(location, null, null) ||
1105 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1106 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1110 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1111 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1114 for(y = 0; y < bh; y++)
1115 for(x = 0; x < bw; x++)
1117 uint bc = y * bw + x;
1118 Color color = picture[bc].color;
1119 picture[bc] = { 255, { color.r, color.g, color.b } };
1125 else if(info->parent->chunkId == MAT_SPECULARMAP)
1127 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1128 char specName[MAX_LOCATION+5];
1129 strcpy(specName, "SPEC:");
1130 strcat(specName, location);
1131 if(!mat.specularMap)
1133 mat.specularMap = displaySystem.GetTexture(specName);
1134 if(!mat.specularMap)
1136 mat.specularMap = Bitmap { };
1137 if(!mat.specularMap.Load(location, null, null) ||
1138 !mat.specularMap.Convert(null, pixelFormat888, null) ||
1139 !displaySystem.AddTexture(specName, mat.specularMap))
1140 delete mat.specularMap;
1143 ColorAlpha * picture = (ColorAlpha *)mat.specularMap.picture;
1144 int bw = mat.specularMap.width, bh = mat.specularMap.height;
1147 for(y = 0; y < bh; y++)
1148 for(x = 0; x < bw; x++)
1150 uint bc = y * bw + x;
1151 Color color = picture[bc].color;
1152 picture[bc] = { 255, { color.r, color.g, color.b } };
1160 Bitmap opacityMap = null;
1161 bool alphaOnly = true;
1162 bool translucent = false;
1165 mat.baseMap = displaySystem.GetTexture(location);
1168 mat.baseMap = Bitmap { };
1169 if(displaySystem.GetTexture(bumpName))
1171 mat.bumpMap = null; // Bad bump map if it's the same as the diffuse map...
1173 if(!mat.baseMap.Load(location, null, null) ||
1174 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1175 !displaySystem.AddTexture(location, mat.baseMap))
1179 opacityMap = mat.baseMap;
1182 else if(info->parent->chunkId == MAT_MAPOPACITY)
1184 opacityMap = Bitmap { };
1185 if(opacityMap.Load(location, null, null))
1187 if(opacityMap.pixelFormat == pixelFormatRGBA)
1189 if(!opacityMap.Convert(null, pixelFormat888, null))
1196 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1198 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1199 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1201 int ow = opacityMap.width, oh = opacityMap.height;
1202 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1204 for(y = 0; y < bh; y++)
1205 for(x = 0; x < bw; x++)
1207 int bc = ((y % bh) * bw + (x % bw));
1208 int oc = ((y % oh) * bw + (x % ow));
1209 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1210 if(alpha && alpha < 255)
1212 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1216 mat.flags.translucent = true;
1217 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1218 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1220 if(opacityMap != mat.baseMap)
1228 MapOptions options = (MapOptions)ReadWORD(info->f);
1229 if(!options.dontTile) mat.flags.tile = true;
1233 mat.uScale = ReadFloat(info->f);
1236 mat.vScale = ReadFloat(info->f);
1242 PrintLn("Unhandled Map block");
1249 static bool ReadMaterial(FileInfo * info, Material mat)
1251 switch(info->chunkId)
1256 char matName[MAX_LOCATION + 100];
1257 strcpy(matName, info->fileName);
1258 ReadASCIIZ(info->f, &name);
1259 strcat(matName, name);
1260 mat.name = CopyString(matName);
1264 case MAT_TRANSPARENCY:
1266 uint16 transparency;
1267 ReadChunks(ReadAmountOf, info, &transparency);
1268 mat.opacity = 1.0f - transparency / 100.0f;
1269 if(mat.opacity < 1.0)
1270 mat.flags.translucent = true;
1275 ReadChunks(ReadRGB, info, &mat.diffuse);
1276 ReadChunks(ReadRGB, info, &mat.diffuse);
1281 ReadChunks(ReadRGB, info, &mat.ambient);
1282 ReadChunks(ReadRGB, info, &mat.ambient);
1287 ReadChunks(ReadRGB, info, &mat.specular);
1288 ReadChunks(ReadRGB, info, &mat.specular);
1294 ReadChunks(ReadAmountOf, info, &emissive);
1295 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1296 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1297 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1300 case MAT_SHINSTRENGTH:
1303 ReadChunks(ReadAmountOf, info, &shininess);
1304 mat.reflectivity = shininess / 100.0f;
1306 mat.specular.r *= shininess / 100.0f;
1307 mat.specular.g *= shininess / 100.0f;
1308 mat.specular.b *= shininess / 100.0f;
1315 ReadChunks(ReadAmountOf, info, &power);
1319 case MAT_MAPTEXTURE1:
1320 ReadChunks(ReadMap, info, mat);
1322 case MAT_MAPOPACITY:
1323 ReadChunks(ReadMap, info, mat);
1325 case MAT_DOUBLESIDED:
1326 mat.flags.doubleSided = true;
1329 ReadChunks(ReadMap, info, mat);
1333 PrintLn("Unhandled MAT type ID", info->chunkId);
1340 static bool ReadLight(FileInfo * info, Object object)
1342 Light * light = &object.light;
1343 switch(info->chunkId)
1346 case RGB_BYTE_GAMMA:
1348 case RGB_FLOAT_GAMMA:
1349 ReadRGB(info, &light->diffuse);
1350 light->specular = light->diffuse;
1355 char targetName[MAXNAMELEN];
1357 strcpy(targetName, object.name);
1358 strcat(targetName, ".target");
1360 light->flags.omni = false;
1361 light->flags.spot = true;
1363 target = Object { };
1364 target.name = CopyString(targetName);
1365 info->rootObject.children.AddName(target);
1366 target.parent = info->rootObject;
1368 light->target = target;
1370 target.transform.position.x = ReadFloat(info->f);
1371 target.transform.position.z = ReadFloat(info->f);
1372 target.transform.position.y =-ReadFloat(info->f);
1374 light->hotSpot = ReadFloat(info->f);
1375 light->fallOff = ReadFloat(info->f);
1380 light->flags.off = true;
1383 case LIT_ATTENUATION:
1387 d = 300, small = 0.001,
1389 d * (Kl + Kq * d) = (1 / small) - Kc
1391 { Kc, Kl, Kq, small, d });
1395 #define MINLIGHT 0.08
1396 light->Kq = 1/(light->end*light->end*MINLIGHT);
1399 #define MINLIGHT 0.15f
1400 // #define MINLIGHT 0.1
1401 //light->Kl = (float)(1/(light->end*MINLIGHT));
1403 float c = 1.0, l = 0.005, q = 0.0005, small = 500, end = light->end;
1404 light->Kc = (c + l * end + q * end * end) / small;
1405 light->Kl = light->Kc * l/c;
1406 light->Kq = light->Kc * q/c;
1407 light->flags.attenuation = true;
1412 light->start = ReadFloat(info->f);
1417 light->end = ReadFloat(info->f);
1420 case LIT_MULTIPLIER:
1422 light->multiplier = ReadFloat(info->f);
1430 static bool ReadCamera(FileInfo * info, Object object)
1432 switch(info->chunkId)
1440 //Camera camera = object.camera;
1441 /*float nearRange = */ReadFloat(info->f);
1442 /*float farRange = */ReadFloat(info->f);
1444 camera.zMin = Max(0.1, nearRange);
1445 camera.zMax = farRange;
1454 static bool ReadEditObject(FileInfo * info, char * name)
1456 DisplaySystem displaySystem = info->displaySystem;
1457 switch(info->chunkId)
1461 Object object = info->rootObject.Find(name);
1464 object = Object { };
1465 object.name = CopyString(name);
1466 info->rootObject.children.AddName(object);
1467 object.parent = info->rootObject;
1469 object.InitializeMesh(displaySystem);
1470 ReadChunks(ReadTriMesh, info, object);
1471 object.flags.mesh = true;
1472 object.mesh.Unlock(0);
1477 Object object = info->rootObject.Find(name);
1483 object = Object { };
1484 object.name = CopyString(name);
1485 info->rootObject.children.AddName(object);
1486 object.parent = info->rootObject;
1488 object.flags.light = true;
1490 light = &object.light;
1491 light->lightObject = object;
1492 light->flags.omni = true;
1493 light->multiplier = 1.0f;
1495 // This is not used?
1496 Read3DVertex(info->f, position);
1497 light->direction = { position.x, position.y, position.z };
1498 info->pos += sizeof(float) * 3;
1500 ReadChunks(ReadLight, info, object);
1505 char targetName[MAXNAMELEN];
1506 Object object = info->rootObject.Find(name);
1509 float /*bankAngle, */focus;
1512 strcpy(targetName, name);
1513 strcat(targetName, ".target");
1514 target = info->rootObject.Find(targetName);
1518 object = Object { };
1519 object.name = CopyString(name);
1520 info->rootObject.children.AddName(object);
1522 object.parent = info->rootObject;
1523 object.camera = Camera { };
1524 object.camera.type = lookAtObject;
1529 target = Object { };
1530 target.name = CopyString(targetName);
1531 info->rootObject.children.AddName(target);
1532 target.parent = info->rootObject;
1535 object.flags.camera = true;
1536 object.cameraTarget = target;
1538 camera = object.camera;
1539 camera.cameraObject = object;
1540 camera.target = target;
1542 //Read3DVertex(info->f, camera.position);
1543 object.transform.position.x = ReadFloat(info->f);
1544 object.transform.position.z = ReadFloat(info->f);
1545 object.transform.position.y =-ReadFloat(info->f);
1547 info->pos += sizeof(float) * 3;
1548 //Read3DVertex(info->f, object.cameraTarget.position);
1549 target.transform.position.x = ReadFloat(info->f);
1550 target.transform.position.z = ReadFloat(info->f);
1551 target.transform.position.y =-ReadFloat(info->f);
1553 info->pos += sizeof(float) * 3;
1554 /*bankAngle = */ReadFloat(info->f);
1555 info->pos += sizeof(float);
1556 focus = ReadFloat(info->f);
1557 info->pos += sizeof(float);
1559 mm = (focus - 5.05659508373109) / 1.13613250717301;
1560 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1562 ReadChunks(ReadCamera, info, object);
1565 case OBJ_HIDDEN: break;
1570 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1572 static bool ReadEditChunks(FileInfo * info, void * data)
1574 switch(info->chunkId)
1578 // Read the ambient color
1579 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1584 Material material { /*flags = { singleSideLight = true }*/ };
1586 ReadChunks(ReadMaterial, info, material);
1588 mat = info->displaySystem.AddNamedMaterial(material.name);
1591 if(material.baseMap)
1592 material.baseMap.MakeMipMaps(info->displaySystem);
1593 if(material.bumpMap)
1594 material.bumpMap.MakeMipMaps(info->displaySystem);
1595 if(material.specularMap)
1596 material.specularMap.MakeMipMaps(info->displaySystem);
1598 // COPY_NITEM(mat, material);
1599 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1603 delete material.baseMap;
1605 delete material.name;
1612 info->pos += ReadASCIIZ(info->f, &name);
1613 ReadChunks(ReadEditObject, info, name);
1621 struct ObjectInfoBlock
1631 // Key Framer Chunks
1632 static class AccelerationFlags : uint32
1634 bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1637 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1639 switch(info->chunkId)
1643 //uint16 flags1, flags2;
1644 ReadASCIIZ(info->f, &block->name);
1645 /*flags1 = */ReadWORD(info->f);
1646 /*flags2 = */ReadWORD(info->f);
1647 block->parent = ReadWORD(info->f);
1651 ReadASCIIZ(info->f, &block->dummyName);
1654 Read3DVertex(info->f, block->pivot);
1657 block->hierarchy = ReadWORD(info->f);
1661 case FRM_TRACKSCALE:
1664 case FRM_TRACKCOLOR:
1665 case FRM_TRACKHOTSPOT:
1666 case FRM_TRACKFALLOFF:
1668 FrameTrack track { };
1675 block->tracks.Add(track);
1677 flags = ReadWORD(info->f);
1679 info->f.Read(unknown, sizeof(unknown), 1);
1681 track.numKeys = ReadDWORD(info->f);
1683 switch(info->chunkId)
1685 case FRM_TRACKPOS: track.type.type = position; break;
1686 case FRM_TRACKROT: track.type.type = rotation; break;
1687 case FRM_TRACKSCALE: track.type.type = scaling; break;
1688 case FRM_TRACKROLL: track.type.type = roll; break;
1689 case FRM_TRACKFOV: track.type.type = fov; break;
1690 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1691 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1692 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1694 if((flags & 0x0003) == 3)
1695 track.type.loop = true;
1697 track.keys = new0 FrameKey[track.numKeys];
1698 for(c = 0; c<track.numKeys; c++)
1700 AccelerationFlags accelerationFlags;
1701 FrameKey * key = track.keys + c;
1703 key->frame = ReadDWORD(info->f);
1704 accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1706 if(accelerationFlags.tension)
1707 key->tension = ReadFloat(info->f);
1708 if(accelerationFlags.continuity)
1709 key->continuity = ReadFloat(info->f);
1710 if(accelerationFlags.bias)
1711 key->bias = ReadFloat(info->f);
1712 if(accelerationFlags.easeTo)
1713 key->easeTo = ReadFloat(info->f);
1714 if(accelerationFlags.easeFrom)
1715 key->easeFrom = ReadFloat(info->f);
1717 switch(info->chunkId)
1722 Read3DVertex(info->f, position);
1723 key->position = { position.x, -position.z, position.y };
1729 Angle angle = ReadFloat(info->f);
1732 Read3DVertex(info->f, axis);
1733 fixedAxis = { axis.x, -axis.z, axis.y };
1737 Quaternion rotation;
1738 rotation.RotationAxis(fixedAxis, angle);
1739 key->orientation.Multiply((key - 1)->orientation, rotation);
1742 key->orientation.RotationAxis(fixedAxis, angle);
1745 case FRM_TRACKSCALE:
1748 Read3DVertex(info->f, scaling);
1749 key->scaling = { scaling.x, scaling.z, scaling.y };
1754 key->fov = ReadFloat(info->f);
1759 key->roll = -ReadFloat(info->f);
1762 case FRM_TRACKCOLOR:
1764 FileInfo childInfo = *info;
1765 childInfo.chunkId = RGB_FLOAT;
1766 ReadRGB(&childInfo, &key->color);
1769 case FRM_TRACKHOTSPOT:
1771 key->hotSpot = ReadFloat(info->f);
1774 case FRM_TRACKFALLOFF:
1776 key->fallOff = ReadFloat(info->f);
1788 static Object FindObjectID(Object object, int id)
1790 Object result = null;
1792 for(child = object.children.first; child; child = child.next)
1794 if(child.flags.hierarchy == (uint16) id)
1801 result = FindObjectID(child, id);
1808 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1810 switch(info->chunkId)
1814 ObjectInfoBlock block { };
1815 Object object = null;
1817 ReadChunks(ReadFrameInfoBlock, info, &block);
1819 if(block.dummyName && block.dummyName[0])
1821 if(!strcmp(block.name, "$$$DUMMY"))
1823 object = Object { };
1824 object.name = block.dummyName;
1825 info->rootObject.children.AddName(object);
1826 object.transform.scaling = { 1,1,1 };
1830 Object model = info->rootObject.Find(block.name);
1833 object = Object { };
1834 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1835 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1836 object.flags = model.flags;
1837 object.flags.ownMesh = false;
1838 object.mesh = model.mesh;
1840 object.min = model.min;
1841 object.max = model.max;
1842 object.radius = model.radius;
1844 object.transform = model.transform;
1845 info->rootObject.children.AddName(object);
1847 delete block.dummyName;
1850 object.parent = info->rootObject;
1853 object = info->rootObject.Find(block.name);
1857 Mesh mesh = object.mesh;
1858 object.flags.hierarchy = block.hierarchy + 1;
1859 if(block.parent != -1)
1861 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1864 object.parent.children.Remove(object);
1865 parent.children.AddName(object);
1866 object.parent = parent;
1869 object.pivot.x = block.pivot.x;
1870 object.pivot.y =-block.pivot.z;
1871 object.pivot.z = block.pivot.y;
1873 if(mesh && object.flags.ownMesh)
1875 if(mesh.Lock({ vertices = true }))
1878 // Take pivot into account
1879 for(c = 0; c<mesh.nVertices; c++)
1881 mesh.vertices[c].x -= object.pivot.x;
1882 mesh.vertices[c].y -= object.pivot.y;
1883 mesh.vertices[c].z -= object.pivot.z;
1885 mesh.Unlock({ vertices = true });
1889 object.tracks = block.tracks;
1890 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1891 object.endFrame = info->rootObject.endFrame;
1894 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1900 ObjectInfoBlock block { };
1901 Object object = null;
1903 ReadChunks(ReadFrameInfoBlock, info, &block);
1905 if(block.dummyName && block.dummyName[0])
1907 if(!strcmp(block.name, "$$$DUMMY"))
1909 object = Object { };
1910 object.name = block.dummyName;
1911 info->rootObject.children.AddName(object);
1912 object.transform.scaling = { 1, 1, 1 };
1913 object.flags.camera = true;
1917 Object model = info->rootObject.Find(block.name);
1920 object = Object { };
1921 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1922 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1923 object.flags = model.flags;
1924 object.flags.ownMesh = false;
1925 object.camera = model.camera;
1926 object.flags.camera = true;
1927 info->rootObject.children.AddName(object);
1929 delete block.dummyName;
1932 object.parent = info->rootObject;
1935 object = info->rootObject.Find(block.name);
1939 object.flags.hierarchy = block.hierarchy + 1;
1940 if(block.parent != -1)
1942 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1945 object.parent.children.Remove(object);
1946 parent.children.AddName(object);
1947 object.parent = parent;
1950 object.pivot.x = block.pivot.x;
1951 object.pivot.y =-block.pivot.z;
1952 object.pivot.z = block.pivot.y;
1954 object.tracks = block.tracks;
1955 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1956 object.endFrame = info->rootObject.endFrame;
1959 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1963 case FRM_CAMERATARGET:
1965 ObjectInfoBlock block { };
1966 Object object = null;
1967 char targetName[MAXNAMELEN];
1969 ReadChunks(ReadFrameInfoBlock, info, &block);
1971 strcpy(targetName, block.name);
1972 strcat(targetName, ".target");
1974 if(block.dummyName && block.dummyName[0])
1976 if(!strcmp(block.name, "$$$DUMMY"))
1978 object = Object { };
1979 object.name = block.dummyName;
1980 info->rootObject.children.AddName(object);
1981 object.transform.scaling = { 1,1,1 };
1985 Object model = info->rootObject.Find(targetName);
1988 object = Object { };
1989 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1990 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1991 object.flags = model.flags;
1992 object.flags.ownMesh = false;
1993 object.camera = model.camera;
1994 info->rootObject.children.AddName(object);
1996 delete block.dummyName;
1999 object.parent = info->rootObject;
2002 object = info->rootObject.Find(targetName);
2006 object.flags.hierarchy = block.hierarchy + 1;
2007 if(block.parent != -1)
2009 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2012 object.parent.children.Remove(object);
2013 parent.children.AddName(object);
2014 object.parent = parent;
2017 object.pivot.x = block.pivot.x;
2018 object.pivot.y =-block.pivot.z;
2019 object.pivot.z = block.pivot.y;
2021 object.tracks = block.tracks;
2022 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2023 object.endFrame = info->rootObject.endFrame;
2026 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2032 ObjectInfoBlock block { };
2034 ReadChunks(ReadFrameInfoBlock, info, &block);
2036 info->rootObject.tracks = block.tracks;
2042 ObjectInfoBlock block { };
2043 Object object = null;
2045 ReadChunks(ReadFrameInfoBlock, info, &block);
2047 if(block.dummyName && block.dummyName[0])
2049 if(!strcmp(block.name, "$$$DUMMY"))
2051 object = Object { };
2052 object.name = block.dummyName;
2053 info->rootObject.children.AddName(object);
2054 object.transform.scaling = { 1, 1, 1 };
2055 object.flags.light = true;
2059 Object model = info->rootObject.Find(block.name);
2062 object = Object { };
2063 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2064 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2065 object.flags = model.flags;
2066 object.flags.ownMesh = false;
2067 object.light = model.light;
2068 object.flags.light = true;
2069 info->rootObject.children.AddName(object);
2071 delete block.dummyName;
2074 object.parent = info->rootObject;
2077 object = info->rootObject.Find(block.name);
2081 object.flags.hierarchy = block.hierarchy + 1;
2082 if(block.parent != -1)
2084 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2087 object.parent.children.Remove(object);
2088 parent.children.AddName(object);
2089 object.parent = parent;
2092 object.pivot.x = block.pivot.x;
2093 object.pivot.y =-block.pivot.z;
2094 object.pivot.z = block.pivot.y;
2096 object.tracks = block.tracks;
2097 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2098 object.endFrame = info->rootObject.endFrame;
2101 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2105 case FRM_SPOTLIGHTTARGET:
2107 ObjectInfoBlock block { };
2108 Object object = null;
2109 char targetName[MAXNAMELEN];
2111 ReadChunks(ReadFrameInfoBlock, info, &block);
2113 strcpy(targetName, block.name);
2114 strcat(targetName, ".target");
2116 if(block.dummyName && block.dummyName[0])
2118 if(!strcmp(block.name, "$$$DUMMY"))
2120 object = Object { };
2121 object.name = block.dummyName;
2122 info->rootObject.children.AddName(object);
2123 object.transform.scaling = { 1,1,1 };
2127 Object model = info->rootObject.Find(targetName);
2130 object = Object { };
2131 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2132 // TODO: When passing a String to a const String, use member if property is const String but member is String
2133 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2134 object.flags = model.flags;
2135 object.flags.ownMesh = false;
2136 object.light = model.light;
2137 info->rootObject.children.AddName(object);
2139 delete block.dummyName;
2142 object.parent = info->rootObject;
2145 object = info->rootObject.Find(targetName);
2149 object.flags.hierarchy = block.hierarchy + 1;
2150 if(block.parent != -1)
2152 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2155 object.parent.children.Remove(object);
2156 parent.children.AddName(object);
2157 object.parent = parent;
2160 object.pivot.x = block.pivot.x;
2161 object.pivot.y =-block.pivot.z;
2162 object.pivot.z = block.pivot.y;
2164 object.tracks = block.tracks;
2165 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2166 object.endFrame = info->rootObject.endFrame;
2169 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2175 info->rootObject.startFrame = ReadDWORD(info->f);
2176 info->rootObject.endFrame = ReadDWORD(info->f);
2177 *&(info->rootObject.frame) = info->rootObject.startFrame;
2185 static bool ReadMainChunks(FileInfo * info, void * data)
2187 switch(info->chunkId)
2190 ReadChunks(ReadEditChunks, info, null);
2194 Object object = data;
2195 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2197 ReadChunks(ReadKeyFrameChunks, info, null);
2198 object.flags.keysLoaded = true;
2206 static bool ReadMain(FileInfo * info, void * data)
2208 switch(info->chunkId)
2211 ReadChunks(ReadMainChunks, info, data);
2217 class Object3DSFormat : ObjectFormat
2219 class_property(extension) = "3ds";
2221 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2223 bool result = false;
2226 FileInfo info = {0};
2227 info.rootObject = object;
2228 info.displaySystem = displaySystem;
2231 info.fileName = fileName;
2232 StripLastDirectory(fileName, info.textureDirectory);
2233 info.f = FileOpen(fileName, read);
2236 // TOFIX: eC reorders that badly
2237 // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2238 if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2240 object.flags.root = true;
2241 object.SetMinMaxRadius(true);
2242 object._Animate(object.frame);
2243 object.UpdateTransform();
2249 info.matFaces.Free();
2250 delete info.matFaces;
2253 object.Free(displaySystem);