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;
175 static struct FileInfo
178 DisplaySystem displaySystem;
180 const String fileName;
188 char textureDirectory[MAX_DIRECTORY];
189 Map<uintptr, Array<int>> matFaces;
192 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
193 | (((unsigned short)(word) & 0xff00) >> 8)
195 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
196 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
197 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
198 | (((unsigned int)(dword) & 0xff000000) >> 24))
200 #ifndef __BIG_ENDIAN__
201 #define BIGENDSWAP_WORD(word)
202 #define BIGENDSWAP_DWORD(dword)
204 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
205 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
208 // Zero Terminated String
209 static int ReadASCIIZ(File f, char ** string)
211 // *** Read String ***
213 char temp[1024] = "";
217 if(!temp[c++]) break;
219 *string = new char[c];
221 strcpy(*string, temp);
225 static float ReadFloat(File f)
228 f.Read(&floatValue, sizeof(float), 1);
229 BIGENDSWAP_DWORD(floatValue);
233 static uint16 ReadWORD(File f)
236 f.Read(&wordValue, sizeof(uint16), 1);
237 BIGENDSWAP_WORD(wordValue);
241 static uint ReadDWORD(File f)
244 f.Read(&dwordValue, sizeof(uint), 1);
245 BIGENDSWAP_DWORD(dwordValue);
250 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
252 for(;info->pos < info->end;)
254 FileInfo childInfo = *info;
257 childInfo.parent = info;
259 info->f.Seek(info->pos, start);
260 childInfo.chunkId = (ChunkID3DS)ReadWORD(info->f);
261 length = ReadDWORD(info->f);
263 childInfo.pos += sizeof(uint16) + sizeof(uint);
264 childInfo.end = info->pos + length;
266 if(!chunkParser(&childInfo, data))
269 info->pos = childInfo.end;
275 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
277 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
280 info->f.Getc((char *)&value); rgb->r = value / 255.0f;
281 info->f.Getc((char *)&value); rgb->g = value / 255.0f;
282 info->f.Getc((char *)&value); rgb->b = value / 255.0f;
284 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
286 rgb->r = ReadFloat(info->f);
287 rgb->g = ReadFloat(info->f);
288 rgb->b = ReadFloat(info->f);
293 static bool Read3DVertex(File f, Vector3Df vertex)
295 vertex.x = ReadFloat(f);
296 vertex.y = ReadFloat(f);
297 vertex.z = ReadFloat(f);
301 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
303 if(info->chunkId == AMOUNT_OF)
304 *amountOf = ReadWORD(info->f);
308 #define WELD_TRESHOLD 0.000001
309 #define SMOOTH_CUTOFF 0 // 45
311 struct SharedSourceVertexInfo
318 int OnCompare(SharedSourceVertexInfo b)
320 if(unique < b.unique) return -1;
321 if(unique > b.unique) return 1;
325 if(face < b.face) return -1;
326 if(face > b.face) return 1;
328 if(index == b.index) return 0;
331 if(value.x < b.value.x - WELD_TRESHOLD) return -1;
332 if(value.x > b.value.x + WELD_TRESHOLD) return 1;
333 if(value.y < b.value.y - WELD_TRESHOLD) return -1;
334 if(value.y > b.value.y + WELD_TRESHOLD) return 1;
335 if(value.z < b.value.z - WELD_TRESHOLD) return -1;
336 if(value.z > b.value.z + WELD_TRESHOLD) return 1;
340 if(index < b.index) return -1;
341 if(index > b.index) return 1;
347 class SharedDestVertexInfo
349 Array<int> faces { };
352 struct SourceVertexInfo
354 SharedSourceVertexInfo * shared;
358 int OnCompare(SourceVertexInfo b)
360 int r = (*shared).OnCompare(*b.shared);
361 if(!r) r = texCoord.OnCompare(b.texCoord);
362 if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
369 int index, copyFromIndex;
373 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
376 Face * faces = info->faces;
377 int nVertices = mesh.nVertices;
380 Vector3Df * mVertices;
381 double cutOff = cos(Degrees { SMOOTH_CUTOFF });
383 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
384 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
385 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
387 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
388 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
390 nNewVertices = nVertices;
391 mVertices = mesh->vertices;
393 for(c = 0; c<info->nFaces; c++)
395 Face * face = &faces[c];
397 plane.FromPointsf(mesh.vertices[face->indices[2]],
398 mesh.vertices[face->indices[1]],
399 mesh.vertices[face->indices[0]]);
400 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
403 for(c = 0; c < info->nFaces; c++)
405 Face * face = &faces[c];
409 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
410 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
415 SharedSourceVertexInfo * source;
416 SharedDestVertexInfo svInfo;
417 DestVertexInfo vInfo;
419 index = face->indices[i];
421 if(face->smoothGroups)
422 itShared.Index({ index = index, mVertices[index], face = face }, true);
424 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
425 svInfo = itShared.data;
426 if(!svInfo) itShared.data = svInfo = { };
429 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
430 // TODO: Allow obtaining address of MapIterator::key
431 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
432 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
438 vInfo.copyFromIndex = index;
443 vertices[index] = (void *)it.pointer;
444 else if(vertices[index] != it.pointer)
446 // If it's a different smoothing group, we'll need extra vertices
447 index = vertices.size;
449 vertices.Add((void *)it.pointer);
452 face->indices[i] = vInfo.index;
456 for(index = 0; index < nNewVertices; index++)
459 it.pointer = vertices[index];
462 DestVertexInfo vInfo = it.data;
463 Vector3Df normal { };
464 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
465 uint smoothing = inf->smoothGroups;
467 SharedSourceVertexInfo * shared = inf->shared;
468 SharedDestVertexInfo svInfo = sharedVertices[*shared];
470 if(!svInfo || vInfo.index != index)
473 for(i : svInfo.faces)
475 Face * face = &info->faces[i];
477 if(smoothing & face->smoothGroups)
478 smoothing |= face->smoothGroups;
481 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
482 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
484 for(i : svInfo.faces)
486 Face * face = &info->faces[i];
487 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
490 for(j = 0; j < 3; j++)
492 if(face->indices[j] == vInfo.index)
494 origIndex = face->origIndices[j];
496 normal.x += face->normal.x;
497 normal.y += face->normal.y;
498 normal.z += face->normal.z;
511 for(i : svInfo.faces)
513 Face * face = &info->faces[i];
514 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
518 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
523 for(k = 0; k < 3; k++)
525 if(face->indices[k] == vInfo.index)
527 origIndexB = face->origIndices[k];
531 valid = origIndex == origIndexB;
534 for(j : svInfo.faces)
536 if(info->faces[j].done)
538 double dot = info->faces[j].normal.DotProduct(face->normal);
539 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
540 valid = fabs(dot) > cutOff;
549 normal.x += face->normal.x;
550 normal.y += face->normal.y;
551 normal.z += face->normal.z;
558 if(!SMOOTH_CUTOFF || !WELD_TRESHOLD) break;
560 normal.Scale(normal, 1.0f / numShared);
561 if(vInfo.index == index)
562 vInfo.normal.Normalize(normal);
564 // Auto welding/smoothing requires extra vertices because angle is too steep
565 if(SMOOTH_CUTOFF && WELD_TRESHOLD)
567 SharedDestVertexInfo newSharedInfo = null;
569 for(i : svInfo.faces)
571 Face * face = &info->faces[i];
572 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
575 for(j = 0; j < 3; j++)
577 if(face->indices[j] == vInfo.index)
581 DestVertexInfo newVert;
582 SharedSourceVertexInfo * source;
584 index = nNewVertices++;
585 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
586 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
587 itShared.data = newSharedInfo = { };
589 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
592 newVert.copyFromIndex = vInfo.copyFromIndex;
593 newVert.index = index;
595 vertices.Add((void *)it.pointer);
597 face->indices[j] = index;
598 newSharedInfo.faces.Add(i);
608 // Allocate some extra vertices
610 Vector3Df * oldVertices = mesh.vertices;
611 Pointf * oldTexCoords = mesh.texCoords;
613 // TODO: Support reallocation?
614 *((void **)&mesh.vertices) = null;
615 *((void **)&mesh.texCoords) = null;
616 *((int *)&mesh.nVertices) = 0;
618 mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
620 // Fill in the new vertices
621 for(index = 0; index < nNewVertices; index++)
623 DestVertexInfo vInfo;
624 it.pointer = vertices[index];
628 mesh.normals[index] = vInfo ? vInfo.normal : { };
629 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
631 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
640 for(i = 0; i < info->nFaces; i++)
641 info->faces[i].done = false;
644 mesh.Unlock({ normals = true });
646 // Free all the temporary stuff
651 sharedVertices.Free();
652 delete sharedVertices;
656 static bool ReadSmoothing(FileInfo * info, Object object)
658 switch(info->chunkId)
663 for(c = 0; c<info->nFaces; c++)
664 info->faces[c].smoothGroups = ReadDWORD(info->f);
671 static bool ReadFacesListChunks(FileInfo * info, Object object)
673 DisplaySystem displaySystem = info->displaySystem;
674 switch(info->chunkId)
683 char matName[MAX_LOCATION + 100];
685 strcpy(matName, info->fileName);
686 ReadASCIIZ(info->f, &name);
687 count = ReadWORD(info->f);
688 strcat(matName, name);
690 mat = displaySystem.GetMaterial(matName);
691 faces = info->matFaces[(uintptr)mat];
693 info->matFaces[(uintptr)mat] = faces = { };
697 for(c = 0; c<count; c++)
699 uint16 face = ReadWORD(info->f);
701 info->faces[face].material = mat;
710 static bool ReadTriMesh(FileInfo * info, Object object)
712 Mesh mesh = object.mesh;
713 switch(info->chunkId)
718 uint16 nVertices = ReadWORD(info->f);
719 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
720 *((int *) &mesh.nVertices) = nVertices;
721 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
724 for(c = 0; c<mesh.nVertices; c++)
727 Read3DVertex(info->f, vertex);
728 mesh.vertices[c].x = vertex.x;
729 mesh.vertices[c].y =-vertex.z;
730 mesh.vertices[c].z = vertex.y;
737 case TRI_MAPPINGCOORS:
740 uint16 count = ReadWORD(info->f);
741 count = (uint16)Min(mesh.nVertices, count);
743 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
744 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
745 mesh.flags.texCoords1 = true;
748 for(c = 0; c<count; c++)
750 mesh.texCoords[c].x = ReadFloat(info->f);
751 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
765 info->nFaces = nFaces = ReadWORD(info->f);
766 info->pos += sizeof(uint16);
768 info->faces = new0 Face[nFaces];
769 for(c = 0; c<nFaces; c++)
773 info->faces[c].origIndices[i] =
774 info->faces[c].indices[i] = ReadWORD(info->f);
776 info->pos += 4*sizeof(uint16);
779 ReadChunks(ReadSmoothing, info, object);
783 info->matFaces.Free();
784 info->matFaces = { };
786 ReadChunks(ReadFacesListChunks, info, object);
788 ComputeNormals(mesh, info, object);
791 for(m : info->matFaces)
793 Material mat = (Material)&m;
794 Array<int> faces = m;
795 if(mat.flags.translucent)
797 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
800 Face * face = &info->faces[i];
801 PrimitiveSingle * triangle;
803 triangle = &mesh.primitives[mesh.nPrimitives++];
804 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
806 triangle->indicesMember[0] = (uintindex)face->indices[0];
807 triangle->indicesMember[1] = (uintindex)face->indices[1];
808 triangle->indicesMember[2] = (uintindex)face->indices[2];
809 triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
810 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
811 triangle->plane.FromPointsf(
812 mesh.vertices[triangle->indicesMember[2]],
813 mesh.vertices[triangle->indicesMember[1]],
814 mesh.vertices[triangle->indicesMember[0]]);
816 mesh.UnlockPrimitive(triangle);
818 triangle->middle.x /= 3;
819 triangle->middle.y /= 3;
820 triangle->middle.z /= 3;
822 triangle->material = mat;
825 object.flags.translucent = true;
830 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
834 group.material = mat;
837 Face * face = &info->faces[i];
839 if(object.flags.flipWindings)
841 group.indicesMember[c*3] = (uintindex)face->indices[2];
842 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
843 group.indicesMember[c*3+2] = (uintindex)face->indices[0];
847 group.indicesMember[c*3] = (uintindex)face->indices[0];
848 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
849 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
854 mesh.UnlockPrimitiveGroup(group);
859 // Add faces without a material all together
861 for(c = 0; c<nFaces; c++)
862 if(!info->faces[c].done)
866 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
869 for(c = 0; c<nFaces; c++)
871 Face * face = &info->faces[c];
874 group.indicesMember[c*3] = (uintindex)face->indices[0];
875 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
876 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
879 mesh.UnlockPrimitiveGroup(group);
886 info->matFaces.Free();
887 delete info->matFaces;
894 Vector3Df xAxis, yAxis, zAxis, center;
897 Matrix inverse/*, source = { 0 }*/;
900 Read3DVertex(info->f, xAxis);
901 Read3DVertex(info->f, yAxis);
902 Read3DVertex(info->f, zAxis);
903 Read3DVertex(info->f, center);
905 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
906 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
907 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
909 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
912 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
913 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
914 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
915 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
917 inverse.Inverse(source);
920 object.flags.flipWindings = false;
922 xAxis.Normalize(xAxis);
923 yAxis.Normalize(yAxis);
924 zAxis.Normalize(zAxis);
926 orth.CrossProduct(yAxis, zAxis);
927 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
928 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
929 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
931 object.flags.flipWindings ^= true;
935 orth.CrossProduct(zAxis, xAxis);
936 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
937 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
938 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
940 object.flags.flipWindings ^= true;
944 orth.CrossProduct(xAxis, yAxis);
945 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
946 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
947 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
949 object.flags.flipWindings ^= true;
956 rotation.m[0][0] = xAxis.x;
957 rotation.m[0][1] =-xAxis.z;
958 rotation.m[0][2] = xAxis.y;
959 rotation.m[0][3] = 0;
960 rotation.m[1][0] =-zAxis.x;
961 rotation.m[1][1] = zAxis.z;
962 rotation.m[1][2] =-zAxis.y;
963 rotation.m[1][3] = 0;
964 rotation.m[2][0] = yAxis.x;
965 rotation.m[2][1] =-yAxis.z;
966 rotation.m[2][2] = yAxis.y;
967 rotation.m[2][3] = 0;
968 rotation.m[3][0] = 0;
969 rotation.m[3][1] = 0;
970 rotation.m[3][2] = 0;
971 rotation.m[3][3] = 1;
975 inverse.Transpose(rotation);
977 temp.Translate(-center.x, center.z, -center.y);
978 temp2.Multiply(temp, inverse);
979 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
983 object.transform.scaling = scaling;
984 // TODO: Improve language to support deep properties on non constant functions
985 // object.transform.orientation.RotationMatrix(rotation);
987 Quaternion orientation;
988 orientation.RotationMatrix(rotation);
989 object.transform.orientation = orientation;
991 object.transform.position = { center.x, -center.z, center.y };
994 // Localize All Vertices
995 for(c = 0; c<mesh.nVertices; c++)
997 Vector3Df vertex = mesh.vertices[c];
999 mesh.vertices[c].MultMatrix(vertex, inverse);
1001 mesh.vertices[c].x -= object.pivot.x;
1002 mesh.vertices[c].y -= object.pivot.y;
1003 mesh.vertices[c].z -= object.pivot.z;
1012 static bool ReadMap(FileInfo * info, Material mat)
1014 DisplaySystem displaySystem = info->displaySystem;
1015 switch(info->chunkId)
1020 char location[MAX_LOCATION];
1022 ReadASCIIZ(info->f, &name);
1024 strcpy(location, info->textureDirectory);
1025 PathCat(location, name);
1026 if(!FileExists(location))
1028 // Attempt all lowercase if original case does not exist
1030 strcpy(location, info->textureDirectory);
1031 PathCat(location, name);
1034 if(info->parent->chunkId == MAT_BUMPMAP)
1036 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1037 char bumpName[MAX_LOCATION+5];
1038 strcpy(bumpName, "BUMP:");
1039 strcat(bumpName, location);
1042 mat.bumpMap = displaySystem.GetTexture(bumpName);
1045 mat.bumpMap = Bitmap { };
1046 if(!mat.bumpMap.Load(location, null, null) ||
1047 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1048 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1052 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1053 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1056 for(y = 0; y < bh; y++)
1057 for(x = 0; x < bw; x++)
1059 uint bc = y * bw + x;
1060 Color color = picture[bc].color;
1061 picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
1069 Bitmap opacityMap = null;
1070 bool alphaOnly = true;
1071 bool translucent = false;
1074 mat.baseMap = displaySystem.GetTexture(location);
1077 mat.baseMap = Bitmap { };
1078 if(!mat.baseMap.Load(location, null, null) ||
1079 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1080 !displaySystem.AddTexture(location, mat.baseMap))
1084 opacityMap = mat.baseMap;
1087 else if(info->parent->chunkId == MAT_MAPOPACITY)
1089 opacityMap = Bitmap { };
1090 if(opacityMap.Load(location, null, null))
1092 if(opacityMap.pixelFormat == pixelFormatRGBA)
1094 if(!opacityMap.Convert(null, pixelFormat888, null))
1101 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1103 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1104 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1106 int ow = opacityMap.width, oh = opacityMap.height;
1107 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1109 for(y = 0; y < bh; y++)
1110 for(x = 0; x < bw; x++)
1112 int bc = ((y % bh) * bw + (x % bw));
1113 int oc = ((y % oh) * bw + (x % ow));
1114 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1115 if(alpha && alpha < 255)
1117 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1121 mat.flags.translucent = true;
1122 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1123 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1125 if(opacityMap != mat.baseMap)
1133 MapOptions options = (MapOptions)ReadWORD(info->f);
1134 if(!options.dontTile) mat.flags.tile = true;
1138 mat.uScale = ReadFloat(info->f);
1141 mat.vScale = ReadFloat(info->f);
1147 PrintLn("Unhandled Map block");
1154 static bool ReadMaterial(FileInfo * info, Material mat)
1156 switch(info->chunkId)
1161 char matName[MAX_LOCATION + 100];
1162 strcpy(matName, info->fileName);
1163 ReadASCIIZ(info->f, &name);
1164 strcat(matName, name);
1165 mat.name = CopyString(matName);
1169 case MAT_TRANSPARENCY:
1171 uint16 transparency;
1172 ReadChunks(ReadAmountOf, info, &transparency);
1173 mat.opacity = 1.0f - transparency / 100.0f;
1174 if(mat.opacity < 1.0)
1175 mat.flags.translucent = true;
1180 ReadChunks(ReadRGB, info, &mat.diffuse);
1181 ReadChunks(ReadRGB, info, &mat.diffuse);
1186 ReadChunks(ReadRGB, info, &mat.ambient);
1187 ReadChunks(ReadRGB, info, &mat.ambient);
1192 ReadChunks(ReadRGB, info, &mat.specular);
1193 ReadChunks(ReadRGB, info, &mat.specular);
1199 ReadChunks(ReadAmountOf, info, &emissive);
1200 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1201 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1202 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1205 case MAT_SHINSTRENGTH:
1208 ReadChunks(ReadAmountOf, info, &shininess);
1209 mat.specular.r *= shininess / 100.0f;
1210 mat.specular.g *= shininess / 100.0f;
1211 mat.specular.b *= shininess / 100.0f;
1217 ReadChunks(ReadAmountOf, info, &power);
1221 case MAT_MAPTEXTURE1:
1222 ReadChunks(ReadMap, info, mat);
1224 case MAT_MAPOPACITY:
1225 ReadChunks(ReadMap, info, mat);
1227 case MAT_DOUBLESIDED:
1228 mat.flags.doubleSided = true;
1231 ReadChunks(ReadMap, info, mat);
1235 PrintLn("Unhandled MAT type ID", info->chunkId);
1242 static bool ReadLight(FileInfo * info, Object object)
1244 Light * light = &object.light;
1245 switch(info->chunkId)
1248 case RGB_BYTE_GAMMA:
1250 case RGB_FLOAT_GAMMA:
1251 ReadRGB(info, &light->diffuse);
1252 light->specular = light->diffuse;
1257 char targetName[MAXNAMELEN];
1259 strcpy(targetName, object.name);
1260 strcat(targetName, ".target");
1262 light->flags.omni = false;
1263 light->flags.spot = true;
1265 target = Object { };
1266 target.name = CopyString(targetName);
1267 info->rootObject.children.AddName(target);
1268 target.parent = info->rootObject;
1270 light->target = target;
1272 target.transform.position.x = ReadFloat(info->f);
1273 target.transform.position.z = ReadFloat(info->f);
1274 target.transform.position.y =-ReadFloat(info->f);
1276 light->hotSpot = ReadFloat(info->f);
1277 light->fallOff = ReadFloat(info->f);
1282 light->flags.off = true;
1285 case LIT_ATTENUATION:
1289 d = 300, small = 0.001,
1291 d * (Kl + Kq * d) = (1 / small) - Kc
1293 { Kc, Kl, Kq, small, d });
1296 light->flags.attenuation = true;
1299 #define MINLIGHT 0.08
1300 light->Kq = 1/(light->end*light->end*MINLIGHT);
1303 #define MINLIGHT 0.15f
1304 // #define MINLIGHT 0.1
1305 light->Kl = (float)(1/(light->end*MINLIGHT));
1311 light->start = ReadFloat(info->f);
1316 light->end = ReadFloat(info->f);
1319 case LIT_MULTIPLIER:
1321 light->multiplier = ReadFloat(info->f);
1329 static bool ReadCamera(FileInfo * info, Object object)
1331 switch(info->chunkId)
1339 //Camera camera = object.camera;
1340 /*float nearRange = */ReadFloat(info->f);
1341 /*float farRange = */ReadFloat(info->f);
1343 camera.zMin = Max(0.1, nearRange);
1344 camera.zMax = farRange;
1353 static bool ReadEditObject(FileInfo * info, char * name)
1355 DisplaySystem displaySystem = info->displaySystem;
1356 switch(info->chunkId)
1360 Object object = info->rootObject.Find(name);
1363 object = Object { };
1364 object.name = CopyString(name);
1365 info->rootObject.children.AddName(object);
1366 object.parent = info->rootObject;
1368 object.InitializeMesh(displaySystem);
1369 ReadChunks(ReadTriMesh, info, object);
1370 object.flags.mesh = true;
1371 object.mesh.Unlock(0);
1376 Object object = info->rootObject.Find(name);
1382 object = Object { };
1383 object.name = CopyString(name);
1384 info->rootObject.children.AddName(object);
1385 object.parent = info->rootObject;
1387 object.flags.light = true;
1389 light = &object.light;
1390 light->lightObject = object;
1391 light->flags.omni = true;
1392 light->multiplier = 1.0f;
1394 // This is not used?
1395 Read3DVertex(info->f, position);
1396 light->direction = { position.x, position.y, position.z };
1397 info->pos += sizeof(float) * 3;
1399 ReadChunks(ReadLight, info, object);
1404 char targetName[MAXNAMELEN];
1405 Object object = info->rootObject.Find(name);
1408 float /*bankAngle, */focus;
1411 strcpy(targetName, name);
1412 strcat(targetName, ".target");
1413 target = info->rootObject.Find(targetName);
1417 object = Object { };
1418 object.name = CopyString(name);
1419 info->rootObject.children.AddName(object);
1421 object.parent = info->rootObject;
1422 object.camera = Camera { };
1423 object.camera.type = lookAtObject;
1428 target = Object { };
1429 target.name = CopyString(targetName);
1430 info->rootObject.children.AddName(target);
1431 target.parent = info->rootObject;
1434 object.flags.camera = true;
1435 object.cameraTarget = target;
1437 camera = object.camera;
1438 camera.cameraObject = object;
1439 camera.target = target;
1441 //Read3DVertex(info->f, camera.position);
1442 object.transform.position.x = ReadFloat(info->f);
1443 object.transform.position.z = ReadFloat(info->f);
1444 object.transform.position.y =-ReadFloat(info->f);
1446 info->pos += sizeof(float) * 3;
1447 //Read3DVertex(info->f, object.cameraTarget.position);
1448 target.transform.position.x = ReadFloat(info->f);
1449 target.transform.position.z = ReadFloat(info->f);
1450 target.transform.position.y =-ReadFloat(info->f);
1452 info->pos += sizeof(float) * 3;
1453 /*bankAngle = */ReadFloat(info->f);
1454 info->pos += sizeof(float);
1455 focus = ReadFloat(info->f);
1456 info->pos += sizeof(float);
1458 mm = (focus - 5.05659508373109) / 1.13613250717301;
1459 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1461 ReadChunks(ReadCamera, info, object);
1464 case OBJ_HIDDEN: break;
1469 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1471 static bool ReadEditChunks(FileInfo * info, void * data)
1473 switch(info->chunkId)
1477 // Read the ambient color
1478 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1483 Material material { /*flags = { singleSideLight = true }*/ };
1485 ReadChunks(ReadMaterial, info, material);
1487 mat = info->displaySystem.AddNamedMaterial(material.name);
1490 if(material.baseMap)
1491 material.baseMap.MakeMipMaps(info->displaySystem);
1492 if(material.bumpMap)
1493 material.bumpMap.MakeMipMaps(info->displaySystem);
1494 // COPY_NITEM(mat, material);
1495 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1499 delete material.baseMap;
1501 delete material.name;
1508 info->pos += ReadASCIIZ(info->f, &name);
1509 ReadChunks(ReadEditObject, info, name);
1517 struct ObjectInfoBlock
1527 // Key Framer Chunks
1528 static class AccelerationFlags : uint32
1530 bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1533 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1535 switch(info->chunkId)
1539 //uint16 flags1, flags2;
1540 ReadASCIIZ(info->f, &block->name);
1541 /*flags1 = */ReadWORD(info->f);
1542 /*flags2 = */ReadWORD(info->f);
1543 block->parent = ReadWORD(info->f);
1547 ReadASCIIZ(info->f, &block->dummyName);
1550 Read3DVertex(info->f, block->pivot);
1553 block->hierarchy = ReadWORD(info->f);
1557 case FRM_TRACKSCALE:
1560 case FRM_TRACKCOLOR:
1561 case FRM_TRACKHOTSPOT:
1562 case FRM_TRACKFALLOFF:
1564 FrameTrack track { };
1571 block->tracks.Add(track);
1573 flags = ReadWORD(info->f);
1575 info->f.Read(unknown, sizeof(unknown), 1);
1577 track.numKeys = ReadDWORD(info->f);
1579 switch(info->chunkId)
1581 case FRM_TRACKPOS: track.type.type = position; break;
1582 case FRM_TRACKROT: track.type.type = rotation; break;
1583 case FRM_TRACKSCALE: track.type.type = scaling; break;
1584 case FRM_TRACKROLL: track.type.type = roll; break;
1585 case FRM_TRACKFOV: track.type.type = fov; break;
1586 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1587 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1588 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1590 if((flags & 0x0003) == 3)
1591 track.type.loop = true;
1593 track.keys = new0 FrameKey[track.numKeys];
1594 for(c = 0; c<track.numKeys; c++)
1596 AccelerationFlags accelerationFlags;
1597 FrameKey * key = track.keys + c;
1599 key->frame = ReadDWORD(info->f);
1600 accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1602 if(accelerationFlags.tension)
1603 key->tension = ReadFloat(info->f);
1604 if(accelerationFlags.continuity)
1605 key->continuity = ReadFloat(info->f);
1606 if(accelerationFlags.bias)
1607 key->bias = ReadFloat(info->f);
1608 if(accelerationFlags.easeTo)
1609 key->easeTo = ReadFloat(info->f);
1610 if(accelerationFlags.easeFrom)
1611 key->easeFrom = ReadFloat(info->f);
1613 switch(info->chunkId)
1618 Read3DVertex(info->f, position);
1619 key->position = { position.x, -position.z, position.y };
1625 Angle angle = ReadFloat(info->f);
1626 Vector3Df fixedAxis;
1628 Read3DVertex(info->f, axis);
1629 fixedAxis.x = axis.x;
1630 fixedAxis.y = -axis.z;
1631 fixedAxis.z = axis.y;
1635 Quaternion rotation;
1636 rotation.RotationAxis(fixedAxis, angle);
1637 key->orientation.Multiply((key - 1)->orientation, rotation);
1640 key->orientation.RotationAxis(fixedAxis, angle);
1643 case FRM_TRACKSCALE:
1646 Read3DVertex(info->f, scaling);
1647 key->scaling = { scaling.x, scaling.z, scaling.y };
1652 key->fov = ReadFloat(info->f);
1657 key->roll = -ReadFloat(info->f);
1660 case FRM_TRACKCOLOR:
1662 FileInfo childInfo = *info;
1663 childInfo.chunkId = RGB_FLOAT;
1664 ReadRGB(&childInfo, &key->color);
1667 case FRM_TRACKHOTSPOT:
1669 key->hotSpot = ReadFloat(info->f);
1672 case FRM_TRACKFALLOFF:
1674 key->fallOff = ReadFloat(info->f);
1686 static Object FindObjectID(Object object, int id)
1688 Object result = null;
1690 for(child = object.children.first; child; child = child.next)
1692 if(child.flags.hierarchy == (uint16) id)
1699 result = FindObjectID(child, id);
1706 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1708 switch(info->chunkId)
1712 ObjectInfoBlock block { };
1713 Object object = null;
1715 ReadChunks(ReadFrameInfoBlock, info, &block);
1717 if(block.dummyName && block.dummyName[0])
1719 if(!strcmp(block.name, "$$$DUMMY"))
1721 object = Object { };
1722 object.name = block.dummyName;
1723 info->rootObject.children.AddName(object);
1724 object.transform.scaling = { 1,1,1 };
1728 Object model = info->rootObject.Find(block.name);
1731 object = Object { };
1732 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1733 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1734 object.flags = model.flags;
1735 object.flags.ownMesh = false;
1736 object.mesh = model.mesh;
1738 object.min = model.min;
1739 object.max = model.max;
1740 object.radius = model.radius;
1742 object.transform = model.transform;
1743 info->rootObject.children.AddName(object);
1745 delete block.dummyName;
1748 object.parent = info->rootObject;
1751 object = info->rootObject.Find(block.name);
1755 Mesh mesh = object.mesh;
1756 object.flags.hierarchy = block.hierarchy + 1;
1757 if(block.parent != -1)
1759 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1762 object.parent.children.Remove(object);
1763 parent.children.AddName(object);
1764 object.parent = parent;
1767 object.pivot.x = block.pivot.x;
1768 object.pivot.y =-block.pivot.z;
1769 object.pivot.z = block.pivot.y;
1771 if(mesh && object.flags.ownMesh)
1773 if(mesh.Lock({ vertices = true }))
1776 // Take pivot into account
1777 for(c = 0; c<mesh.nVertices; c++)
1779 mesh.vertices[c].x -= object.pivot.x;
1780 mesh.vertices[c].y -= object.pivot.y;
1781 mesh.vertices[c].z -= object.pivot.z;
1783 mesh.Unlock({ vertices = true });
1787 object.tracks = block.tracks;
1788 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1789 object.endFrame = info->rootObject.endFrame;
1792 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1798 ObjectInfoBlock block { };
1799 Object object = null;
1801 ReadChunks(ReadFrameInfoBlock, info, &block);
1803 if(block.dummyName && block.dummyName[0])
1805 if(!strcmp(block.name, "$$$DUMMY"))
1807 object = Object { };
1808 object.name = block.dummyName;
1809 info->rootObject.children.AddName(object);
1810 object.transform.scaling = { 1, 1, 1 };
1811 object.flags.camera = true;
1815 Object model = info->rootObject.Find(block.name);
1818 object = Object { };
1819 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1820 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1821 object.flags = model.flags;
1822 object.flags.ownMesh = false;
1823 object.camera = model.camera;
1824 object.flags.camera = true;
1825 info->rootObject.children.AddName(object);
1827 delete block.dummyName;
1830 object.parent = info->rootObject;
1833 object = info->rootObject.Find(block.name);
1837 object.flags.hierarchy = block.hierarchy + 1;
1838 if(block.parent != -1)
1840 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1843 object.parent.children.Remove(object);
1844 parent.children.AddName(object);
1845 object.parent = parent;
1848 object.pivot.x = block.pivot.x;
1849 object.pivot.y =-block.pivot.z;
1850 object.pivot.z = block.pivot.y;
1852 object.tracks = block.tracks;
1853 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1854 object.endFrame = info->rootObject.endFrame;
1857 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1861 case FRM_CAMERATARGET:
1863 ObjectInfoBlock block { };
1864 Object object = null;
1865 char targetName[MAXNAMELEN];
1867 ReadChunks(ReadFrameInfoBlock, info, &block);
1869 strcpy(targetName, block.name);
1870 strcat(targetName, ".target");
1872 if(block.dummyName && block.dummyName[0])
1874 if(!strcmp(block.name, "$$$DUMMY"))
1876 object = Object { };
1877 object.name = block.dummyName;
1878 info->rootObject.children.AddName(object);
1879 object.transform.scaling = { 1,1,1 };
1883 Object model = info->rootObject.Find(targetName);
1886 object = Object { };
1887 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1888 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1889 object.flags = model.flags;
1890 object.flags.ownMesh = false;
1891 object.camera = model.camera;
1892 info->rootObject.children.AddName(object);
1894 delete block.dummyName;
1897 object.parent = info->rootObject;
1900 object = info->rootObject.Find(targetName);
1904 object.flags.hierarchy = block.hierarchy + 1;
1905 if(block.parent != -1)
1907 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1910 object.parent.children.Remove(object);
1911 parent.children.AddName(object);
1912 object.parent = parent;
1915 object.pivot.x = block.pivot.x;
1916 object.pivot.y =-block.pivot.z;
1917 object.pivot.z = block.pivot.y;
1919 object.tracks = block.tracks;
1920 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1921 object.endFrame = info->rootObject.endFrame;
1924 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1930 ObjectInfoBlock block { };
1932 ReadChunks(ReadFrameInfoBlock, info, &block);
1934 info->rootObject.tracks = block.tracks;
1940 ObjectInfoBlock block { };
1941 Object object = null;
1943 ReadChunks(ReadFrameInfoBlock, info, &block);
1945 if(block.dummyName && block.dummyName[0])
1947 if(!strcmp(block.name, "$$$DUMMY"))
1949 object = Object { };
1950 object.name = block.dummyName;
1951 info->rootObject.children.AddName(object);
1952 object.transform.scaling = { 1, 1, 1 };
1953 object.flags.light = true;
1957 Object model = info->rootObject.Find(block.name);
1960 object = Object { };
1961 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1962 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1963 object.flags = model.flags;
1964 object.flags.ownMesh = false;
1965 object.light = model.light;
1966 object.flags.light = true;
1967 info->rootObject.children.AddName(object);
1969 delete block.dummyName;
1972 object.parent = info->rootObject;
1975 object = info->rootObject.Find(block.name);
1979 object.flags.hierarchy = block.hierarchy + 1;
1980 if(block.parent != -1)
1982 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1985 object.parent.children.Remove(object);
1986 parent.children.AddName(object);
1987 object.parent = parent;
1990 object.pivot.x = block.pivot.x;
1991 object.pivot.y =-block.pivot.z;
1992 object.pivot.z = block.pivot.y;
1994 object.tracks = block.tracks;
1995 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1996 object.endFrame = info->rootObject.endFrame;
1999 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2003 case FRM_SPOTLIGHTTARGET:
2005 ObjectInfoBlock block { };
2006 Object object = null;
2007 char targetName[MAXNAMELEN];
2009 ReadChunks(ReadFrameInfoBlock, info, &block);
2011 strcpy(targetName, block.name);
2012 strcat(targetName, ".target");
2014 if(block.dummyName && block.dummyName[0])
2016 if(!strcmp(block.name, "$$$DUMMY"))
2018 object = Object { };
2019 object.name = block.dummyName;
2020 info->rootObject.children.AddName(object);
2021 object.transform.scaling = { 1,1,1 };
2025 Object model = info->rootObject.Find(targetName);
2028 object = Object { };
2029 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2030 // TODO: When passing a String to a const String, use member if property is const String but member is String
2031 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2032 object.flags = model.flags;
2033 object.flags.ownMesh = false;
2034 object.light = model.light;
2035 info->rootObject.children.AddName(object);
2037 delete block.dummyName;
2040 object.parent = info->rootObject;
2043 object = info->rootObject.Find(targetName);
2047 object.flags.hierarchy = block.hierarchy + 1;
2048 if(block.parent != -1)
2050 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2053 object.parent.children.Remove(object);
2054 parent.children.AddName(object);
2055 object.parent = parent;
2058 object.pivot.x = block.pivot.x;
2059 object.pivot.y =-block.pivot.z;
2060 object.pivot.z = block.pivot.y;
2062 object.tracks = block.tracks;
2063 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2064 object.endFrame = info->rootObject.endFrame;
2067 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2073 info->rootObject.startFrame = ReadDWORD(info->f);
2074 info->rootObject.endFrame = ReadDWORD(info->f);
2075 *&(info->rootObject.frame) = info->rootObject.startFrame;
2083 static bool ReadMainChunks(FileInfo * info, void * data)
2085 switch(info->chunkId)
2088 ReadChunks(ReadEditChunks, info, null);
2092 Object object = data;
2093 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2095 ReadChunks(ReadKeyFrameChunks, info, null);
2096 object.flags.keysLoaded = true;
2104 static bool ReadMain(FileInfo * info, void * data)
2106 switch(info->chunkId)
2109 ReadChunks(ReadMainChunks, info, data);
2115 class Object3DSFormat : ObjectFormat
2117 class_property(extension) = "3ds";
2119 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2121 bool result = false;
2124 FileInfo info = {0};
2125 info.rootObject = object;
2126 info.displaySystem = displaySystem;
2129 info.fileName = fileName;
2130 StripLastDirectory(fileName, info.textureDirectory);
2131 info.f = FileOpen(fileName, read);
2134 // TOFIX: eC reorders that badly
2135 // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2136 if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2138 object.flags.root = true;
2139 object.SetMinMaxRadius(true);
2140 object._Animate(object.frame);
2141 object.UpdateTransform();
2147 info.matFaces.Free();
2148 delete info.matFaces;
2151 object.Free(displaySystem);