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