1 namespace gfx3D::models;
6 #define USE_32_BIT_INDICES true
7 #define indicesMember indices32
8 #define uintindex uint32
10 #define USE_32_BIT_INDICES false
11 #define indicesMember indices
12 #define uintindex uint16
18 #define RGB_FLOAT 0x0010
19 #define RGB_BYTE 0x0011
20 #define RGB_BYTE_GAMMA 0x0012
21 #define RGB_FLOAT_GAMMA 0x0013
24 #define AMOUNT_OF 0x0030
26 #define MAIN3DS 0x4D4D
27 #define EDIT3DS 0x3D3D
28 #define EDIT_AMBIENT 0x2100
29 #define EDIT_MATERIAL 0xAFFF
30 #define EDIT_OBJECT 0x4000
31 #define OBJ_HIDDEN 0x4010
32 #define OBJ_TRIMESH 0x4100
33 #define OBJ_LIGHT 0x4600
34 #define OBJ_CAMERA 0x4700
36 // Triangular Mesh Chunks
37 #define TRI_VERTEXL 0x4110
38 #define TRI_FACEL1 0x4120
39 #define TRI_MATERIAL 0x4130
40 #define TRI_MAPPINGCOORS 0x4140
41 #define TRI_SMOOTHING 0x4150
42 #define TRI_LOCAL 0x4160
45 #define LIT_SPOT 0x4610
46 #define LIT_ONOFF 0x4620
47 #define LIT_ATTENUATION 0x4625
48 #define LIT_START 0x4659
49 #define LIT_END 0x465A
50 #define LIT_MULTIPLIER 0x465B
53 #define CAM_SEECONE 0x4710
54 #define CAM_RANGES 0x4720
57 #define MAT_NAME 0xA000
58 #define MAT_AMBIENT 0xA010
59 #define MAT_DIFFUSE 0xA020
60 #define MAT_SPECULAR 0xA030
61 #define MAT_SHININESS 0xA040
62 #define MAT_SHINSTRENGTH 0xA041
63 #define MAT_SHIN3PC 0xA042
64 #define MAT_TRANSPARENCY 0xA050
65 #define MAT_XPFALL 0xA052
66 #define MAT_REFBLUR 0xA053
67 #define MAT_SELFILLUM 0xA080
68 #define MAT_DOUBLESIDED 0xA081
69 #define MAT_ADDITIVE 0xA083
70 #define MAT_SELFILPCT 0xA084
71 #define MAT_WIRE 0xA085
72 #define MAT_SUPERSMP 0xA086
73 #define MAT_WIRETHICKNESS 0xA087
74 #define MAT_FACEMAP 0xA088
75 #define MAT_XPFALLIN 0xA08A
76 #define MAT_PHONG 0xA08C
77 #define MAT_WIREABS 0xA08E
78 #define MAT_SHADING 0xA100
79 #define MAT_MAPTEXTURE1 0xA200
80 #define MAT_SPECULARMAP 0xA204
81 #define MAT_MAPOPACITY 0xA210
82 #define MAT_REFLECTIONMAP 0xA220
83 #define MAT_BUMPMAP 0xA230
86 #define MAP_FILENAME 0xA300
88 #define MAT_SHININESSMAP 0xA33C
89 #define MAT_EMISSIVEMAP 0xA33D
90 #define MAP_OPTIONS 0xA351
91 #define MAP_1_U_SCALE 0xA354
92 #define MAP_1_V_SCALE 0xA356
93 #define MAP_U_OFFSET 0xA358
94 #define MAP_V_OFFSET 0xA35A
95 #define MAP_ROTATION 0xA35C
97 #define MAP_OPTIONS_DECAL 0x0001 // (related to MAP_OPTIONS_DONTTILE)
98 #define MAP_OPTIONS_MIRROR 0x0002
99 #define MAP_OPTIONS_NEGATIVE 0x0008
100 #define MAP_OPTIONS_DONTTILE 0x0010
101 #define MAP_OPTIONS_SUMMEDFILTERING 0x0020
102 #define MAP_OPTIONS_USEALPHA 0x0040
103 #define MAP_OPTIONS_LUMORALPHATINT 0x0080
104 #define MAP_OPTIONS_IGNOREALPHA 0x0100
105 #define MAP_OPTIONS_RGBTINT 0x0200
107 #define MAP_FILTERBLUR 0xA353
108 #define MAP_1_U_SCALE 0xA354
109 #define MAP_1_V_SCALE 0xA356
110 #define MAP_U_OFFSET 0xA358
111 #define MAP_V_OFFSET 0xA35A
112 #define MAP_ROTATION 0xA35C
114 #define MAP_LUMTINT1 0xA360
115 #define MAP_LUMTINT2 0xA362
117 #define MAP_TINTR 0xA364
118 #define MAP_TINTG 0xA366
119 #define MAP_TINTB 0xA368
122 #define KEYFRAME3DS 0xB000
123 #define FRM_AMBIENT 0xB001
124 #define FRM_MESHINFO 0xB002
125 #define FRM_CAMERA 0xB003
126 #define FRM_CAMERATARGET 0xB004
127 #define FRM_OMNILIGHT 0xB005
128 #define FRM_SPOTLIGHTTARGET 0xB006
129 #define FRM_SPOTLIGHT 0xB007
130 #define FRM_FRAMES 0xB008
131 #define FRM_PARAM 0xB010
132 #define FRM_DUMMYNAME 0xB011
133 #define FRM_PIVOT 0xB013
134 #define FRM_TRACKPOS 0xB020
135 #define FRM_TRACKROT 0xB021
136 #define FRM_TRACKSCALE 0xB022
137 #define FRM_TRACKFOV 0xB023
138 #define FRM_TRACKROLL 0xB024
139 #define FRM_TRACKCOLOR 0xB025
140 #define FRM_TRACKMORPH 0xB026 // What does this do?
141 #define FRM_TRACKHOTSPOT 0xB027
142 #define FRM_TRACKFALLOFF 0xB028
143 #define FRM_TRACKHIDE 0xB029
144 #define FRM_HIERARCHY 0xB030
146 typedef struct FileInfo FileInfo;
161 DisplaySystem displaySystem;
163 const String fileName;
171 char textureDirectory[MAX_DIRECTORY];
172 Map<uintptr, Array<int>> matFaces;
175 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
176 | (((unsigned short)(word) & 0xff00) >> 8)
178 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
179 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
180 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
181 | (((unsigned int)(dword) & 0xff000000) >> 24))
183 #ifndef __BIG_ENDIAN__
184 #define BIGENDSWAP_WORD(word)
185 #define BIGENDSWAP_DWORD(dword)
187 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
188 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
191 // Zero Terminated String
192 static int ReadASCIIZ(File f, char ** string)
194 // *** Read String ***
196 char temp[1024] = "";
200 if(!temp[c++]) break;
202 *string = new char[c];
204 strcpy(*string, temp);
208 static float ReadFloat(File f)
211 f.Read(&floatValue, sizeof(float), 1);
212 BIGENDSWAP_DWORD(floatValue);
216 static uint16 ReadWORD(File f)
219 f.Read(&wordValue, sizeof(uint16), 1);
220 BIGENDSWAP_WORD(wordValue);
224 static uint ReadDWORD(File f)
227 f.Read(&dwordValue, sizeof(uint), 1);
228 BIGENDSWAP_DWORD(dwordValue);
233 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
235 for(;info->pos < info->end;)
237 FileInfo childInfo = *info;
240 childInfo.parent = info;
242 info->f.Seek(info->pos, start);
243 childInfo.chunkId = ReadWORD(info->f);
244 length = ReadDWORD(info->f);
246 childInfo.pos += sizeof(uint16) + sizeof(uint);
247 childInfo.end = info->pos + length;
249 if(!chunkParser(&childInfo, data))
252 info->pos = childInfo.end;
258 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
260 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
263 info->f.Getc((char *)&value); rgb->r = value / 255.0f;
264 info->f.Getc((char *)&value); rgb->g = value / 255.0f;
265 info->f.Getc((char *)&value); rgb->b = value / 255.0f;
267 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
269 rgb->r = ReadFloat(info->f);
270 rgb->g = ReadFloat(info->f);
271 rgb->b = ReadFloat(info->f);
276 static bool Read3DVertex(File f, Vector3Df vertex)
278 vertex.x = ReadFloat(f);
279 vertex.y = ReadFloat(f);
280 vertex.z = ReadFloat(f);
284 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
286 if(info->chunkId == AMOUNT_OF)
287 *amountOf = ReadWORD(info->f);
291 #define WELD_TRESHOLD 0.000001
292 #define SMOOTH_CUTOFF 0 // 45
294 struct SharedSourceVertexInfo
301 int OnCompare(SharedSourceVertexInfo b)
303 if(unique < b.unique) return -1;
304 if(unique > b.unique) return 1;
308 if(face < b.face) return -1;
309 if(face > b.face) return 1;
311 if(index == b.index) return 0;
314 if(value.x < b.value.x - WELD_TRESHOLD) return -1;
315 if(value.x > b.value.x + WELD_TRESHOLD) return 1;
316 if(value.y < b.value.y - WELD_TRESHOLD) return -1;
317 if(value.y > b.value.y + WELD_TRESHOLD) return 1;
318 if(value.z < b.value.z - WELD_TRESHOLD) return -1;
319 if(value.z > b.value.z + WELD_TRESHOLD) return 1;
323 if(index < b.index) return -1;
324 if(index > b.index) return 1;
330 class SharedDestVertexInfo
332 Array<int> faces { };
335 struct SourceVertexInfo
337 SharedSourceVertexInfo * shared;
341 int OnCompare(SourceVertexInfo b)
343 int r = (*shared).OnCompare(*b.shared);
344 if(!r) r = texCoord.OnCompare(b.texCoord);
345 if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
352 int index, copyFromIndex;
356 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
359 Face * faces = info->faces;
360 int nVertices = mesh.nVertices;
363 Vector3Df * mVertices;
364 double cutOff = cos(Degrees { SMOOTH_CUTOFF });
366 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
367 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
368 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
370 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
371 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
373 nNewVertices = nVertices;
374 mVertices = mesh->vertices;
376 for(c = 0; c<info->nFaces; c++)
378 Face * face = &faces[c];
380 plane.FromPointsf(mesh.vertices[face->indices[2]],
381 mesh.vertices[face->indices[1]],
382 mesh.vertices[face->indices[0]]);
383 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
386 for(c = 0; c < info->nFaces; c++)
388 Face * face = &faces[c];
392 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
393 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
398 SharedSourceVertexInfo * source;
399 SharedDestVertexInfo svInfo;
400 DestVertexInfo vInfo;
402 index = face->indices[i];
404 if(face->smoothGroups)
405 itShared.Index({ index = index, mVertices[index], face = face }, true);
407 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
408 svInfo = itShared.data;
409 if(!svInfo) itShared.data = svInfo = { };
412 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
413 // TODO: Allow obtaining address of MapIterator::key
414 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
415 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
421 vInfo.copyFromIndex = index;
426 vertices[index] = (void *)it.pointer;
427 else if(vertices[index] != it.pointer)
429 // If it's a different smoothing group, we'll need extra vertices
430 index = vertices.size;
432 vertices.Add((void *)it.pointer);
435 face->indices[i] = vInfo.index;
439 for(index = 0; index < nNewVertices; index++)
442 it.pointer = vertices[index];
445 DestVertexInfo vInfo = it.data;
446 Vector3Df normal { };
447 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
448 uint smoothing = inf->smoothGroups;
450 SharedSourceVertexInfo * shared = inf->shared;
451 SharedDestVertexInfo svInfo = sharedVertices[*shared];
453 if(!svInfo || vInfo.index != index)
456 for(i : svInfo.faces)
458 Face * face = &info->faces[i];
460 if(smoothing & face->smoothGroups)
461 smoothing |= face->smoothGroups;
464 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
465 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
467 for(i : svInfo.faces)
469 Face * face = &info->faces[i];
470 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
473 for(j = 0; j < 3; j++)
475 if(face->indices[j] == vInfo.index)
477 origIndex = face->origIndices[j];
479 normal.x += face->normal.x;
480 normal.y += face->normal.y;
481 normal.z += face->normal.z;
494 for(i : svInfo.faces)
496 Face * face = &info->faces[i];
497 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
501 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
506 for(k = 0; k < 3; k++)
508 if(face->indices[k] == vInfo.index)
510 origIndexB = face->origIndices[k];
514 valid = origIndex == origIndexB;
517 for(j : svInfo.faces)
519 if(info->faces[j].done)
521 double dot = info->faces[j].normal.DotProduct(face->normal);
522 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
523 valid = fabs(dot) > cutOff;
532 normal.x += face->normal.x;
533 normal.y += face->normal.y;
534 normal.z += face->normal.z;
541 if(!SMOOTH_CUTOFF || !WELD_TRESHOLD) break;
543 normal.Scale(normal, 1.0f / numShared);
544 if(vInfo.index == index)
545 vInfo.normal.Normalize(normal);
547 // Auto welding/smoothing requires extra vertices because angle is too steep
548 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
550 SharedDestVertexInfo newSharedInfo = null;
552 for(i : svInfo.faces)
554 Face * face = &info->faces[i];
555 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
558 for(j = 0; j < 3; j++)
560 if(face->indices[j] == vInfo.index)
564 DestVertexInfo newVert;
565 SharedSourceVertexInfo * source;
567 index = nNewVertices++;
568 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
569 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
570 itShared.data = newSharedInfo = { };
572 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
575 newVert.copyFromIndex = vInfo.copyFromIndex;
576 newVert.index = index;
578 vertices.Add((void *)it.pointer);
580 face->indices[j] = index;
581 newSharedInfo.faces.Add(i);
591 // Allocate some extra vertices
593 Vector3Df * oldVertices = mesh.vertices;
594 Pointf * oldTexCoords = mesh.texCoords;
596 // TODO: Support reallocation?
597 *((void **)&mesh.vertices) = null;
598 *((void **)&mesh.texCoords) = null;
599 *((int *)&mesh.nVertices) = 0;
601 mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
603 // Fill in the new vertices
604 for(index = 0; index < nNewVertices; index++)
606 DestVertexInfo vInfo;
607 it.pointer = vertices[index];
611 mesh.normals[index] = vInfo ? vInfo.normal : { };
612 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
614 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
623 for(i = 0; i < info->nFaces; i++)
624 info->faces[i].done = false;
627 mesh.Unlock({ normals = true });
629 // Free all the temporary stuff
634 sharedVertices.Free();
635 delete sharedVertices;
639 static bool ReadSmoothing(FileInfo * info, Object object)
641 switch(info->chunkId)
646 for(c = 0; c<info->nFaces; c++)
647 info->faces[c].smoothGroups = ReadDWORD(info->f);
654 static bool ReadFacesListChunks(FileInfo * info, Object object)
656 DisplaySystem displaySystem = info->displaySystem;
657 switch(info->chunkId)
666 char matName[MAX_LOCATION + 100];
668 strcpy(matName, info->fileName);
669 ReadASCIIZ(info->f, &name);
670 count = ReadWORD(info->f);
671 strcat(matName, name);
673 mat = displaySystem.GetMaterial(matName);
674 faces = info->matFaces[(uintptr)mat];
676 info->matFaces[(uintptr)mat] = faces = { };
680 for(c = 0; c<count; c++)
682 uint16 face = ReadWORD(info->f);
684 info->faces[face].material = mat;
693 static bool ReadTriMesh(FileInfo * info, Object object)
695 Mesh mesh = object.mesh;
696 switch(info->chunkId)
701 uint16 nVertices = ReadWORD(info->f);
702 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
703 *((int *) &mesh.nVertices) = nVertices;
704 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
707 for(c = 0; c<mesh.nVertices; c++)
710 Read3DVertex(info->f, vertex);
711 mesh.vertices[c].x = vertex.x;
712 mesh.vertices[c].y =-vertex.z;
713 mesh.vertices[c].z = vertex.y;
720 case TRI_MAPPINGCOORS:
723 uint16 count = ReadWORD(info->f);
724 count = (uint16)Min(mesh.nVertices, count);
726 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
727 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
728 mesh.flags.texCoords1 = true;
731 for(c = 0; c<count; c++)
733 mesh.texCoords[c].x = ReadFloat(info->f);
734 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
748 info->nFaces = nFaces = ReadWORD(info->f);
749 info->pos += sizeof(uint16);
751 info->faces = new0 Face[nFaces];
752 for(c = 0; c<nFaces; c++)
756 info->faces[c].origIndices[i] =
757 info->faces[c].indices[i] = ReadWORD(info->f);
759 info->pos += 4*sizeof(uint16);
762 ReadChunks(ReadSmoothing, info, object);
766 info->matFaces.Free();
767 info->matFaces = { };
769 ReadChunks(ReadFacesListChunks, info, object);
771 ComputeNormals(mesh, info, object);
774 for(m : info->matFaces)
776 Material mat = (Material)&m;
777 Array<int> faces = m;
778 if(mat.flags.translucent)
780 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
783 Face * face = &info->faces[i];
784 PrimitiveSingle * triangle;
786 triangle = &mesh.primitives[mesh.nPrimitives++];
787 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
789 triangle->indicesMember[0] = (uintindex)face->indices[0];
790 triangle->indicesMember[1] = (uintindex)face->indices[1];
791 triangle->indicesMember[2] = (uintindex)face->indices[2];
792 triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
793 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
794 triangle->plane.FromPointsf(
795 mesh.vertices[triangle->indicesMember[2]],
796 mesh.vertices[triangle->indicesMember[1]],
797 mesh.vertices[triangle->indicesMember[0]]);
799 mesh.UnlockPrimitive(triangle);
801 triangle->middle.x /= 3;
802 triangle->middle.y /= 3;
803 triangle->middle.z /= 3;
805 triangle->material = mat;
808 object.flags.translucent = true;
813 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
817 group.material = mat;
820 Face * face = &info->faces[i];
822 if(object.flags.flipWindings)
824 group.indicesMember[c*3] = (uintindex)face->indices[2];
825 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
826 group.indicesMember[c*3+2] = (uintindex)face->indices[0];
830 group.indicesMember[c*3] = (uintindex)face->indices[0];
831 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
832 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
837 mesh.UnlockPrimitiveGroup(group);
842 // Add faces without a material all together
844 for(c = 0; c<nFaces; c++)
845 if(!info->faces[c].done)
849 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
852 for(c = 0; c<nFaces; c++)
854 Face * face = &info->faces[c];
857 group.indicesMember[c*3] = (uintindex)face->indices[0];
858 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
859 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
862 mesh.UnlockPrimitiveGroup(group);
869 info->matFaces.Free();
870 delete info->matFaces;
877 Vector3Df xAxis, yAxis, zAxis, center;
880 Matrix inverse/*, source = { 0 }*/;
883 Read3DVertex(info->f, xAxis);
884 Read3DVertex(info->f, yAxis);
885 Read3DVertex(info->f, zAxis);
886 Read3DVertex(info->f, center);
888 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
889 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
890 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
892 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
895 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
896 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
897 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
898 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
900 inverse.Inverse(source);
903 object.flags.flipWindings = false;
905 xAxis.Normalize(xAxis);
906 yAxis.Normalize(yAxis);
907 zAxis.Normalize(zAxis);
909 orth.CrossProduct(yAxis, zAxis);
910 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
911 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
912 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
914 object.flags.flipWindings ^= true;
918 orth.CrossProduct(zAxis, xAxis);
919 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
920 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
921 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
923 object.flags.flipWindings ^= true;
927 orth.CrossProduct(xAxis, yAxis);
928 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
929 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
930 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
932 object.flags.flipWindings ^= true;
939 rotation.m[0][0] = xAxis.x;
940 rotation.m[0][1] =-xAxis.z;
941 rotation.m[0][2] = xAxis.y;
942 rotation.m[0][3] = 0;
943 rotation.m[1][0] =-zAxis.x;
944 rotation.m[1][1] = zAxis.z;
945 rotation.m[1][2] =-zAxis.y;
946 rotation.m[1][3] = 0;
947 rotation.m[2][0] = yAxis.x;
948 rotation.m[2][1] =-yAxis.z;
949 rotation.m[2][2] = yAxis.y;
950 rotation.m[2][3] = 0;
951 rotation.m[3][0] = 0;
952 rotation.m[3][1] = 0;
953 rotation.m[3][2] = 0;
954 rotation.m[3][3] = 1;
958 inverse.Transpose(rotation);
960 temp.Translate(-center.x, center.z, -center.y);
961 temp2.Multiply(temp, inverse);
962 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
966 object.transform.scaling = scaling;
967 // TODO: Improve language to support deep properties on non constant functions
968 // object.transform.orientation.RotationMatrix(rotation);
970 Quaternion orientation;
971 orientation.RotationMatrix(rotation);
972 object.transform.orientation = orientation;
974 object.transform.position = { center.x, -center.z, center.y };
977 // Localize All Vertices
978 for(c = 0; c<mesh.nVertices; c++)
980 Vector3Df vertex = mesh.vertices[c];
982 mesh.vertices[c].MultMatrix(vertex, inverse);
984 mesh.vertices[c].x -= object.pivot.x;
985 mesh.vertices[c].y -= object.pivot.y;
986 mesh.vertices[c].z -= object.pivot.z;
995 static bool ReadMap(FileInfo * info, Material mat)
997 DisplaySystem displaySystem = info->displaySystem;
998 switch(info->chunkId)
1003 char location[MAX_LOCATION];
1005 ReadASCIIZ(info->f, &name);
1007 strcpy(location, info->textureDirectory);
1008 PathCat(location, name);
1009 if(!FileExists(location))
1011 // Attempt all lowercase if original case does not exist
1013 strcpy(location, info->textureDirectory);
1014 PathCat(location, name);
1017 if(info->parent->chunkId == MAT_BUMPMAP)
1019 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1020 char bumpName[MAX_LOCATION+5];
1021 strcpy(bumpName, "BUMP:");
1022 strcat(bumpName, location);
1025 mat.bumpMap = displaySystem.GetTexture(bumpName);
1028 mat.bumpMap = Bitmap { };
1029 if(!mat.bumpMap.Load(location, null, null) ||
1030 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1031 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1035 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1036 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1039 for(y = 0; y < bh; y++)
1040 for(x = 0; x < bw; x++)
1042 uint bc = y * bw + x;
1043 Color color = picture[bc].color;
1044 picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
1052 Bitmap opacityMap = null;
1053 bool alphaOnly = true;
1054 bool translucent = false;
1057 mat.baseMap = displaySystem.GetTexture(location);
1060 mat.baseMap = Bitmap { };
1061 if(!mat.baseMap.Load(location, null, null) ||
1062 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1063 !displaySystem.AddTexture(location, mat.baseMap))
1067 opacityMap = mat.baseMap;
1070 else if(info->parent->chunkId == MAT_MAPOPACITY)
1072 opacityMap = Bitmap { };
1073 if(opacityMap.Load(location, null, null))
1075 if(opacityMap.pixelFormat == pixelFormatRGBA)
1077 if(!opacityMap.Convert(null, pixelFormat888, null))
1084 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1086 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1087 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1089 int ow = opacityMap.width, oh = opacityMap.height;
1090 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1092 for(y = 0; y < bh; y++)
1093 for(x = 0; x < bw; x++)
1095 int bc = ((y % bh) * bw + (x % bw));
1096 int oc = ((y % oh) * bw + (x % ow));
1097 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1098 if(alpha && alpha < 255)
1100 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1104 mat.flags.translucent = true;
1105 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1106 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1108 if(opacityMap != mat.baseMap)
1116 uint16 options = ReadWORD(info->f);
1117 if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
1121 mat.uScale = ReadFloat(info->f);
1124 mat.vScale = ReadFloat(info->f);
1130 PrintLn("Unhandled Map block");
1137 static bool ReadMaterial(FileInfo * info, Material mat)
1139 switch(info->chunkId)
1144 char matName[MAX_LOCATION + 100];
1145 strcpy(matName, info->fileName);
1146 ReadASCIIZ(info->f, &name);
1147 strcat(matName, name);
1148 mat.name = CopyString(matName);
1152 case MAT_TRANSPARENCY:
1154 uint16 transparency;
1155 ReadChunks(ReadAmountOf, info, &transparency);
1156 mat.opacity = 1.0f - transparency / 100.0f;
1157 if(mat.opacity < 1.0)
1158 mat.flags.translucent = true;
1163 ReadChunks(ReadRGB, info, &mat.diffuse);
1164 ReadChunks(ReadRGB, info, &mat.diffuse);
1169 ReadChunks(ReadRGB, info, &mat.ambient);
1170 ReadChunks(ReadRGB, info, &mat.ambient);
1175 ReadChunks(ReadRGB, info, &mat.specular);
1176 ReadChunks(ReadRGB, info, &mat.specular);
1182 ReadChunks(ReadAmountOf, info, &emissive);
1183 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1184 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1185 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1188 case MAT_SHINSTRENGTH:
1191 ReadChunks(ReadAmountOf, info, &shininess);
1192 mat.specular.r *= shininess / 100.0f;
1193 mat.specular.g *= shininess / 100.0f;
1194 mat.specular.b *= shininess / 100.0f;
1200 ReadChunks(ReadAmountOf, info, &power);
1204 case MAT_MAPTEXTURE1:
1205 ReadChunks(ReadMap, info, mat);
1207 case MAT_MAPOPACITY:
1208 ReadChunks(ReadMap, info, mat);
1210 case MAT_DOUBLESIDED:
1211 mat.flags.doubleSided = true;
1214 ReadChunks(ReadMap, info, mat);
1218 PrintLn("Unhandled MAT type ID", info->chunkId);
1225 static bool ReadLight(FileInfo * info, Object object)
1227 Light * light = &object.light;
1228 switch(info->chunkId)
1231 case RGB_BYTE_GAMMA:
1233 case RGB_FLOAT_GAMMA:
1234 ReadRGB(info, &light->diffuse);
1235 light->specular = light->diffuse;
1240 char targetName[MAXNAMELEN];
1242 strcpy(targetName, object.name);
1243 strcat(targetName, ".target");
1245 light->flags.omni = false;
1246 light->flags.spot = true;
1248 target = Object { };
1249 target.name = CopyString(targetName);
1250 info->rootObject.children.AddName(target);
1251 target.parent = info->rootObject;
1253 light->target = target;
1255 target.transform.position.x = ReadFloat(info->f);
1256 target.transform.position.z = ReadFloat(info->f);
1257 target.transform.position.y =-ReadFloat(info->f);
1259 light->hotSpot = ReadFloat(info->f);
1260 light->fallOff = ReadFloat(info->f);
1265 light->flags.off = true;
1268 case LIT_ATTENUATION:
1272 d = 300, small = 0.001,
1274 d * (Kl + Kq * d) = (1 / small) - Kc
1276 { Kc, Kl, Kq, small, d });
1279 light->flags.attenuation = true;
1282 #define MINLIGHT 0.08
1283 light->Kq = 1/(light->end*light->end*MINLIGHT);
1286 #define MINLIGHT 0.15f
1287 // #define MINLIGHT 0.1
1288 light->Kl = (float)(1/(light->end*MINLIGHT));
1294 light->start = ReadFloat(info->f);
1299 light->end = ReadFloat(info->f);
1302 case LIT_MULTIPLIER:
1304 light->multiplier = ReadFloat(info->f);
1312 static bool ReadCamera(FileInfo * info, Object object)
1314 switch(info->chunkId)
1322 //Camera camera = object.camera;
1323 /*float nearRange = */ReadFloat(info->f);
1324 /*float farRange = */ReadFloat(info->f);
1326 camera.zMin = Max(0.1, nearRange);
1327 camera.zMax = farRange;
1336 static bool ReadEditObject(FileInfo * info, char * name)
1338 DisplaySystem displaySystem = info->displaySystem;
1339 switch(info->chunkId)
1343 Object object = info->rootObject.Find(name);
1346 object = Object { };
1347 object.name = CopyString(name);
1348 info->rootObject.children.AddName(object);
1349 object.parent = info->rootObject;
1351 object.InitializeMesh(displaySystem);
1352 ReadChunks(ReadTriMesh, info, object);
1353 object.flags.mesh = true;
1354 object.mesh.Unlock(0);
1359 Object object = info->rootObject.Find(name);
1365 object = Object { };
1366 object.name = CopyString(name);
1367 info->rootObject.children.AddName(object);
1368 object.parent = info->rootObject;
1370 object.flags.light = true;
1372 light = &object.light;
1373 light->lightObject = object;
1374 light->flags.omni = true;
1375 light->multiplier = 1.0f;
1377 // This is not used?
1378 Read3DVertex(info->f, position);
1379 light->direction = { position.x, position.y, position.z };
1380 info->pos += sizeof(float) * 3;
1382 ReadChunks(ReadLight, info, object);
1387 char targetName[MAXNAMELEN];
1388 Object object = info->rootObject.Find(name);
1391 float /*bankAngle, */focus;
1394 strcpy(targetName, name);
1395 strcat(targetName, ".target");
1396 target = info->rootObject.Find(targetName);
1400 object = Object { };
1401 object.name = CopyString(name);
1402 info->rootObject.children.AddName(object);
1404 object.parent = info->rootObject;
1405 object.camera = Camera { };
1406 object.camera.type = lookAtObject;
1411 target = Object { };
1412 target.name = CopyString(targetName);
1413 info->rootObject.children.AddName(target);
1414 target.parent = info->rootObject;
1417 object.flags.camera = true;
1418 object.cameraTarget = target;
1420 camera = object.camera;
1421 camera.cameraObject = object;
1422 camera.target = target;
1424 //Read3DVertex(info->f, camera.position);
1425 object.transform.position.x = ReadFloat(info->f);
1426 object.transform.position.z = ReadFloat(info->f);
1427 object.transform.position.y =-ReadFloat(info->f);
1429 info->pos += sizeof(float) * 3;
1430 //Read3DVertex(info->f, object.cameraTarget.position);
1431 target.transform.position.x = ReadFloat(info->f);
1432 target.transform.position.z = ReadFloat(info->f);
1433 target.transform.position.y =-ReadFloat(info->f);
1435 info->pos += sizeof(float) * 3;
1436 /*bankAngle = */ReadFloat(info->f);
1437 info->pos += sizeof(float);
1438 focus = ReadFloat(info->f);
1439 info->pos += sizeof(float);
1441 mm = (focus - 5.05659508373109) / 1.13613250717301;
1442 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1444 ReadChunks(ReadCamera, info, object);
1447 case OBJ_HIDDEN: break;
1452 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1454 static bool ReadEditChunks(FileInfo * info, void * data)
1456 switch(info->chunkId)
1460 // Read the ambient color
1461 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1466 Material material { /*flags = { singleSideLight = true }*/ };
1468 ReadChunks(ReadMaterial, info, material);
1470 mat = info->displaySystem.AddNamedMaterial(material.name);
1473 if(material.baseMap)
1474 material.baseMap.MakeMipMaps(info->displaySystem);
1475 if(material.bumpMap)
1476 material.bumpMap.MakeMipMaps(info->displaySystem);
1477 // COPY_NITEM(mat, material);
1478 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1482 delete material.baseMap;
1484 delete material.name;
1491 info->pos += ReadASCIIZ(info->f, &name);
1492 ReadChunks(ReadEditObject, info, name);
1500 struct ObjectInfoBlock
1510 // Key Framer Chunks
1512 #define ACCFLAG_TENSION 0x00000001
1513 #define ACCFLAG_CONTINUITY 0x00000002
1514 #define ACCFLAG_BIAS 0x00000004
1515 #define ACCFLAG_EASETO 0x00000008
1516 #define ACCFLAG_EASEFROM 0x00000010
1518 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1520 switch(info->chunkId)
1524 //uint16 flags1, flags2;
1525 ReadASCIIZ(info->f, &block->name);
1526 /*flags1 = */ReadWORD(info->f);
1527 /*flags2 = */ReadWORD(info->f);
1528 block->parent = ReadWORD(info->f);
1532 ReadASCIIZ(info->f, &block->dummyName);
1535 Read3DVertex(info->f, block->pivot);
1538 block->hierarchy = ReadWORD(info->f);
1542 case FRM_TRACKSCALE:
1545 case FRM_TRACKCOLOR:
1546 case FRM_TRACKHOTSPOT:
1547 case FRM_TRACKFALLOFF:
1549 FrameTrack track { };
1556 block->tracks.Add(track);
1558 flags = ReadWORD(info->f);
1560 info->f.Read(unknown, sizeof(unknown), 1);
1562 track.numKeys = ReadDWORD(info->f);
1564 switch(info->chunkId)
1566 case FRM_TRACKPOS: track.type.type = position; break;
1567 case FRM_TRACKROT: track.type.type = rotation; break;
1568 case FRM_TRACKSCALE: track.type.type = scaling; break;
1569 case FRM_TRACKROLL: track.type.type = roll; break;
1570 case FRM_TRACKFOV: track.type.type = fov; break;
1571 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1572 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1573 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1575 if((flags & 0x0003) == 3)
1576 track.type.loop = true;
1578 track.keys = new0 FrameKey[track.numKeys];
1579 for(c = 0; c<track.numKeys; c++)
1581 uint16 accelerationFlags;
1582 FrameKey * key = track.keys + c;
1584 key->frame = ReadDWORD(info->f);
1585 accelerationFlags = ReadWORD(info->f);
1587 if(accelerationFlags & ACCFLAG_TENSION)
1588 key->tension = ReadFloat(info->f);
1589 if(accelerationFlags & ACCFLAG_CONTINUITY)
1590 key->continuity = ReadFloat(info->f);
1591 if(accelerationFlags & ACCFLAG_BIAS)
1592 key->bias = ReadFloat(info->f);
1593 if(accelerationFlags & ACCFLAG_EASETO)
1594 key->easeTo = ReadFloat(info->f);
1595 if(accelerationFlags & ACCFLAG_EASEFROM)
1596 key->easeFrom = ReadFloat(info->f);
1598 switch(info->chunkId)
1603 Read3DVertex(info->f, position);
1604 key->position = { position.x, -position.z, position.y };
1610 Angle angle = ReadFloat(info->f);
1611 Vector3Df fixedAxis;
1613 Read3DVertex(info->f, axis);
1614 fixedAxis.x = axis.x;
1615 fixedAxis.y = -axis.z;
1616 fixedAxis.z = axis.y;
1620 Quaternion rotation;
1621 rotation.RotationAxis(fixedAxis, angle);
1622 key->orientation.Multiply((key - 1)->orientation, rotation);
1625 key->orientation.RotationAxis(fixedAxis, angle);
1628 case FRM_TRACKSCALE:
1631 Read3DVertex(info->f, scaling);
1632 key->scaling = { scaling.x, scaling.z, scaling.y };
1637 key->fov = ReadFloat(info->f);
1642 key->roll = -ReadFloat(info->f);
1645 case FRM_TRACKCOLOR:
1647 FileInfo childInfo = *info;
1648 childInfo.chunkId = RGB_FLOAT;
1649 ReadRGB(&childInfo, &key->color);
1652 case FRM_TRACKHOTSPOT:
1654 key->hotSpot = ReadFloat(info->f);
1657 case FRM_TRACKFALLOFF:
1659 key->fallOff = ReadFloat(info->f);
1671 static Object FindObjectID(Object object, int id)
1673 Object result = null;
1675 for(child = object.children.first; child; child = child.next)
1677 if(child.flags.hierarchy == (uint16) id)
1684 result = FindObjectID(child, id);
1691 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1693 switch(info->chunkId)
1697 ObjectInfoBlock block { };
1698 Object object = null;
1700 ReadChunks(ReadFrameInfoBlock, info, &block);
1702 if(block.dummyName && block.dummyName[0])
1704 if(!strcmp(block.name, "$$$DUMMY"))
1706 object = Object { };
1707 object.name = block.dummyName;
1708 info->rootObject.children.AddName(object);
1709 object.transform.scaling = { 1,1,1 };
1713 Object model = info->rootObject.Find(block.name);
1716 object = Object { };
1717 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1718 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1719 object.flags = model.flags;
1720 object.flags.ownMesh = false;
1721 object.mesh = model.mesh;
1723 object.min = model.min;
1724 object.max = model.max;
1725 object.radius = model.radius;
1727 object.transform = model.transform;
1728 info->rootObject.children.AddName(object);
1730 delete block.dummyName;
1733 object.parent = info->rootObject;
1736 object = info->rootObject.Find(block.name);
1740 Mesh mesh = object.mesh;
1741 object.flags.hierarchy = block.hierarchy + 1;
1742 if(block.parent != -1)
1744 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1747 object.parent.children.Remove(object);
1748 parent.children.AddName(object);
1749 object.parent = parent;
1752 object.pivot.x = block.pivot.x;
1753 object.pivot.y =-block.pivot.z;
1754 object.pivot.z = block.pivot.y;
1756 if(mesh && object.flags.ownMesh)
1758 if(mesh.Lock({ vertices = true }))
1761 // Take pivot into account
1762 for(c = 0; c<mesh.nVertices; c++)
1764 mesh.vertices[c].x -= object.pivot.x;
1765 mesh.vertices[c].y -= object.pivot.y;
1766 mesh.vertices[c].z -= object.pivot.z;
1768 mesh.Unlock({ vertices = true });
1772 object.tracks = block.tracks;
1773 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1774 object.endFrame = info->rootObject.endFrame;
1777 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1783 ObjectInfoBlock block { };
1784 Object object = null;
1786 ReadChunks(ReadFrameInfoBlock, info, &block);
1788 if(block.dummyName && block.dummyName[0])
1790 if(!strcmp(block.name, "$$$DUMMY"))
1792 object = Object { };
1793 object.name = block.dummyName;
1794 info->rootObject.children.AddName(object);
1795 object.transform.scaling = { 1, 1, 1 };
1796 object.flags.camera = true;
1800 Object model = info->rootObject.Find(block.name);
1803 object = Object { };
1804 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1805 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1806 object.flags = model.flags;
1807 object.flags.ownMesh = false;
1808 object.camera = model.camera;
1809 object.flags.camera = true;
1810 info->rootObject.children.AddName(object);
1812 delete block.dummyName;
1815 object.parent = info->rootObject;
1818 object = info->rootObject.Find(block.name);
1822 object.flags.hierarchy = block.hierarchy + 1;
1823 if(block.parent != -1)
1825 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1828 object.parent.children.Remove(object);
1829 parent.children.AddName(object);
1830 object.parent = parent;
1833 object.pivot.x = block.pivot.x;
1834 object.pivot.y =-block.pivot.z;
1835 object.pivot.z = block.pivot.y;
1837 object.tracks = block.tracks;
1838 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1839 object.endFrame = info->rootObject.endFrame;
1842 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1846 case FRM_CAMERATARGET:
1848 ObjectInfoBlock block { };
1849 Object object = null;
1850 char targetName[MAXNAMELEN];
1852 ReadChunks(ReadFrameInfoBlock, info, &block);
1854 strcpy(targetName, block.name);
1855 strcat(targetName, ".target");
1857 if(block.dummyName && block.dummyName[0])
1859 if(!strcmp(block.name, "$$$DUMMY"))
1861 object = Object { };
1862 object.name = block.dummyName;
1863 info->rootObject.children.AddName(object);
1864 object.transform.scaling = { 1,1,1 };
1868 Object model = info->rootObject.Find(targetName);
1871 object = Object { };
1872 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1873 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1874 object.flags = model.flags;
1875 object.flags.ownMesh = false;
1876 object.camera = model.camera;
1877 info->rootObject.children.AddName(object);
1879 delete block.dummyName;
1882 object.parent = info->rootObject;
1885 object = info->rootObject.Find(targetName);
1889 object.flags.hierarchy = block.hierarchy + 1;
1890 if(block.parent != -1)
1892 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1895 object.parent.children.Remove(object);
1896 parent.children.AddName(object);
1897 object.parent = parent;
1900 object.pivot.x = block.pivot.x;
1901 object.pivot.y =-block.pivot.z;
1902 object.pivot.z = block.pivot.y;
1904 object.tracks = block.tracks;
1905 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1906 object.endFrame = info->rootObject.endFrame;
1909 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1915 ObjectInfoBlock block { };
1917 ReadChunks(ReadFrameInfoBlock, info, &block);
1919 info->rootObject.tracks = block.tracks;
1925 ObjectInfoBlock block { };
1926 Object object = null;
1928 ReadChunks(ReadFrameInfoBlock, info, &block);
1930 if(block.dummyName && block.dummyName[0])
1932 if(!strcmp(block.name, "$$$DUMMY"))
1934 object = Object { };
1935 object.name = block.dummyName;
1936 info->rootObject.children.AddName(object);
1937 object.transform.scaling = { 1, 1, 1 };
1938 object.flags.light = true;
1942 Object model = info->rootObject.Find(block.name);
1945 object = Object { };
1946 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1947 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1948 object.flags = model.flags;
1949 object.flags.ownMesh = false;
1950 object.light = model.light;
1951 object.flags.light = true;
1952 info->rootObject.children.AddName(object);
1954 delete block.dummyName;
1957 object.parent = info->rootObject;
1960 object = info->rootObject.Find(block.name);
1964 object.flags.hierarchy = block.hierarchy + 1;
1965 if(block.parent != -1)
1967 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1970 object.parent.children.Remove(object);
1971 parent.children.AddName(object);
1972 object.parent = parent;
1975 object.pivot.x = block.pivot.x;
1976 object.pivot.y =-block.pivot.z;
1977 object.pivot.z = block.pivot.y;
1979 object.tracks = block.tracks;
1980 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1981 object.endFrame = info->rootObject.endFrame;
1984 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1988 case FRM_SPOTLIGHTTARGET:
1990 ObjectInfoBlock block { };
1991 Object object = null;
1992 char targetName[MAXNAMELEN];
1994 ReadChunks(ReadFrameInfoBlock, info, &block);
1996 strcpy(targetName, block.name);
1997 strcat(targetName, ".target");
1999 if(block.dummyName && block.dummyName[0])
2001 if(!strcmp(block.name, "$$$DUMMY"))
2003 object = Object { };
2004 object.name = block.dummyName;
2005 info->rootObject.children.AddName(object);
2006 object.transform.scaling = { 1,1,1 };
2010 Object model = info->rootObject.Find(targetName);
2013 object = Object { };
2014 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2015 // TODO: When passing a String to a const String, use member if property is const String but member is String
2016 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2017 object.flags = model.flags;
2018 object.flags.ownMesh = false;
2019 object.light = model.light;
2020 info->rootObject.children.AddName(object);
2022 delete block.dummyName;
2025 object.parent = info->rootObject;
2028 object = info->rootObject.Find(targetName);
2032 object.flags.hierarchy = block.hierarchy + 1;
2033 if(block.parent != -1)
2035 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2038 object.parent.children.Remove(object);
2039 parent.children.AddName(object);
2040 object.parent = parent;
2043 object.pivot.x = block.pivot.x;
2044 object.pivot.y =-block.pivot.z;
2045 object.pivot.z = block.pivot.y;
2047 object.tracks = block.tracks;
2048 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2049 object.endFrame = info->rootObject.endFrame;
2052 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2058 info->rootObject.startFrame = ReadDWORD(info->f);
2059 info->rootObject.endFrame = ReadDWORD(info->f);
2060 *&(info->rootObject.frame) = info->rootObject.startFrame;
2068 static bool ReadMainChunks(FileInfo * info, void * data)
2070 switch(info->chunkId)
2073 ReadChunks(ReadEditChunks, info, null);
2077 Object object = data;
2078 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2080 ReadChunks(ReadKeyFrameChunks, info, null);
2081 object.flags.keysLoaded = true;
2089 static bool ReadMain(FileInfo * info, void * data)
2091 switch(info->chunkId)
2094 ReadChunks(ReadMainChunks, info, data);
2100 class Object3DSFormat : ObjectFormat
2102 class_property(extension) = "3ds";
2104 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2106 bool result = false;
2109 FileInfo info = {0};
2110 info.rootObject = object;
2111 info.displaySystem = displaySystem;
2114 info.fileName = fileName;
2115 StripLastDirectory(fileName, info.textureDirectory);
2116 info.f = FileOpen(fileName, read);
2119 if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2121 object.flags.root = true;
2122 object.SetMinMaxRadius(true);
2123 object._Animate(object.frame);
2124 object.UpdateTransform();
2130 info.matFaces.Free();
2131 delete info.matFaces;
2134 object.Free(displaySystem);