ecere/gfx/3D: Object, 3DS, DisplaySystem: Fixed memory leaks
[sdk] / ecere / src / gfx / 3D / models / Object3DSFormat.ec
1 namespace gfx3D::models;
2
3 import "Object"
4
5 #define MAXNAMELEN   64
6
7 // RGB Chunks
8 #define RGB_FLOAT             0x0010
9 #define RGB_BYTE              0x0011
10 #define RGB_BYTE_GAMMA        0x0012
11 #define RGB_FLOAT_GAMMA       0x0013
12
13 // Amount Of Chunks
14 #define AMOUNT_OF             0x0030
15
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
25
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
33
34 // Light Chunks
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
41
42 // Camera Chunks
43 #define CAM_SEECONE           0x4710
44 #define CAM_RANGES            0x4720
45
46 // Material Chunks
47 #define MAT_NAME              0xA000
48 #define MAT_AMBIENT           0xA010
49 #define MAT_DIFFUSE           0xA020
50 #define MAT_SPECULAR          0xA030
51 #define MAT_SHININESS         0xA040
52 #define MAT_SHINSTRENGTH      0xA041
53 #define MAT_SHIN3PC           0xA042
54 #define MAT_TRANSPARENCY      0xA050
55 #define MAT_XPFALL            0xA052
56 #define MAT_REFBLUR           0xA053
57 #define MAT_SELFILLUM         0xA080
58 #define MAT_DOUBLESIDED       0xA081
59 #define MAT_ADDITIVE          0xA083
60 #define MAT_SELFILPCT         0xA084
61 #define MAT_WIRE              0xA085
62 #define MAT_SUPERSMP          0xA086
63 #define MAT_WIRETHICKNESS     0xA087
64 #define MAT_FACEMAP           0xA088
65 #define MAT_XPFALLIN          0xA08A
66 #define MAT_PHONG             0xA08C
67 #define MAT_WIREABS           0xA08E
68 #define MAT_SHADING           0xA100
69 #define MAT_MAPTEXTURE1       0xA200
70 #define MAT_SPECULARMAP       0xA204
71 #define MAT_MAPOPACITY        0xA210
72 #define MAT_REFLECTIONMAP     0xA220
73 #define MAT_BUMPMAP           0xA230
74
75 // Map Chunks
76 #define MAP_FILENAME          0xA300
77
78 #define MAT_SHININESSMAP      0xA33C
79 #define MAT_EMISSIVEMAP       0xA33D
80 #define MAP_OPTIONS           0xA351
81 #define MAP_1_U_SCALE   0xA354
82 #define MAP_1_V_SCALE   0xA356
83 #define MAP_U_OFFSET    0xA358
84 #define MAP_V_OFFSET    0xA35A
85 #define MAP_ROTATION    0xA35C
86
87 #define MAP_OPTIONS_DECAL           0x0001      // (related to MAP_OPTIONS_DONTTILE)
88 #define MAP_OPTIONS_MIRROR          0x0002
89 #define MAP_OPTIONS_NEGATIVE        0x0008
90 #define MAP_OPTIONS_DONTTILE        0x0010
91 #define MAP_OPTIONS_SUMMEDFILTERING 0x0020
92 #define MAP_OPTIONS_USEALPHA        0x0040
93 #define MAP_OPTIONS_LUMORALPHATINT  0x0080
94 #define MAP_OPTIONS_IGNOREALPHA     0x0100
95 #define MAP_OPTIONS_RGBTINT         0x0200
96
97 #define MAP_FILTERBLUR        0xA353
98 #define MAP_1_U_SCALE         0xA354
99 #define MAP_1_V_SCALE         0xA356
100 #define MAP_U_OFFSET          0xA358
101 #define MAP_V_OFFSET          0xA35A
102 #define MAP_ROTATION          0xA35C
103
104 #define MAP_LUMTINT1          0xA360
105 #define MAP_LUMTINT2          0xA362
106
107 #define MAP_TINTR             0xA364
108 #define MAP_TINTG             0xA366
109 #define MAP_TINTB             0xA368
110
111 // Keyframer Chunks
112 #define KEYFRAME3DS           0xB000
113 #define FRM_AMBIENT           0xB001
114 #define FRM_MESHINFO          0xB002
115 #define FRM_CAMERA            0xB003
116 #define FRM_CAMERATARGET      0xB004
117 #define FRM_OMNILIGHT         0xB005
118 #define FRM_SPOTLIGHTTARGET   0xB006
119 #define FRM_SPOTLIGHT         0xB007
120 #define FRM_FRAMES            0xB008
121 #define FRM_PARAM             0xB010
122 #define FRM_DUMMYNAME         0xB011
123 #define FRM_PIVOT             0xB013
124 #define FRM_TRACKPOS          0xB020
125 #define FRM_TRACKROT          0xB021
126 #define FRM_TRACKSCALE        0xB022
127 #define FRM_TRACKFOV          0xB023
128 #define FRM_TRACKROLL         0xB024
129 #define FRM_TRACKCOLOR        0xB025
130 #define FRM_TRACKMORPH        0xB026   // What does this do?
131 #define FRM_TRACKHOTSPOT      0xB027
132 #define FRM_TRACKFALLOFF      0xB028
133 #define FRM_TRACKHIDE         0xB029
134 #define FRM_HIERARCHY         0xB030
135
136 typedef struct FileInfo FileInfo;
137
138 typedef struct
139 {
140    uint16 indices[3];
141    uint16 oldIndices[3];
142    uint smoothGroups;
143    bool done:1;
144 } Face;
145
146 struct FileInfo
147 {
148    File f;
149    DisplaySystem displaySystem;
150    Object rootObject;
151
152    uint16 chunkId;
153    uint pos, end;
154
155    FileInfo * parent;
156    int nFaces;
157    Face * faces;
158    char textureDirectory[MAX_DIRECTORY];
159 };
160
161 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
162                       | (((unsigned short)(word) & 0xff00) >> 8)
163
164 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
165                          | (((unsigned int)(dword) & 0x0000ff00) <<  8) \
166                          | (((unsigned int)(dword) & 0x00ff0000) >>  8) \
167                          | (((unsigned int)(dword) & 0xff000000) >> 24))
168
169 #ifndef __BIG_ENDIAN__
170 #define BIGENDSWAP_WORD(word)
171 #define BIGENDSWAP_DWORD(dword)
172 #else
173 #define BIGENDSWAP_WORD(word)      (*(uint16 *)(&(word)))  = SWAP_WORD((*(uint16 *)(&(word))));
174 #define BIGENDSWAP_DWORD(dword)    (*(uint *)(&(dword))) = SWAP_DWORD((*(uint *)(&(dword))));
175 #endif
176
177 // Zero Terminated String
178 static int ReadASCIIZ(File f, char ** string)
179 {
180    // *** Read String ***
181    int c;
182    char temp[1024] = "";
183    for(c=0; c<1024;)
184    {
185       f.Getc(&temp[c]);
186       if(!temp[c++]) break;
187    }
188    *string = new char[c];
189    if(*string)
190       strcpy(*string, temp);
191    return c;
192 }
193
194 static float ReadFloat(File f)
195 {
196    float floatValue;
197    f.Read(&floatValue, sizeof(float), 1);
198    BIGENDSWAP_DWORD(floatValue);
199    return floatValue;
200 }
201
202 static uint16 ReadWORD(File f)
203 {
204    uint16 wordValue;
205    f.Read(&wordValue, sizeof(uint16), 1);
206    BIGENDSWAP_WORD(wordValue);
207    return wordValue;
208 }
209
210 static uint ReadDWORD(File f)
211 {
212    uint dwordValue;
213    f.Read(&dwordValue, sizeof(uint), 1);
214    BIGENDSWAP_DWORD(dwordValue);
215    return dwordValue;
216 }
217
218 // Sub Chunks
219 static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileInfo * info, void * data)
220 {
221    for(;info->pos < info->end;)
222    {
223       FileInfo childInfo = *info;
224       uint length;
225
226       childInfo.parent = info;
227
228       info->f.Seek(info->pos, start);
229       childInfo.chunkId = ReadWORD(info->f);
230       length = ReadDWORD(info->f);
231
232       childInfo.pos += sizeof(uint16) + sizeof(uint);
233       childInfo.end = info->pos + length;
234
235       if(!chunkParser(&childInfo, data))
236          return false;
237
238       info->pos = childInfo.end;
239    }
240    return true;
241 }
242
243 // General Chunks
244 static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
245 {
246    if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
247    {
248       byte value;
249       info->f.Getc(&value); rgb->r = value / 255.0f;
250       info->f.Getc(&value); rgb->g = value / 255.0f;
251       info->f.Getc(&value); rgb->b = value / 255.0f;
252    }
253    else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
254    {
255       rgb->r = ReadFloat(info->f);
256       rgb->g = ReadFloat(info->f);
257       rgb->b = ReadFloat(info->f);
258    }
259    return true;
260 }
261
262 static bool Read3DVertex(File f, Vector3Df vertex)
263 {
264    vertex.x = ReadFloat(f);
265    vertex.y = ReadFloat(f);
266    vertex.z = ReadFloat(f);
267    return true;
268 }
269
270 static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
271 {
272    if(info->chunkId == AMOUNT_OF)
273       *amountOf = ReadWORD(info->f);
274    return true;
275 }
276
277 typedef struct
278 {
279    uint smoothGroups;
280    int index;
281 } VertexConfig;
282
283 typedef struct
284 {
285    int numConfig;
286    VertexConfig * config;
287 } VertexConfigList;
288
289 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
290 {
291    int c;
292    Face * faces = info->faces;
293    //int nFaces = info->nFaces;
294    int nVertices = mesh.nVertices;
295    int index;
296    int nNewVertices;
297    int * numShared;
298
299    VertexConfigList * configLists = new0 VertexConfigList[nVertices];
300
301    nNewVertices = nVertices;
302    for(c = 0; c<info->nFaces; c++)
303    {
304       Face * face = &faces[c];
305       int i;
306
307       for(i = 0; i<3; i++)
308       {
309          int index = face->indices[i];
310          int v;
311          VertexConfigList * configList = &configLists[index];
312          VertexConfig * config = null;
313          for(v = 0; v<configList->numConfig; v++)
314          {
315             uint smoothGroups = configList->config[v].smoothGroups;
316             if(smoothGroups == face->smoothGroups)
317             {
318                config = &configList->config[v];
319                break;
320             }
321             else if(smoothGroups && face->smoothGroups)
322             {
323                int g;
324                for(g = 0; g<32; g++)
325                   if(smoothGroups & (1<<g) && face->smoothGroups & (1<<g))
326                   {
327                      config = &configList->config[v];
328                      config->smoothGroups |= face->smoothGroups;
329                      break;
330                   }
331             }
332          }
333
334          if(!config || !face->smoothGroups)
335          {
336             // Duplicate the vertex and make the face use it
337             if(configList->numConfig)
338                index = nNewVertices++;
339             face->indices[i] = (uint16)index;
340             if(!config)
341             {
342                configList->config = renew configList->config VertexConfig[configList->numConfig + 1];
343                config = &configList->config[configList->numConfig++];
344                config->index = index;
345                config->smoothGroups = face->smoothGroups;
346             }
347          }
348          else
349          {
350             face->indices[i] = (uint16)config->index;
351          }
352       }
353    }
354
355    // Allocate some extra vertices
356    {
357       Vector3Df * oldVertices = mesh.vertices;
358       Pointf * oldTexCoords = mesh.texCoords;
359
360       *((void **)&mesh.vertices) = null;
361       *((void **)&mesh.texCoords) = null;
362       *((int *)&mesh.nVertices) = 0;
363
364       mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
365
366       // Fill in the new vertices
367       for(index = 0; index<nVertices; index++)
368       {
369          int v;
370          VertexConfigList * configList = &configLists[index];
371          for(v = 0; v<configList->numConfig; v++)
372          {
373             VertexConfig * config = &configList->config[v];
374             Vector3Df * normal;
375             if(config->smoothGroups)
376             {
377                //if(v > 0)
378                {
379                   // Duplicate vertex
380                   mesh.vertices[config->index] = oldVertices[index]; //mesh.vertices[index];
381                   if(mesh.texCoords)
382                      mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
383                }
384             }
385             else
386             {
387                mesh.vertices[config->index] = oldVertices[index];
388                if(mesh.texCoords)
389                   mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
390             }
391             normal = &mesh.normals[config->index];
392             *normal = { 0,0,0 };
393          }
394       }
395
396       delete oldVertices;
397       delete oldTexCoords;
398    }
399
400    numShared = new0 int[nNewVertices];
401
402    for(c = 0; c<info->nFaces; c++)
403    {
404       Face * face = &faces[c];
405       int i;
406       Plane plane;
407       Vector3Df planeNormal;
408       plane.FromPointsf(mesh.vertices[face->oldIndices[2]],
409                        mesh.vertices[face->oldIndices[1]],
410                        mesh.vertices[face->oldIndices[0]]);
411       planeNormal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
412       if(face->smoothGroups)
413       {
414          for(i = 0; i<3; i++)
415          {
416             int index = face->indices[i];
417             Vector3Df * normal = &mesh.normals[index];
418             normal->Add(normal, planeNormal);
419             numShared[index]++;
420          }
421       }
422       else
423       {
424          for(i = 0; i<3; i++)
425          {
426             int index = face->oldIndices[i];
427             int newIndex = face->indices[i];
428             mesh.normals[newIndex] = planeNormal;
429             // Duplicate vertex
430             if(index != newIndex)
431             {
432                mesh.vertices[newIndex] = mesh.vertices[index];
433                if(mesh.texCoords)
434                   mesh.texCoords[newIndex] = mesh.texCoords[index];
435             }
436             numShared[newIndex]++;
437          }
438       }
439    }
440    for(index = 0; index<nNewVertices; index++)
441    {
442       Vector3Df * normal = &mesh.normals[index];
443       normal->Scale(normal, 1.0f / numShared[index]);
444       normal->Normalize(normal);
445    }
446
447    mesh.Unlock({ normals = true });
448
449    // Free all the temporary stuff
450    if(configLists)
451    {
452       for(index = 0; index < nVertices; index++)
453       {
454          VertexConfigList * configList = &configLists[index];
455          if(configList->config) delete configList->config;
456       }
457       delete configLists;
458    }
459    delete numShared;
460 }
461
462 // Meshes
463 static bool ReadSmoothing(FileInfo * info, Object object)
464 {
465    Mesh mesh = object.mesh;
466    switch(info->chunkId)
467    {
468       case TRI_SMOOTHING:
469       {
470          int c;
471          for(c = 0; c<info->nFaces; c++)
472             info->faces[c].smoothGroups = ReadDWORD(info->f);
473          break;
474       }
475    }
476    return true;
477 }
478
479 static bool ReadFacesListChunks(FileInfo * info, Object object)
480 {
481    DisplaySystem displaySystem = info->displaySystem;
482    Mesh mesh = object.mesh;
483    switch(info->chunkId)
484    {
485       case TRI_MATERIAL:
486       {
487          char * name;
488          Material mat;
489
490          ReadASCIIZ(info->f, &name);
491          mat = displaySystem.GetMaterial(name);
492          if(mat)
493          {
494             if(mat.flags.translucent)
495             {
496                int c;
497                uint16 count = ReadWORD(info->f);
498                mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + count];
499                for(c = 0; c<count; c++)
500                {
501                   uint16 face = ReadWORD(info->f);
502                   PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
503
504                   if(mesh.AllocatePrimitive(triangle, triangles, 3))
505                   {
506                      triangle->indices[0] = info->faces[face].indices[0];
507                      triangle->indices[1] = info->faces[face].indices[1];
508                      triangle->indices[2] = info->faces[face].indices[2];
509                      triangle->middle.Add(mesh.vertices[triangle->indices[0]], mesh.vertices[triangle->indices[1]]);
510                      triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices[2]]);
511                      triangle->plane.FromPointsf(
512                         mesh.vertices[triangle->indices[2]],
513                         mesh.vertices[triangle->indices[1]],
514                         mesh.vertices[triangle->indices[0]]);
515
516                      mesh.UnlockPrimitive(triangle);
517                   }
518                   triangle->middle.x /= 3;
519                   triangle->middle.y /= 3;
520                   triangle->middle.z /= 3;
521
522                   triangle->material = mat;
523
524                   info->faces[face].done = (byte)bool::true;
525                }
526                object.flags.translucent = true;
527             }
528             else
529             {
530                PrimitiveGroup group;
531                uint16 count = ReadWORD(info->f);
532                group = mesh.AddPrimitiveGroup(triangles, count * 3);
533                if(group)
534                {
535                   int c;
536                   group.material = mat;
537                   for(c = 0; c<count; c++)
538                   {
539                      uint16 face = ReadWORD(info->f);
540                      if(object.flags.flipWindings)
541                      {
542                         group.indices[c*3]   = info->faces[face].indices[2];
543                         group.indices[c*3+1] = info->faces[face].indices[1];
544                         group.indices[c*3+2] = info->faces[face].indices[0];
545                      }
546                      else
547                      {
548                         group.indices[c*3]   = info->faces[face].indices[0];
549                         group.indices[c*3+1] = info->faces[face].indices[1];
550                         group.indices[c*3+2] = info->faces[face].indices[2];
551                      }
552                      info->faces[face].done = (byte)bool::true;
553                   }
554                   mesh.UnlockPrimitiveGroup(group);
555                }
556             }
557          }
558          delete name;
559          break;
560       }
561    }
562    return true;
563 }
564
565 static bool ReadTriMesh(FileInfo * info, Object object)
566 {
567    Mesh mesh = object.mesh;
568    switch(info->chunkId)
569    {
570       case TRI_VERTEXL:
571       {
572          int c;
573          uint16 nVertices = ReadWORD(info->f);
574          //if(eMesh_Allocate(mesh, MESH_VERTICES, nVertices, info->display->displaySystem))
575          *((int *) &mesh.nVertices) = nVertices;
576          *((Vector3Df **)&mesh.vertices) = new Vector3Df[mesh.nVertices];
577          if(mesh.vertices)
578          {
579             for(c = 0; c<mesh.nVertices; c++)
580             {
581                Vector3Df vertex;
582                Read3DVertex(info->f, vertex);
583                mesh.vertices[c].x = vertex.x;
584                mesh.vertices[c].y =-vertex.z;
585                mesh.vertices[c].z = vertex.y;
586             }
587          }
588          else
589             return false;
590          break;
591       }
592       case TRI_MAPPINGCOORS:
593       {
594          int c;
595          uint16 count = ReadWORD(info->f);
596          count = (uint16)Min(mesh.nVertices, count);
597
598          //if(eMesh_Allocate(mesh, MESH_TEXCOORDS1, mesh.nVertices, null /*info->display->displaySystem*/))
599          *((Pointf **)&mesh.texCoords) = new Pointf[mesh.nVertices];
600          mesh.flags.texCoords1 = true;
601          if(mesh.texCoords)
602          {
603             for(c = 0; c<count; c++)
604             {
605                mesh.texCoords[c].x = ReadFloat(info->f);
606                mesh.texCoords[c].y = 1.0f - ReadFloat(info->f);
607             }
608          }
609          else
610             return false;
611          break;
612       }
613       case TRI_FACEL1:
614       {
615          int c;
616          uint16 nFaces = 0;
617          int count;
618          uint pos;
619
620          info->nFaces = nFaces = ReadWORD(info->f);
621          info->pos += sizeof(uint16);
622
623          info->faces = new0 Face[nFaces];
624          for(c = 0; c<nFaces; c++)
625          {
626             int i;
627             for(i = 0; i<3; i++)
628             {
629                info->faces[c].oldIndices[i] =
630                info->faces[c].indices[i] = ReadWORD(info->f);
631             }
632             ReadWORD(info->f);
633             info->pos += 4*sizeof(uint16);
634          }
635          pos = info->pos;
636          ReadChunks(ReadSmoothing, info, object);
637          info->pos = pos;
638
639          ComputeNormals(mesh, info, object);
640
641          ReadChunks(ReadFacesListChunks, info, object);
642
643          // Add faces without a material all together
644          count = 0;
645          for(c = 0; c<nFaces; c++)
646             if(!info->faces[c].done)
647                count++;
648          if(count)
649          {
650             PrimitiveGroup group = mesh.AddPrimitiveGroup(triangles, count * 3);
651             if(group)
652             {
653                for(c = 0; c<nFaces; c++)
654                   if(!info->faces[c].done)
655                   {
656                      group.indices[c*3]   = info->faces[c].indices[0];
657                      group.indices[c*3+1] = info->faces[c].indices[1];
658                      group.indices[c*3+2] = info->faces[c].indices[2];
659                   }
660                mesh.UnlockPrimitiveGroup(group);
661             }
662          }
663
664          delete info->faces;
665
666          /*
667          mesh.ComputeNormals();
668
669          if(object.flags.flipWindings)
670          {
671             if(mesh.Lock({ normals = true }))
672             {
673                for(c = 0; c<mesh.nVertices; c++)
674                {
675                   mesh.normals[c].x *= -1;
676                   mesh.normals[c].y *= -1;
677                   mesh.normals[c].z *= -1;
678                }
679                mesh.Unlock({ normals = true });
680             }
681          }*/
682
683          // could use this instead? : mesh.ApplyTranslucency(object);
684          break;
685       }
686       case TRI_LOCAL:
687       {
688          int c;
689          Vector3Df xAxis, yAxis, zAxis, center;
690          Vector3Df scaling;
691          Vector3Df orth;
692          Matrix inverse/*, source = { 0 }*/;
693
694          // Local Axes
695          Read3DVertex(info->f, xAxis);
696          Read3DVertex(info->f, yAxis);
697          Read3DVertex(info->f, zAxis);
698          Read3DVertex(info->f, center);
699
700          scaling.x = (float)sqrt(xAxis.x * xAxis.x + xAxis.y * xAxis.y + xAxis.z * xAxis.z);
701          scaling.y = (float)sqrt(zAxis.x * zAxis.x + zAxis.y * zAxis.y + zAxis.z * zAxis.z);
702          scaling.z = (float)sqrt(yAxis.x * yAxis.x + yAxis.y * yAxis.y + yAxis.z * yAxis.z);
703
704          // Inverse of this doesn't give a good enough result with small numbers (bellrang.3ds)
705 /*
706          source.m[3][3] = 1;
707          source.m[0][0] = xAxis.x; source.m[0][1]  = -xAxis.z;  source.m[0][2] = xAxis.y;
708          source.m[1][0] =-zAxis.x; source.m[1][1]  = zAxis.z;  source.m[1][2] = -zAxis.y;
709          source.m[2][0] = yAxis.x; source.m[2][1]  = -yAxis.z;  source.m[2][2] = yAxis.y;
710          source.m[3][0] = center.x; source.m[3][1] = -center.z; source.m[3][2] = center.y;
711
712          inverse.Inverse(source);
713 */
714
715          object.flags.flipWindings = false;
716
717          xAxis.Normalize(xAxis);
718          yAxis.Normalize(yAxis);
719          zAxis.Normalize(zAxis);
720
721          orth.CrossProduct(yAxis, zAxis);
722          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(xAxis.x)) ||
723             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(xAxis.y)) ||
724             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(xAxis.z)))
725          {
726             object.flags.flipWindings ^= true;
727             xAxis = orth;
728          }
729
730          orth.CrossProduct(zAxis, xAxis);
731          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(yAxis.x)) ||
732             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(yAxis.y)) ||
733             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(yAxis.z)))
734          {
735             object.flags.flipWindings ^= true;
736             yAxis = orth;
737          }
738
739          orth.CrossProduct(xAxis, yAxis);
740          if((Abs(orth.x) > 0.00001 && Sgn(orth.x) != Sgn(zAxis.x)) ||
741             (Abs(orth.y) > 0.00001 && Sgn(orth.y) != Sgn(zAxis.y)) ||
742             (Abs(orth.z) > 0.00001 && Sgn(orth.z) != Sgn(zAxis.z)))
743          {
744             object.flags.flipWindings ^= true;
745             zAxis = orth;
746          }
747
748          {
749             Matrix rotation;
750
751             rotation.m[0][0] = xAxis.x;
752             rotation.m[0][1] =-xAxis.z;
753             rotation.m[0][2] = xAxis.y;
754             rotation.m[0][3] = 0;
755             rotation.m[1][0] =-zAxis.x;
756             rotation.m[1][1] = zAxis.z;
757             rotation.m[1][2] =-zAxis.y;
758             rotation.m[1][3] = 0;
759             rotation.m[2][0] = yAxis.x;
760             rotation.m[2][1] =-yAxis.z;
761             rotation.m[2][2] = yAxis.y;
762             rotation.m[2][3] = 0;
763             rotation.m[3][0] = 0;
764             rotation.m[3][1] = 0;
765             rotation.m[3][2] = 0;
766             rotation.m[3][3] = 1;
767
768             {
769                Matrix temp, temp2;
770                inverse.Transpose(rotation);
771                temp.Identity();
772                temp.Translate(-center.x, center.z, -center.y);
773                temp2.Multiply(temp, inverse);
774                temp2.Scale(1.0f/scaling.x, 1.0f/scaling.y, 1.0f/scaling.z);
775                inverse = temp2;
776             }
777
778             object.transform.scaling = scaling;
779             // TODO: Improve language to support deep properties on non constant functions
780             // object.transform.orientation.RotationMatrix(rotation);
781             {
782                Quaternion orientation;
783                orientation.RotationMatrix(rotation);
784                object.transform.orientation = orientation;
785             }
786             object.transform.position = { center.x, -center.z, center.y };
787          }
788
789          // Localize All Vertices
790          for(c = 0; c<mesh.nVertices; c++)
791          {
792             Vector3Df vertex = mesh.vertices[c];
793
794             mesh.vertices[c].MultMatrix(vertex, inverse);
795
796             mesh.vertices[c].x -= object.pivot.x;
797             mesh.vertices[c].y -= object.pivot.y;
798             mesh.vertices[c].z -= object.pivot.z;
799          }
800          break;
801       }
802    }
803    return true;
804 }
805
806 // Material Library
807 static bool ReadMap(FileInfo * info, Material mat)
808 {
809    DisplaySystem displaySystem = info->displaySystem;
810    switch(info->chunkId)
811    {
812       case MAP_FILENAME:
813       {
814          char * name;
815          char location[MAX_LOCATION];
816
817          ReadASCIIZ(info->f, &name);
818          strlwr(name);
819
820          strcpy(location, info->textureDirectory);
821          PathCat(location, name);
822
823          if(info->parent->chunkId == MAT_BUMPMAP)
824          {
825             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
826             char bumpName[MAX_FILENAME];
827             strcpy(bumpName, "BUMP:");
828             strcat(bumpName, name);
829             if(!mat.bumpMap)
830             {
831                mat.bumpMap = displaySystem.GetTexture(bumpName);
832                if(!mat.bumpMap)
833                {
834                   mat.bumpMap = Bitmap { };
835                   if(!mat.bumpMap.Load(location, null, null) ||
836                      !mat.bumpMap.Convert(null, pixelFormat888, null) ||
837                      !displaySystem.AddTexture(bumpName, mat.bumpMap))
838                      delete mat.bumpMap;
839                   if(mat.bumpMap)
840                   {
841                      ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
842                      int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
843                      int y, x;
844
845                      for(y = 0; y < bh; y++)
846                         for(x = 0; x < bw; x++)
847                         {
848                            uint bc = y * bw + x;
849                            Color color = picture[bc].color;
850                            picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
851                         }
852                   }
853                }
854             }
855          }
856          else
857          {
858             Bitmap opacityMap = null;
859             bool alphaOnly = true;
860             bool translucent = false;
861             if(!mat.baseMap)
862             {
863                mat.baseMap = displaySystem.GetTexture(name);
864                if(!mat.baseMap)
865                {
866                   mat.baseMap = Bitmap { };
867                   if(!mat.baseMap.Load(location, null, null) ||
868                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
869                      !displaySystem.AddTexture(name, mat.baseMap))
870                   {
871                      delete mat.baseMap;
872                   }
873                   opacityMap = mat.baseMap;
874                }
875             }
876             else if(info->parent->chunkId == MAT_MAPOPACITY)
877             {
878                opacityMap = Bitmap { };
879                if(opacityMap.Load(location, null, null))
880                {
881                   if(opacityMap.pixelFormat == pixelFormatRGBA)
882                      alphaOnly = false;
883                   if(!opacityMap.Convert(null, pixelFormat888, null))
884                      delete opacityMap;
885                }
886             }
887
888             if(mat.baseMap)
889             {
890                if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
891                {
892                   ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
893                   ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
894                   uint x, y;
895                   int ow = opacityMap.width, oh = opacityMap.height;
896                   int bw = mat.baseMap.width, bh = mat.baseMap.height;
897
898                   for(y = 0; y < bh; y++)
899                      for(x = 0; x < bw; x++)
900                      {
901                         int bc = ((y % bh) * bw + (x % bw));
902                         int oc = ((y % oh) * bw + (x % ow));
903                         byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
904                         if(alpha && alpha < 255)
905                            translucent = true;
906                         picture[bc] = ColorAlpha { alpha, picture[bc].color };
907                      }
908                }
909                if(translucent)
910                   mat.flags.translucent = true;
911                mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
912                mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
913             }
914             if(opacityMap != mat.baseMap)
915                delete opacityMap;
916          }
917          delete name;
918          break;
919       }
920       case MAP_OPTIONS:
921       {
922          uint16 options = ReadWORD(info->f);
923          if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
924          break;
925       }
926       case MAP_1_U_SCALE:
927          mat.uScale = ReadFloat(info->f);
928          break;
929       case MAP_1_V_SCALE:
930          mat.vScale = ReadFloat(info->f);
931          break;
932       /*
933       case AMOUNT_OF:
934          break;
935       default:
936          PrintLn("Unhandled Map block");
937          break;
938       */
939    }
940    return true;
941 }
942
943 static bool ReadMaterial(FileInfo * info, Material mat)
944 {
945    switch(info->chunkId)
946    {
947       case MAT_NAME:
948       {
949          ReadASCIIZ(info->f, &mat.name);
950          break;
951       }
952       case MAT_TRANSPARENCY:
953       {
954          uint16 transparency;
955          ReadChunks(ReadAmountOf, info, &transparency);
956          mat.opacity = 1.0f - transparency / 100.0f;
957          if(mat.opacity < 1.0)
958             mat.flags.translucent = true;
959          break;
960       }
961       case MAT_DIFFUSE:
962       {
963          ReadChunks(ReadRGB, info, &mat.diffuse);
964          ReadChunks(ReadRGB, info, &mat.diffuse);
965          break;
966       }
967       case MAT_AMBIENT:
968       {
969          ReadChunks(ReadRGB, info, &mat.ambient);
970          ReadChunks(ReadRGB, info, &mat.ambient);
971          break;
972       }
973       case MAT_SPECULAR:
974       {
975          ReadChunks(ReadRGB, info, &mat.specular);
976          ReadChunks(ReadRGB, info, &mat.specular);
977          break;
978       }
979       case MAT_SELFILLUM:
980       {
981          uint16 emissive;
982          ReadChunks(ReadAmountOf, info, &emissive);
983          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
984          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
985          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
986          break;
987       }
988       case MAT_SHINSTRENGTH:
989       {
990          uint16 shininess;
991          ReadChunks(ReadAmountOf, info, &shininess);
992          mat.specular.r *= shininess / 100.0f;
993          mat.specular.g *= shininess / 100.0f;
994          mat.specular.b *= shininess / 100.0f;
995          break;
996       }
997       case MAT_SHININESS:
998       {
999          uint16 power;
1000          ReadChunks(ReadAmountOf, info, &power);
1001          mat.power = power;
1002          break;
1003       }
1004       case MAT_MAPTEXTURE1:
1005          ReadChunks(ReadMap, info, mat);
1006          break;
1007       case MAT_MAPOPACITY:
1008          ReadChunks(ReadMap, info, mat);
1009          break;
1010       case MAT_DOUBLESIDED:
1011          mat.flags.doubleSided = true;
1012          break;
1013       case MAT_BUMPMAP:
1014          ReadChunks(ReadMap, info, mat);
1015          break;
1016       /*
1017       default:
1018          PrintLn("Unhandled MAT type ID", info->chunkId);
1019       */
1020    }
1021    return true;
1022 }
1023
1024 // Lights
1025 static bool ReadLight(FileInfo * info, Object object)
1026 {
1027    Mesh mesh = object.mesh;
1028    Light * light = &object.light;
1029    switch(info->chunkId)
1030    {
1031       case RGB_BYTE:
1032       case RGB_BYTE_GAMMA:
1033       case RGB_FLOAT:
1034       case RGB_FLOAT_GAMMA:
1035          ReadRGB(info, &light->diffuse);
1036          light->specular = light->diffuse;
1037          break;
1038       case LIT_SPOT:
1039       {
1040          Object target;
1041          char targetName[MAXNAMELEN];
1042
1043          strcpy(targetName, object.name);
1044          strcat(targetName, ".target");
1045
1046          light->flags.omni = false;
1047          light->flags.spot = true;
1048
1049          target = Object { };
1050          target.name = CopyString(targetName);
1051          info->rootObject.children.AddName(target);
1052          target.parent = info->rootObject;
1053
1054          light->target = target;
1055
1056          target.transform.position.x = ReadFloat(info->f);
1057          target.transform.position.z = ReadFloat(info->f);
1058          target.transform.position.y =-ReadFloat(info->f);
1059
1060          light->hotSpot = ReadFloat(info->f);
1061          light->fallOff = ReadFloat(info->f);
1062          break;
1063       }
1064       case LIT_ONOFF:
1065       {
1066          light->flags.off = true;
1067          break;
1068       }
1069       case LIT_ATTENUATION:
1070       {
1071          /* solve (
1072             {
1073                d = 300, small = 0.001,
1074                Kl = 0, Kc = 0,
1075                d * (Kl + Kq * d) = (1 / small) - Kc
1076             },
1077             { Kc, Kl, Kq, small, d });
1078          */
1079
1080          light->flags.attenuation = true;
1081
1082          /*
1083          #define MINLIGHT     0.08
1084          light->Kq = 1/(light->end*light->end*MINLIGHT);
1085          */
1086
1087          #define MINLIGHT     0.15f
1088          // #define MINLIGHT     0.1
1089          light->Kl = (float)(1/(light->end*MINLIGHT));
1090
1091          break;
1092       }
1093       case LIT_START:
1094       {
1095          light->start = ReadFloat(info->f);
1096          break;
1097       }
1098       case LIT_END:
1099       {
1100          light->end = ReadFloat(info->f);
1101          break;
1102       }
1103       case LIT_MULTIPLIER:
1104       {
1105          light->multiplier = ReadFloat(info->f);
1106          break;
1107       }
1108    }
1109    return true;
1110 }
1111
1112 // Cameras
1113 static bool ReadCamera(FileInfo * info, Object object)
1114 {
1115    Mesh mesh = object.mesh;
1116    switch(info->chunkId)
1117    {
1118       case CAM_SEECONE:
1119       {
1120          break;
1121       }
1122       case CAM_RANGES:
1123       {
1124          Camera camera = object.camera;
1125          float nearRange = ReadFloat(info->f);
1126          float farRange = ReadFloat(info->f);
1127          /*
1128          camera.zMin = Max(0.1, nearRange);
1129          camera.zMax = farRange;
1130          */
1131          break;
1132       }
1133    }
1134    return true;
1135 }
1136
1137 // Edit Chunks
1138 static bool ReadEditObject(FileInfo * info, char * name)
1139 {
1140    DisplaySystem displaySystem = info->displaySystem;
1141    switch(info->chunkId)
1142    {
1143       case OBJ_TRIMESH:
1144       {
1145          Object object = info->rootObject.Find(name);
1146          if(!object)
1147          {
1148             object = Object { };
1149             object.name = CopyString(name);
1150             info->rootObject.children.AddName(object);
1151             object.parent = info->rootObject;
1152          }
1153          object.InitializeMesh(displaySystem);
1154          ReadChunks(ReadTriMesh, info, object);
1155          object.flags.mesh = true;
1156          object.mesh.Unlock(0);
1157          break;
1158       }
1159       case OBJ_LIGHT:
1160       {
1161          Object object = info->rootObject.Find(name);
1162          Light * light;
1163          Vector3Df position;
1164
1165          if(!object)
1166          {
1167             object = Object { };
1168             object.name = CopyString(name);
1169             info->rootObject.children.AddName(object);
1170             object.parent = info->rootObject;
1171          }
1172          object.flags.light = true;
1173
1174          light = &object.light;
1175          light->lightObject = object;
1176          light->flags.omni = true;
1177          light->multiplier = 1.0f;
1178
1179          // This is not used?
1180          Read3DVertex(info->f, position);
1181          light->direction = { position.x, position.y, position.z };
1182          info->pos += sizeof(float) * 3;
1183
1184          ReadChunks(ReadLight, info, object);
1185          break;
1186       }
1187       case OBJ_CAMERA:
1188       {
1189          char targetName[MAXNAMELEN];
1190          Object object = info->rootObject.Find(name);
1191          Object target;
1192          Camera camera;
1193          float bankAngle, focus;
1194          double mm;
1195
1196          strcpy(targetName, name);
1197          strcat(targetName, ".target");
1198          target = info->rootObject.Find(targetName);
1199
1200          if(!object)
1201          {
1202             object = Object { };
1203             object.name = CopyString(name);
1204             info->rootObject.children.AddName(object);
1205
1206             object.parent = info->rootObject;
1207             object.camera = Camera { };
1208             object.camera.type = lookAtObject;
1209          }
1210
1211          if(!target)
1212          {
1213             target = Object { };
1214             target.name = CopyString(targetName);
1215             info->rootObject.children.AddName(target);
1216             target.parent = info->rootObject;
1217          }
1218
1219          object.flags.camera = true;
1220          object.cameraTarget = target;
1221
1222          camera = object.camera;
1223          camera.cameraObject = object;
1224          camera.target = target;
1225
1226          //Read3DVertex(info->f, camera.position);
1227          object.transform.position.x = ReadFloat(info->f);
1228          object.transform.position.z = ReadFloat(info->f);
1229          object.transform.position.y =-ReadFloat(info->f);
1230
1231          info->pos += sizeof(float) * 3;
1232          //Read3DVertex(info->f, object.cameraTarget.position);
1233          target.transform.position.x = ReadFloat(info->f);
1234          target.transform.position.z = ReadFloat(info->f);
1235          target.transform.position.y =-ReadFloat(info->f);
1236
1237          info->pos += sizeof(float) * 3;
1238          bankAngle = ReadFloat(info->f);
1239          info->pos += sizeof(float);
1240          focus = ReadFloat(info->f);
1241          info->pos += sizeof(float);
1242
1243          mm = (focus - 5.05659508373109) / 1.13613250717301;
1244          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1245
1246          ReadChunks(ReadCamera, info, object);
1247          break;
1248       }
1249       case OBJ_HIDDEN: break;
1250    }
1251    return true;
1252 }
1253
1254 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1255
1256 static bool ReadEditChunks(FileInfo * info, void * data)
1257 {
1258    switch(info->chunkId)
1259    {
1260       case EDIT_AMBIENT:
1261       {
1262          // Read the ambient color
1263          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1264          break;
1265       }
1266       case EDIT_MATERIAL:
1267       {
1268          Material material { };
1269          Material mat;
1270          ReadChunks(ReadMaterial, info, material);
1271
1272          mat = info->displaySystem.AddNamedMaterial(material.name);
1273          if(mat)
1274          {
1275             if(material.baseMap)
1276                material.baseMap.MakeMipMaps(info->displaySystem);
1277             if(material.bumpMap)
1278                material.bumpMap.MakeMipMaps(info->displaySystem);
1279             // COPY_NITEM(mat, material);
1280             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1281          }
1282          else
1283          {
1284             delete material.baseMap;
1285          }
1286          delete material.name;
1287          delete material;
1288          break;
1289       }
1290       case EDIT_OBJECT:
1291       {
1292          char * name;
1293          info->pos += ReadASCIIZ(info->f, &name);
1294          ReadChunks(ReadEditObject, info, name);
1295          delete name;
1296          break;
1297       }
1298    }
1299    return true;
1300 }
1301
1302 struct ObjectInfoBlock
1303 {
1304    OldList tracks;
1305    short hierarchy;
1306    short parent;
1307    char * name;
1308    char * dummyName;
1309    Vector3Df pivot;
1310 };
1311
1312 // Key Framer Chunks
1313
1314 #define ACCFLAG_TENSION    0x00000001
1315 #define ACCFLAG_CONTINUITY 0x00000002
1316 #define ACCFLAG_BIAS       0x00000004
1317 #define ACCFLAG_EASETO     0x00000008
1318 #define ACCFLAG_EASEFROM   0x00000010
1319
1320 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1321 {
1322    switch(info->chunkId)
1323    {
1324       case FRM_PARAM:
1325       {
1326          uint16 flags1, flags2;
1327          ReadASCIIZ(info->f, &block->name);
1328          flags1 = ReadWORD(info->f);
1329          flags2 = ReadWORD(info->f);
1330          block->parent = ReadWORD(info->f);
1331          break;
1332       }
1333       case FRM_DUMMYNAME:
1334          ReadASCIIZ(info->f, &block->dummyName);
1335          break;
1336       case FRM_PIVOT:
1337          Read3DVertex(info->f, block->pivot);
1338          break;
1339       case FRM_HIERARCHY:
1340          block->hierarchy = ReadWORD(info->f);
1341          break;
1342       case FRM_TRACKPOS:
1343       case FRM_TRACKROT:
1344       case FRM_TRACKSCALE:
1345       case FRM_TRACKROLL:
1346       case FRM_TRACKFOV:
1347       case FRM_TRACKCOLOR:
1348       case FRM_TRACKHOTSPOT:
1349       case FRM_TRACKFALLOFF:
1350       {
1351          FrameTrack track { };
1352          if(track)
1353          {
1354             uint16 flags;
1355             byte unknown[8];
1356             uint c;
1357
1358             block->tracks.Add(track);
1359
1360             flags = ReadWORD(info->f);
1361
1362             info->f.Read(unknown, sizeof(unknown), 1);
1363
1364             track.numKeys = ReadDWORD(info->f);
1365
1366             switch(info->chunkId)
1367             {
1368                case FRM_TRACKPOS: track.type.type = position; break;
1369                case FRM_TRACKROT: track.type.type = rotation; break;
1370                case FRM_TRACKSCALE: track.type.type = scaling; break;
1371                case FRM_TRACKROLL: track.type.type = roll; break;
1372                case FRM_TRACKFOV: track.type.type = fov; break;
1373                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1374                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1375                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1376             }
1377             if((flags & 0x0003) == 3)
1378                track.type.loop = true;
1379
1380             track.keys = new0 FrameKey[track.numKeys];
1381             for(c = 0; c<track.numKeys; c++)
1382             {
1383                uint16 accelerationFlags;
1384                FrameKey * key = track.keys + c;
1385
1386                key->frame = ReadDWORD(info->f);
1387                accelerationFlags = ReadWORD(info->f);
1388
1389                if(accelerationFlags & ACCFLAG_TENSION)
1390                   key->tension = ReadFloat(info->f);
1391                if(accelerationFlags & ACCFLAG_CONTINUITY)
1392                   key->continuity = ReadFloat(info->f);
1393                if(accelerationFlags & ACCFLAG_BIAS)
1394                   key->bias = ReadFloat(info->f);
1395                if(accelerationFlags & ACCFLAG_EASETO)
1396                   key->easeTo = ReadFloat(info->f);
1397                if(accelerationFlags & ACCFLAG_EASEFROM)
1398                   key->easeFrom = ReadFloat(info->f);
1399
1400                switch(info->chunkId)
1401                {
1402                   case FRM_TRACKPOS:
1403                   {
1404                      Vector3Df position;
1405                      Read3DVertex(info->f, position);
1406                      key->position = { position.x, -position.z, position.y };
1407                      break;
1408                   }
1409                   case FRM_TRACKROT:
1410                   {
1411                      Vector3Df axis;
1412                      Angle angle = ReadFloat(info->f);
1413                      Vector3Df fixedAxis;
1414
1415                      Read3DVertex(info->f, axis);
1416                      fixedAxis.x = axis.x;
1417                      fixedAxis.y = -axis.z;
1418                      fixedAxis.z = axis.y;
1419
1420                      if(c > 0)
1421                      {
1422                         Quaternion rotation;
1423                         rotation.RotationAxis(fixedAxis, angle);
1424                         key->orientation.Multiply((key - 1)->orientation, rotation);
1425                      }
1426                      else
1427                         key->orientation.RotationAxis(fixedAxis, angle);
1428                      break;
1429                   }
1430                   case FRM_TRACKSCALE:
1431                   {
1432                      Vector3Df scaling;
1433                      Read3DVertex(info->f, scaling);
1434                      key->scaling = { scaling.x, scaling.z, scaling.y };
1435                      break;
1436                   }
1437                   case FRM_TRACKFOV:
1438                   {
1439                      key->fov = ReadFloat(info->f);
1440                      break;
1441                   }
1442                   case FRM_TRACKROLL:
1443                   {
1444                      key->roll = -ReadFloat(info->f);
1445                      break;
1446                   }
1447                   case FRM_TRACKCOLOR:
1448                   {
1449                      FileInfo childInfo = *info;
1450                      childInfo.chunkId = RGB_FLOAT;
1451                      ReadRGB(&childInfo, &key->color);
1452                      break;
1453                   }
1454                   case FRM_TRACKHOTSPOT:
1455                   {
1456                      key->hotSpot = ReadFloat(info->f);
1457                      break;
1458                   }
1459                   case FRM_TRACKFALLOFF:
1460                   {
1461                      key->fallOff = ReadFloat(info->f);
1462                      break;
1463                   }
1464                }
1465             }
1466          }
1467          break;
1468       }
1469    }
1470    return true;
1471 }
1472
1473 static Object FindObjectID(Object object, int id)
1474 {
1475    Object result = null;
1476    Object child;
1477    for(child = object.children.first; child; child = child.next)
1478    {
1479       if(child.flags.hierarchy == (uint16) id)
1480       {
1481          result = child;
1482          break;
1483       }
1484       else
1485       {
1486          result = FindObjectID(child, id);
1487          if(result) break;
1488       }
1489    }
1490    return result;
1491 }
1492
1493 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1494 {
1495    switch(info->chunkId)
1496    {
1497       case FRM_MESHINFO:
1498       {
1499          ObjectInfoBlock block { };
1500          Object object;
1501
1502          ReadChunks(ReadFrameInfoBlock, info, &block);
1503
1504          if(block.dummyName && block.dummyName[0])
1505          {
1506             if(!strcmp(block.name, "$$$DUMMY"))
1507             {
1508                object = Object { };
1509                object.name = block.dummyName;
1510                info->rootObject.children.AddName(object);
1511                object.transform.scaling = { 1,1,1 };
1512             }
1513             else
1514             {
1515                Object model = info->rootObject.Find(block.name);
1516                if(model)
1517                {
1518                   object = Object { };
1519                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1520                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1521                   object.flags = model.flags;
1522                   object.flags.ownMesh = false;
1523                   object.mesh = model.mesh;
1524                   /*
1525                   object.min = model.min;
1526                   object.max = model.max;
1527                   object.radius = model.radius;
1528                   */
1529                   object.transform = model.transform;
1530                   info->rootObject.children.AddName(object);
1531                }
1532                delete block.dummyName;
1533             }
1534             object.parent = info->rootObject;
1535          }
1536          else
1537             object = info->rootObject.Find(block.name);
1538
1539          if(object)
1540          {
1541             Mesh mesh = object.mesh;
1542             object.flags.hierarchy = block.hierarchy + 1;
1543             if(block.parent != -1)
1544             {
1545                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1546                if(parent)
1547                {
1548                   object.parent.children.Remove(object);
1549                   parent.children.AddName(object);
1550                   object.parent = parent;
1551                }
1552             }
1553             object.pivot.x = block.pivot.x;
1554             object.pivot.y =-block.pivot.z;
1555             object.pivot.z = block.pivot.y;
1556
1557             if(mesh && object.flags.ownMesh)
1558             {
1559                if(mesh.Lock({ vertices = true }))
1560                {
1561                   int c;
1562                   // Take pivot into account
1563                   for(c = 0; c<mesh.nVertices; c++)
1564                   {
1565                      mesh.vertices[c].x -= object.pivot.x;
1566                      mesh.vertices[c].y -= object.pivot.y;
1567                      mesh.vertices[c].z -= object.pivot.z;
1568                   }
1569                   mesh.Unlock({ vertices = true });
1570                }
1571             }
1572
1573             object.tracks = block.tracks;
1574             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1575             object.endFrame = info->rootObject.endFrame;
1576          }
1577          /*else
1578             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1579          delete block.name;
1580          break;
1581       }
1582       case FRM_CAMERA:
1583       {
1584          ObjectInfoBlock block { };
1585          Object object;
1586
1587          ReadChunks(ReadFrameInfoBlock, info, &block);
1588
1589          if(block.dummyName && block.dummyName[0])
1590          {
1591             if(!strcmp(block.name, "$$$DUMMY"))
1592             {
1593                object = Object { };
1594                object.name = block.dummyName;
1595                info->rootObject.children.AddName(object);
1596                object.transform.scaling = { 1, 1, 1 };
1597                object.flags.camera = true;
1598             }
1599             else
1600             {
1601                Object model = info->rootObject.Find(block.name);
1602                if(model)
1603                {
1604                   object = Object { };
1605                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1606                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1607                   object.flags = model.flags;
1608                   object.flags.ownMesh = false;
1609                   object.camera = model.camera;
1610                   object.flags.camera = true;
1611                   info->rootObject.children.AddName(object);
1612                }
1613                delete block.dummyName;
1614             }
1615             object.parent = info->rootObject;
1616          }
1617          else
1618             object = info->rootObject.Find(block.name);
1619
1620          if(object)
1621          {
1622             object.flags.hierarchy = block.hierarchy + 1;
1623             if(block.parent != -1)
1624             {
1625                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1626                if(parent)
1627                {
1628                   object.parent.children.Remove(object);
1629                   parent.children.AddName(object);
1630                   object.parent = parent;
1631                }
1632             }
1633             object.pivot.x = block.pivot.x;
1634             object.pivot.y =-block.pivot.z;
1635             object.pivot.z = block.pivot.y;
1636
1637             object.tracks = block.tracks;
1638             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1639             object.endFrame = info->rootObject.endFrame;
1640          }
1641          /*else
1642             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1643          delete block.name;
1644          break;
1645       }
1646       case FRM_CAMERATARGET:
1647       {
1648          ObjectInfoBlock block { };
1649          Object object;
1650          char targetName[MAXNAMELEN];
1651
1652          ReadChunks(ReadFrameInfoBlock, info, &block);
1653
1654          strcpy(targetName, block.name);
1655          strcat(targetName, ".target");
1656
1657          if(block.dummyName && block.dummyName[0])
1658          {
1659             if(!strcmp(block.name, "$$$DUMMY"))
1660             {
1661                object = Object { };
1662                object.name = block.dummyName;
1663                info->rootObject.children.AddName(object);
1664                object.transform.scaling = { 1,1,1 };
1665             }
1666             else
1667             {
1668                Object model = info->rootObject.Find(targetName);
1669                if(model)
1670                {
1671                   object = Object { };
1672                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1673                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1674                   object.flags = model.flags;
1675                   object.flags.ownMesh = false;
1676                   object.camera = model.camera;
1677                   info->rootObject.children.AddName(object);
1678                }
1679                delete block.dummyName;
1680             }
1681             object.parent = info->rootObject;
1682          }
1683          else
1684             object = info->rootObject.Find(targetName);
1685
1686          if(object)
1687          {
1688             object.flags.hierarchy = block.hierarchy + 1;
1689             if(block.parent != -1)
1690             {
1691                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1692                if(parent)
1693                {
1694                   object.parent.children.Remove(object);
1695                   parent.children.AddName(object);
1696                   object.parent = parent;
1697                }
1698             }
1699             object.pivot.x = block.pivot.x;
1700             object.pivot.y =-block.pivot.z;
1701             object.pivot.z = block.pivot.y;
1702
1703             object.tracks = block.tracks;
1704             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1705             object.endFrame = info->rootObject.endFrame;
1706          }
1707          /*else
1708             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1709          delete block.name;
1710          break;
1711       }
1712       case FRM_AMBIENT:
1713       {
1714          ObjectInfoBlock block { };
1715
1716          ReadChunks(ReadFrameInfoBlock, info, &block);
1717
1718          info->rootObject.tracks = block.tracks;
1719          break;
1720       }
1721       case FRM_OMNILIGHT:
1722       case FRM_SPOTLIGHT:
1723       {
1724          ObjectInfoBlock block { };
1725          Object object;
1726
1727          ReadChunks(ReadFrameInfoBlock, info, &block);
1728
1729          if(block.dummyName && block.dummyName[0])
1730          {
1731             if(!strcmp(block.name, "$$$DUMMY"))
1732             {
1733                object = Object { };
1734                object.name = block.dummyName;
1735                info->rootObject.children.AddName(object);
1736                object.transform.scaling = { 1, 1, 1 };
1737                object.flags.light = true;
1738             }
1739             else
1740             {
1741                Object model = info->rootObject.Find(block.name);
1742                if(model)
1743                {
1744                   object = Object { };
1745                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1746                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1747                   object.flags = model.flags;
1748                   object.flags.ownMesh = false;
1749                   object.light = model.light;
1750                   object.flags.light = true;
1751                   info->rootObject.children.AddName(object);
1752                }
1753                delete block.dummyName;
1754             }
1755             object.parent = info->rootObject;
1756          }
1757          else
1758             object = info->rootObject.Find(block.name);
1759
1760          if(object)
1761          {
1762             object.flags.hierarchy = block.hierarchy + 1;
1763             if(block.parent != -1)
1764             {
1765                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1766                if(parent)
1767                {
1768                   object.parent.children.Remove(object);
1769                   parent.children.AddName(object);
1770                   object.parent = parent;
1771                }
1772             }
1773             object.pivot.x = block.pivot.x;
1774             object.pivot.y =-block.pivot.z;
1775             object.pivot.z = block.pivot.y;
1776
1777             object.tracks = block.tracks;
1778             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1779             object.endFrame = info->rootObject.endFrame;
1780          }
1781          /*else
1782             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1783          delete block.name;
1784          break;
1785       }
1786       case FRM_SPOTLIGHTTARGET:
1787       {
1788          ObjectInfoBlock block { };
1789          Object object;
1790          char targetName[MAXNAMELEN];
1791
1792          ReadChunks(ReadFrameInfoBlock, info, &block);
1793
1794          strcpy(targetName, block.name);
1795          strcat(targetName, ".target");
1796
1797          if(block.dummyName && block.dummyName[0])
1798          {
1799             if(!strcmp(block.name, "$$$DUMMY"))
1800             {
1801                object = Object { };
1802                object.name = block.dummyName;
1803                info->rootObject.children.AddName(object);
1804                object.transform.scaling = { 1,1,1 };
1805             }
1806             else
1807             {
1808                Object model = info->rootObject.Find(targetName);
1809                if(model)
1810                {
1811                   object = Object { };
1812                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1813                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1814                   object.flags = model.flags;
1815                   object.flags.ownMesh = false;
1816                   object.light = model.light;
1817                   info->rootObject.children.AddName(object);
1818                }
1819                delete block.dummyName;
1820             }
1821             object.parent = info->rootObject;
1822          }
1823          else
1824             object = info->rootObject.Find(targetName);
1825
1826          if(object)
1827          {
1828             object.flags.hierarchy = block.hierarchy + 1;
1829             if(block.parent != -1)
1830             {
1831                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1832                if(parent)
1833                {
1834                   object.parent.children.Remove(object);
1835                   parent.children.AddName(object);
1836                   object.parent = parent;
1837                }
1838             }
1839             object.pivot.x = block.pivot.x;
1840             object.pivot.y =-block.pivot.z;
1841             object.pivot.z = block.pivot.y;
1842
1843             object.tracks = block.tracks;
1844             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1845             object.endFrame = info->rootObject.endFrame;
1846          }
1847          /*else
1848             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1849          delete block.name;
1850          break;
1851       }
1852       case FRM_FRAMES:
1853       {
1854          info->rootObject.startFrame = ReadDWORD(info->f);
1855          info->rootObject.endFrame = ReadDWORD(info->f);
1856          *&(info->rootObject.frame) = info->rootObject.startFrame;
1857          break;
1858       }
1859    }
1860    return true;
1861 }
1862
1863 // Main Chunks
1864 static bool ReadMainChunks(FileInfo * info, void * data)
1865 {
1866    switch(info->chunkId)
1867    {
1868       case EDIT3DS:
1869          ReadChunks(ReadEditChunks, info, null);
1870          break;
1871       case KEYFRAME3DS:
1872       {
1873          Object object = data;
1874          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
1875          {
1876             ReadChunks(ReadKeyFrameChunks, info, null);
1877             object.flags.keysLoaded = true;
1878          }
1879          break;
1880       }
1881    }
1882    return true;
1883 }
1884
1885 static bool ReadMain(FileInfo * info, void * data)
1886 {
1887    switch(info->chunkId)
1888    {
1889       case MAIN3DS:
1890          ReadChunks(ReadMainChunks, info, data);
1891          break;
1892    }
1893    return true;
1894 }
1895
1896 class Object3DSFormat : ObjectFormat
1897 {
1898    class_property(extension) = "3ds";
1899
1900    bool Load(Object object, char * fileName, DisplaySystem displaySystem)
1901    {
1902       bool result = false;
1903       if(fileName)
1904       {
1905          FileInfo info = {0};
1906          info.rootObject = object;
1907          info.displaySystem = displaySystem;
1908          info.pos = 0;
1909          info.end = 1;
1910          StripLastDirectory(fileName, info.textureDirectory);
1911          info.f = FileOpen(fileName, read);
1912          if(info.f)
1913          {
1914             if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
1915             {
1916                object.flags.root = true;
1917                object.SetMinMaxRadius(true);
1918                object._Animate(object.frame);
1919                object.UpdateTransform();
1920                result = true;
1921             }
1922             delete info.f;
1923          }
1924       }
1925       if(!result)
1926          object.Free(displaySystem);
1927       return result;
1928    }
1929 }