ecere/gfx/3D/Object3DSFormat: Fixed potential memory errors
[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             mat.bumpMap = displaySystem.GetTexture(bumpName);
830             if(!mat.bumpMap)
831             {
832                mat.bumpMap = Bitmap { };
833                if(!mat.bumpMap.Load(location, null, null) ||
834                   !mat.bumpMap.Convert(null, pixelFormat888, null) ||
835                   !displaySystem.AddTexture(bumpName, mat.bumpMap))
836                   delete mat.bumpMap;
837                if(mat.bumpMap)
838                {
839                   ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
840                   int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
841                   int y, x;
842
843                   for(y = 0; y < bh; y++)
844                      for(x = 0; x < bw; x++)
845                      {
846                         uint bc = y * bw + x;
847                         Color color = picture[bc].color;
848                         picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
849                      }
850                }
851             }
852          }
853          else
854          {
855             Bitmap opacityMap = null;
856             bool alphaOnly = true;
857             bool translucent = false;
858             if(!mat.baseMap)
859             {
860                mat.baseMap = displaySystem.GetTexture(name);
861                if(!mat.baseMap)
862                {
863                   mat.baseMap = Bitmap { };
864                   if(!mat.baseMap.Load(location, null, null) ||
865                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
866                      !displaySystem.AddTexture(name, mat.baseMap))
867                   {
868                      delete mat.baseMap;
869                   }
870                   opacityMap = mat.baseMap;
871                }
872             }
873             else if(info->parent->chunkId == MAT_MAPOPACITY)
874             {
875                opacityMap = Bitmap { };
876                if(opacityMap.Load(location, null, null))
877                {
878                   if(opacityMap.pixelFormat == pixelFormatRGBA)
879                      alphaOnly = false;
880                   if(!opacityMap.Convert(null, pixelFormat888, null))
881                      delete opacityMap;
882                }
883             }
884
885             if(mat.baseMap)
886             {
887                if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
888                {
889                   ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
890                   ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
891                   uint x, y;
892                   int ow = opacityMap.width, oh = opacityMap.height;
893                   int bw = mat.baseMap.width, bh = mat.baseMap.height;
894
895                   for(y = 0; y < bh; y++)
896                      for(x = 0; x < bw; x++)
897                      {
898                         int bc = ((y % bh) * bw + (x % bw));
899                         int oc = ((y % oh) * bw + (x % ow));
900                         byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
901                         if(alpha && alpha < 255)
902                            translucent = true;
903                         picture[bc] = ColorAlpha { alpha, picture[bc].color };
904                      }
905                }
906                if(translucent)
907                   mat.flags.translucent = true;
908                mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
909                mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
910             }
911             if(opacityMap != mat.baseMap)
912                delete opacityMap;
913          }
914          delete name;
915          break;
916       }
917       case MAP_OPTIONS:
918       {
919          uint16 options = ReadWORD(info->f);
920          if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
921          break;
922       }
923       case MAP_1_U_SCALE:
924          mat.uScale = ReadFloat(info->f);
925          break;
926       case MAP_1_V_SCALE:
927          mat.vScale = ReadFloat(info->f);
928          break;
929       /*
930       case AMOUNT_OF:
931          break;
932       default:
933          PrintLn("Unhandled Map block");
934          break;
935       */
936    }
937    return true;
938 }
939
940 static bool ReadMaterial(FileInfo * info, Material mat)
941 {
942    switch(info->chunkId)
943    {
944       case MAT_NAME:
945       {
946          ReadASCIIZ(info->f, &mat.name);
947          break;
948       }
949       case MAT_TRANSPARENCY:
950       {
951          uint16 transparency;
952          ReadChunks(ReadAmountOf, info, &transparency);
953          mat.opacity = 1.0f - transparency / 100.0f;
954          if(mat.opacity < 1.0)
955             mat.flags.translucent = true;
956          break;
957       }
958       case MAT_DIFFUSE:
959       {
960          ReadChunks(ReadRGB, info, &mat.diffuse);
961          ReadChunks(ReadRGB, info, &mat.diffuse);
962          break;
963       }
964       case MAT_AMBIENT:
965       {
966          ReadChunks(ReadRGB, info, &mat.ambient);
967          ReadChunks(ReadRGB, info, &mat.ambient);
968          break;
969       }
970       case MAT_SPECULAR:
971       {
972          ReadChunks(ReadRGB, info, &mat.specular);
973          ReadChunks(ReadRGB, info, &mat.specular);
974          break;
975       }
976       case MAT_SELFILLUM:
977       {
978          uint16 emissive;
979          ReadChunks(ReadAmountOf, info, &emissive);
980          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
981          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
982          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
983          break;
984       }
985       case MAT_SHINSTRENGTH:
986       {
987          uint16 shininess;
988          ReadChunks(ReadAmountOf, info, &shininess);
989          mat.specular.r *= shininess / 100.0f;
990          mat.specular.g *= shininess / 100.0f;
991          mat.specular.b *= shininess / 100.0f;
992          break;
993       }
994       case MAT_SHININESS:
995       {
996          uint16 power;
997          ReadChunks(ReadAmountOf, info, &power);
998          mat.power = power;
999          break;
1000       }
1001       case MAT_MAPTEXTURE1:
1002          ReadChunks(ReadMap, info, mat);
1003          break;
1004       case MAT_MAPOPACITY:
1005          ReadChunks(ReadMap, info, mat);
1006          break;
1007       case MAT_DOUBLESIDED:
1008          mat.flags.doubleSided = true;
1009          break;
1010       case MAT_BUMPMAP:
1011          ReadChunks(ReadMap, info, mat);
1012          break;
1013       /*
1014       default:
1015          PrintLn("Unhandled MAT type ID", info->chunkId);
1016       */
1017    }
1018    return true;
1019 }
1020
1021 // Lights
1022 static bool ReadLight(FileInfo * info, Object object)
1023 {
1024    Mesh mesh = object.mesh;
1025    Light * light = &object.light;
1026    switch(info->chunkId)
1027    {
1028       case RGB_BYTE:
1029       case RGB_BYTE_GAMMA:
1030       case RGB_FLOAT:
1031       case RGB_FLOAT_GAMMA:
1032          ReadRGB(info, &light->diffuse);
1033          light->specular = light->diffuse;
1034          break;
1035       case LIT_SPOT:
1036       {
1037          Object target;
1038          char targetName[MAXNAMELEN];
1039
1040          strcpy(targetName, object.name);
1041          strcat(targetName, ".target");
1042
1043          light->flags.omni = false;
1044          light->flags.spot = true;
1045
1046          target = Object { };
1047          target.name = CopyString(targetName);
1048          info->rootObject.children.AddName(target);
1049          target.parent = info->rootObject;
1050
1051          light->target = target;
1052
1053          target.transform.position.x = ReadFloat(info->f);
1054          target.transform.position.z = ReadFloat(info->f);
1055          target.transform.position.y =-ReadFloat(info->f);
1056
1057          light->hotSpot = ReadFloat(info->f);
1058          light->fallOff = ReadFloat(info->f);
1059          break;
1060       }
1061       case LIT_ONOFF:
1062       {
1063          light->flags.off = true;
1064          break;
1065       }
1066       case LIT_ATTENUATION:
1067       {
1068          /* solve (
1069             {
1070                d = 300, small = 0.001,
1071                Kl = 0, Kc = 0,
1072                d * (Kl + Kq * d) = (1 / small) - Kc
1073             },
1074             { Kc, Kl, Kq, small, d });
1075          */
1076
1077          light->flags.attenuation = true;
1078
1079          /*
1080          #define MINLIGHT     0.08
1081          light->Kq = 1/(light->end*light->end*MINLIGHT);
1082          */
1083
1084          #define MINLIGHT     0.15f
1085          // #define MINLIGHT     0.1
1086          light->Kl = (float)(1/(light->end*MINLIGHT));
1087
1088          break;
1089       }
1090       case LIT_START:
1091       {
1092          light->start = ReadFloat(info->f);
1093          break;
1094       }
1095       case LIT_END:
1096       {
1097          light->end = ReadFloat(info->f);
1098          break;
1099       }
1100       case LIT_MULTIPLIER:
1101       {
1102          light->multiplier = ReadFloat(info->f);
1103          break;
1104       }
1105    }
1106    return true;
1107 }
1108
1109 // Cameras
1110 static bool ReadCamera(FileInfo * info, Object object)
1111 {
1112    Mesh mesh = object.mesh;
1113    switch(info->chunkId)
1114    {
1115       case CAM_SEECONE:
1116       {
1117          break;
1118       }
1119       case CAM_RANGES:
1120       {
1121          Camera camera = object.camera;
1122          float nearRange = ReadFloat(info->f);
1123          float farRange = ReadFloat(info->f);
1124          /*
1125          camera.zMin = Max(0.1, nearRange);
1126          camera.zMax = farRange;
1127          */
1128          break;
1129       }
1130    }
1131    return true;
1132 }
1133
1134 // Edit Chunks
1135 static bool ReadEditObject(FileInfo * info, char * name)
1136 {
1137    DisplaySystem displaySystem = info->displaySystem;
1138    switch(info->chunkId)
1139    {
1140       case OBJ_TRIMESH:
1141       {
1142          Object object = info->rootObject.Find(name);
1143          if(!object)
1144          {
1145             object = Object { };
1146             object.name = CopyString(name);
1147             info->rootObject.children.AddName(object);
1148             object.parent = info->rootObject;
1149          }
1150          object.InitializeMesh(displaySystem);
1151          ReadChunks(ReadTriMesh, info, object);
1152          object.flags.mesh = true;
1153          object.mesh.Unlock(0);
1154          break;
1155       }
1156       case OBJ_LIGHT:
1157       {
1158          Object object = info->rootObject.Find(name);
1159          Light * light;
1160          Vector3Df position;
1161
1162          if(!object)
1163          {
1164             object = Object { };
1165             object.name = CopyString(name);
1166             info->rootObject.children.AddName(object);
1167             object.parent = info->rootObject;
1168          }
1169          object.flags.light = true;
1170
1171          light = &object.light;
1172          light->lightObject = object;
1173          light->flags.omni = true;
1174          light->multiplier = 1.0f;
1175
1176          // This is not used?
1177          Read3DVertex(info->f, position);
1178          light->direction = { position.x, position.y, position.z };
1179          info->pos += sizeof(float) * 3;
1180
1181          ReadChunks(ReadLight, info, object);
1182          break;
1183       }
1184       case OBJ_CAMERA:
1185       {
1186          char targetName[MAXNAMELEN];
1187          Object object = info->rootObject.Find(name);
1188          Object target;
1189          Camera camera;
1190          float bankAngle, focus;
1191          double mm;
1192
1193          strcpy(targetName, name);
1194          strcat(targetName, ".target");
1195          target = info->rootObject.Find(targetName);
1196
1197          if(!object)
1198          {
1199             object = Object { };
1200             object.name = CopyString(name);
1201             info->rootObject.children.AddName(object);
1202
1203             object.parent = info->rootObject;
1204             object.camera = Camera { };
1205             object.camera.type = lookAtObject;
1206          }
1207
1208          if(!target)
1209          {
1210             target = Object { };
1211             target.name = CopyString(targetName);
1212             info->rootObject.children.AddName(target);
1213             target.parent = info->rootObject;
1214          }
1215
1216          object.flags.camera = true;
1217          object.cameraTarget = target;
1218
1219          camera = object.camera;
1220          camera.cameraObject = object;
1221          camera.target = target;
1222
1223          //Read3DVertex(info->f, camera.position);
1224          object.transform.position.x = ReadFloat(info->f);
1225          object.transform.position.z = ReadFloat(info->f);
1226          object.transform.position.y =-ReadFloat(info->f);
1227
1228          info->pos += sizeof(float) * 3;
1229          //Read3DVertex(info->f, object.cameraTarget.position);
1230          target.transform.position.x = ReadFloat(info->f);
1231          target.transform.position.z = ReadFloat(info->f);
1232          target.transform.position.y =-ReadFloat(info->f);
1233
1234          info->pos += sizeof(float) * 3;
1235          bankAngle = ReadFloat(info->f);
1236          info->pos += sizeof(float);
1237          focus = ReadFloat(info->f);
1238          info->pos += sizeof(float);
1239
1240          mm = (focus - 5.05659508373109) / 1.13613250717301;
1241          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1242
1243          ReadChunks(ReadCamera, info, object);
1244          break;
1245       }
1246       case OBJ_HIDDEN: break;
1247    }
1248    return true;
1249 }
1250
1251 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1252
1253 static bool ReadEditChunks(FileInfo * info, void * data)
1254 {
1255    switch(info->chunkId)
1256    {
1257       case EDIT_AMBIENT:
1258       {
1259          // Read the ambient color
1260          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1261          break;
1262       }
1263       case EDIT_MATERIAL:
1264       {
1265          Material material { };
1266          Material mat;
1267          ReadChunks(ReadMaterial, info, material);
1268
1269          mat = info->displaySystem.AddNamedMaterial(material.name);
1270          if(mat)
1271          {
1272             if(material.baseMap)
1273                material.baseMap.MakeMipMaps(info->displaySystem);
1274             if(material.bumpMap)
1275                material.bumpMap.MakeMipMaps(info->displaySystem);
1276             // COPY_NITEM(mat, material);
1277             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1278          }
1279          else
1280          {
1281             delete material.baseMap;
1282          }
1283          delete material.name;
1284          delete material;
1285          break;
1286       }
1287       case EDIT_OBJECT:
1288       {
1289          char * name;
1290          info->pos += ReadASCIIZ(info->f, &name);
1291          ReadChunks(ReadEditObject, info, name);
1292          delete name;
1293          break;
1294       }
1295    }
1296    return true;
1297 }
1298
1299 struct ObjectInfoBlock
1300 {
1301    OldList tracks;
1302    short hierarchy;
1303    short parent;
1304    char * name;
1305    char * dummyName;
1306    Vector3Df pivot;
1307 };
1308
1309 // Key Framer Chunks
1310
1311 #define ACCFLAG_TENSION    0x00000001
1312 #define ACCFLAG_CONTINUITY 0x00000002
1313 #define ACCFLAG_BIAS       0x00000004
1314 #define ACCFLAG_EASETO     0x00000008
1315 #define ACCFLAG_EASEFROM   0x00000010
1316
1317 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1318 {
1319    switch(info->chunkId)
1320    {
1321       case FRM_PARAM:
1322       {
1323          uint16 flags1, flags2;
1324          ReadASCIIZ(info->f, &block->name);
1325          flags1 = ReadWORD(info->f);
1326          flags2 = ReadWORD(info->f);
1327          block->parent = ReadWORD(info->f);
1328          break;
1329       }
1330       case FRM_DUMMYNAME:
1331          ReadASCIIZ(info->f, &block->dummyName);
1332          break;
1333       case FRM_PIVOT:
1334          Read3DVertex(info->f, block->pivot);
1335          break;
1336       case FRM_HIERARCHY:
1337          block->hierarchy = ReadWORD(info->f);
1338          break;
1339       case FRM_TRACKPOS:
1340       case FRM_TRACKROT:
1341       case FRM_TRACKSCALE:
1342       case FRM_TRACKROLL:
1343       case FRM_TRACKFOV:
1344       case FRM_TRACKCOLOR:
1345       case FRM_TRACKHOTSPOT:
1346       case FRM_TRACKFALLOFF:
1347       {
1348          FrameTrack track { };
1349          if(track)
1350          {
1351             uint16 flags;
1352             byte unknown[8];
1353             uint c;
1354
1355             block->tracks.Add(track);
1356
1357             flags = ReadWORD(info->f);
1358
1359             info->f.Read(unknown, sizeof(unknown), 1);
1360
1361             track.numKeys = ReadDWORD(info->f);
1362
1363             switch(info->chunkId)
1364             {
1365                case FRM_TRACKPOS: track.type.type = position; break;
1366                case FRM_TRACKROT: track.type.type = rotation; break;
1367                case FRM_TRACKSCALE: track.type.type = scaling; break;
1368                case FRM_TRACKROLL: track.type.type = roll; break;
1369                case FRM_TRACKFOV: track.type.type = fov; break;
1370                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1371                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1372                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1373             }
1374             if((flags & 0x0003) == 3)
1375                track.type.loop = true;
1376
1377             track.keys = new0 FrameKey[track.numKeys];
1378             for(c = 0; c<track.numKeys; c++)
1379             {
1380                uint16 accelerationFlags;
1381                FrameKey * key = track.keys + c;
1382
1383                key->frame = ReadDWORD(info->f);
1384                accelerationFlags = ReadWORD(info->f);
1385
1386                if(accelerationFlags & ACCFLAG_TENSION)
1387                   key->tension = ReadFloat(info->f);
1388                if(accelerationFlags & ACCFLAG_CONTINUITY)
1389                   key->continuity = ReadFloat(info->f);
1390                if(accelerationFlags & ACCFLAG_BIAS)
1391                   key->bias = ReadFloat(info->f);
1392                if(accelerationFlags & ACCFLAG_EASETO)
1393                   key->easeTo = ReadFloat(info->f);
1394                if(accelerationFlags & ACCFLAG_EASEFROM)
1395                   key->easeFrom = ReadFloat(info->f);
1396
1397                switch(info->chunkId)
1398                {
1399                   case FRM_TRACKPOS:
1400                   {
1401                      Vector3Df position;
1402                      Read3DVertex(info->f, position);
1403                      key->position = { position.x, -position.z, position.y };
1404                      break;
1405                   }
1406                   case FRM_TRACKROT:
1407                   {
1408                      Vector3Df axis;
1409                      Angle angle = ReadFloat(info->f);
1410                      Vector3Df fixedAxis;
1411
1412                      Read3DVertex(info->f, axis);
1413                      fixedAxis.x = axis.x;
1414                      fixedAxis.y = -axis.z;
1415                      fixedAxis.z = axis.y;
1416
1417                      if(c > 0)
1418                      {
1419                         Quaternion rotation;
1420                         rotation.RotationAxis(fixedAxis, angle);
1421                         key->orientation.Multiply((key - 1)->orientation, rotation);
1422                      }
1423                      else
1424                         key->orientation.RotationAxis(fixedAxis, angle);
1425                      break;
1426                   }
1427                   case FRM_TRACKSCALE:
1428                   {
1429                      Vector3Df scaling;
1430                      Read3DVertex(info->f, scaling);
1431                      key->scaling = { scaling.x, scaling.z, scaling.y };
1432                      break;
1433                   }
1434                   case FRM_TRACKFOV:
1435                   {
1436                      key->fov = ReadFloat(info->f);
1437                      break;
1438                   }
1439                   case FRM_TRACKROLL:
1440                   {
1441                      key->roll = -ReadFloat(info->f);
1442                      break;
1443                   }
1444                   case FRM_TRACKCOLOR:
1445                   {
1446                      FileInfo childInfo = *info;
1447                      childInfo.chunkId = RGB_FLOAT;
1448                      ReadRGB(&childInfo, &key->color);
1449                      break;
1450                   }
1451                   case FRM_TRACKHOTSPOT:
1452                   {
1453                      key->hotSpot = ReadFloat(info->f);
1454                      break;
1455                   }
1456                   case FRM_TRACKFALLOFF:
1457                   {
1458                      key->fallOff = ReadFloat(info->f);
1459                      break;
1460                   }
1461                }
1462             }
1463          }
1464          break;
1465       }
1466    }
1467    return true;
1468 }
1469
1470 static Object FindObjectID(Object object, int id)
1471 {
1472    Object result = null;
1473    Object child;
1474    for(child = object.children.first; child; child = child.next)
1475    {
1476       if(child.flags.hierarchy == (uint16) id)
1477       {
1478          result = child;
1479          break;
1480       }
1481       else
1482       {
1483          result = FindObjectID(child, id);
1484          if(result) break;
1485       }
1486    }
1487    return result;
1488 }
1489
1490 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1491 {
1492    switch(info->chunkId)
1493    {
1494       case FRM_MESHINFO:
1495       {
1496          ObjectInfoBlock block { };
1497          Object object;
1498
1499          ReadChunks(ReadFrameInfoBlock, info, &block);
1500
1501          if(block.dummyName && block.dummyName[0])
1502          {
1503             if(!strcmp(block.name, "$$$DUMMY"))
1504             {
1505                object = Object { };
1506                object.name = block.dummyName;
1507                info->rootObject.children.AddName(object);
1508                object.transform.scaling = { 1,1,1 };
1509             }
1510             else
1511             {
1512                Object model = info->rootObject.Find(block.name);
1513                if(model)
1514                {
1515                   object = Object { };
1516                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1517                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1518                   object.flags = model.flags;
1519                   object.flags.ownMesh = false;
1520                   object.mesh = model.mesh;
1521                   /*
1522                   object.min = model.min;
1523                   object.max = model.max;
1524                   object.radius = model.radius;
1525                   */
1526                   object.transform = model.transform;
1527                   info->rootObject.children.AddName(object);
1528                }
1529                delete block.dummyName;
1530             }
1531             object.parent = info->rootObject;
1532          }
1533          else
1534             object = info->rootObject.Find(block.name);
1535
1536          if(object)
1537          {
1538             Mesh mesh = object.mesh;
1539             object.flags.hierarchy = block.hierarchy + 1;
1540             if(block.parent != -1)
1541             {
1542                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1543                if(parent)
1544                {
1545                   object.parent.children.Remove(object);
1546                   parent.children.AddName(object);
1547                   object.parent = parent;
1548                }
1549             }
1550             object.pivot.x = block.pivot.x;
1551             object.pivot.y =-block.pivot.z;
1552             object.pivot.z = block.pivot.y;
1553
1554             if(mesh && object.flags.ownMesh)
1555             {
1556                if(mesh.Lock({ vertices = true }))
1557                {
1558                   int c;
1559                   // Take pivot into account
1560                   for(c = 0; c<mesh.nVertices; c++)
1561                   {
1562                      mesh.vertices[c].x -= object.pivot.x;
1563                      mesh.vertices[c].y -= object.pivot.y;
1564                      mesh.vertices[c].z -= object.pivot.z;
1565                   }
1566                   mesh.Unlock({ vertices = true });
1567                }
1568             }
1569
1570             object.tracks = block.tracks;
1571             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1572             object.endFrame = info->rootObject.endFrame;
1573          }
1574          /*else
1575             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1576          delete block.name;
1577          break;
1578       }
1579       case FRM_CAMERA:
1580       {
1581          ObjectInfoBlock block { };
1582          Object object;
1583
1584          ReadChunks(ReadFrameInfoBlock, info, &block);
1585
1586          if(block.dummyName && block.dummyName[0])
1587          {
1588             if(!strcmp(block.name, "$$$DUMMY"))
1589             {
1590                object = Object { };
1591                object.name = block.dummyName;
1592                info->rootObject.children.AddName(object);
1593                object.transform.scaling = { 1, 1, 1 };
1594                object.flags.camera = true;
1595             }
1596             else
1597             {
1598                Object model = info->rootObject.Find(block.name);
1599                if(model)
1600                {
1601                   object = Object { };
1602                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1603                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1604                   object.flags = model.flags;
1605                   object.flags.ownMesh = false;
1606                   object.camera = model.camera;
1607                   object.flags.camera = true;
1608                   info->rootObject.children.AddName(object);
1609                }
1610                delete block.dummyName;
1611             }
1612             object.parent = info->rootObject;
1613          }
1614          else
1615             object = info->rootObject.Find(block.name);
1616
1617          if(object)
1618          {
1619             object.flags.hierarchy = block.hierarchy + 1;
1620             if(block.parent != -1)
1621             {
1622                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1623                if(parent)
1624                {
1625                   object.parent.children.Remove(object);
1626                   parent.children.AddName(object);
1627                   object.parent = parent;
1628                }
1629             }
1630             object.pivot.x = block.pivot.x;
1631             object.pivot.y =-block.pivot.z;
1632             object.pivot.z = block.pivot.y;
1633
1634             object.tracks = block.tracks;
1635             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1636             object.endFrame = info->rootObject.endFrame;
1637          }
1638          /*else
1639             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1640          delete block.name;
1641          break;
1642       }
1643       case FRM_CAMERATARGET:
1644       {
1645          ObjectInfoBlock block { };
1646          Object object;
1647          char targetName[MAXNAMELEN];
1648
1649          ReadChunks(ReadFrameInfoBlock, info, &block);
1650
1651          strcpy(targetName, block.name);
1652          strcat(targetName, ".target");
1653
1654          if(block.dummyName && block.dummyName[0])
1655          {
1656             if(!strcmp(block.name, "$$$DUMMY"))
1657             {
1658                object = Object { };
1659                object.name = block.dummyName;
1660                info->rootObject.children.AddName(object);
1661                object.transform.scaling = { 1,1,1 };
1662             }
1663             else
1664             {
1665                Object model = info->rootObject.Find(targetName);
1666                if(model)
1667                {
1668                   object = Object { };
1669                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1670                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1671                   object.flags = model.flags;
1672                   object.flags.ownMesh = false;
1673                   object.camera = model.camera;
1674                   info->rootObject.children.AddName(object);
1675                }
1676                delete block.dummyName;
1677             }
1678             object.parent = info->rootObject;
1679          }
1680          else
1681             object = info->rootObject.Find(targetName);
1682
1683          if(object)
1684          {
1685             object.flags.hierarchy = block.hierarchy + 1;
1686             if(block.parent != -1)
1687             {
1688                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1689                if(parent)
1690                {
1691                   object.parent.children.Remove(object);
1692                   parent.children.AddName(object);
1693                   object.parent = parent;
1694                }
1695             }
1696             object.pivot.x = block.pivot.x;
1697             object.pivot.y =-block.pivot.z;
1698             object.pivot.z = block.pivot.y;
1699
1700             object.tracks = block.tracks;
1701             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1702             object.endFrame = info->rootObject.endFrame;
1703          }
1704          /*else
1705             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1706          delete block.name;
1707          break;
1708       }
1709       case FRM_AMBIENT:
1710       {
1711          ObjectInfoBlock block { };
1712
1713          ReadChunks(ReadFrameInfoBlock, info, &block);
1714
1715          info->rootObject.tracks = block.tracks;
1716          break;
1717       }
1718       case FRM_OMNILIGHT:
1719       case FRM_SPOTLIGHT:
1720       {
1721          ObjectInfoBlock block { };
1722          Object object;
1723
1724          ReadChunks(ReadFrameInfoBlock, info, &block);
1725
1726          if(block.dummyName && block.dummyName[0])
1727          {
1728             if(!strcmp(block.name, "$$$DUMMY"))
1729             {
1730                object = Object { };
1731                object.name = block.dummyName;
1732                info->rootObject.children.AddName(object);
1733                object.transform.scaling = { 1, 1, 1 };
1734                object.flags.light = true;
1735             }
1736             else
1737             {
1738                Object model = info->rootObject.Find(block.name);
1739                if(model)
1740                {
1741                   object = Object { };
1742                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1743                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1744                   object.flags = model.flags;
1745                   object.flags.ownMesh = false;
1746                   object.light = model.light;
1747                   object.flags.light = true;
1748                   info->rootObject.children.AddName(object);
1749                }
1750                delete block.dummyName;
1751             }
1752             object.parent = info->rootObject;
1753          }
1754          else
1755             object = info->rootObject.Find(block.name);
1756
1757          if(object)
1758          {
1759             object.flags.hierarchy = block.hierarchy + 1;
1760             if(block.parent != -1)
1761             {
1762                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1763                if(parent)
1764                {
1765                   object.parent.children.Remove(object);
1766                   parent.children.AddName(object);
1767                   object.parent = parent;
1768                }
1769             }
1770             object.pivot.x = block.pivot.x;
1771             object.pivot.y =-block.pivot.z;
1772             object.pivot.z = block.pivot.y;
1773
1774             object.tracks = block.tracks;
1775             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1776             object.endFrame = info->rootObject.endFrame;
1777          }
1778          /*else
1779             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1780          delete block.name;
1781          break;
1782       }
1783       case FRM_SPOTLIGHTTARGET:
1784       {
1785          ObjectInfoBlock block { };
1786          Object object;
1787          char targetName[MAXNAMELEN];
1788
1789          ReadChunks(ReadFrameInfoBlock, info, &block);
1790
1791          strcpy(targetName, block.name);
1792          strcat(targetName, ".target");
1793
1794          if(block.dummyName && block.dummyName[0])
1795          {
1796             if(!strcmp(block.name, "$$$DUMMY"))
1797             {
1798                object = Object { };
1799                object.name = block.dummyName;
1800                info->rootObject.children.AddName(object);
1801                object.transform.scaling = { 1,1,1 };
1802             }
1803             else
1804             {
1805                Object model = info->rootObject.Find(targetName);
1806                if(model)
1807                {
1808                   object = Object { };
1809                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1810                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1811                   object.flags = model.flags;
1812                   object.flags.ownMesh = false;
1813                   object.light = model.light;
1814                   info->rootObject.children.AddName(object);
1815                }
1816                delete block.dummyName;
1817             }
1818             object.parent = info->rootObject;
1819          }
1820          else
1821             object = info->rootObject.Find(targetName);
1822
1823          if(object)
1824          {
1825             object.flags.hierarchy = block.hierarchy + 1;
1826             if(block.parent != -1)
1827             {
1828                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1829                if(parent)
1830                {
1831                   object.parent.children.Remove(object);
1832                   parent.children.AddName(object);
1833                   object.parent = parent;
1834                }
1835             }
1836             object.pivot.x = block.pivot.x;
1837             object.pivot.y =-block.pivot.z;
1838             object.pivot.z = block.pivot.y;
1839
1840             object.tracks = block.tracks;
1841             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1842             object.endFrame = info->rootObject.endFrame;
1843          }
1844          /*else
1845             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1846          delete block.name;
1847          break;
1848       }
1849       case FRM_FRAMES:
1850       {
1851          info->rootObject.startFrame = ReadDWORD(info->f);
1852          info->rootObject.endFrame = ReadDWORD(info->f);
1853          *&(info->rootObject.frame) = info->rootObject.startFrame;
1854          break;
1855       }
1856    }
1857    return true;
1858 }
1859
1860 // Main Chunks
1861 static bool ReadMainChunks(FileInfo * info, void * data)
1862 {
1863    switch(info->chunkId)
1864    {
1865       case EDIT3DS:
1866          ReadChunks(ReadEditChunks, info, null);
1867          break;
1868       case KEYFRAME3DS:
1869       {
1870          Object object = data;
1871          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
1872          {
1873             ReadChunks(ReadKeyFrameChunks, info, null);
1874             object.flags.keysLoaded = true;
1875          }
1876          break;
1877       }
1878    }
1879    return true;
1880 }
1881
1882 static bool ReadMain(FileInfo * info, void * data)
1883 {
1884    switch(info->chunkId)
1885    {
1886       case MAIN3DS:
1887          ReadChunks(ReadMainChunks, info, data);
1888          break;
1889    }
1890    return true;
1891 }
1892
1893 class Object3DSFormat : ObjectFormat
1894 {
1895    class_property(extension) = "3ds";
1896
1897    bool Load(Object object, char * fileName, DisplaySystem displaySystem)
1898    {
1899       bool result = false;
1900       if(fileName)
1901       {
1902          FileInfo info = {0};
1903          info.rootObject = object;
1904          info.displaySystem = displaySystem;
1905          info.pos = 0;
1906          info.end = 1;
1907          StripLastDirectory(fileName, info.textureDirectory);
1908          info.f = FileOpen(fileName, read);
1909          if(info.f)
1910          {
1911             if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
1912             {
1913                object.flags.root = true;
1914                object.SetMinMaxRadius(true);
1915                object._Animate(object.frame);
1916                object.UpdateTransform();
1917                result = true;
1918             }
1919             delete info.f;
1920          }
1921       }
1922       if(!result)
1923          object.Free(displaySystem);
1924       return result;
1925    }
1926 }