1 namespace gfx3D::models;
8 #define RGB_FLOAT 0x0010
9 #define RGB_BYTE 0x0011
10 #define RGB_BYTE_GAMMA 0x0012
11 #define RGB_FLOAT_GAMMA 0x0013
14 #define AMOUNT_OF 0x0030
16 #define MAIN3DS 0x4D4D
17 #define EDIT3DS 0x3D3D
18 #define EDIT_AMBIENT 0x2100
19 #define EDIT_MATERIAL 0xAFFF
20 #define EDIT_OBJECT 0x4000
21 #define OBJ_HIDDEN 0x4010
22 #define OBJ_TRIMESH 0x4100
23 #define OBJ_LIGHT 0x4600
24 #define OBJ_CAMERA 0x4700
26 // Triangular Mesh Chunks
27 #define TRI_VERTEXL 0x4110
28 #define TRI_FACEL1 0x4120
29 #define TRI_MATERIAL 0x4130
30 #define TRI_MAPPINGCOORS 0x4140
31 #define TRI_SMOOTHING 0x4150
32 #define TRI_LOCAL 0x4160
35 #define LIT_SPOT 0x4610
36 #define LIT_ONOFF 0x4620
37 #define LIT_ATTENUATION 0x4625
38 #define LIT_START 0x4659
39 #define LIT_END 0x465A
40 #define LIT_MULTIPLIER 0x465B
43 #define CAM_SEECONE 0x4710
44 #define CAM_RANGES 0x4720
47 #define MAT_NAME 0xA000
48 #define MAT_AMBIENT 0xA010
49 #define MAT_DIFFUSE 0xA020
50 #define MAT_SPECULAR 0xA030
51 #define MAT_SHININESS 0xA040
52 #define MAT_SHINSTRENGTH 0xA041
53 #define MAT_SHIN3PC 0xA042
54 #define MAT_TRANSPARENCY 0xA050
55 #define MAT_XPFALL 0xA052
56 #define MAT_REFBLUR 0xA053
57 #define MAT_SELFILLUM 0xA080
58 #define MAT_DOUBLESIDED 0xA081
59 #define MAT_ADDITIVE 0xA083
60 #define MAT_SELFILPCT 0xA084
61 #define MAT_WIRE 0xA085
62 #define MAT_SUPERSMP 0xA086
63 #define MAT_WIRETHICKNESS 0xA087
64 #define MAT_FACEMAP 0xA088
65 #define MAT_XPFALLIN 0xA08A
66 #define MAT_PHONG 0xA08C
67 #define MAT_WIREABS 0xA08E
68 #define MAT_SHADING 0xA100
69 #define MAT_MAPTEXTURE1 0xA200
70 #define MAT_SPECULARMAP 0xA204
71 #define MAT_MAPOPACITY 0xA210
72 #define MAT_REFLECTIONMAP 0xA220
73 #define MAT_BUMPMAP 0xA230
76 #define MAP_FILENAME 0xA300
78 #define MAT_SHININESSMAP 0xA33C
79 #define MAT_EMISSIVEMAP 0xA33D
80 #define MAP_OPTIONS 0xA351
81 #define MAP_1_U_SCALE 0xA354
82 #define MAP_1_V_SCALE 0xA356
83 #define MAP_U_OFFSET 0xA358
84 #define MAP_V_OFFSET 0xA35A
85 #define MAP_ROTATION 0xA35C
87 #define MAP_OPTIONS_DECAL 0x0001 // (related to MAP_OPTIONS_DONTTILE)
88 #define MAP_OPTIONS_MIRROR 0x0002
89 #define MAP_OPTIONS_NEGATIVE 0x0008
90 #define MAP_OPTIONS_DONTTILE 0x0010
91 #define MAP_OPTIONS_SUMMEDFILTERING 0x0020
92 #define MAP_OPTIONS_USEALPHA 0x0040
93 #define MAP_OPTIONS_LUMORALPHATINT 0x0080
94 #define MAP_OPTIONS_IGNOREALPHA 0x0100
95 #define MAP_OPTIONS_RGBTINT 0x0200
97 #define MAP_FILTERBLUR 0xA353
98 #define MAP_1_U_SCALE 0xA354
99 #define MAP_1_V_SCALE 0xA356
100 #define MAP_U_OFFSET 0xA358
101 #define MAP_V_OFFSET 0xA35A
102 #define MAP_ROTATION 0xA35C
104 #define MAP_LUMTINT1 0xA360
105 #define MAP_LUMTINT2 0xA362
107 #define MAP_TINTR 0xA364
108 #define MAP_TINTG 0xA366
109 #define MAP_TINTB 0xA368
112 #define KEYFRAME3DS 0xB000
113 #define FRM_AMBIENT 0xB001
114 #define FRM_MESHINFO 0xB002
115 #define FRM_CAMERA 0xB003
116 #define FRM_CAMERATARGET 0xB004
117 #define FRM_OMNILIGHT 0xB005
118 #define FRM_SPOTLIGHTTARGET 0xB006
119 #define FRM_SPOTLIGHT 0xB007
120 #define FRM_FRAMES 0xB008
121 #define FRM_PARAM 0xB010
122 #define FRM_DUMMYNAME 0xB011
123 #define FRM_PIVOT 0xB013
124 #define FRM_TRACKPOS 0xB020
125 #define FRM_TRACKROT 0xB021
126 #define FRM_TRACKSCALE 0xB022
127 #define FRM_TRACKFOV 0xB023
128 #define FRM_TRACKROLL 0xB024
129 #define FRM_TRACKCOLOR 0xB025
130 #define FRM_TRACKMORPH 0xB026 // What does this do?
131 #define FRM_TRACKHOTSPOT 0xB027
132 #define FRM_TRACKFALLOFF 0xB028
133 #define FRM_TRACKHIDE 0xB029
134 #define FRM_HIERARCHY 0xB030
136 typedef struct FileInfo FileInfo;
151 DisplaySystem displaySystem;
153 const String fileName;
161 char textureDirectory[MAX_DIRECTORY];
162 Map<uintptr, Array<int>> matFaces;
165 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
166 | (((unsigned short)(word) & 0xff00) >> 8)
168 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
169 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
170 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
171 | (((unsigned int)(dword) & 0xff000000) >> 24))
173 #ifndef __BIG_ENDIAN__
174 #define BIGENDSWAP_WORD(word)
175 #define BIGENDSWAP_DWORD(dword)
177 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
178 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
181 // Zero Terminated String
182 static int ReadASCIIZ(File f, char ** string)
184 // *** Read String ***
186 char temp[1024] = "";
190 if(!temp[c++]) break;
192 *string = new char[c];
194 strcpy(*string, temp);
198 static float ReadFloat(File f)
201 f.Read(&floatValue, sizeof(float), 1);
202 BIGENDSWAP_DWORD(floatValue);
206 static uint16 ReadWORD(File f)
209 f.Read(&wordValue, sizeof(uint16), 1);
210 BIGENDSWAP_WORD(wordValue);
214 static uint ReadDWORD(File f)
217 f.Read(&dwordValue, sizeof(uint), 1);
218 BIGENDSWAP_DWORD(dwordValue);
223 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
225 for(;info->pos < info->end;)
227 FileInfo childInfo = *info;
230 childInfo.parent = info;
232 info->f.Seek(info->pos, start);
233 childInfo.chunkId = ReadWORD(info->f);
234 length = ReadDWORD(info->f);
236 childInfo.pos += sizeof(uint16) + sizeof(uint);
237 childInfo.end = info->pos + length;
239 if(!chunkParser(&childInfo, data))
242 info->pos = childInfo.end;
248 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
250 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
253 info->f.Getc((char *)&value); rgb->r = value / 255.0f;
254 info->f.Getc((char *)&value); rgb->g = value / 255.0f;
255 info->f.Getc((char *)&value); rgb->b = value / 255.0f;
257 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
259 rgb->r = ReadFloat(info->f);
260 rgb->g = ReadFloat(info->f);
261 rgb->b = ReadFloat(info->f);
266 static bool Read3DVertex(File f, Vector3Df vertex)
268 vertex.x = ReadFloat(f);
269 vertex.y = ReadFloat(f);
270 vertex.z = ReadFloat(f);
274 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
276 if(info->chunkId == AMOUNT_OF)
277 *amountOf = ReadWORD(info->f);
281 #define WELD_TRESHOLD 0.000001
282 #define SMOOTH_CUTOFF 0 // 45
284 struct SharedSourceVertexInfo
291 int OnCompare(SharedSourceVertexInfo b)
293 if(unique < b.unique) return -1;
294 if(unique > b.unique) return 1;
298 if(face < b.face) return -1;
299 if(face > b.face) return 1;
301 if(index == b.index) return 0;
304 if(value.x < b.value.x - WELD_TRESHOLD) return -1;
305 if(value.x > b.value.x + WELD_TRESHOLD) return 1;
306 if(value.y < b.value.y - WELD_TRESHOLD) return -1;
307 if(value.y > b.value.y + WELD_TRESHOLD) return 1;
308 if(value.z < b.value.z - WELD_TRESHOLD) return -1;
309 if(value.z > b.value.z + WELD_TRESHOLD) return 1;
313 if(index < b.index) return -1;
314 if(index > b.index) return 1;
320 class SharedDestVertexInfo
322 Array<int> faces { };
325 struct SourceVertexInfo
327 SharedSourceVertexInfo * shared;
331 int OnCompare(SourceVertexInfo b)
333 int r = (*shared).OnCompare(*b.shared);
334 if(!r) r = texCoord.OnCompare(b.texCoord);
335 if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
342 int index, copyFromIndex;
346 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
349 Face * faces = info->faces;
350 int nVertices = mesh.nVertices;
353 Vector3Df * mVertices;
354 double cutOff = cos(Degrees { SMOOTH_CUTOFF });
356 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
357 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
358 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
360 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
361 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
363 nNewVertices = nVertices;
364 mVertices = mesh->vertices;
366 for(c = 0; c<info->nFaces; c++)
368 Face * face = &faces[c];
370 plane.FromPointsf(mesh.vertices[face->indices[2]],
371 mesh.vertices[face->indices[1]],
372 mesh.vertices[face->indices[0]]);
373 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
376 for(c = 0; c < info->nFaces; c++)
378 Face * face = &faces[c];
382 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
383 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
388 SharedSourceVertexInfo * source;
389 SharedDestVertexInfo svInfo;
390 DestVertexInfo vInfo;
392 index = face->indices[i];
394 if(face->smoothGroups)
395 itShared.Index({ index = index, mVertices[index], face = face }, true);
397 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
398 svInfo = itShared.data;
399 if(!svInfo) itShared.data = svInfo = { };
402 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
403 // TODO: Allow obtaining address of MapIterator::key
404 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
405 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
411 vInfo.copyFromIndex = index;
416 vertices[index] = (void *)it.pointer;
417 else if(vertices[index] != it.pointer)
419 // If it's a different smoothing group, we'll need extra vertices
420 index = vertices.size;
422 vertices.Add((void *)it.pointer);
425 face->indices[i] = vInfo.index;
429 for(index = 0; index < nNewVertices; index++)
432 it.pointer = vertices[index];
435 DestVertexInfo vInfo = it.data;
436 Vector3Df normal { };
437 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
438 uint smoothing = inf->smoothGroups;
440 SharedSourceVertexInfo * shared = inf->shared;
441 SharedDestVertexInfo svInfo = sharedVertices[*shared];
443 if(!svInfo || vInfo.index != index)
446 for(i : svInfo.faces)
448 Face * face = &info->faces[i];
450 if(smoothing & face->smoothGroups)
451 smoothing |= face->smoothGroups;
454 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
455 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
457 for(i : svInfo.faces)
459 Face * face = &info->faces[i];
460 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
463 for(j = 0; j < 3; j++)
465 if(face->indices[j] == vInfo.index)
467 origIndex = face->origIndices[j];
469 normal.x += face->normal.x;
470 normal.y += face->normal.y;
471 normal.z += face->normal.z;
484 for(i : svInfo.faces)
486 Face * face = &info->faces[i];
487 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
491 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
496 for(k = 0; k < 3; k++)
498 if(face->indices[k] == vInfo.index)
500 origIndexB = face->origIndices[k];
504 valid = origIndex == origIndexB;
507 for(j : svInfo.faces)
509 if(info->faces[j].done)
511 double dot = info->faces[j].normal.DotProduct(face->normal);
512 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
513 valid = fabs(dot) > cutOff;
522 normal.x += face->normal.x;
523 normal.y += face->normal.y;
524 normal.z += face->normal.z;
531 if(!SMOOTH_CUTOFF || !WELD_TRESHOLD) break;
533 normal.Scale(normal, 1.0f / numShared);
534 if(vInfo.index == index)
535 vInfo.normal.Normalize(normal);
537 // Auto welding/smoothing requires extra vertices because angle is too steep
538 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
540 SharedDestVertexInfo newSharedInfo = null;
542 for(i : svInfo.faces)
544 Face * face = &info->faces[i];
545 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
548 for(j = 0; j < 3; j++)
550 if(face->indices[j] == vInfo.index)
554 DestVertexInfo newVert;
555 SharedSourceVertexInfo * source;
557 index = nNewVertices++;
558 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
559 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
560 itShared.data = newSharedInfo = { };
562 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
565 newVert.copyFromIndex = vInfo.copyFromIndex;
566 newVert.index = index;
568 vertices.Add((void *)it.pointer);
570 face->indices[j] = index;
571 newSharedInfo.faces.Add(i);
581 // Allocate some extra vertices
583 Vector3Df * oldVertices = mesh.vertices;
584 Pointf * oldTexCoords = mesh.texCoords;
586 // TODO: Support reallocation?
587 *((void **)&mesh.vertices) = null;
588 *((void **)&mesh.texCoords) = null;
589 *((int *)&mesh.nVertices) = 0;
591 mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
593 // Fill in the new vertices
594 for(index = 0; index < nNewVertices; index++)
596 DestVertexInfo vInfo;
597 it.pointer = vertices[index];
601 mesh.normals[index] = vInfo ? vInfo.normal : { };
602 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
604 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
613 for(i = 0; i < info->nFaces; i++)
614 info->faces[i].done = false;
617 mesh.Unlock({ normals = true });
619 // Free all the temporary stuff
624 sharedVertices.Free();
625 delete sharedVertices;
629 static bool ReadSmoothing(FileInfo * info, Object object)
631 switch(info->chunkId)
636 for(c = 0; c<info->nFaces; c++)
637 info->faces[c].smoothGroups = ReadDWORD(info->f);
644 static bool ReadFacesListChunks(FileInfo * info, Object object)
646 DisplaySystem displaySystem = info->displaySystem;
647 switch(info->chunkId)
656 char matName[MAX_LOCATION + 100];
658 strcpy(matName, info->fileName);
659 ReadASCIIZ(info->f, &name);
660 count = ReadWORD(info->f);
661 strcat(matName, name);
663 mat = displaySystem.GetMaterial(matName);
664 faces = info->matFaces[(uintptr)mat];
666 info->matFaces[(uintptr)mat] = faces = { };
670 for(c = 0; c<count; c++)
672 uint16 face = ReadWORD(info->f);
674 info->faces[face].material = mat;
683 static bool ReadTriMesh(FileInfo * info, Object object)
685 Mesh mesh = object.mesh;
686 switch(info->chunkId)
691 uint16 nVertices = ReadWORD(info->f);
692 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
693 *((int *) &mesh.nVertices) = nVertices;
694 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
697 for(c = 0; c<mesh.nVertices; c++)
700 Read3DVertex(info->f, vertex);
701 mesh.vertices[c].x = vertex.x;
702 mesh.vertices[c].y =-vertex.z;
703 mesh.vertices[c].z = vertex.y;
710 case TRI_MAPPINGCOORS:
713 uint16 count = ReadWORD(info->f);
714 count = (uint16)Min(mesh.nVertices, count);
716 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
717 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
718 mesh.flags.texCoords1 = true;
721 for(c = 0; c<count; c++)
723 mesh.texCoords[c].x = ReadFloat(info->f);
724 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
738 info->nFaces = nFaces = ReadWORD(info->f);
739 info->pos += sizeof(uint16);
741 info->faces = new0 Face[nFaces];
742 for(c = 0; c<nFaces; c++)
746 info->faces[c].origIndices[i] =
747 info->faces[c].indices[i] = ReadWORD(info->f);
749 info->pos += 4*sizeof(uint16);
752 ReadChunks(ReadSmoothing, info, object);
756 info->matFaces.Free();
757 info->matFaces = { };
759 ReadChunks(ReadFacesListChunks, info, object);
761 ComputeNormals(mesh, info, object);
764 for(m : info->matFaces)
766 Material mat = (Material)&m;
767 Array<int> faces = m;
768 if(mat.flags.translucent)
770 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
773 Face * face = &info->faces[i];
774 PrimitiveSingle * triangle;
776 triangle = &mesh.primitives[mesh.nPrimitives++];
777 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = true }, 3))
779 triangle->indices32[0] = face->indices[0];
780 triangle->indices32[1] = face->indices[1];
781 triangle->indices32[2] = face->indices[2];
782 triangle->middle.Add(mesh.vertices[triangle->indices32[0]], mesh.vertices[triangle->indices32[1]]);
783 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices32[2]]);
784 triangle->plane.FromPointsf(
785 mesh.vertices[triangle->indices32[2]],
786 mesh.vertices[triangle->indices32[1]],
787 mesh.vertices[triangle->indices32[0]]);
789 mesh.UnlockPrimitive(triangle);
791 triangle->middle.x /= 3;
792 triangle->middle.y /= 3;
793 triangle->middle.z /= 3;
795 triangle->material = mat;
798 object.flags.translucent = true;
803 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = true }, faces.count * 3);
807 group.material = mat;
810 Face * face = &info->faces[i];
812 if(object.flags.flipWindings)
814 group.indices32[c*3] = face->indices[2];
815 group.indices32[c*3+1] = face->indices[1];
816 group.indices32[c*3+2] = face->indices[0];
820 group.indices32[c*3] = face->indices[0];
821 group.indices32[c*3+1] = face->indices[1];
822 group.indices32[c*3+2] = face->indices[2];
827 mesh.UnlockPrimitiveGroup(group);
832 // Add faces without a material all together
834 for(c = 0; c<nFaces; c++)
835 if(!info->faces[c].done)
839 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = true }, count * 3);
842 for(c = 0; c<nFaces; c++)
844 Face * face = &info->faces[c];
847 group.indices32[c*3] = face->indices[0];
848 group.indices32[c*3+1] = face->indices[1];
849 group.indices32[c*3+2] = face->indices[2];
852 mesh.UnlockPrimitiveGroup(group);
859 info->matFaces.Free();
860 delete info->matFaces;
867 Vector3Df xAxis, yAxis, zAxis, center;
870 Matrix inverse/*, source = { 0 }*/;
873 Read3DVertex(info->f, xAxis);
874 Read3DVertex(info->f, yAxis);
875 Read3DVertex(info->f, zAxis);
876 Read3DVertex(info->f, center);
878 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
879 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
880 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
882 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
885 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
886 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
887 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
888 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
890 inverse.Inverse(source);
893 object.flags.flipWindings = false;
895 xAxis.Normalize(xAxis);
896 yAxis.Normalize(yAxis);
897 zAxis.Normalize(zAxis);
899 orth.CrossProduct(yAxis, zAxis);
900 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
901 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
902 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
904 object.flags.flipWindings ^= true;
908 orth.CrossProduct(zAxis, xAxis);
909 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
910 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
911 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
913 object.flags.flipWindings ^= true;
917 orth.CrossProduct(xAxis, yAxis);
918 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
919 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
920 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
922 object.flags.flipWindings ^= true;
929 rotation.m[0][0] = xAxis.x;
930 rotation.m[0][1] =-xAxis.z;
931 rotation.m[0][2] = xAxis.y;
932 rotation.m[0][3] = 0;
933 rotation.m[1][0] =-zAxis.x;
934 rotation.m[1][1] = zAxis.z;
935 rotation.m[1][2] =-zAxis.y;
936 rotation.m[1][3] = 0;
937 rotation.m[2][0] = yAxis.x;
938 rotation.m[2][1] =-yAxis.z;
939 rotation.m[2][2] = yAxis.y;
940 rotation.m[2][3] = 0;
941 rotation.m[3][0] = 0;
942 rotation.m[3][1] = 0;
943 rotation.m[3][2] = 0;
944 rotation.m[3][3] = 1;
948 inverse.Transpose(rotation);
950 temp.Translate(-center.x, center.z, -center.y);
951 temp2.Multiply(temp, inverse);
952 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
956 object.transform.scaling = scaling;
957 // TODO: Improve language to support deep properties on non constant functions
958 // object.transform.orientation.RotationMatrix(rotation);
960 Quaternion orientation;
961 orientation.RotationMatrix(rotation);
962 object.transform.orientation = orientation;
964 object.transform.position = { center.x, -center.z, center.y };
967 // Localize All Vertices
968 for(c = 0; c<mesh.nVertices; c++)
970 Vector3Df vertex = mesh.vertices[c];
972 mesh.vertices[c].MultMatrix(vertex, inverse);
974 mesh.vertices[c].x -= object.pivot.x;
975 mesh.vertices[c].y -= object.pivot.y;
976 mesh.vertices[c].z -= object.pivot.z;
985 static bool ReadMap(FileInfo * info, Material mat)
987 DisplaySystem displaySystem = info->displaySystem;
988 switch(info->chunkId)
993 char location[MAX_LOCATION];
995 ReadASCIIZ(info->f, &name);
997 strcpy(location, info->textureDirectory);
998 PathCat(location, name);
999 if(!FileExists(location))
1001 // Attempt all lowercase if original case does not exist
1003 strcpy(location, info->textureDirectory);
1004 PathCat(location, name);
1007 if(info->parent->chunkId == MAT_BUMPMAP)
1009 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1010 char bumpName[MAX_LOCATION+5];
1011 strcpy(bumpName, "BUMP:");
1012 strcat(bumpName, location);
1015 mat.bumpMap = displaySystem.GetTexture(bumpName);
1018 mat.bumpMap = Bitmap { };
1019 if(!mat.bumpMap.Load(location, null, null) ||
1020 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1021 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1025 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1026 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1029 for(y = 0; y < bh; y++)
1030 for(x = 0; x < bw; x++)
1032 uint bc = y * bw + x;
1033 Color color = picture[bc].color;
1034 picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
1042 Bitmap opacityMap = null;
1043 bool alphaOnly = true;
1044 bool translucent = false;
1047 mat.baseMap = displaySystem.GetTexture(location);
1050 mat.baseMap = Bitmap { };
1051 if(!mat.baseMap.Load(location, null, null) ||
1052 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1053 !displaySystem.AddTexture(location, mat.baseMap))
1057 opacityMap = mat.baseMap;
1060 else if(info->parent->chunkId == MAT_MAPOPACITY)
1062 opacityMap = Bitmap { };
1063 if(opacityMap.Load(location, null, null))
1065 if(opacityMap.pixelFormat == pixelFormatRGBA)
1067 if(!opacityMap.Convert(null, pixelFormat888, null))
1074 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1076 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1077 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1079 int ow = opacityMap.width, oh = opacityMap.height;
1080 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1082 for(y = 0; y < bh; y++)
1083 for(x = 0; x < bw; x++)
1085 int bc = ((y % bh) * bw + (x % bw));
1086 int oc = ((y % oh) * bw + (x % ow));
1087 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1088 if(alpha && alpha < 255)
1090 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1094 mat.flags.translucent = true;
1095 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1096 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1098 if(opacityMap != mat.baseMap)
1106 uint16 options = ReadWORD(info->f);
1107 if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
1111 mat.uScale = ReadFloat(info->f);
1114 mat.vScale = ReadFloat(info->f);
1120 PrintLn("Unhandled Map block");
1127 static bool ReadMaterial(FileInfo * info, Material mat)
1129 switch(info->chunkId)
1134 char matName[MAX_LOCATION + 100];
1135 strcpy(matName, info->fileName);
1136 ReadASCIIZ(info->f, &name);
1137 strcat(matName, name);
1138 mat.name = CopyString(matName);
1142 case MAT_TRANSPARENCY:
1144 uint16 transparency;
1145 ReadChunks(ReadAmountOf, info, &transparency);
1146 mat.opacity = 1.0f - transparency / 100.0f;
1147 if(mat.opacity < 1.0)
1148 mat.flags.translucent = true;
1153 ReadChunks(ReadRGB, info, &mat.diffuse);
1154 ReadChunks(ReadRGB, info, &mat.diffuse);
1159 ReadChunks(ReadRGB, info, &mat.ambient);
1160 ReadChunks(ReadRGB, info, &mat.ambient);
1165 ReadChunks(ReadRGB, info, &mat.specular);
1166 ReadChunks(ReadRGB, info, &mat.specular);
1172 ReadChunks(ReadAmountOf, info, &emissive);
1173 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1174 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1175 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1178 case MAT_SHINSTRENGTH:
1181 ReadChunks(ReadAmountOf, info, &shininess);
1182 mat.specular.r *= shininess / 100.0f;
1183 mat.specular.g *= shininess / 100.0f;
1184 mat.specular.b *= shininess / 100.0f;
1190 ReadChunks(ReadAmountOf, info, &power);
1194 case MAT_MAPTEXTURE1:
1195 ReadChunks(ReadMap, info, mat);
1197 case MAT_MAPOPACITY:
1198 ReadChunks(ReadMap, info, mat);
1200 case MAT_DOUBLESIDED:
1201 mat.flags.doubleSided = true;
1204 ReadChunks(ReadMap, info, mat);
1208 PrintLn("Unhandled MAT type ID", info->chunkId);
1215 static bool ReadLight(FileInfo * info, Object object)
1217 Light * light = &object.light;
1218 switch(info->chunkId)
1221 case RGB_BYTE_GAMMA:
1223 case RGB_FLOAT_GAMMA:
1224 ReadRGB(info, &light->diffuse);
1225 light->specular = light->diffuse;
1230 char targetName[MAXNAMELEN];
1232 strcpy(targetName, object.name);
1233 strcat(targetName, ".target");
1235 light->flags.omni = false;
1236 light->flags.spot = true;
1238 target = Object { };
1239 target.name = CopyString(targetName);
1240 info->rootObject.children.AddName(target);
1241 target.parent = info->rootObject;
1243 light->target = target;
1245 target.transform.position.x = ReadFloat(info->f);
1246 target.transform.position.z = ReadFloat(info->f);
1247 target.transform.position.y =-ReadFloat(info->f);
1249 light->hotSpot = ReadFloat(info->f);
1250 light->fallOff = ReadFloat(info->f);
1255 light->flags.off = true;
1258 case LIT_ATTENUATION:
1262 d = 300, small = 0.001,
1264 d * (Kl + Kq * d) = (1 / small) - Kc
1266 { Kc, Kl, Kq, small, d });
1269 light->flags.attenuation = true;
1272 #define MINLIGHT 0.08
1273 light->Kq = 1/(light->end*light->end*MINLIGHT);
1276 #define MINLIGHT 0.15f
1277 // #define MINLIGHT 0.1
1278 light->Kl = (float)(1/(light->end*MINLIGHT));
1284 light->start = ReadFloat(info->f);
1289 light->end = ReadFloat(info->f);
1292 case LIT_MULTIPLIER:
1294 light->multiplier = ReadFloat(info->f);
1302 static bool ReadCamera(FileInfo * info, Object object)
1304 switch(info->chunkId)
1312 //Camera camera = object.camera;
1313 /*float nearRange = */ReadFloat(info->f);
1314 /*float farRange = */ReadFloat(info->f);
1316 camera.zMin = Max(0.1, nearRange);
1317 camera.zMax = farRange;
1326 static bool ReadEditObject(FileInfo * info, char * name)
1328 DisplaySystem displaySystem = info->displaySystem;
1329 switch(info->chunkId)
1333 Object object = info->rootObject.Find(name);
1336 object = Object { };
1337 object.name = CopyString(name);
1338 info->rootObject.children.AddName(object);
1339 object.parent = info->rootObject;
1341 object.InitializeMesh(displaySystem);
1342 ReadChunks(ReadTriMesh, info, object);
1343 object.flags.mesh = true;
1344 object.mesh.Unlock(0);
1349 Object object = info->rootObject.Find(name);
1355 object = Object { };
1356 object.name = CopyString(name);
1357 info->rootObject.children.AddName(object);
1358 object.parent = info->rootObject;
1360 object.flags.light = true;
1362 light = &object.light;
1363 light->lightObject = object;
1364 light->flags.omni = true;
1365 light->multiplier = 1.0f;
1367 // This is not used?
1368 Read3DVertex(info->f, position);
1369 light->direction = { position.x, position.y, position.z };
1370 info->pos += sizeof(float) * 3;
1372 ReadChunks(ReadLight, info, object);
1377 char targetName[MAXNAMELEN];
1378 Object object = info->rootObject.Find(name);
1381 float /*bankAngle, */focus;
1384 strcpy(targetName, name);
1385 strcat(targetName, ".target");
1386 target = info->rootObject.Find(targetName);
1390 object = Object { };
1391 object.name = CopyString(name);
1392 info->rootObject.children.AddName(object);
1394 object.parent = info->rootObject;
1395 object.camera = Camera { };
1396 object.camera.type = lookAtObject;
1401 target = Object { };
1402 target.name = CopyString(targetName);
1403 info->rootObject.children.AddName(target);
1404 target.parent = info->rootObject;
1407 object.flags.camera = true;
1408 object.cameraTarget = target;
1410 camera = object.camera;
1411 camera.cameraObject = object;
1412 camera.target = target;
1414 //Read3DVertex(info->f, camera.position);
1415 object.transform.position.x = ReadFloat(info->f);
1416 object.transform.position.z = ReadFloat(info->f);
1417 object.transform.position.y =-ReadFloat(info->f);
1419 info->pos += sizeof(float) * 3;
1420 //Read3DVertex(info->f, object.cameraTarget.position);
1421 target.transform.position.x = ReadFloat(info->f);
1422 target.transform.position.z = ReadFloat(info->f);
1423 target.transform.position.y =-ReadFloat(info->f);
1425 info->pos += sizeof(float) * 3;
1426 /*bankAngle = */ReadFloat(info->f);
1427 info->pos += sizeof(float);
1428 focus = ReadFloat(info->f);
1429 info->pos += sizeof(float);
1431 mm = (focus - 5.05659508373109) / 1.13613250717301;
1432 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1434 ReadChunks(ReadCamera, info, object);
1437 case OBJ_HIDDEN: break;
1442 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1444 static bool ReadEditChunks(FileInfo * info, void * data)
1446 switch(info->chunkId)
1450 // Read the ambient color
1451 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1456 Material material { /*flags = { singleSideLight = true }*/ };
1458 ReadChunks(ReadMaterial, info, material);
1460 mat = info->displaySystem.AddNamedMaterial(material.name);
1463 if(material.baseMap)
1464 material.baseMap.MakeMipMaps(info->displaySystem);
1465 if(material.bumpMap)
1466 material.bumpMap.MakeMipMaps(info->displaySystem);
1467 // COPY_NITEM(mat, material);
1468 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1472 delete material.baseMap;
1474 delete material.name;
1481 info->pos += ReadASCIIZ(info->f, &name);
1482 ReadChunks(ReadEditObject, info, name);
1490 struct ObjectInfoBlock
1500 // Key Framer Chunks
1502 #define ACCFLAG_TENSION 0x00000001
1503 #define ACCFLAG_CONTINUITY 0x00000002
1504 #define ACCFLAG_BIAS 0x00000004
1505 #define ACCFLAG_EASETO 0x00000008
1506 #define ACCFLAG_EASEFROM 0x00000010
1508 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1510 switch(info->chunkId)
1514 //uint16 flags1, flags2;
1515 ReadASCIIZ(info->f, &block->name);
1516 /*flags1 = */ReadWORD(info->f);
1517 /*flags2 = */ReadWORD(info->f);
1518 block->parent = ReadWORD(info->f);
1522 ReadASCIIZ(info->f, &block->dummyName);
1525 Read3DVertex(info->f, block->pivot);
1528 block->hierarchy = ReadWORD(info->f);
1532 case FRM_TRACKSCALE:
1535 case FRM_TRACKCOLOR:
1536 case FRM_TRACKHOTSPOT:
1537 case FRM_TRACKFALLOFF:
1539 FrameTrack track { };
1546 block->tracks.Add(track);
1548 flags = ReadWORD(info->f);
1550 info->f.Read(unknown, sizeof(unknown), 1);
1552 track.numKeys = ReadDWORD(info->f);
1554 switch(info->chunkId)
1556 case FRM_TRACKPOS: track.type.type = position; break;
1557 case FRM_TRACKROT: track.type.type = rotation; break;
1558 case FRM_TRACKSCALE: track.type.type = scaling; break;
1559 case FRM_TRACKROLL: track.type.type = roll; break;
1560 case FRM_TRACKFOV: track.type.type = fov; break;
1561 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1562 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1563 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1565 if((flags & 0x0003) == 3)
1566 track.type.loop = true;
1568 track.keys = new0 FrameKey[track.numKeys];
1569 for(c = 0; c<track.numKeys; c++)
1571 uint16 accelerationFlags;
1572 FrameKey * key = track.keys + c;
1574 key->frame = ReadDWORD(info->f);
1575 accelerationFlags = ReadWORD(info->f);
1577 if(accelerationFlags & ACCFLAG_TENSION)
1578 key->tension = ReadFloat(info->f);
1579 if(accelerationFlags & ACCFLAG_CONTINUITY)
1580 key->continuity = ReadFloat(info->f);
1581 if(accelerationFlags & ACCFLAG_BIAS)
1582 key->bias = ReadFloat(info->f);
1583 if(accelerationFlags & ACCFLAG_EASETO)
1584 key->easeTo = ReadFloat(info->f);
1585 if(accelerationFlags & ACCFLAG_EASEFROM)
1586 key->easeFrom = ReadFloat(info->f);
1588 switch(info->chunkId)
1593 Read3DVertex(info->f, position);
1594 key->position = { position.x, -position.z, position.y };
1600 Angle angle = ReadFloat(info->f);
1601 Vector3Df fixedAxis;
1603 Read3DVertex(info->f, axis);
1604 fixedAxis.x = axis.x;
1605 fixedAxis.y = -axis.z;
1606 fixedAxis.z = axis.y;
1610 Quaternion rotation;
1611 rotation.RotationAxis(fixedAxis, angle);
1612 key->orientation.Multiply((key - 1)->orientation, rotation);
1615 key->orientation.RotationAxis(fixedAxis, angle);
1618 case FRM_TRACKSCALE:
1621 Read3DVertex(info->f, scaling);
1622 key->scaling = { scaling.x, scaling.z, scaling.y };
1627 key->fov = ReadFloat(info->f);
1632 key->roll = -ReadFloat(info->f);
1635 case FRM_TRACKCOLOR:
1637 FileInfo childInfo = *info;
1638 childInfo.chunkId = RGB_FLOAT;
1639 ReadRGB(&childInfo, &key->color);
1642 case FRM_TRACKHOTSPOT:
1644 key->hotSpot = ReadFloat(info->f);
1647 case FRM_TRACKFALLOFF:
1649 key->fallOff = ReadFloat(info->f);
1661 static Object FindObjectID(Object object, int id)
1663 Object result = null;
1665 for(child = object.children.first; child; child = child.next)
1667 if(child.flags.hierarchy == (uint16) id)
1674 result = FindObjectID(child, id);
1681 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1683 switch(info->chunkId)
1687 ObjectInfoBlock block { };
1688 Object object = null;
1690 ReadChunks(ReadFrameInfoBlock, info, &block);
1692 if(block.dummyName && block.dummyName[0])
1694 if(!strcmp(block.name, "$$$DUMMY"))
1696 object = Object { };
1697 object.name = block.dummyName;
1698 info->rootObject.children.AddName(object);
1699 object.transform.scaling = { 1,1,1 };
1703 Object model = info->rootObject.Find(block.name);
1706 object = Object { };
1707 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1708 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1709 object.flags = model.flags;
1710 object.flags.ownMesh = false;
1711 object.mesh = model.mesh;
1713 object.min = model.min;
1714 object.max = model.max;
1715 object.radius = model.radius;
1717 object.transform = model.transform;
1718 info->rootObject.children.AddName(object);
1720 delete block.dummyName;
1723 object.parent = info->rootObject;
1726 object = info->rootObject.Find(block.name);
1730 Mesh mesh = object.mesh;
1731 object.flags.hierarchy = block.hierarchy + 1;
1732 if(block.parent != -1)
1734 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1737 object.parent.children.Remove(object);
1738 parent.children.AddName(object);
1739 object.parent = parent;
1742 object.pivot.x = block.pivot.x;
1743 object.pivot.y =-block.pivot.z;
1744 object.pivot.z = block.pivot.y;
1746 if(mesh && object.flags.ownMesh)
1748 if(mesh.Lock({ vertices = true }))
1751 // Take pivot into account
1752 for(c = 0; c<mesh.nVertices; c++)
1754 mesh.vertices[c].x -= object.pivot.x;
1755 mesh.vertices[c].y -= object.pivot.y;
1756 mesh.vertices[c].z -= object.pivot.z;
1758 mesh.Unlock({ vertices = true });
1762 object.tracks = block.tracks;
1763 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1764 object.endFrame = info->rootObject.endFrame;
1767 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1773 ObjectInfoBlock block { };
1774 Object object = null;
1776 ReadChunks(ReadFrameInfoBlock, info, &block);
1778 if(block.dummyName && block.dummyName[0])
1780 if(!strcmp(block.name, "$$$DUMMY"))
1782 object = Object { };
1783 object.name = block.dummyName;
1784 info->rootObject.children.AddName(object);
1785 object.transform.scaling = { 1, 1, 1 };
1786 object.flags.camera = true;
1790 Object model = info->rootObject.Find(block.name);
1793 object = Object { };
1794 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1795 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1796 object.flags = model.flags;
1797 object.flags.ownMesh = false;
1798 object.camera = model.camera;
1799 object.flags.camera = true;
1800 info->rootObject.children.AddName(object);
1802 delete block.dummyName;
1805 object.parent = info->rootObject;
1808 object = info->rootObject.Find(block.name);
1812 object.flags.hierarchy = block.hierarchy + 1;
1813 if(block.parent != -1)
1815 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1818 object.parent.children.Remove(object);
1819 parent.children.AddName(object);
1820 object.parent = parent;
1823 object.pivot.x = block.pivot.x;
1824 object.pivot.y =-block.pivot.z;
1825 object.pivot.z = block.pivot.y;
1827 object.tracks = block.tracks;
1828 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1829 object.endFrame = info->rootObject.endFrame;
1832 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1836 case FRM_CAMERATARGET:
1838 ObjectInfoBlock block { };
1839 Object object = null;
1840 char targetName[MAXNAMELEN];
1842 ReadChunks(ReadFrameInfoBlock, info, &block);
1844 strcpy(targetName, block.name);
1845 strcat(targetName, ".target");
1847 if(block.dummyName && block.dummyName[0])
1849 if(!strcmp(block.name, "$$$DUMMY"))
1851 object = Object { };
1852 object.name = block.dummyName;
1853 info->rootObject.children.AddName(object);
1854 object.transform.scaling = { 1,1,1 };
1858 Object model = info->rootObject.Find(targetName);
1861 object = Object { };
1862 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1863 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1864 object.flags = model.flags;
1865 object.flags.ownMesh = false;
1866 object.camera = model.camera;
1867 info->rootObject.children.AddName(object);
1869 delete block.dummyName;
1872 object.parent = info->rootObject;
1875 object = info->rootObject.Find(targetName);
1879 object.flags.hierarchy = block.hierarchy + 1;
1880 if(block.parent != -1)
1882 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1885 object.parent.children.Remove(object);
1886 parent.children.AddName(object);
1887 object.parent = parent;
1890 object.pivot.x = block.pivot.x;
1891 object.pivot.y =-block.pivot.z;
1892 object.pivot.z = block.pivot.y;
1894 object.tracks = block.tracks;
1895 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1896 object.endFrame = info->rootObject.endFrame;
1899 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1905 ObjectInfoBlock block { };
1907 ReadChunks(ReadFrameInfoBlock, info, &block);
1909 info->rootObject.tracks = block.tracks;
1915 ObjectInfoBlock block { };
1916 Object object = null;
1918 ReadChunks(ReadFrameInfoBlock, info, &block);
1920 if(block.dummyName && block.dummyName[0])
1922 if(!strcmp(block.name, "$$$DUMMY"))
1924 object = Object { };
1925 object.name = block.dummyName;
1926 info->rootObject.children.AddName(object);
1927 object.transform.scaling = { 1, 1, 1 };
1928 object.flags.light = true;
1932 Object model = info->rootObject.Find(block.name);
1935 object = Object { };
1936 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1937 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1938 object.flags = model.flags;
1939 object.flags.ownMesh = false;
1940 object.light = model.light;
1941 object.flags.light = true;
1942 info->rootObject.children.AddName(object);
1944 delete block.dummyName;
1947 object.parent = info->rootObject;
1950 object = info->rootObject.Find(block.name);
1954 object.flags.hierarchy = block.hierarchy + 1;
1955 if(block.parent != -1)
1957 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1960 object.parent.children.Remove(object);
1961 parent.children.AddName(object);
1962 object.parent = parent;
1965 object.pivot.x = block.pivot.x;
1966 object.pivot.y =-block.pivot.z;
1967 object.pivot.z = block.pivot.y;
1969 object.tracks = block.tracks;
1970 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1971 object.endFrame = info->rootObject.endFrame;
1974 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1978 case FRM_SPOTLIGHTTARGET:
1980 ObjectInfoBlock block { };
1981 Object object = null;
1982 char targetName[MAXNAMELEN];
1984 ReadChunks(ReadFrameInfoBlock, info, &block);
1986 strcpy(targetName, block.name);
1987 strcat(targetName, ".target");
1989 if(block.dummyName && block.dummyName[0])
1991 if(!strcmp(block.name, "$$$DUMMY"))
1993 object = Object { };
1994 object.name = block.dummyName;
1995 info->rootObject.children.AddName(object);
1996 object.transform.scaling = { 1,1,1 };
2000 Object model = info->rootObject.Find(targetName);
2003 object = Object { };
2004 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2005 // TODO: When passing a String to a const String, use member if property is const String but member is String
2006 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2007 object.flags = model.flags;
2008 object.flags.ownMesh = false;
2009 object.light = model.light;
2010 info->rootObject.children.AddName(object);
2012 delete block.dummyName;
2015 object.parent = info->rootObject;
2018 object = info->rootObject.Find(targetName);
2022 object.flags.hierarchy = block.hierarchy + 1;
2023 if(block.parent != -1)
2025 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2028 object.parent.children.Remove(object);
2029 parent.children.AddName(object);
2030 object.parent = parent;
2033 object.pivot.x = block.pivot.x;
2034 object.pivot.y =-block.pivot.z;
2035 object.pivot.z = block.pivot.y;
2037 object.tracks = block.tracks;
2038 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2039 object.endFrame = info->rootObject.endFrame;
2042 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2048 info->rootObject.startFrame = ReadDWORD(info->f);
2049 info->rootObject.endFrame = ReadDWORD(info->f);
2050 *&(info->rootObject.frame) = info->rootObject.startFrame;
2058 static bool ReadMainChunks(FileInfo * info, void * data)
2060 switch(info->chunkId)
2063 ReadChunks(ReadEditChunks, info, null);
2067 Object object = data;
2068 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2070 ReadChunks(ReadKeyFrameChunks, info, null);
2071 object.flags.keysLoaded = true;
2079 static bool ReadMain(FileInfo * info, void * data)
2081 switch(info->chunkId)
2084 ReadChunks(ReadMainChunks, info, data);
2090 class Object3DSFormat : ObjectFormat
2092 class_property(extension) = "3ds";
2094 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2096 bool result = false;
2099 FileInfo info = {0};
2100 info.rootObject = object;
2101 info.displaySystem = displaySystem;
2104 info.fileName = fileName;
2105 StripLastDirectory(fileName, info.textureDirectory);
2106 info.f = FileOpen(fileName, read);
2109 if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2111 object.flags.root = true;
2112 object.SetMinMaxRadius(true);
2113 object._Animate(object.frame);
2114 object.UpdateTransform();
2120 info.matFaces.Free();
2121 delete info.matFaces;
2124 object.Free(displaySystem);