1 namespace gfx3D::models;
5 #if defined(__EMSCRIPTEN__)
11 #if (defined(__ANDROID__) || defined(__ODROID__)) && !defined(_GLES)
15 #if !defined(_GLES) && !defined(_GLES2)
16 #define USE_32_BIT_INDICES true
17 #define indicesMember indices32
18 #define uintindex uint32
20 #define USE_32_BIT_INDICES false
21 #define indicesMember indices
22 #define uintindex uint16
27 static enum ChunkID3DS : uint16
32 RGB_BYTE_GAMMA = 0x0012,
33 RGB_FLOAT_GAMMA = 0x0013,
40 EDIT_AMBIENT = 0x2100,
41 EDIT_MATERIAL = 0xAFFF,
48 // Triangular Mesh Chunks
51 TRI_MATERIAL = 0x4130,
52 TRI_MAPPINGCOORS = 0x4140,
53 TRI_SMOOTHING = 0x4150,
59 LIT_ATTENUATION = 0x4625,
62 LIT_MULTIPLIER = 0x465B,
72 MAT_SPECULAR = 0xA030,
73 MAT_SHININESS = 0xA040,
74 MAT_SHINSTRENGTH = 0xA041,
76 MAT_TRANSPARENCY = 0xA050,
79 MAT_SELFILLUM = 0xA080,
80 MAT_DOUBLESIDED = 0xA081,
81 MAT_ADDITIVE = 0xA083,
82 MAT_SELFILPCT = 0xA084,
84 MAT_SUPERSMP = 0xA086,
85 MAT_WIRETHICKNESS = 0xA087,
87 MAT_XPFALLIN = 0xA08A,
91 MAT_MAPTEXTURE1 = 0xA200,
92 MAT_SPECULARMAP = 0xA204,
93 MAT_MAPOPACITY = 0xA210,
94 MAT_REFLECTIONMAP = 0xA220,
98 MAP_FILENAME = 0xA300,
100 MAT_SHININESSMAP = 0xA33C,
101 MAT_EMISSIVEMAP = 0xA33D,
103 MAP_OPTIONS = 0xA351,
104 MAP_1_U_SCALE = 0xA354,
105 MAP_1_V_SCALE = 0xA356,
106 MAP_U_OFFSET = 0xA358,
107 MAP_V_OFFSET = 0xA35A,
108 MAP_ROTATION = 0xA35C,
110 MAP_FILTERBLUR = 0xA353,
111 MAP_1_U_SCALE = 0xA354,
112 MAP_1_V_SCALE = 0xA356,
113 MAP_U_OFFSET = 0xA358,
114 MAP_V_OFFSET = 0xA35A,
115 MAP_ROTATION = 0xA35C,
117 MAP_LUMTINT1 = 0xA360,
118 MAP_LUMTINT2 = 0xA362,
125 KEYFRAME3DS = 0xB000,
126 FRM_AMBIENT = 0xB001,
127 FRM_MESHINFO = 0xB002,
129 FRM_CAMERATARGET = 0xB004,
130 FRM_OMNILIGHT = 0xB005,
131 FRM_SPOTLIGHTTARGET = 0xB006,
132 FRM_SPOTLIGHT = 0xB007,
135 FRM_DUMMYNAME = 0xB011,
137 FRM_TRACKPOS = 0xB020,
138 FRM_TRACKROT = 0xB021,
139 FRM_TRACKSCALE = 0xB022,
140 FRM_TRACKFOV = 0xB023,
141 FRM_TRACKROLL = 0xB024,
142 FRM_TRACKCOLOR = 0xB025,
143 FRM_TRACKMORPH = 0xB026, // What does this do?
144 FRM_TRACKHOTSPOT = 0xB027,
145 FRM_TRACKFALLOFF = 0xB028,
146 FRM_TRACKHIDE = 0xB029,
147 FRM_HIERARCHY = 0xB030
150 static class MapOptions : uint32
152 bool decal:1; // (related to dontTile)
156 bool summedFiltering:1;
158 bool lumOrAlphaTint:1;
163 typedef struct FileInfo FileInfo;
176 static struct FileInfo
179 DisplaySystem displaySystem;
181 const String fileName;
189 char textureDirectory[MAX_DIRECTORY];
190 Map<uintptr, Array<int>> matFaces;
193 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
194 | (((unsigned short)(word) & 0xff00) >> 8)
196 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
197 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
198 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
199 | (((unsigned int)(dword) & 0xff000000) >> 24))
201 #ifndef __BIG_ENDIAN__
202 #define BIGENDSWAP_WORD(word)
203 #define BIGENDSWAP_DWORD(dword)
205 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
206 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
209 // Zero Terminated String
210 static int ReadASCIIZ(File f, char ** string)
212 // *** Read String ***
214 char temp[1024] = "";
218 if(!temp[c++]) break;
220 *string = new char[c];
222 strcpy(*string, temp);
226 static float ReadFloat(File f)
228 float floatValue = 0;
229 f.Read(&floatValue, sizeof(float), 1);
230 BIGENDSWAP_DWORD(floatValue);
234 static uint16 ReadWORD(File f)
236 uint16 wordValue = 0;
237 f.Read(&wordValue, sizeof(uint16), 1);
238 BIGENDSWAP_WORD(wordValue);
242 static uint ReadDWORD(File f)
245 f.Read(&dwordValue, sizeof(uint), 1);
246 BIGENDSWAP_DWORD(dwordValue);
251 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
253 for(;info->pos < info->end;)
255 FileInfo childInfo = *info;
258 childInfo.parent = info;
260 info->f.Seek(info->pos, start);
261 childInfo.chunkId = (ChunkID3DS)ReadWORD(info->f);
262 length = ReadDWORD(info->f);
264 childInfo.pos += sizeof(uint16) + sizeof(uint);
265 childInfo.end = info->pos + length;
267 if(!chunkParser(&childInfo, data))
270 info->pos = childInfo.end;
276 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
278 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
281 info->f.Getc((char *)&value); rgb->r = value / 255.0f;
282 info->f.Getc((char *)&value); rgb->g = value / 255.0f;
283 info->f.Getc((char *)&value); rgb->b = value / 255.0f;
285 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
287 rgb->r = ReadFloat(info->f);
288 rgb->g = ReadFloat(info->f);
289 rgb->b = ReadFloat(info->f);
294 static bool Read3DVertex(File f, Vector3Df vertex)
296 vertex.x = ReadFloat(f);
297 vertex.y = ReadFloat(f);
298 vertex.z = ReadFloat(f);
302 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
304 if(info->chunkId == AMOUNT_OF)
305 *amountOf = ReadWORD(info->f);
309 #define WELD_TRESHOLD 0.000001
310 #define SMOOTH_CUTOFF 0 // 45
312 struct SharedSourceVertexInfo
319 int OnCompare(SharedSourceVertexInfo b)
321 if(unique < b.unique) return -1;
322 if(unique > b.unique) return 1;
326 if(face < b.face) return -1;
327 if(face > b.face) return 1;
329 if(index == b.index) return 0;
330 if(WELD_TRESHOLD > 0)
332 if(value.x < b.value.x - WELD_TRESHOLD) return -1;
333 if(value.x > b.value.x + WELD_TRESHOLD) return 1;
334 if(value.y < b.value.y - WELD_TRESHOLD) return -1;
335 if(value.y > b.value.y + WELD_TRESHOLD) return 1;
336 if(value.z < b.value.z - WELD_TRESHOLD) return -1;
337 if(value.z > b.value.z + WELD_TRESHOLD) return 1;
341 if(index < b.index) return -1;
342 if(index > b.index) return 1;
348 class SharedDestVertexInfo
350 Array<int> faces { };
353 struct SourceVertexInfo
355 SharedSourceVertexInfo * shared;
359 int OnCompare(SourceVertexInfo b)
361 int r = (*shared).OnCompare(*b.shared);
362 if(!r) r = texCoord.OnCompare(b.texCoord);
363 if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
370 int index, copyFromIndex;
371 Vector3Df normal, tangent1, tangent2;
374 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
377 Face * faces = info->faces;
378 int nVertices = mesh.nVertices;
381 Vector3Df * mVertices;
382 double cutOff = cos(Degrees { SMOOTH_CUTOFF });
384 Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
385 Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
386 Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
388 MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
389 MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
391 nNewVertices = nVertices;
392 mVertices = mesh->vertices;
394 for(c = 0; c<info->nFaces; c++)
396 Face * face = &faces[c];
398 uint * indices = face->indices;
399 Vector3D edges[3], rEdges[3];
400 computeNormalWeights(3, mVertices, indices, true, 0, face->dots, edges, rEdges);
402 plane.FromPointsf(mVertices[indices[2]], mVertices[indices[1]], mVertices[indices[0]]);
403 face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
406 for(c = 0; c < info->nFaces; c++)
408 Face * face = &faces[c];
412 if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
413 !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
418 SharedSourceVertexInfo * source;
419 SharedDestVertexInfo svInfo;
420 DestVertexInfo vInfo;
422 index = face->indices[i];
424 if(face->smoothGroups)
425 itShared.Index({ index = index, mVertices[index], face = face }, true);
427 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
428 svInfo = itShared.data;
429 if(!svInfo) itShared.data = svInfo = { };
432 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
433 // TODO: Allow obtaining address of MapIterator::key
434 // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
435 it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
441 vInfo.copyFromIndex = index;
446 vertices[index] = (void *)it.pointer;
447 else if(vertices[index] != it.pointer)
449 // If it's a different smoothing group, we'll need extra vertices
450 index = vertices.size;
452 vertices.Add((void *)it.pointer);
455 face->indices[i] = vInfo.index;
459 for(index = 0; index < nNewVertices; index++)
461 it.pointer = vertices[index];
466 DestVertexInfo vInfo = it.data;
468 Vector3D tangent1 { };
469 Vector3D tangent2 { };
470 SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
471 uint smoothing = inf->smoothGroups;
473 SharedSourceVertexInfo * shared = inf->shared;
474 SharedDestVertexInfo svInfo = sharedVertices[*shared];
476 if(!svInfo || vInfo.index != index)
479 for(i : svInfo.faces)
481 Face * face = &info->faces[i];
483 if(smoothing & face->smoothGroups)
484 smoothing |= face->smoothGroups;
487 // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
488 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
490 for(i : svInfo.faces)
492 Face * face = &info->faces[i];
493 if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
496 for(j = 0; j < 3; j++)
498 if(face->indices[j] == vInfo.index)
500 origIndex = face->origIndices[j];
502 normal.x += face->normal.x;
503 normal.y += face->normal.y;
504 normal.z += face->normal.z;
517 for(i : svInfo.faces)
519 Face * face = &info->faces[i];
520 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
524 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
529 for(k = 0; k < 3; k++)
531 if(face->indices[k] == vInfo.index)
533 origIndexB = face->origIndices[k];
537 valid = origIndex == origIndexB;
540 for(j : svInfo.faces)
542 if(info->faces[j].done)
544 double dot = info->faces[j].normal.DotProduct(face->normal);
545 if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
546 valid = fabs(dot) > cutOff;
555 uint * indices = face->origIndices;
556 uint ix = vInfo.copyFromIndex;
557 uint ix0 = indices[0], ix1 = indices[1], ix2 = indices[2];
558 double dot = ix == ix0 ? face->dots[0] : ix == ix1 ? face->dots[1] : ix == ix2 ? face->dots[2] : 1;
559 if(ix != indices[0] && ix != indices[1] && ix != indices[2])
561 if(!mVertices[ix0].OnCompare(mVertices[ix])) dot = face->dots[0];
562 else if(!mVertices[ix1].OnCompare(mVertices[ix])) dot = face->dots[1];
563 else if(!mVertices[ix2].OnCompare(mVertices[ix])) dot = face->dots[2];
565 normal.x += face->normal.x * dot;
566 normal.y += face->normal.y * dot;
567 normal.z += face->normal.z * dot;
571 Vector3Df * p0 = &mVertices[ix0], * p1 = &mVertices[ix1], * p2 = &mVertices[ix2];
572 Pointf * t0 = &mesh->texCoords[ix0], * t1 = &mesh->texCoords[ix1], * t2 = &mesh->texCoords[ix2];
573 Vector3D v01 { (double)p1->x - (double)p0->x, (double)p1->y - (double)p0->y, (double)p1->z - (double)p0->z };
574 Vector3D v02 { (double)p2->x - (double)p0->x, (double)p2->y - (double)p0->y, (double)p2->z - (double)p0->z };
576 Vector3D t01 { (double)t1->x - (double)t0->x, (double)t1->y - (double)t0->y };
577 Vector3D t02 { (double)t2->x - (double)t0->x, (double)t2->y - (double)t0->y };
578 double f = dot / (t01.x * t02.y - t02.x * t01.y);
580 tangent1.x += f * (v01.x * t02.y - v02.x * t01.y);
581 tangent1.y += f * (v01.y * t02.y - v02.y * t01.y);
582 tangent1.z += f * (v01.z * t02.y - v02.z * t01.y);
584 tangent2.x += f * (v02.x * t01.x - v01.x * t02.x);
585 tangent2.y += f * (v02.y * t01.x - v01.y * t02.x);
586 tangent2.z += f * (v02.z * t01.x - v01.z * t02.x);
596 if(!SMOOTH_CUTOFF || !(WELD_TRESHOLD > 0)) break;
598 // normal.Scale(normal, 1.0f / numShared);
599 normal.Scale(normal, 1.0 / dotSum);
600 normal.Normalize(normal);
602 //tangent1.Scale(tangent1, 1.0 / dotSum);
603 tangent1.Normalize(tangent1);
605 //tangent2.Scale(tangent2, 1.0 / dotSum);
606 tangent2.Normalize(tangent2);
608 if(vInfo.index == index)
610 vInfo.normal = { (float)normal.x, (float)normal.y, (float)normal.z };
611 vInfo.tangent1 = { (float)tangent1.x, (float)tangent1.y, (float)tangent1.z };
612 vInfo.tangent2 = { (float)tangent2.x, (float)tangent2.y, (float)tangent2.z };
615 // Auto welding/smoothing requires extra vertices because angle is too steep
616 if(SMOOTH_CUTOFF && WELD_TRESHOLD > 0)
618 SharedDestVertexInfo newSharedInfo = null;
620 for(i : svInfo.faces)
622 Face * face = &info->faces[i];
623 if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
626 for(j = 0; j < 3; j++)
628 if(face->indices[j] == vInfo.index)
632 DestVertexInfo newVert;
633 SharedSourceVertexInfo * source;
635 index = nNewVertices++;
636 itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
637 source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
638 itShared.data = newSharedInfo = { };
640 it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
643 newVert.copyFromIndex = vInfo.copyFromIndex;
644 newVert.index = index;
646 vertices.Add((void *)it.pointer);
648 face->indices[j] = index;
649 newSharedInfo.faces.Add(i);
659 // Allocate some extra vertices
661 Vector3Df * oldVertices = mesh.vertices;
662 Pointf * oldTexCoords = mesh.texCoords;
664 // TODO: Support reallocation?
665 *((void **)&mesh.vertices) = null;
666 *((void **)&mesh.texCoords) = null;
667 *((int *)&mesh.nVertices) = 0;
669 mesh.Allocate( { vertices = true, normals = true, tangents = oldTexCoords ? true : false, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
671 // Fill in the new vertices
672 for(index = 0; index < nNewVertices; index++)
674 DestVertexInfo vInfo;
675 it.pointer = vertices[index];
679 mesh.normals[index] = vInfo ? vInfo.normal : { };
680 mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
683 mesh.tangents[2*index+0] = vInfo ? vInfo.tangent1 : { };
684 mesh.tangents[2*index+1] = vInfo ? vInfo.tangent2 : { };
687 mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
696 for(i = 0; i < info->nFaces; i++)
697 info->faces[i].done = false;
700 mesh.Unlock({ normals = true, tangents = true });
702 // Free all the temporary stuff
707 sharedVertices.Free();
708 delete sharedVertices;
712 static bool ReadSmoothing(FileInfo * info, Object object)
714 switch(info->chunkId)
719 for(c = 0; c<info->nFaces; c++)
720 info->faces[c].smoothGroups = ReadDWORD(info->f);
727 static bool ReadFacesListChunks(FileInfo * info, Object object)
729 DisplaySystem displaySystem = info->displaySystem;
730 switch(info->chunkId)
739 char matName[MAX_LOCATION + 100];
741 strcpy(matName, info->fileName);
742 ReadASCIIZ(info->f, &name);
743 count = ReadWORD(info->f);
744 strcat(matName, name);
746 mat = displaySystem.GetMaterial(matName);
747 faces = info->matFaces[(uintptr)mat];
749 info->matFaces[(uintptr)mat] = faces = { };
753 for(c = 0; c<count; c++)
755 uint16 face = ReadWORD(info->f);
757 info->faces[face].material = mat;
766 static bool ReadTriMesh(FileInfo * info, Object object)
768 Mesh mesh = object.mesh;
769 switch(info->chunkId)
774 uint16 nVertices = ReadWORD(info->f);
775 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
776 *((int *) &mesh.nVertices) = nVertices;
777 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
780 for(c = 0; c<mesh.nVertices; c++)
783 Read3DVertex(info->f, vertex);
784 mesh.vertices[c].x = vertex.x;
785 mesh.vertices[c].y =-vertex.z;
786 mesh.vertices[c].z = vertex.y;
793 case TRI_MAPPINGCOORS:
796 uint16 count = ReadWORD(info->f);
797 count = (uint16)Min(mesh.nVertices, count);
799 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
800 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
801 mesh.flags.texCoords1 = true;
804 for(c = 0; c<count; c++)
806 mesh.texCoords[c].x = ReadFloat(info->f);
807 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
821 info->nFaces = nFaces = ReadWORD(info->f);
822 info->pos += sizeof(uint16);
824 info->faces = new0 Face[nFaces];
825 for(c = 0; c<nFaces; c++)
829 info->faces[c].origIndices[i] =
830 info->faces[c].indices[i] = ReadWORD(info->f);
832 info->pos += 4*sizeof(uint16);
835 ReadChunks(ReadSmoothing, info, object);
839 info->matFaces.Free();
840 info->matFaces = { };
842 ReadChunks(ReadFacesListChunks, info, object);
844 ComputeNormals(mesh, info, object);
847 for(m : info->matFaces)
849 Material mat = (Material)&m;
850 Array<int> faces = m;
851 if(mat.flags.translucent)
853 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
856 Face * face = &info->faces[i];
857 PrimitiveSingle * triangle;
859 triangle = &mesh.primitives[mesh.nPrimitives++];
860 if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
862 triangle->indicesMember[0] = (uintindex)face->indices[0];
863 triangle->indicesMember[1] = (uintindex)face->indices[1];
864 triangle->indicesMember[2] = (uintindex)face->indices[2];
865 triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
866 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
867 triangle->plane.FromPointsf(
868 mesh.vertices[triangle->indicesMember[2]],
869 mesh.vertices[triangle->indicesMember[1]],
870 mesh.vertices[triangle->indicesMember[0]]);
872 mesh.UnlockPrimitive(triangle);
874 triangle->middle.x /= 3;
875 triangle->middle.y /= 3;
876 triangle->middle.z /= 3;
878 triangle->material = mat;
881 object.flags.translucent = true;
886 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
890 group.material = mat;
893 Face * face = &info->faces[i];
895 if(object.flags.flipWindings)
897 group.indicesMember[c*3] = (uintindex)face->indices[2];
898 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
899 group.indicesMember[c*3+2] = (uintindex)face->indices[0];
903 group.indicesMember[c*3] = (uintindex)face->indices[0];
904 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
905 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
910 mesh.UnlockPrimitiveGroup(group);
915 // Add faces without a material all together
917 for(c = 0; c<nFaces; c++)
918 if(!info->faces[c].done)
922 PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
925 for(c = 0; c<nFaces; c++)
927 Face * face = &info->faces[c];
930 group.indicesMember[c*3] = (uintindex)face->indices[0];
931 group.indicesMember[c*3+1] = (uintindex)face->indices[1];
932 group.indicesMember[c*3+2] = (uintindex)face->indices[2];
935 mesh.UnlockPrimitiveGroup(group);
942 info->matFaces.Free();
943 delete info->matFaces;
950 Vector3Df xAxis, yAxis, zAxis, center;
953 Matrix inverse/*, source = { 0 }*/;
956 Read3DVertex(info->f, xAxis);
957 Read3DVertex(info->f, yAxis);
958 Read3DVertex(info->f, zAxis);
959 Read3DVertex(info->f, center);
961 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
962 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
963 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
965 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
968 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
969 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
970 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
971 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
973 inverse.Inverse(source);
976 object.flags.flipWindings = false;
978 xAxis.Normalize(xAxis);
979 yAxis.Normalize(yAxis);
980 zAxis.Normalize(zAxis);
982 orth.CrossProduct(yAxis, zAxis);
983 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
984 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
985 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
987 object.flags.flipWindings ^= true;
991 orth.CrossProduct(zAxis, xAxis);
992 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
993 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
994 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
996 object.flags.flipWindings ^= true;
1000 orth.CrossProduct(xAxis, yAxis);
1001 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
1002 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
1003 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
1005 object.flags.flipWindings ^= true;
1012 rotation.m[0][0] = xAxis.x;
1013 rotation.m[0][1] =-xAxis.z;
1014 rotation.m[0][2] = xAxis.y;
1015 rotation.m[0][3] = 0;
1016 rotation.m[1][0] =-zAxis.x;
1017 rotation.m[1][1] = zAxis.z;
1018 rotation.m[1][2] =-zAxis.y;
1019 rotation.m[1][3] = 0;
1020 rotation.m[2][0] = yAxis.x;
1021 rotation.m[2][1] =-yAxis.z;
1022 rotation.m[2][2] = yAxis.y;
1023 rotation.m[2][3] = 0;
1024 rotation.m[3][0] = 0;
1025 rotation.m[3][1] = 0;
1026 rotation.m[3][2] = 0;
1027 rotation.m[3][3] = 1;
1031 inverse.Transpose(rotation);
1033 temp.Translate(-center.x, center.z, -center.y);
1034 temp2.Multiply(temp, inverse);
1035 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
1039 object.transform.scaling = scaling;
1040 // TODO: Improve language to support deep properties on non constant functions
1041 // object.transform.orientation.RotationMatrix(rotation);
1043 Quaternion orientation;
1044 orientation.RotationMatrix(rotation);
1045 object.transform.orientation = orientation;
1047 object.transform.position = { center.x, -center.z, center.y };
1050 // Localize All Vertices
1051 for(c = 0; c<mesh.nVertices; c++)
1053 Vector3Df vertex = mesh.vertices[c];
1055 mesh.vertices[c].MultMatrix(vertex, inverse);
1057 mesh.vertices[c].x -= object.pivot.x;
1058 mesh.vertices[c].y -= object.pivot.y;
1059 mesh.vertices[c].z -= object.pivot.z;
1068 static bool ReadMap(FileInfo * info, Material mat)
1070 DisplaySystem displaySystem = info->displaySystem;
1071 switch(info->chunkId)
1076 char location[MAX_LOCATION];
1078 ReadASCIIZ(info->f, &name);
1080 strcpy(location, info->textureDirectory);
1081 PathCat(location, name);
1082 if(!FileExists(location))
1084 // Attempt all lowercase if original case does not exist
1086 strcpy(location, info->textureDirectory);
1087 PathCat(location, name);
1090 if(info->parent->chunkId == MAT_BUMPMAP)
1092 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1093 char bumpName[MAX_LOCATION+5];
1094 strcpy(bumpName, "BUMP:");
1095 strcat(bumpName, location);
1098 mat.bumpMap = displaySystem.GetTexture(bumpName);
1101 mat.bumpMap = Bitmap { };
1102 if(!mat.bumpMap.Load(location, null, null) ||
1103 !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1104 !displaySystem.AddTexture(bumpName, mat.bumpMap))
1108 ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1109 int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1112 for(y = 0; y < bh; y++)
1113 for(x = 0; x < bw; x++)
1115 uint bc = y * bw + x;
1116 Color color = picture[bc].color;
1117 picture[bc] = { 255, { color.r, color.g, color.b } };
1123 else if(info->parent->chunkId == MAT_SPECULARMAP)
1125 // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1126 char specName[MAX_LOCATION+5];
1127 strcpy(specName, "SPEC:");
1128 strcat(specName, location);
1129 if(!mat.specularMap)
1131 mat.specularMap = displaySystem.GetTexture(specName);
1132 if(!mat.specularMap)
1134 mat.specularMap = Bitmap { };
1135 if(!mat.specularMap.Load(location, null, null) ||
1136 !mat.specularMap.Convert(null, pixelFormat888, null) ||
1137 !displaySystem.AddTexture(specName, mat.specularMap))
1138 delete mat.specularMap;
1141 ColorAlpha * picture = (ColorAlpha *)mat.specularMap.picture;
1142 int bw = mat.specularMap.width, bh = mat.specularMap.height;
1145 for(y = 0; y < bh; y++)
1146 for(x = 0; x < bw; x++)
1148 uint bc = y * bw + x;
1149 Color color = picture[bc].color;
1150 picture[bc] = { 255, { color.r, color.g, color.b } };
1158 Bitmap opacityMap = null;
1159 bool alphaOnly = true;
1160 bool translucent = false;
1163 mat.baseMap = displaySystem.GetTexture(location);
1166 mat.baseMap = Bitmap { };
1167 if(!mat.baseMap.Load(location, null, null) ||
1168 !mat.baseMap.Convert(null, pixelFormat888, null) ||
1169 !displaySystem.AddTexture(location, mat.baseMap))
1173 opacityMap = mat.baseMap;
1176 else if(info->parent->chunkId == MAT_MAPOPACITY)
1178 opacityMap = Bitmap { };
1179 if(opacityMap.Load(location, null, null))
1181 if(opacityMap.pixelFormat == pixelFormatRGBA)
1183 if(!opacityMap.Convert(null, pixelFormat888, null))
1190 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1192 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1193 ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1195 int ow = opacityMap.width, oh = opacityMap.height;
1196 int bw = mat.baseMap.width, bh = mat.baseMap.height;
1198 for(y = 0; y < bh; y++)
1199 for(x = 0; x < bw; x++)
1201 int bc = ((y % bh) * bw + (x % bw));
1202 int oc = ((y % oh) * bw + (x % ow));
1203 byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1204 if(alpha && alpha < 255)
1206 picture[bc] = ColorAlpha { alpha, picture[bc].color };
1210 mat.flags.translucent = true;
1211 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1212 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1214 if(opacityMap != mat.baseMap)
1222 MapOptions options = (MapOptions)ReadWORD(info->f);
1223 if(!options.dontTile) mat.flags.tile = true;
1227 mat.uScale = ReadFloat(info->f);
1230 mat.vScale = ReadFloat(info->f);
1236 PrintLn("Unhandled Map block");
1243 static bool ReadMaterial(FileInfo * info, Material mat)
1245 switch(info->chunkId)
1250 char matName[MAX_LOCATION + 100];
1251 strcpy(matName, info->fileName);
1252 ReadASCIIZ(info->f, &name);
1253 strcat(matName, name);
1254 mat.name = CopyString(matName);
1258 case MAT_TRANSPARENCY:
1260 uint16 transparency;
1261 ReadChunks(ReadAmountOf, info, &transparency);
1262 mat.opacity = 1.0f - transparency / 100.0f;
1263 if(mat.opacity < 1.0)
1264 mat.flags.translucent = true;
1269 ReadChunks(ReadRGB, info, &mat.diffuse);
1270 ReadChunks(ReadRGB, info, &mat.diffuse);
1275 ReadChunks(ReadRGB, info, &mat.ambient);
1276 ReadChunks(ReadRGB, info, &mat.ambient);
1281 ReadChunks(ReadRGB, info, &mat.specular);
1282 ReadChunks(ReadRGB, info, &mat.specular);
1288 ReadChunks(ReadAmountOf, info, &emissive);
1289 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1290 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1291 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1294 case MAT_SHINSTRENGTH:
1297 ReadChunks(ReadAmountOf, info, &shininess);
1298 mat.reflectivity = shininess / 100.0f;
1300 mat.specular.r *= shininess / 100.0f;
1301 mat.specular.g *= shininess / 100.0f;
1302 mat.specular.b *= shininess / 100.0f;
1309 ReadChunks(ReadAmountOf, info, &power);
1313 case MAT_MAPTEXTURE1:
1314 ReadChunks(ReadMap, info, mat);
1316 case MAT_MAPOPACITY:
1317 ReadChunks(ReadMap, info, mat);
1319 case MAT_DOUBLESIDED:
1320 mat.flags.doubleSided = true;
1323 ReadChunks(ReadMap, info, mat);
1327 PrintLn("Unhandled MAT type ID", info->chunkId);
1334 static bool ReadLight(FileInfo * info, Object object)
1336 Light * light = &object.light;
1337 switch(info->chunkId)
1340 case RGB_BYTE_GAMMA:
1342 case RGB_FLOAT_GAMMA:
1343 ReadRGB(info, &light->diffuse);
1344 light->specular = light->diffuse;
1349 char targetName[MAXNAMELEN];
1351 strcpy(targetName, object.name);
1352 strcat(targetName, ".target");
1354 light->flags.omni = false;
1355 light->flags.spot = true;
1357 target = Object { };
1358 target.name = CopyString(targetName);
1359 info->rootObject.children.AddName(target);
1360 target.parent = info->rootObject;
1362 light->target = target;
1364 target.transform.position.x = ReadFloat(info->f);
1365 target.transform.position.z = ReadFloat(info->f);
1366 target.transform.position.y =-ReadFloat(info->f);
1368 light->hotSpot = ReadFloat(info->f);
1369 light->fallOff = ReadFloat(info->f);
1374 light->flags.off = true;
1377 case LIT_ATTENUATION:
1381 d = 300, small = 0.001,
1383 d * (Kl + Kq * d) = (1 / small) - Kc
1385 { Kc, Kl, Kq, small, d });
1389 #define MINLIGHT 0.08
1390 light->Kq = 1/(light->end*light->end*MINLIGHT);
1393 #define MINLIGHT 0.15f
1394 // #define MINLIGHT 0.1
1395 //light->Kl = (float)(1/(light->end*MINLIGHT));
1397 float c = 1.0, l = 0.005, q = 0.0005, small = 500, end = light->end;
1398 light->Kc = (c + l * end + q * end * end) / small;
1399 light->Kl = light->Kc * l/c;
1400 light->Kq = light->Kc * q/c;
1401 light->flags.attenuation = true;
1406 light->start = ReadFloat(info->f);
1411 light->end = ReadFloat(info->f);
1414 case LIT_MULTIPLIER:
1416 light->multiplier = ReadFloat(info->f);
1424 static bool ReadCamera(FileInfo * info, Object object)
1426 switch(info->chunkId)
1434 //Camera camera = object.camera;
1435 /*float nearRange = */ReadFloat(info->f);
1436 /*float farRange = */ReadFloat(info->f);
1438 camera.zMin = Max(0.1, nearRange);
1439 camera.zMax = farRange;
1448 static bool ReadEditObject(FileInfo * info, char * name)
1450 DisplaySystem displaySystem = info->displaySystem;
1451 switch(info->chunkId)
1455 Object object = info->rootObject.Find(name);
1458 object = Object { };
1459 object.name = CopyString(name);
1460 info->rootObject.children.AddName(object);
1461 object.parent = info->rootObject;
1463 object.InitializeMesh(displaySystem);
1464 ReadChunks(ReadTriMesh, info, object);
1465 object.flags.mesh = true;
1466 object.mesh.Unlock(0);
1471 Object object = info->rootObject.Find(name);
1477 object = Object { };
1478 object.name = CopyString(name);
1479 info->rootObject.children.AddName(object);
1480 object.parent = info->rootObject;
1482 object.flags.light = true;
1484 light = &object.light;
1485 light->lightObject = object;
1486 light->flags.omni = true;
1487 light->multiplier = 1.0f;
1489 // This is not used?
1490 Read3DVertex(info->f, position);
1491 light->direction = { position.x, position.y, position.z };
1492 info->pos += sizeof(float) * 3;
1494 ReadChunks(ReadLight, info, object);
1499 char targetName[MAXNAMELEN];
1500 Object object = info->rootObject.Find(name);
1503 float /*bankAngle, */focus;
1506 strcpy(targetName, name);
1507 strcat(targetName, ".target");
1508 target = info->rootObject.Find(targetName);
1512 object = Object { };
1513 object.name = CopyString(name);
1514 info->rootObject.children.AddName(object);
1516 object.parent = info->rootObject;
1517 object.camera = Camera { };
1518 object.camera.type = lookAtObject;
1523 target = Object { };
1524 target.name = CopyString(targetName);
1525 info->rootObject.children.AddName(target);
1526 target.parent = info->rootObject;
1529 object.flags.camera = true;
1530 object.cameraTarget = target;
1532 camera = object.camera;
1533 camera.cameraObject = object;
1534 camera.target = target;
1536 //Read3DVertex(info->f, camera.position);
1537 object.transform.position.x = ReadFloat(info->f);
1538 object.transform.position.z = ReadFloat(info->f);
1539 object.transform.position.y =-ReadFloat(info->f);
1541 info->pos += sizeof(float) * 3;
1542 //Read3DVertex(info->f, object.cameraTarget.position);
1543 target.transform.position.x = ReadFloat(info->f);
1544 target.transform.position.z = ReadFloat(info->f);
1545 target.transform.position.y =-ReadFloat(info->f);
1547 info->pos += sizeof(float) * 3;
1548 /*bankAngle = */ReadFloat(info->f);
1549 info->pos += sizeof(float);
1550 focus = ReadFloat(info->f);
1551 info->pos += sizeof(float);
1553 mm = (focus - 5.05659508373109) / 1.13613250717301;
1554 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1556 ReadChunks(ReadCamera, info, object);
1559 case OBJ_HIDDEN: break;
1564 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1566 static bool ReadEditChunks(FileInfo * info, void * data)
1568 switch(info->chunkId)
1572 // Read the ambient color
1573 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1578 Material material { /*flags = { singleSideLight = true }*/ };
1580 ReadChunks(ReadMaterial, info, material);
1582 mat = info->displaySystem.AddNamedMaterial(material.name);
1585 if(material.baseMap)
1586 material.baseMap.MakeMipMaps(info->displaySystem);
1587 if(material.bumpMap)
1588 material.bumpMap.MakeMipMaps(info->displaySystem);
1589 if(material.specularMap)
1590 material.specularMap.MakeMipMaps(info->displaySystem);
1592 // COPY_NITEM(mat, material);
1593 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1597 delete material.baseMap;
1599 delete material.name;
1606 info->pos += ReadASCIIZ(info->f, &name);
1607 ReadChunks(ReadEditObject, info, name);
1615 struct ObjectInfoBlock
1625 // Key Framer Chunks
1626 static class AccelerationFlags : uint32
1628 bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
1631 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1633 switch(info->chunkId)
1637 //uint16 flags1, flags2;
1638 ReadASCIIZ(info->f, &block->name);
1639 /*flags1 = */ReadWORD(info->f);
1640 /*flags2 = */ReadWORD(info->f);
1641 block->parent = ReadWORD(info->f);
1645 ReadASCIIZ(info->f, &block->dummyName);
1648 Read3DVertex(info->f, block->pivot);
1651 block->hierarchy = ReadWORD(info->f);
1655 case FRM_TRACKSCALE:
1658 case FRM_TRACKCOLOR:
1659 case FRM_TRACKHOTSPOT:
1660 case FRM_TRACKFALLOFF:
1662 FrameTrack track { };
1669 block->tracks.Add(track);
1671 flags = ReadWORD(info->f);
1673 info->f.Read(unknown, sizeof(unknown), 1);
1675 track.numKeys = ReadDWORD(info->f);
1677 switch(info->chunkId)
1679 case FRM_TRACKPOS: track.type.type = position; break;
1680 case FRM_TRACKROT: track.type.type = rotation; break;
1681 case FRM_TRACKSCALE: track.type.type = scaling; break;
1682 case FRM_TRACKROLL: track.type.type = roll; break;
1683 case FRM_TRACKFOV: track.type.type = fov; break;
1684 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1685 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1686 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1688 if((flags & 0x0003) == 3)
1689 track.type.loop = true;
1691 track.keys = new0 FrameKey[track.numKeys];
1692 for(c = 0; c<track.numKeys; c++)
1694 AccelerationFlags accelerationFlags;
1695 FrameKey * key = track.keys + c;
1697 key->frame = ReadDWORD(info->f);
1698 accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
1700 if(accelerationFlags.tension)
1701 key->tension = ReadFloat(info->f);
1702 if(accelerationFlags.continuity)
1703 key->continuity = ReadFloat(info->f);
1704 if(accelerationFlags.bias)
1705 key->bias = ReadFloat(info->f);
1706 if(accelerationFlags.easeTo)
1707 key->easeTo = ReadFloat(info->f);
1708 if(accelerationFlags.easeFrom)
1709 key->easeFrom = ReadFloat(info->f);
1711 switch(info->chunkId)
1716 Read3DVertex(info->f, position);
1717 key->position = { position.x, -position.z, position.y };
1723 Angle angle = ReadFloat(info->f);
1726 Read3DVertex(info->f, axis);
1727 fixedAxis = { axis.x, -axis.z, axis.y };
1731 Quaternion rotation;
1732 rotation.RotationAxis(fixedAxis, angle);
1733 key->orientation.Multiply((key - 1)->orientation, rotation);
1736 key->orientation.RotationAxis(fixedAxis, angle);
1739 case FRM_TRACKSCALE:
1742 Read3DVertex(info->f, scaling);
1743 key->scaling = { scaling.x, scaling.z, scaling.y };
1748 key->fov = ReadFloat(info->f);
1753 key->roll = -ReadFloat(info->f);
1756 case FRM_TRACKCOLOR:
1758 FileInfo childInfo = *info;
1759 childInfo.chunkId = RGB_FLOAT;
1760 ReadRGB(&childInfo, &key->color);
1763 case FRM_TRACKHOTSPOT:
1765 key->hotSpot = ReadFloat(info->f);
1768 case FRM_TRACKFALLOFF:
1770 key->fallOff = ReadFloat(info->f);
1782 static Object FindObjectID(Object object, int id)
1784 Object result = null;
1786 for(child = object.children.first; child; child = child.next)
1788 if(child.flags.hierarchy == (uint16) id)
1795 result = FindObjectID(child, id);
1802 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1804 switch(info->chunkId)
1808 ObjectInfoBlock block { };
1809 Object object = null;
1811 ReadChunks(ReadFrameInfoBlock, info, &block);
1813 if(block.dummyName && block.dummyName[0])
1815 if(!strcmp(block.name, "$$$DUMMY"))
1817 object = Object { };
1818 object.name = block.dummyName;
1819 info->rootObject.children.AddName(object);
1820 object.transform.scaling = { 1,1,1 };
1824 Object model = info->rootObject.Find(block.name);
1827 object = Object { };
1828 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1829 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1830 object.flags = model.flags;
1831 object.flags.ownMesh = false;
1832 object.mesh = model.mesh;
1834 object.min = model.min;
1835 object.max = model.max;
1836 object.radius = model.radius;
1838 object.transform = model.transform;
1839 info->rootObject.children.AddName(object);
1841 delete block.dummyName;
1844 object.parent = info->rootObject;
1847 object = info->rootObject.Find(block.name);
1851 Mesh mesh = object.mesh;
1852 object.flags.hierarchy = block.hierarchy + 1;
1853 if(block.parent != -1)
1855 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1858 object.parent.children.Remove(object);
1859 parent.children.AddName(object);
1860 object.parent = parent;
1863 object.pivot.x = block.pivot.x;
1864 object.pivot.y =-block.pivot.z;
1865 object.pivot.z = block.pivot.y;
1867 if(mesh && object.flags.ownMesh)
1869 if(mesh.Lock({ vertices = true }))
1872 // Take pivot into account
1873 for(c = 0; c<mesh.nVertices; c++)
1875 mesh.vertices[c].x -= object.pivot.x;
1876 mesh.vertices[c].y -= object.pivot.y;
1877 mesh.vertices[c].z -= object.pivot.z;
1879 mesh.Unlock({ vertices = true });
1883 object.tracks = block.tracks;
1884 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1885 object.endFrame = info->rootObject.endFrame;
1888 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1894 ObjectInfoBlock block { };
1895 Object object = null;
1897 ReadChunks(ReadFrameInfoBlock, info, &block);
1899 if(block.dummyName && block.dummyName[0])
1901 if(!strcmp(block.name, "$$$DUMMY"))
1903 object = Object { };
1904 object.name = block.dummyName;
1905 info->rootObject.children.AddName(object);
1906 object.transform.scaling = { 1, 1, 1 };
1907 object.flags.camera = true;
1911 Object model = info->rootObject.Find(block.name);
1914 object = Object { };
1915 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1916 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1917 object.flags = model.flags;
1918 object.flags.ownMesh = false;
1919 object.camera = model.camera;
1920 object.flags.camera = true;
1921 info->rootObject.children.AddName(object);
1923 delete block.dummyName;
1926 object.parent = info->rootObject;
1929 object = info->rootObject.Find(block.name);
1933 object.flags.hierarchy = block.hierarchy + 1;
1934 if(block.parent != -1)
1936 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1939 object.parent.children.Remove(object);
1940 parent.children.AddName(object);
1941 object.parent = parent;
1944 object.pivot.x = block.pivot.x;
1945 object.pivot.y =-block.pivot.z;
1946 object.pivot.z = block.pivot.y;
1948 object.tracks = block.tracks;
1949 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1950 object.endFrame = info->rootObject.endFrame;
1953 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1957 case FRM_CAMERATARGET:
1959 ObjectInfoBlock block { };
1960 Object object = null;
1961 char targetName[MAXNAMELEN];
1963 ReadChunks(ReadFrameInfoBlock, info, &block);
1965 strcpy(targetName, block.name);
1966 strcat(targetName, ".target");
1968 if(block.dummyName && block.dummyName[0])
1970 if(!strcmp(block.name, "$$$DUMMY"))
1972 object = Object { };
1973 object.name = block.dummyName;
1974 info->rootObject.children.AddName(object);
1975 object.transform.scaling = { 1,1,1 };
1979 Object model = info->rootObject.Find(targetName);
1982 object = Object { };
1983 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1984 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1985 object.flags = model.flags;
1986 object.flags.ownMesh = false;
1987 object.camera = model.camera;
1988 info->rootObject.children.AddName(object);
1990 delete block.dummyName;
1993 object.parent = info->rootObject;
1996 object = info->rootObject.Find(targetName);
2000 object.flags.hierarchy = block.hierarchy + 1;
2001 if(block.parent != -1)
2003 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2006 object.parent.children.Remove(object);
2007 parent.children.AddName(object);
2008 object.parent = parent;
2011 object.pivot.x = block.pivot.x;
2012 object.pivot.y =-block.pivot.z;
2013 object.pivot.z = block.pivot.y;
2015 object.tracks = block.tracks;
2016 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2017 object.endFrame = info->rootObject.endFrame;
2020 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2026 ObjectInfoBlock block { };
2028 ReadChunks(ReadFrameInfoBlock, info, &block);
2030 info->rootObject.tracks = block.tracks;
2036 ObjectInfoBlock block { };
2037 Object object = null;
2039 ReadChunks(ReadFrameInfoBlock, info, &block);
2041 if(block.dummyName && block.dummyName[0])
2043 if(!strcmp(block.name, "$$$DUMMY"))
2045 object = Object { };
2046 object.name = block.dummyName;
2047 info->rootObject.children.AddName(object);
2048 object.transform.scaling = { 1, 1, 1 };
2049 object.flags.light = true;
2053 Object model = info->rootObject.Find(block.name);
2056 object = Object { };
2057 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2058 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2059 object.flags = model.flags;
2060 object.flags.ownMesh = false;
2061 object.light = model.light;
2062 object.flags.light = true;
2063 info->rootObject.children.AddName(object);
2065 delete block.dummyName;
2068 object.parent = info->rootObject;
2071 object = info->rootObject.Find(block.name);
2075 object.flags.hierarchy = block.hierarchy + 1;
2076 if(block.parent != -1)
2078 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2081 object.parent.children.Remove(object);
2082 parent.children.AddName(object);
2083 object.parent = parent;
2086 object.pivot.x = block.pivot.x;
2087 object.pivot.y =-block.pivot.z;
2088 object.pivot.z = block.pivot.y;
2090 object.tracks = block.tracks;
2091 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2092 object.endFrame = info->rootObject.endFrame;
2095 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2099 case FRM_SPOTLIGHTTARGET:
2101 ObjectInfoBlock block { };
2102 Object object = null;
2103 char targetName[MAXNAMELEN];
2105 ReadChunks(ReadFrameInfoBlock, info, &block);
2107 strcpy(targetName, block.name);
2108 strcat(targetName, ".target");
2110 if(block.dummyName && block.dummyName[0])
2112 if(!strcmp(block.name, "$$$DUMMY"))
2114 object = Object { };
2115 object.name = block.dummyName;
2116 info->rootObject.children.AddName(object);
2117 object.transform.scaling = { 1,1,1 };
2121 Object model = info->rootObject.Find(targetName);
2124 object = Object { };
2125 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2126 // TODO: When passing a String to a const String, use member if property is const String but member is String
2127 sprintf(object.name, "%s.%s", model.name, block.dummyName);
2128 object.flags = model.flags;
2129 object.flags.ownMesh = false;
2130 object.light = model.light;
2131 info->rootObject.children.AddName(object);
2133 delete block.dummyName;
2136 object.parent = info->rootObject;
2139 object = info->rootObject.Find(targetName);
2143 object.flags.hierarchy = block.hierarchy + 1;
2144 if(block.parent != -1)
2146 Object parent = FindObjectID(info->rootObject, block.parent + 1);
2149 object.parent.children.Remove(object);
2150 parent.children.AddName(object);
2151 object.parent = parent;
2154 object.pivot.x = block.pivot.x;
2155 object.pivot.y =-block.pivot.z;
2156 object.pivot.z = block.pivot.y;
2158 object.tracks = block.tracks;
2159 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2160 object.endFrame = info->rootObject.endFrame;
2163 Logf("Object not found while loading animation frame: %s\n", block.name);*/
2169 info->rootObject.startFrame = ReadDWORD(info->f);
2170 info->rootObject.endFrame = ReadDWORD(info->f);
2171 *&(info->rootObject.frame) = info->rootObject.startFrame;
2179 static bool ReadMainChunks(FileInfo * info, void * data)
2181 switch(info->chunkId)
2184 ReadChunks(ReadEditChunks, info, null);
2188 Object object = data;
2189 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2191 ReadChunks(ReadKeyFrameChunks, info, null);
2192 object.flags.keysLoaded = true;
2200 static bool ReadMain(FileInfo * info, void * data)
2202 switch(info->chunkId)
2205 ReadChunks(ReadMainChunks, info, data);
2211 class Object3DSFormat : ObjectFormat
2213 class_property(extension) = "3ds";
2215 bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
2217 bool result = false;
2220 FileInfo info = {0};
2221 info.rootObject = object;
2222 info.displaySystem = displaySystem;
2225 info.fileName = fileName;
2226 StripLastDirectory(fileName, info.textureDirectory);
2227 info.f = FileOpen(fileName, read);
2230 // TOFIX: eC reorders that badly
2231 // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2232 if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
2234 object.flags.root = true;
2235 object.SetMinMaxRadius(true);
2236 object._Animate(object.frame);
2237 object.UpdateTransform();
2243 info.matFaces.Free();
2244 delete info.matFaces;
2247 object.Free(displaySystem);