1 namespace gfx3D::models;
8 #define RGB_FLOAT 0x0010
9 #define RGB_BYTE 0x0011
10 #define RGB_BYTE_GAMMA 0x0012
11 #define RGB_FLOAT_GAMMA 0x0013
14 #define AMOUNT_OF 0x0030
16 #define MAIN3DS 0x4D4D
17 #define EDIT3DS 0x3D3D
18 #define EDIT_AMBIENT 0x2100
19 #define EDIT_MATERIAL 0xAFFF
20 #define EDIT_OBJECT 0x4000
21 #define OBJ_HIDDEN 0x4010
22 #define OBJ_TRIMESH 0x4100
23 #define OBJ_LIGHT 0x4600
24 #define OBJ_CAMERA 0x4700
26 // Triangular Mesh Chunks
27 #define TRI_VERTEXL 0x4110
28 #define TRI_FACEL1 0x4120
29 #define TRI_MATERIAL 0x4130
30 #define TRI_MAPPINGCOORS 0x4140
31 #define TRI_SMOOTHING 0x4150
32 #define TRI_LOCAL 0x4160
35 #define LIT_SPOT 0x4610
36 #define LIT_ONOFF 0x4620
37 #define LIT_ATTENUATION 0x4625
38 #define LIT_START 0x4659
39 #define LIT_END 0x465A
40 #define LIT_MULTIPLIER 0x465B
43 #define CAM_SEECONE 0x4710
44 #define CAM_RANGES 0x4720
47 #define MAT_NAME 0xA000
48 #define MAT_AMBIENT 0xA010
49 #define MAT_DIFFUSE 0xA020
50 #define MAT_SPECULAR 0xA030
51 #define MAT_SHININESS 0xA040
52 #define MAT_SHINSTRENGTH 0xA041
53 #define MAT_TRANSPARENCY 0xA050
54 #define MAT_DOUBLESIDED 0xA081
55 #define MAT_SELFILLUM 0xA084
56 #define MAT_MAPTEXTURE1 0xA200
57 #define MAT_MAPOPACITY 0xA210
60 #define MAP_FILENAME 0xA300
61 #define MAP_OPTIONS 0xA351
64 #define KEYFRAME3DS 0xB000
65 #define FRM_AMBIENT 0xB001
66 #define FRM_MESHINFO 0xB002
67 #define FRM_CAMERA 0xB003
68 #define FRM_CAMERATARGET 0xB004
69 #define FRM_OMNILIGHT 0xB005
70 #define FRM_SPOTLIGHTTARGET 0xB006
71 #define FRM_SPOTLIGHT 0xB007
72 #define FRM_FRAMES 0xB008
73 #define FRM_PARAM 0xB010
74 #define FRM_DUMMYNAME 0xB011
75 #define FRM_PIVOT 0xB013
76 #define FRM_TRACKPOS 0xB020
77 #define FRM_TRACKROT 0xB021
78 #define FRM_TRACKSCALE 0xB022
79 #define FRM_TRACKFOV 0xB023
80 #define FRM_TRACKROLL 0xB024
81 #define FRM_TRACKCOLOR 0xB025
82 #define FRM_TRACKMORPH 0xB026 // What does this do?
83 #define FRM_TRACKHOTSPOT 0xB027
84 #define FRM_TRACKFALLOFF 0xB028
85 #define FRM_TRACKHIDE 0xB029
86 #define FRM_HIERARCHY 0xB030
88 typedef struct FileInfo FileInfo;
101 DisplaySystem displaySystem;
110 char textureDirectory[MAX_DIRECTORY];
113 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
114 | (((unsigned short)(word) & 0xff00) >> 8)
116 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
117 | (((unsigned int)(dword) & 0x0000ff00) << 8) \
118 | (((unsigned int)(dword) & 0x00ff0000) >> 8) \
119 | (((unsigned int)(dword) & 0xff000000) >> 24))
121 #ifndef __BIG_ENDIAN__
122 #define BIGENDSWAP_WORD(word)
123 #define BIGENDSWAP_DWORD(dword)
125 #define BIGENDSWAP_WORD(word) (*(uint16 *)(&(word))) = SWAP_WORD((*(uint16 *)(&(word))));
126 #define BIGENDSWAP_DWORD(dword) (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
129 // Zero Terminated String
130 static int ReadASCIIZ(File f, char ** string)
132 // *** Read String ***
134 char temp[1024] = "";
138 if(!temp[c++]) break;
140 *string = new char[c];
142 strcpy(*string, temp);
146 static float ReadFloat(File f)
149 f.Read(&floatValue, sizeof(float), 1);
150 BIGENDSWAP_DWORD(floatValue);
154 static uint16 ReadWORD(File f)
157 f.Read(&wordValue, sizeof(uint16), 1);
158 BIGENDSWAP_WORD(wordValue);
162 static uint ReadDWORD(File f)
165 f.Read(&dwordValue, sizeof(uint), 1);
166 BIGENDSWAP_DWORD(dwordValue);
171 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
173 for(;info->pos < info->end;)
175 FileInfo childInfo = *info;
178 childInfo.parent = info;
180 info->f.Seek(info->pos, start);
181 childInfo.chunkId = ReadWORD(info->f);
182 length = ReadDWORD(info->f);
184 childInfo.pos += sizeof(uint16) + sizeof(uint);
185 childInfo.end = info->pos + length;
187 if(!chunkParser(&childInfo, data))
190 info->pos = childInfo.end;
196 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
198 if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
201 info->f.Getc(&value); rgb->r = value / 255.0f;
202 info->f.Getc(&value); rgb->g = value / 255.0f;
203 info->f.Getc(&value); rgb->b = value / 255.0f;
205 else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
207 rgb->r = ReadFloat(info->f);
208 rgb->g = ReadFloat(info->f);
209 rgb->b = ReadFloat(info->f);
214 static bool Read3DVertex(File f, Vector3Df vertex)
216 vertex.x = ReadFloat(f);
217 vertex.y = ReadFloat(f);
218 vertex.z = ReadFloat(f);
222 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
224 if(info->chunkId == AMOUNT_OF)
225 *amountOf = ReadWORD(info->f);
238 VertexConfig * config;
241 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
244 Face * faces = info->faces;
245 //int nFaces = info->nFaces;
246 int nVertices = mesh.nVertices;
251 VertexConfigList * configLists = new0 VertexConfigList[nVertices];
253 nNewVertices = nVertices;
254 for(c = 0; c<info->nFaces; c++)
256 Face * face = &faces[c];
261 int index = face->indices[i];
263 VertexConfigList * configList = &configLists[index];
264 VertexConfig * config = null;
265 for(v = 0; v<configList->numConfig; v++)
267 uint smoothGroups = configList->config[v].smoothGroups;
268 if(smoothGroups == face->smoothGroups)
270 config = &configList->config[v];
273 else if(smoothGroups && face->smoothGroups)
276 for(g = 0; g<32; g++)
277 if(smoothGroups & (1<<g) && face->smoothGroups & (1<<g))
279 config = &configList->config[v];
280 config->smoothGroups |= face->smoothGroups;
286 if(!config || !face->smoothGroups)
288 // Duplicate the vertex and make the face use it
289 if(configList->numConfig)
290 index = nNewVertices++;
291 face->indices[i] = (uint16)index;
294 configList->config = renew configList->config VertexConfig[configList->numConfig + 1];
295 config = &configList->config[configList->numConfig++];
296 config->index = index;
297 config->smoothGroups = face->smoothGroups;
302 face->indices[i] = (uint16)config->index;
307 // Allocate some extra vertices
309 Vector3Df * oldVertices = mesh.vertices;
310 Pointf * oldTexCoords = mesh.texCoords;
312 *((void **)&mesh.vertices) = null;
313 *((void **)&mesh.texCoords) = null;
314 *((int *)&mesh.nVertices) = 0;
316 mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
318 // Fill in the new vertices
319 for(index = 0; index<nVertices; index++)
322 VertexConfigList * configList = &configLists[index];
323 for(v = 0; v<configList->numConfig; v++)
325 VertexConfig * config = &configList->config[v];
327 if(config->smoothGroups)
332 mesh.vertices[config->index] = oldVertices[index]; //mesh.vertices[index];
334 mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
339 mesh.vertices[config->index] = oldVertices[index];
341 mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
343 normal = &mesh.normals[config->index];
352 numShared = new0 int[nNewVertices];
354 for(c = 0; c<info->nFaces; c++)
356 Face * face = &faces[c];
359 Vector3Df planeNormal;
360 plane.FromPointsf(mesh.vertices[face->oldIndices[2]],
361 mesh.vertices[face->oldIndices[1]],
362 mesh.vertices[face->oldIndices[0]]);
363 planeNormal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
364 if(face->smoothGroups)
368 int index = face->indices[i];
369 Vector3Df * normal = &mesh.normals[index];
370 normal->Add(normal, planeNormal);
378 int index = face->oldIndices[i];
379 int newIndex = face->indices[i];
380 mesh.normals[newIndex] = planeNormal;
382 if(index != newIndex)
384 mesh.vertices[newIndex] = mesh.vertices[index];
386 mesh.texCoords[newIndex] = mesh.texCoords[index];
388 numShared[newIndex]++;
392 for(index = 0; index<nNewVertices; index++)
394 Vector3Df * normal = &mesh.normals[index];
395 normal->Scale(normal, 1.0f / numShared[index]);
396 normal->Normalize(normal);
399 mesh.Unlock({ normals = true });
401 // Free all the temporary stuff
404 for(index = 0; index < nVertices; index++)
406 VertexConfigList * configList = &configLists[index];
407 if(configList->config) delete configList->config;
415 static bool ReadSmoothing(FileInfo * info, Object object)
417 Mesh mesh = object.mesh;
418 switch(info->chunkId)
423 for(c = 0; c<info->nFaces; c++)
424 info->faces[c].smoothGroups = ReadDWORD(info->f);
431 static bool ReadFacesListChunks(FileInfo * info, Object object)
433 DisplaySystem displaySystem = info->displaySystem;
434 Mesh mesh = object.mesh;
435 switch(info->chunkId)
442 ReadASCIIZ(info->f, &name);
443 mat = displaySystem.GetMaterial(name);
446 if(mat.flags.translucent)
449 uint16 count = ReadWORD(info->f);
450 mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + count];
451 for(c = 0; c<count; c++)
453 uint16 face = ReadWORD(info->f);
454 PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
456 if(mesh.AllocatePrimitive(triangle, triangles, 3))
458 triangle->indices[0] = info->faces[face].indices[0];
459 triangle->indices[1] = info->faces[face].indices[1];
460 triangle->indices[2] = info->faces[face].indices[2];
461 triangle->middle.Add(mesh.vertices[triangle->indices[0]], mesh.vertices[triangle->indices[1]]);
462 triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices[2]]);
463 triangle->plane.FromPointsf(
464 mesh.vertices[triangle->indices[2]],
465 mesh.vertices[triangle->indices[1]],
466 mesh.vertices[triangle->indices[0]]);
468 mesh.UnlockPrimitive(triangle);
470 triangle->middle.x /= 3;
471 triangle->middle.y /= 3;
472 triangle->middle.z /= 3;
474 triangle->material = mat;
476 info->faces[face].done = (byte)bool::true;
478 object.flags.translucent = true;
482 PrimitiveGroup group;
483 uint16 count = ReadWORD(info->f);
484 group = mesh.AddPrimitiveGroup(triangles, count * 3);
488 group.material = mat;
489 for(c = 0; c<count; c++)
491 uint16 face = ReadWORD(info->f);
492 if(object.flags.flipWindings)
494 group.indices[c*3] = info->faces[face].indices[2];
495 group.indices[c*3+1] = info->faces[face].indices[1];
496 group.indices[c*3+2] = info->faces[face].indices[0];
500 group.indices[c*3] = info->faces[face].indices[0];
501 group.indices[c*3+1] = info->faces[face].indices[1];
502 group.indices[c*3+2] = info->faces[face].indices[2];
504 info->faces[face].done = (byte)bool::true;
506 mesh.UnlockPrimitiveGroup(group);
517 static bool ReadTriMesh(FileInfo * info, Object object)
519 Mesh mesh = object.mesh;
520 switch(info->chunkId)
525 uint16 nVertices = ReadWORD(info->f);
526 //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
527 *((int *) &mesh.nVertices) = nVertices;
528 *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
531 for(c = 0; c<mesh.nVertices; c++)
534 Read3DVertex(info->f, vertex);
535 mesh.vertices[c].x = vertex.x;
536 mesh.vertices[c].y =-vertex.z;
537 mesh.vertices[c].z = vertex.y;
544 case TRI_MAPPINGCOORS:
547 uint16 count = ReadWORD(info->f);
548 count = (uint16)Min(mesh.nVertices, count);
550 //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
551 *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
552 mesh.flags.texCoords1 = true;
555 for(c = 0; c<count; c++)
557 mesh.texCoords[c].x = ReadFloat(info->f);
558 mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
572 info->nFaces = nFaces = ReadWORD(info->f);
573 info->pos += sizeof(uint16);
575 info->faces = new0 Face[nFaces];
576 for(c = 0; c<nFaces; c++)
581 info->faces[c].oldIndices[i] =
582 info->faces[c].indices[i] = ReadWORD(info->f);
585 info->pos += 4*sizeof(uint16);
588 ReadChunks(ReadSmoothing, info, object);
591 ComputeNormals(mesh, info, object);
593 ReadChunks(ReadFacesListChunks, info, object);
595 // Add faces without a material all together
597 for(c = 0; c<nFaces; c++)
598 if(!info->faces[c].done)
602 PrimitiveGroup group = mesh.AddPrimitiveGroup(triangles, count * 3);
605 for(c = 0; c<nFaces; c++)
606 if(!info->faces[c].done)
608 group.indices[c*3] = info->faces[c].indices[0];
609 group.indices[c*3+1] = info->faces[c].indices[1];
610 group.indices[c*3+2] = info->faces[c].indices[2];
612 mesh.UnlockPrimitiveGroup(group);
619 mesh.ComputeNormals();
621 if(object.flags.flipWindings)
623 if(mesh.Lock({ normals = true }))
625 for(c = 0; c<mesh.nVertices; c++)
627 mesh.normals[c].x *= -1;
628 mesh.normals[c].y *= -1;
629 mesh.normals[c].z *= -1;
631 mesh.Unlock({ normals = true });
635 // could use this instead? : mesh.ApplyTranslucency(object);
641 Vector3Df xAxis, yAxis, zAxis, center;
644 Matrix inverse/*, source = { 0 }*/;
647 Read3DVertex(info->f, xAxis);
648 Read3DVertex(info->f, yAxis);
649 Read3DVertex(info->f, zAxis);
650 Read3DVertex(info->f, center);
652 scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
653 scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
654 scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
656 // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
659 source.m[0][0] = xAxis.x; source.m[0][1] = -xAxis.z; source.m[0][2] = xAxis.y;
660 source.m[1][0] =-zAxis.x; source.m[1][1] = zAxis.z; source.m[1][2] = -zAxis.y;
661 source.m[2][0] = yAxis.x; source.m[2][1] = -yAxis.z; source.m[2][2] = yAxis.y;
662 source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
664 inverse.Inverse(source);
667 object.flags.flipWindings = false;
669 xAxis.Normalize(xAxis);
670 yAxis.Normalize(yAxis);
671 zAxis.Normalize(zAxis);
673 orth.CrossProduct(yAxis, zAxis);
674 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
675 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
676 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
678 object.flags.flipWindings ^= true;
682 orth.CrossProduct(zAxis, xAxis);
683 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
684 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
685 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
687 object.flags.flipWindings ^= true;
691 orth.CrossProduct(xAxis, yAxis);
692 if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
693 (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
694 (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
696 object.flags.flipWindings ^= true;
703 rotation.m[0][0] = xAxis.x;
704 rotation.m[0][1] =-xAxis.z;
705 rotation.m[0][2] = xAxis.y;
706 rotation.m[0][3] = 0;
707 rotation.m[1][0] =-zAxis.x;
708 rotation.m[1][1] = zAxis.z;
709 rotation.m[1][2] =-zAxis.y;
710 rotation.m[1][3] = 0;
711 rotation.m[2][0] = yAxis.x;
712 rotation.m[2][1] =-yAxis.z;
713 rotation.m[2][2] = yAxis.y;
714 rotation.m[2][3] = 0;
715 rotation.m[3][0] = 0;
716 rotation.m[3][1] = 0;
717 rotation.m[3][2] = 0;
718 rotation.m[3][3] = 1;
722 inverse.Transpose(rotation);
724 temp.Translate(-center.x, center.z, -center.y);
725 temp2.Multiply(temp, inverse);
726 temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
730 object.transform.scaling = scaling;
731 // TODO: Improve language to support deep properties on non constant functions
732 // object.transform.orientation.RotationMatrix(rotation);
734 Quaternion orientation;
735 orientation.RotationMatrix(rotation);
736 object.transform.orientation = orientation;
738 object.transform.position = { center.x, -center.z, center.y };
741 // Localize All Vertices
742 for(c = 0; c<mesh.nVertices; c++)
744 Vector3Df vertex = mesh.vertices[c];
746 mesh.vertices[c].MultMatrix(vertex, inverse);
748 mesh.vertices[c].x -= object.pivot.x;
749 mesh.vertices[c].y -= object.pivot.y;
750 mesh.vertices[c].z -= object.pivot.z;
759 static bool ReadMap(FileInfo * info, Material mat)
761 DisplaySystem displaySystem = info->displaySystem;
762 switch(info->chunkId)
766 Bitmap opacityMap = null;
768 char location[MAX_LOCATION];
770 ReadASCIIZ(info->f, &name);
773 strcpy(location, info->textureDirectory);
774 PathCat(location, name);
778 mat.baseMap = displaySystem.GetTexture(name);
781 mat.baseMap = Bitmap { };
782 if(!mat.baseMap.Load(location, null, null) ||
783 !mat.baseMap.Convert(null, pixelFormat888, null) ||
784 !displaySystem.AddTexture(name, mat.baseMap))
788 opacityMap = mat.baseMap;
791 else if(info->parent->chunkId == MAT_MAPOPACITY)
793 opacityMap = Bitmap { };
794 if(!opacityMap.Load(location, null, null) ||
795 !opacityMap.Convert(null, pixelFormat888, null))
803 if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap)
806 ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
808 for(c = 0; c < opacityMap.width * opacityMap.height; c++)
809 picture[c] = ColorAlpha { ((ColorAlpha *)opacityMap.picture)[c].color.r, picture[c].color };
811 mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
812 mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
814 if(opacityMap != mat.baseMap)
821 uint16 options = ReadWORD(info->f);
822 if(!(options & 0x10)) mat.flags.tile = true;
829 static bool ReadMaterial(FileInfo * info, Material mat)
831 switch(info->chunkId)
835 ReadASCIIZ(info->f, &mat.name);
838 case MAT_TRANSPARENCY:
841 ReadChunks(ReadAmountOf, info, &transparency);
842 mat.opacity = 1.0f - transparency / 100.0f;
843 if(mat.opacity < 1.0)
844 mat.flags.translucent = true;
849 ReadChunks(ReadRGB, info, &mat.diffuse);
850 ReadChunks(ReadRGB, info, &mat.diffuse);
855 ReadChunks(ReadRGB, info, &mat.ambient);
856 ReadChunks(ReadRGB, info, &mat.ambient);
861 ReadChunks(ReadRGB, info, &mat.specular);
862 ReadChunks(ReadRGB, info, &mat.specular);
868 ReadChunks(ReadAmountOf, info, &emissive);
869 mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
870 mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
871 mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
874 case MAT_SHINSTRENGTH:
877 ReadChunks(ReadAmountOf, info, &shininess);
878 mat.specular.r *= shininess / 100.0f;
879 mat.specular.g *= shininess / 100.0f;
880 mat.specular.b *= shininess / 100.0f;
886 ReadChunks(ReadAmountOf, info, &power);
890 case MAT_MAPTEXTURE1:
891 ReadChunks(ReadMap, info, mat);
894 ReadChunks(ReadMap, info, mat);
895 mat.flags.translucent = true;
897 case MAT_DOUBLESIDED:
898 mat.flags.doubleSided = true;
905 static bool ReadLight(FileInfo * info, Object object)
907 Mesh mesh = object.mesh;
908 Light * light = &object.light;
909 switch(info->chunkId)
914 case RGB_FLOAT_GAMMA:
915 ReadRGB(info, &light->diffuse);
916 light->specular = light->diffuse;
921 char targetName[MAXNAMELEN];
923 strcpy(targetName, object.name);
924 strcat(targetName, ".target");
926 light->flags.omni = false;
927 light->flags.spot = true;
930 target.name = CopyString(targetName);
931 info->rootObject.children.AddName(target);
932 target.parent = info->rootObject;
934 light->target = target;
936 target.transform.position.x = ReadFloat(info->f);
937 target.transform.position.z = ReadFloat(info->f);
938 target.transform.position.y =-ReadFloat(info->f);
940 light->hotSpot = ReadFloat(info->f);
941 light->fallOff = ReadFloat(info->f);
946 light->flags.off = true;
949 case LIT_ATTENUATION:
953 d = 300, small = 0.001,
955 d * (Kl + Kq * d) = (1 / small) - Kc
957 { Kc, Kl, Kq, small, d });
960 light->flags.attenuation = true;
963 #define MINLIGHT 0.08
964 light->Kq = 1/(light->end*light->end*MINLIGHT);
967 #define MINLIGHT 0.15f
968 // #define MINLIGHT 0.1
969 light->Kl = (float)(1/(light->end*MINLIGHT));
975 light->start = ReadFloat(info->f);
980 light->end = ReadFloat(info->f);
985 light->multiplier = ReadFloat(info->f);
993 static bool ReadCamera(FileInfo * info, Object object)
995 Mesh mesh = object.mesh;
996 switch(info->chunkId)
1004 Camera camera = object.camera;
1005 float nearRange = ReadFloat(info->f);
1006 float farRange = ReadFloat(info->f);
1008 camera.zMin = Max(0.1, nearRange);
1009 camera.zMax = farRange;
1018 static bool ReadEditObject(FileInfo * info, char * name)
1020 DisplaySystem displaySystem = info->displaySystem;
1021 switch(info->chunkId)
1025 Object object = info->rootObject.Find(name);
1028 object = Object { };
1030 info->rootObject.children.AddName(object);
1031 object.parent = info->rootObject;
1035 object.InitializeMesh(displaySystem);
1036 ReadChunks(ReadTriMesh, info, object);
1037 object.flags.mesh = true;
1038 object.mesh.Unlock(0);
1043 Object object = info->rootObject.Find(name);
1049 object = Object { };
1051 info->rootObject.children.AddName(object);
1052 object.parent = info->rootObject;
1056 object.flags.light = true;
1058 light = &object.light;
1059 light->lightObject = object;
1060 light->flags.omni = true;
1061 light->multiplier = 1.0f;
1063 // This is not used?
1064 Read3DVertex(info->f, position);
1065 light->direction = { position.x, position.y, position.z };
1066 info->pos += sizeof(float) * 3;
1068 ReadChunks(ReadLight, info, object);
1073 char targetName[MAXNAMELEN];
1074 Object object = info->rootObject.Find(name);
1077 float bankAngle, focus;
1080 strcpy(targetName, name);
1081 strcat(targetName, ".target");
1085 object = Object { };
1087 info->rootObject.children.AddName(object);
1089 object.parent = info->rootObject;
1090 object.camera = Camera { };
1091 object.camera.type = lookAtObject;
1093 target = Object { };
1094 target.name = CopyString(targetName);
1095 info->rootObject.children.AddName(target);
1096 target.parent = info->rootObject;
1101 object.flags.camera = true;
1102 object.cameraTarget = target;
1104 camera = object.camera;
1105 camera.cameraObject = object;
1106 camera.target = target;
1108 //Read3DVertex(info->f, camera.position);
1109 object.transform.position.x = ReadFloat(info->f);
1110 object.transform.position.z = ReadFloat(info->f);
1111 object.transform.position.y =-ReadFloat(info->f);
1113 info->pos += sizeof(float) * 3;
1114 //Read3DVertex(info->f, object.cameraTarget.position);
1115 target.transform.position.x = ReadFloat(info->f);
1116 target.transform.position.z = ReadFloat(info->f);
1117 target.transform.position.y =-ReadFloat(info->f);
1119 info->pos += sizeof(float) * 3;
1120 bankAngle = ReadFloat(info->f);
1121 info->pos += sizeof(float);
1122 focus = ReadFloat(info->f);
1123 info->pos += sizeof(float);
1125 mm = (focus - 5.05659508373109) / 1.13613250717301;
1126 camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1128 ReadChunks(ReadCamera, info, object);
1131 case OBJ_HIDDEN: break;
1138 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1140 static bool ReadEditChunks(FileInfo * info, void * data)
1142 switch(info->chunkId)
1146 // Read the ambient color
1147 ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1152 Material material { };
1154 ReadChunks(ReadMaterial, info, material);
1156 mat = info->displaySystem.AddNamedMaterial(material.name);
1159 if(material.baseMap)
1160 material.baseMap.MakeMipMaps(info->displaySystem);
1161 // COPY_NITEM(mat, material);
1162 CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1166 delete material.baseMap;
1168 delete material.name;
1175 info->pos += ReadASCIIZ(info->f, &name);
1176 ReadChunks(ReadEditObject, info, name);
1183 struct ObjectInfoBlock
1193 // Key Framer Chunks
1195 #define ACCFLAG_TENSION 0x00000001
1196 #define ACCFLAG_CONTINUITY 0x00000002
1197 #define ACCFLAG_BIAS 0x00000004
1198 #define ACCFLAG_EASETO 0x00000008
1199 #define ACCFLAG_EASEFROM 0x00000010
1201 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1203 switch(info->chunkId)
1207 uint16 flags1, flags2;
1208 ReadASCIIZ(info->f, &block->name);
1209 flags1 = ReadWORD(info->f);
1210 flags2 = ReadWORD(info->f);
1211 block->parent = ReadWORD(info->f);
1215 ReadASCIIZ(info->f, &block->dummyName);
1218 Read3DVertex(info->f, block->pivot);
1221 block->hierarchy = ReadWORD(info->f);
1225 case FRM_TRACKSCALE:
1228 case FRM_TRACKCOLOR:
1229 case FRM_TRACKHOTSPOT:
1230 case FRM_TRACKFALLOFF:
1232 FrameTrack track { };
1239 block->tracks.Add(track);
1241 flags = ReadWORD(info->f);
1243 info->f.Read(unknown, sizeof(unknown), 1);
1245 track.numKeys = ReadDWORD(info->f);
1247 switch(info->chunkId)
1249 case FRM_TRACKPOS: track.type.type = position; break;
1250 case FRM_TRACKROT: track.type.type = rotation; break;
1251 case FRM_TRACKSCALE: track.type.type = scaling; break;
1252 case FRM_TRACKROLL: track.type.type = roll; break;
1253 case FRM_TRACKFOV: track.type.type = fov; break;
1254 case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1255 case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1256 case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1258 if((flags & 0x0003) == 3)
1259 track.type.loop = true;
1261 track.keys = new0 FrameKey[track.numKeys];
1262 for(c = 0; c<track.numKeys; c++)
1264 uint16 accelerationFlags;
1265 FrameKey * key = track.keys + c;
1267 key->frame = ReadDWORD(info->f);
1268 accelerationFlags = ReadWORD(info->f);
1270 if(accelerationFlags & ACCFLAG_TENSION)
1271 key->tension = ReadFloat(info->f);
1272 if(accelerationFlags & ACCFLAG_CONTINUITY)
1273 key->continuity = ReadFloat(info->f);
1274 if(accelerationFlags & ACCFLAG_BIAS)
1275 key->bias = ReadFloat(info->f);
1276 if(accelerationFlags & ACCFLAG_EASETO)
1277 key->easeTo = ReadFloat(info->f);
1278 if(accelerationFlags & ACCFLAG_EASEFROM)
1279 key->easeFrom = ReadFloat(info->f);
1281 switch(info->chunkId)
1286 Read3DVertex(info->f, position);
1287 key->position = { position.x, -position.z, position.y };
1293 Angle angle = ReadFloat(info->f);
1294 Vector3Df fixedAxis;
1296 Read3DVertex(info->f, axis);
1297 fixedAxis.x = axis.x;
1298 fixedAxis.y = -axis.z;
1299 fixedAxis.z = axis.y;
1303 Quaternion rotation;
1304 rotation.RotationAxis(fixedAxis, angle);
1305 key->orientation.Multiply((key - 1)->orientation, rotation);
1308 key->orientation.RotationAxis(fixedAxis, angle);
1311 case FRM_TRACKSCALE:
1314 Read3DVertex(info->f, scaling);
1315 key->scaling = { scaling.x, scaling.z, scaling.y };
1320 key->fov = ReadFloat(info->f);
1325 key->roll = -ReadFloat(info->f);
1328 case FRM_TRACKCOLOR:
1330 FileInfo childInfo = *info;
1331 childInfo.chunkId = RGB_FLOAT;
1332 ReadRGB(&childInfo, &key->color);
1335 case FRM_TRACKHOTSPOT:
1337 key->hotSpot = ReadFloat(info->f);
1340 case FRM_TRACKFALLOFF:
1342 key->fallOff = ReadFloat(info->f);
1354 static Object FindObjectID(Object object, int id)
1356 Object result = null;
1358 for(child = object.children.first; child; child = child.next)
1360 if(child.flags.hierarchy == (uint16) id)
1367 result = FindObjectID(child, id);
1374 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1376 switch(info->chunkId)
1380 ObjectInfoBlock block { };
1383 ReadChunks(ReadFrameInfoBlock, info, &block);
1385 if(block.dummyName && block.dummyName[0])
1387 if(!strcmp(block.name, "$$$DUMMY"))
1389 object = Object { };
1390 object.name = block.dummyName;
1391 info->rootObject.children.AddName(object);
1392 object.transform.scaling = { 1,1,1 };
1396 Object model = info->rootObject.Find(block.name);
1399 object = Object { };
1400 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1401 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1402 object.flags = model.flags;
1403 object.flags.ownMesh = false;
1404 object.mesh = model.mesh;
1406 object.min = model.min;
1407 object.max = model.max;
1408 object.radius = model.radius;
1410 object.transform = model.transform;
1411 info->rootObject.children.AddName(object);
1413 delete block.dummyName;
1415 object.parent = info->rootObject;
1418 object = info->rootObject.Find(block.name);
1422 Mesh mesh = object.mesh;
1423 object.flags.hierarchy = block.hierarchy + 1;
1424 if(block.parent != -1)
1426 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1429 object.parent.children.Remove(object);
1430 parent.children.AddName(object);
1431 object.parent = parent;
1434 object.pivot.x = block.pivot.x;
1435 object.pivot.y =-block.pivot.z;
1436 object.pivot.z = block.pivot.y;
1438 if(mesh && object.flags.ownMesh)
1440 if(mesh.Lock({ vertices = true }))
1443 // Take pivot into account
1444 for(c = 0; c<mesh.nVertices; c++)
1446 mesh.vertices[c].x -= object.pivot.x;
1447 mesh.vertices[c].y -= object.pivot.y;
1448 mesh.vertices[c].z -= object.pivot.z;
1450 mesh.Unlock({ vertices = true });
1454 object.tracks = block.tracks;
1455 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1456 object.endFrame = info->rootObject.endFrame;
1459 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1465 ObjectInfoBlock block { };
1468 ReadChunks(ReadFrameInfoBlock, info, &block);
1470 if(block.dummyName && block.dummyName[0])
1472 if(!strcmp(block.name, "$$$DUMMY"))
1474 object = Object { };
1475 object.name = block.dummyName;
1476 info->rootObject.children.AddName(object);
1477 object.transform.scaling = { 1, 1, 1 };
1478 object.flags.camera = true;
1482 Object model = info->rootObject.Find(block.name);
1485 object = Object { };
1486 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1487 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1488 object.flags = model.flags;
1489 object.flags.ownMesh = false;
1490 object.camera = model.camera;
1491 object.flags.camera = true;
1492 info->rootObject.children.AddName(object);
1494 delete block.dummyName;
1496 object.parent = info->rootObject;
1499 object = info->rootObject.Find(block.name);
1503 object.flags.hierarchy = block.hierarchy + 1;
1504 if(block.parent != -1)
1506 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1509 object.parent.children.Remove(object);
1510 parent.children.AddName(object);
1511 object.parent = parent;
1514 object.pivot.x = block.pivot.x;
1515 object.pivot.y =-block.pivot.z;
1516 object.pivot.z = block.pivot.y;
1518 object.tracks = block.tracks;
1519 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1520 object.endFrame = info->rootObject.endFrame;
1523 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1527 case FRM_CAMERATARGET:
1529 ObjectInfoBlock block { };
1531 char targetName[MAXNAMELEN];
1533 ReadChunks(ReadFrameInfoBlock, info, &block);
1535 strcpy(targetName, block.name);
1536 strcat(targetName, ".target");
1538 if(block.dummyName && block.dummyName[0])
1540 if(!strcmp(block.name, "$$$DUMMY"))
1542 object = Object { };
1543 object.name = block.dummyName;
1544 info->rootObject.children.AddName(object);
1545 object.transform.scaling = { 1,1,1 };
1549 Object model = info->rootObject.Find(targetName);
1552 object = Object { };
1553 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1554 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1555 object.flags = model.flags;
1556 object.flags.ownMesh = false;
1557 object.camera = model.camera;
1558 info->rootObject.children.AddName(object);
1560 delete block.dummyName;
1562 object.parent = info->rootObject;
1565 object = info->rootObject.Find(targetName);
1569 object.flags.hierarchy = block.hierarchy + 1;
1570 if(block.parent != -1)
1572 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1575 object.parent.children.Remove(object);
1576 parent.children.AddName(object);
1577 object.parent = parent;
1580 object.pivot.x = block.pivot.x;
1581 object.pivot.y =-block.pivot.z;
1582 object.pivot.z = block.pivot.y;
1584 object.tracks = block.tracks;
1585 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1586 object.endFrame = info->rootObject.endFrame;
1589 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1595 ObjectInfoBlock block { };
1597 ReadChunks(ReadFrameInfoBlock, info, &block);
1599 info->rootObject.tracks = block.tracks;
1605 ObjectInfoBlock block { };
1608 ReadChunks(ReadFrameInfoBlock, info, &block);
1610 if(block.dummyName && block.dummyName[0])
1612 if(!strcmp(block.name, "$$$DUMMY"))
1614 object = Object { };
1615 object.name = block.dummyName;
1616 info->rootObject.children.AddName(object);
1617 object.transform.scaling = { 1, 1, 1 };
1618 object.flags.light = true;
1622 Object model = info->rootObject.Find(block.name);
1625 object = Object { };
1626 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1627 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1628 object.flags = model.flags;
1629 object.flags.ownMesh = false;
1630 object.light = model.light;
1631 object.flags.light = true;
1632 info->rootObject.children.AddName(object);
1634 delete block.dummyName;
1636 object.parent = info->rootObject;
1639 object = info->rootObject.Find(block.name);
1643 object.flags.hierarchy = block.hierarchy + 1;
1644 if(block.parent != -1)
1646 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1649 object.parent.children.Remove(object);
1650 parent.children.AddName(object);
1651 object.parent = parent;
1654 object.pivot.x = block.pivot.x;
1655 object.pivot.y =-block.pivot.z;
1656 object.pivot.z = block.pivot.y;
1658 object.tracks = block.tracks;
1659 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1660 object.endFrame = info->rootObject.endFrame;
1663 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1667 case FRM_SPOTLIGHTTARGET:
1669 ObjectInfoBlock block { };
1671 char targetName[MAXNAMELEN];
1673 ReadChunks(ReadFrameInfoBlock, info, &block);
1675 strcpy(targetName, block.name);
1676 strcat(targetName, ".target");
1678 if(block.dummyName && block.dummyName[0])
1680 if(!strcmp(block.name, "$$$DUMMY"))
1682 object = Object { };
1683 object.name = block.dummyName;
1684 info->rootObject.children.AddName(object);
1685 object.transform.scaling = { 1,1,1 };
1689 Object model = info->rootObject.Find(targetName);
1692 object = Object { };
1693 object.name = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1694 sprintf(object.name, "%s.%s", model.name, block.dummyName);
1695 object.flags = model.flags;
1696 object.flags.ownMesh = false;
1697 object.light = model.light;
1698 info->rootObject.children.AddName(object);
1700 delete block.dummyName;
1702 object.parent = info->rootObject;
1705 object = info->rootObject.Find(targetName);
1709 object.flags.hierarchy = block.hierarchy + 1;
1710 if(block.parent != -1)
1712 Object parent = FindObjectID(info->rootObject, block.parent + 1);
1715 object.parent.children.Remove(object);
1716 parent.children.AddName(object);
1717 object.parent = parent;
1720 object.pivot.x = block.pivot.x;
1721 object.pivot.y =-block.pivot.z;
1722 object.pivot.z = block.pivot.y;
1724 object.tracks = block.tracks;
1725 *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1726 object.endFrame = info->rootObject.endFrame;
1729 Logf("Object not found while loading animation frame: %s\n", block.name);*/
1735 info->rootObject.startFrame = ReadDWORD(info->f);
1736 info->rootObject.endFrame = ReadDWORD(info->f);
1737 *&(info->rootObject.frame) = info->rootObject.startFrame;
1745 static bool ReadMainChunks(FileInfo * info, void * data)
1747 switch(info->chunkId)
1750 ReadChunks(ReadEditChunks, info, null);
1754 Object object = data;
1755 if(!(object.flags.keysLoaded)) // Don't read key frames on reload
1757 ReadChunks(ReadKeyFrameChunks, info, null);
1758 object.flags.keysLoaded = true;
1766 static bool ReadMain(FileInfo * info, void * data)
1768 switch(info->chunkId)
1771 ReadChunks(ReadMainChunks, info, data);
1777 class Object3DSFormat : ObjectFormat
1779 class_property(extension) = "3ds";
1781 bool Load(Object object, char * fileName, DisplaySystem displaySystem)
1783 bool result = false;
1786 FileInfo info = {0};
1787 info.rootObject = object;
1788 info.displaySystem = displaySystem;
1791 StripLastDirectory(fileName, info.textureDirectory);
1792 info.f = FileOpen(fileName, read);
1795 if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
1797 object.Animate(object.frame);
1798 object.flags.root = true;
1799 object.SetMinMaxRadius(true);
1806 object.Free(displaySystem);