ecere/gfx/3D/Object3DSFormat: Improvements to Smoothing Groups handling
[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          strlwr(name);
1002
1003          strcpy(location, info->textureDirectory);
1004          PathCat(location, name);
1005
1006          if(info->parent->chunkId == MAT_BUMPMAP)
1007          {
1008             // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
1009             char bumpName[MAX_LOCATION+5];
1010             strcpy(bumpName, "BUMP:");
1011             strcat(bumpName, location);
1012             if(!mat.bumpMap)
1013             {
1014                mat.bumpMap = displaySystem.GetTexture(bumpName);
1015                if(!mat.bumpMap)
1016                {
1017                   mat.bumpMap = Bitmap { };
1018                   if(!mat.bumpMap.Load(location, null, null) ||
1019                      !mat.bumpMap.Convert(null, pixelFormat888, null) ||
1020                      !displaySystem.AddTexture(bumpName, mat.bumpMap))
1021                      delete mat.bumpMap;
1022                   if(mat.bumpMap)
1023                   {
1024                      ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
1025                      int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
1026                      int y, x;
1027
1028                      for(y = 0; y < bh; y++)
1029                         for(x = 0; x < bw; x++)
1030                         {
1031                            uint bc = y * bw + x;
1032                            Color color = picture[bc].color;
1033                            picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
1034                         }
1035                   }
1036                }
1037             }
1038          }
1039          else
1040          {
1041             Bitmap opacityMap = null;
1042             bool alphaOnly = true;
1043             bool translucent = false;
1044             if(!mat.baseMap)
1045             {
1046                mat.baseMap = displaySystem.GetTexture(location);
1047                if(!mat.baseMap)
1048                {
1049                   mat.baseMap = Bitmap { };
1050                   if(!mat.baseMap.Load(location, null, null) ||
1051                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
1052                      !displaySystem.AddTexture(location, mat.baseMap))
1053                   {
1054                      delete mat.baseMap;
1055                   }
1056                   opacityMap = mat.baseMap;
1057                }
1058             }
1059             else if(info->parent->chunkId == MAT_MAPOPACITY)
1060             {
1061                opacityMap = Bitmap { };
1062                if(opacityMap.Load(location, null, null))
1063                {
1064                   if(opacityMap.pixelFormat == pixelFormatRGBA)
1065                      alphaOnly = false;
1066                   if(!opacityMap.Convert(null, pixelFormat888, null))
1067                      delete opacityMap;
1068                }
1069             }
1070
1071             if(mat.baseMap)
1072             {
1073                if(!mat.baseMap.displaySystem && info->parent->chunkId == MAT_MAPOPACITY && opacityMap && opacityMap.width && opacityMap.height)
1074                {
1075                   ColorAlpha * picture = (ColorAlpha *)mat.baseMap.picture;
1076                   ColorAlpha * opacityPicture = (ColorAlpha *)opacityMap.picture;
1077                   uint x, y;
1078                   int ow = opacityMap.width, oh = opacityMap.height;
1079                   int bw = mat.baseMap.width, bh = mat.baseMap.height;
1080
1081                   for(y = 0; y < bh; y++)
1082                      for(x = 0; x < bw; x++)
1083                      {
1084                         int bc = ((y % bh) * bw + (x % bw));
1085                         int oc = ((y % oh) * bw + (x % ow));
1086                         byte alpha = alphaOnly ? opacityPicture[oc].color.r : opacityPicture[oc].a;
1087                         if(alpha && alpha < 255)
1088                            translucent = true;
1089                         picture[bc] = ColorAlpha { alpha, picture[bc].color };
1090                      }
1091                }
1092                if(translucent)
1093                   mat.flags.translucent = true;
1094                mat.diffuse.r = mat.diffuse.g = mat.diffuse.b =
1095                mat.ambient.r = mat.ambient.g = mat.ambient.b = 1.0f;
1096             }
1097             if(opacityMap != mat.baseMap)
1098                delete opacityMap;
1099          }
1100          delete name;
1101          break;
1102       }
1103       case MAP_OPTIONS:
1104       {
1105          uint16 options = ReadWORD(info->f);
1106          if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
1107          break;
1108       }
1109       case MAP_1_U_SCALE:
1110          mat.uScale = ReadFloat(info->f);
1111          break;
1112       case MAP_1_V_SCALE:
1113          mat.vScale = ReadFloat(info->f);
1114          break;
1115       /*
1116       case AMOUNT_OF:
1117          break;
1118       default:
1119          PrintLn("Unhandled Map block");
1120          break;
1121       */
1122    }
1123    return true;
1124 }
1125
1126 static bool ReadMaterial(FileInfo * info, Material mat)
1127 {
1128    switch(info->chunkId)
1129    {
1130       case MAT_NAME:
1131       {
1132          String name;
1133          char matName[MAX_LOCATION + 100];
1134          strcpy(matName, info->fileName);
1135          ReadASCIIZ(info->f, &name);
1136          strcat(matName, name);
1137          mat.name = CopyString(matName);
1138          delete name;
1139          break;
1140       }
1141       case MAT_TRANSPARENCY:
1142       {
1143          uint16 transparency;
1144          ReadChunks(ReadAmountOf, info, &transparency);
1145          mat.opacity = 1.0f - transparency / 100.0f;
1146          if(mat.opacity < 1.0)
1147             mat.flags.translucent = true;
1148          break;
1149       }
1150       case MAT_DIFFUSE:
1151       {
1152          ReadChunks(ReadRGB, info, &mat.diffuse);
1153          ReadChunks(ReadRGB, info, &mat.diffuse);
1154          break;
1155       }
1156       case MAT_AMBIENT:
1157       {
1158          ReadChunks(ReadRGB, info, &mat.ambient);
1159          ReadChunks(ReadRGB, info, &mat.ambient);
1160          break;
1161       }
1162       case MAT_SPECULAR:
1163       {
1164          ReadChunks(ReadRGB, info, &mat.specular);
1165          ReadChunks(ReadRGB, info, &mat.specular);
1166          break;
1167       }
1168       case MAT_SELFILLUM:
1169       {
1170          uint16 emissive;
1171          ReadChunks(ReadAmountOf, info, &emissive);
1172          mat.emissive.r = mat.diffuse.r * emissive / 100.0f;
1173          mat.emissive.g = mat.diffuse.g * emissive / 100.0f;
1174          mat.emissive.b = mat.diffuse.b * emissive / 100.0f;
1175          break;
1176       }
1177       case MAT_SHINSTRENGTH:
1178       {
1179          uint16 shininess;
1180          ReadChunks(ReadAmountOf, info, &shininess);
1181          mat.specular.r *= shininess / 100.0f;
1182          mat.specular.g *= shininess / 100.0f;
1183          mat.specular.b *= shininess / 100.0f;
1184          break;
1185       }
1186       case MAT_SHININESS:
1187       {
1188          uint16 power;
1189          ReadChunks(ReadAmountOf, info, &power);
1190          mat.power = power;
1191          break;
1192       }
1193       case MAT_MAPTEXTURE1:
1194          ReadChunks(ReadMap, info, mat);
1195          break;
1196       case MAT_MAPOPACITY:
1197          ReadChunks(ReadMap, info, mat);
1198          break;
1199       case MAT_DOUBLESIDED:
1200          mat.flags.doubleSided = true;
1201          break;
1202       case MAT_BUMPMAP:
1203          ReadChunks(ReadMap, info, mat);
1204          break;
1205       /*
1206       default:
1207          PrintLn("Unhandled MAT type ID", info->chunkId);
1208       */
1209    }
1210    return true;
1211 }
1212
1213 // Lights
1214 static bool ReadLight(FileInfo * info, Object object)
1215 {
1216    Mesh mesh = object.mesh;
1217    Light * light = &object.light;
1218    switch(info->chunkId)
1219    {
1220       case RGB_BYTE:
1221       case RGB_BYTE_GAMMA:
1222       case RGB_FLOAT:
1223       case RGB_FLOAT_GAMMA:
1224          ReadRGB(info, &light->diffuse);
1225          light->specular = light->diffuse;
1226          break;
1227       case LIT_SPOT:
1228       {
1229          Object target;
1230          char targetName[MAXNAMELEN];
1231
1232          strcpy(targetName, object.name);
1233          strcat(targetName, ".target");
1234
1235          light->flags.omni = false;
1236          light->flags.spot = true;
1237
1238          target = Object { };
1239          target.name = CopyString(targetName);
1240          info->rootObject.children.AddName(target);
1241          target.parent = info->rootObject;
1242
1243          light->target = target;
1244
1245          target.transform.position.x = ReadFloat(info->f);
1246          target.transform.position.z = ReadFloat(info->f);
1247          target.transform.position.y =-ReadFloat(info->f);
1248
1249          light->hotSpot = ReadFloat(info->f);
1250          light->fallOff = ReadFloat(info->f);
1251          break;
1252       }
1253       case LIT_ONOFF:
1254       {
1255          light->flags.off = true;
1256          break;
1257       }
1258       case LIT_ATTENUATION:
1259       {
1260          /* solve (
1261             {
1262                d = 300, small = 0.001,
1263                Kl = 0, Kc = 0,
1264                d * (Kl + Kq * d) = (1 / small) - Kc
1265             },
1266             { Kc, Kl, Kq, small, d });
1267          */
1268
1269          light->flags.attenuation = true;
1270
1271          /*
1272          #define MINLIGHT     0.08
1273          light->Kq = 1/(light->end*light->end*MINLIGHT);
1274          */
1275
1276          #define MINLIGHT     0.15f
1277          // #define MINLIGHT     0.1
1278          light->Kl = (float)(1/(light->end*MINLIGHT));
1279
1280          break;
1281       }
1282       case LIT_START:
1283       {
1284          light->start = ReadFloat(info->f);
1285          break;
1286       }
1287       case LIT_END:
1288       {
1289          light->end = ReadFloat(info->f);
1290          break;
1291       }
1292       case LIT_MULTIPLIER:
1293       {
1294          light->multiplier = ReadFloat(info->f);
1295          break;
1296       }
1297    }
1298    return true;
1299 }
1300
1301 // Cameras
1302 static bool ReadCamera(FileInfo * info, Object object)
1303 {
1304    Mesh mesh = object.mesh;
1305    switch(info->chunkId)
1306    {
1307       case CAM_SEECONE:
1308       {
1309          break;
1310       }
1311       case CAM_RANGES:
1312       {
1313          Camera camera = object.camera;
1314          float nearRange = ReadFloat(info->f);
1315          float farRange = ReadFloat(info->f);
1316          /*
1317          camera.zMin = Max(0.1, nearRange);
1318          camera.zMax = farRange;
1319          */
1320          break;
1321       }
1322    }
1323    return true;
1324 }
1325
1326 // Edit Chunks
1327 static bool ReadEditObject(FileInfo * info, char * name)
1328 {
1329    DisplaySystem displaySystem = info->displaySystem;
1330    switch(info->chunkId)
1331    {
1332       case OBJ_TRIMESH:
1333       {
1334          Object object = info->rootObject.Find(name);
1335          if(!object)
1336          {
1337             object = Object { };
1338             object.name = CopyString(name);
1339             info->rootObject.children.AddName(object);
1340             object.parent = info->rootObject;
1341          }
1342          object.InitializeMesh(displaySystem);
1343          ReadChunks(ReadTriMesh, info, object);
1344          object.flags.mesh = true;
1345          object.mesh.Unlock(0);
1346          break;
1347       }
1348       case OBJ_LIGHT:
1349       {
1350          Object object = info->rootObject.Find(name);
1351          Light * light;
1352          Vector3Df position;
1353
1354          if(!object)
1355          {
1356             object = Object { };
1357             object.name = CopyString(name);
1358             info->rootObject.children.AddName(object);
1359             object.parent = info->rootObject;
1360          }
1361          object.flags.light = true;
1362
1363          light = &object.light;
1364          light->lightObject = object;
1365          light->flags.omni = true;
1366          light->multiplier = 1.0f;
1367
1368          // This is not used?
1369          Read3DVertex(info->f, position);
1370          light->direction = { position.x, position.y, position.z };
1371          info->pos += sizeof(float) * 3;
1372
1373          ReadChunks(ReadLight, info, object);
1374          break;
1375       }
1376       case OBJ_CAMERA:
1377       {
1378          char targetName[MAXNAMELEN];
1379          Object object = info->rootObject.Find(name);
1380          Object target;
1381          Camera camera;
1382          float bankAngle, focus;
1383          double mm;
1384
1385          strcpy(targetName, name);
1386          strcat(targetName, ".target");
1387          target = info->rootObject.Find(targetName);
1388
1389          if(!object)
1390          {
1391             object = Object { };
1392             object.name = CopyString(name);
1393             info->rootObject.children.AddName(object);
1394
1395             object.parent = info->rootObject;
1396             object.camera = Camera { };
1397             object.camera.type = lookAtObject;
1398          }
1399
1400          if(!target)
1401          {
1402             target = Object { };
1403             target.name = CopyString(targetName);
1404             info->rootObject.children.AddName(target);
1405             target.parent = info->rootObject;
1406          }
1407
1408          object.flags.camera = true;
1409          object.cameraTarget = target;
1410
1411          camera = object.camera;
1412          camera.cameraObject = object;
1413          camera.target = target;
1414
1415          //Read3DVertex(info->f, camera.position);
1416          object.transform.position.x = ReadFloat(info->f);
1417          object.transform.position.z = ReadFloat(info->f);
1418          object.transform.position.y =-ReadFloat(info->f);
1419
1420          info->pos += sizeof(float) * 3;
1421          //Read3DVertex(info->f, object.cameraTarget.position);
1422          target.transform.position.x = ReadFloat(info->f);
1423          target.transform.position.z = ReadFloat(info->f);
1424          target.transform.position.y =-ReadFloat(info->f);
1425
1426          info->pos += sizeof(float) * 3;
1427          bankAngle = ReadFloat(info->f);
1428          info->pos += sizeof(float);
1429          focus = ReadFloat(info->f);
1430          info->pos += sizeof(float);
1431
1432          mm = (focus - 5.05659508373109) / 1.13613250717301;
1433          camera.fov = (float)(1248.58921609766 * pow(mm, -0.895625414990581));
1434
1435          ReadChunks(ReadCamera, info, object);
1436          break;
1437       }
1438       case OBJ_HIDDEN: break;
1439    }
1440    return true;
1441 }
1442
1443 #define COPY_NITEM(d, s) CopyBytes(((byte *)(d)) + sizeof(class NamedItem), ((byte *)(s)) + sizeof(class NamedItem), sizeof((*s)) - sizeof(class NamedItem));
1444
1445 static bool ReadEditChunks(FileInfo * info, void * data)
1446 {
1447    switch(info->chunkId)
1448    {
1449       case EDIT_AMBIENT:
1450       {
1451          // Read the ambient color
1452          ReadChunks(ReadRGB, info, &info->rootObject.ambient);
1453          break;
1454       }
1455       case EDIT_MATERIAL:
1456       {
1457          Material material { /*flags = { singleSideLight = true }*/ };
1458          Material mat;
1459          ReadChunks(ReadMaterial, info, material);
1460
1461          mat = info->displaySystem.AddNamedMaterial(material.name);
1462          if(mat)
1463          {
1464             if(material.baseMap)
1465                material.baseMap.MakeMipMaps(info->displaySystem);
1466             if(material.bumpMap)
1467                material.bumpMap.MakeMipMaps(info->displaySystem);
1468             // COPY_NITEM(mat, material);
1469             CopyBytes(((byte *)(mat)) + sizeof(class NamedItem), ((byte *)(material)) + sizeof(class NamedItem), sizeof(class Material) - sizeof(class NamedItem));
1470          }
1471          else
1472          {
1473             delete material.baseMap;
1474          }
1475          delete material.name;
1476          delete material;
1477          break;
1478       }
1479       case EDIT_OBJECT:
1480       {
1481          char * name;
1482          info->pos += ReadASCIIZ(info->f, &name);
1483          ReadChunks(ReadEditObject, info, name);
1484          delete name;
1485          break;
1486       }
1487    }
1488    return true;
1489 }
1490
1491 struct ObjectInfoBlock
1492 {
1493    OldList tracks;
1494    short hierarchy;
1495    short parent;
1496    char * name;
1497    char * dummyName;
1498    Vector3Df pivot;
1499 };
1500
1501 // Key Framer Chunks
1502
1503 #define ACCFLAG_TENSION    0x00000001
1504 #define ACCFLAG_CONTINUITY 0x00000002
1505 #define ACCFLAG_BIAS       0x00000004
1506 #define ACCFLAG_EASETO     0x00000008
1507 #define ACCFLAG_EASEFROM   0x00000010
1508
1509 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
1510 {
1511    switch(info->chunkId)
1512    {
1513       case FRM_PARAM:
1514       {
1515          uint16 flags1, flags2;
1516          ReadASCIIZ(info->f, &block->name);
1517          flags1 = ReadWORD(info->f);
1518          flags2 = ReadWORD(info->f);
1519          block->parent = ReadWORD(info->f);
1520          break;
1521       }
1522       case FRM_DUMMYNAME:
1523          ReadASCIIZ(info->f, &block->dummyName);
1524          break;
1525       case FRM_PIVOT:
1526          Read3DVertex(info->f, block->pivot);
1527          break;
1528       case FRM_HIERARCHY:
1529          block->hierarchy = ReadWORD(info->f);
1530          break;
1531       case FRM_TRACKPOS:
1532       case FRM_TRACKROT:
1533       case FRM_TRACKSCALE:
1534       case FRM_TRACKROLL:
1535       case FRM_TRACKFOV:
1536       case FRM_TRACKCOLOR:
1537       case FRM_TRACKHOTSPOT:
1538       case FRM_TRACKFALLOFF:
1539       {
1540          FrameTrack track { };
1541          if(track)
1542          {
1543             uint16 flags;
1544             byte unknown[8];
1545             uint c;
1546
1547             block->tracks.Add(track);
1548
1549             flags = ReadWORD(info->f);
1550
1551             info->f.Read(unknown, sizeof(unknown), 1);
1552
1553             track.numKeys = ReadDWORD(info->f);
1554
1555             switch(info->chunkId)
1556             {
1557                case FRM_TRACKPOS: track.type.type = position; break;
1558                case FRM_TRACKROT: track.type.type = rotation; break;
1559                case FRM_TRACKSCALE: track.type.type = scaling; break;
1560                case FRM_TRACKROLL: track.type.type = roll; break;
1561                case FRM_TRACKFOV: track.type.type = fov; break;
1562                case FRM_TRACKCOLOR: track.type.type = colorChange; break;
1563                case FRM_TRACKHOTSPOT: track.type.type = hotSpot; break;
1564                case FRM_TRACKFALLOFF: track.type.type = fallOff; break;
1565             }
1566             if((flags & 0x0003) == 3)
1567                track.type.loop = true;
1568
1569             track.keys = new0 FrameKey[track.numKeys];
1570             for(c = 0; c<track.numKeys; c++)
1571             {
1572                uint16 accelerationFlags;
1573                FrameKey * key = track.keys + c;
1574
1575                key->frame = ReadDWORD(info->f);
1576                accelerationFlags = ReadWORD(info->f);
1577
1578                if(accelerationFlags & ACCFLAG_TENSION)
1579                   key->tension = ReadFloat(info->f);
1580                if(accelerationFlags & ACCFLAG_CONTINUITY)
1581                   key->continuity = ReadFloat(info->f);
1582                if(accelerationFlags & ACCFLAG_BIAS)
1583                   key->bias = ReadFloat(info->f);
1584                if(accelerationFlags & ACCFLAG_EASETO)
1585                   key->easeTo = ReadFloat(info->f);
1586                if(accelerationFlags & ACCFLAG_EASEFROM)
1587                   key->easeFrom = ReadFloat(info->f);
1588
1589                switch(info->chunkId)
1590                {
1591                   case FRM_TRACKPOS:
1592                   {
1593                      Vector3Df position;
1594                      Read3DVertex(info->f, position);
1595                      key->position = { position.x, -position.z, position.y };
1596                      break;
1597                   }
1598                   case FRM_TRACKROT:
1599                   {
1600                      Vector3Df axis;
1601                      Angle angle = ReadFloat(info->f);
1602                      Vector3Df fixedAxis;
1603
1604                      Read3DVertex(info->f, axis);
1605                      fixedAxis.x = axis.x;
1606                      fixedAxis.y = -axis.z;
1607                      fixedAxis.z = axis.y;
1608
1609                      if(c > 0)
1610                      {
1611                         Quaternion rotation;
1612                         rotation.RotationAxis(fixedAxis, angle);
1613                         key->orientation.Multiply((key - 1)->orientation, rotation);
1614                      }
1615                      else
1616                         key->orientation.RotationAxis(fixedAxis, angle);
1617                      break;
1618                   }
1619                   case FRM_TRACKSCALE:
1620                   {
1621                      Vector3Df scaling;
1622                      Read3DVertex(info->f, scaling);
1623                      key->scaling = { scaling.x, scaling.z, scaling.y };
1624                      break;
1625                   }
1626                   case FRM_TRACKFOV:
1627                   {
1628                      key->fov = ReadFloat(info->f);
1629                      break;
1630                   }
1631                   case FRM_TRACKROLL:
1632                   {
1633                      key->roll = -ReadFloat(info->f);
1634                      break;
1635                   }
1636                   case FRM_TRACKCOLOR:
1637                   {
1638                      FileInfo childInfo = *info;
1639                      childInfo.chunkId = RGB_FLOAT;
1640                      ReadRGB(&childInfo, &key->color);
1641                      break;
1642                   }
1643                   case FRM_TRACKHOTSPOT:
1644                   {
1645                      key->hotSpot = ReadFloat(info->f);
1646                      break;
1647                   }
1648                   case FRM_TRACKFALLOFF:
1649                   {
1650                      key->fallOff = ReadFloat(info->f);
1651                      break;
1652                   }
1653                }
1654             }
1655          }
1656          break;
1657       }
1658    }
1659    return true;
1660 }
1661
1662 static Object FindObjectID(Object object, int id)
1663 {
1664    Object result = null;
1665    Object child;
1666    for(child = object.children.first; child; child = child.next)
1667    {
1668       if(child.flags.hierarchy == (uint16) id)
1669       {
1670          result = child;
1671          break;
1672       }
1673       else
1674       {
1675          result = FindObjectID(child, id);
1676          if(result) break;
1677       }
1678    }
1679    return result;
1680 }
1681
1682 static bool ReadKeyFrameChunks(FileInfo * info, void * data)
1683 {
1684    switch(info->chunkId)
1685    {
1686       case FRM_MESHINFO:
1687       {
1688          ObjectInfoBlock block { };
1689          Object object;
1690
1691          ReadChunks(ReadFrameInfoBlock, info, &block);
1692
1693          if(block.dummyName && block.dummyName[0])
1694          {
1695             if(!strcmp(block.name, "$$$DUMMY"))
1696             {
1697                object = Object { };
1698                object.name = block.dummyName;
1699                info->rootObject.children.AddName(object);
1700                object.transform.scaling = { 1,1,1 };
1701             }
1702             else
1703             {
1704                Object model = info->rootObject.Find(block.name);
1705                if(model)
1706                {
1707                   object = Object { };
1708                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1709                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1710                   object.flags = model.flags;
1711                   object.flags.ownMesh = false;
1712                   object.mesh = model.mesh;
1713                   /*
1714                   object.min = model.min;
1715                   object.max = model.max;
1716                   object.radius = model.radius;
1717                   */
1718                   object.transform = model.transform;
1719                   info->rootObject.children.AddName(object);
1720                }
1721                delete block.dummyName;
1722             }
1723             object.parent = info->rootObject;
1724          }
1725          else
1726             object = info->rootObject.Find(block.name);
1727
1728          if(object)
1729          {
1730             Mesh mesh = object.mesh;
1731             object.flags.hierarchy = block.hierarchy + 1;
1732             if(block.parent != -1)
1733             {
1734                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1735                if(parent)
1736                {
1737                   object.parent.children.Remove(object);
1738                   parent.children.AddName(object);
1739                   object.parent = parent;
1740                }
1741             }
1742             object.pivot.x = block.pivot.x;
1743             object.pivot.y =-block.pivot.z;
1744             object.pivot.z = block.pivot.y;
1745
1746             if(mesh && object.flags.ownMesh)
1747             {
1748                if(mesh.Lock({ vertices = true }))
1749                {
1750                   int c;
1751                   // Take pivot into account
1752                   for(c = 0; c<mesh.nVertices; c++)
1753                   {
1754                      mesh.vertices[c].x -= object.pivot.x;
1755                      mesh.vertices[c].y -= object.pivot.y;
1756                      mesh.vertices[c].z -= object.pivot.z;
1757                   }
1758                   mesh.Unlock({ vertices = true });
1759                }
1760             }
1761
1762             object.tracks = block.tracks;
1763             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1764             object.endFrame = info->rootObject.endFrame;
1765          }
1766          /*else
1767             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1768          delete block.name;
1769          break;
1770       }
1771       case FRM_CAMERA:
1772       {
1773          ObjectInfoBlock block { };
1774          Object object;
1775
1776          ReadChunks(ReadFrameInfoBlock, info, &block);
1777
1778          if(block.dummyName && block.dummyName[0])
1779          {
1780             if(!strcmp(block.name, "$$$DUMMY"))
1781             {
1782                object = Object { };
1783                object.name = block.dummyName;
1784                info->rootObject.children.AddName(object);
1785                object.transform.scaling = { 1, 1, 1 };
1786                object.flags.camera = true;
1787             }
1788             else
1789             {
1790                Object model = info->rootObject.Find(block.name);
1791                if(model)
1792                {
1793                   object = Object { };
1794                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1795                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1796                   object.flags = model.flags;
1797                   object.flags.ownMesh = false;
1798                   object.camera = model.camera;
1799                   object.flags.camera = true;
1800                   info->rootObject.children.AddName(object);
1801                }
1802                delete block.dummyName;
1803             }
1804             object.parent = info->rootObject;
1805          }
1806          else
1807             object = info->rootObject.Find(block.name);
1808
1809          if(object)
1810          {
1811             object.flags.hierarchy = block.hierarchy + 1;
1812             if(block.parent != -1)
1813             {
1814                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1815                if(parent)
1816                {
1817                   object.parent.children.Remove(object);
1818                   parent.children.AddName(object);
1819                   object.parent = parent;
1820                }
1821             }
1822             object.pivot.x = block.pivot.x;
1823             object.pivot.y =-block.pivot.z;
1824             object.pivot.z = block.pivot.y;
1825
1826             object.tracks = block.tracks;
1827             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1828             object.endFrame = info->rootObject.endFrame;
1829          }
1830          /*else
1831             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1832          delete block.name;
1833          break;
1834       }
1835       case FRM_CAMERATARGET:
1836       {
1837          ObjectInfoBlock block { };
1838          Object object;
1839          char targetName[MAXNAMELEN];
1840
1841          ReadChunks(ReadFrameInfoBlock, info, &block);
1842
1843          strcpy(targetName, block.name);
1844          strcat(targetName, ".target");
1845
1846          if(block.dummyName && block.dummyName[0])
1847          {
1848             if(!strcmp(block.name, "$$$DUMMY"))
1849             {
1850                object = Object { };
1851                object.name = block.dummyName;
1852                info->rootObject.children.AddName(object);
1853                object.transform.scaling = { 1,1,1 };
1854             }
1855             else
1856             {
1857                Object model = info->rootObject.Find(targetName);
1858                if(model)
1859                {
1860                   object = Object { };
1861                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1862                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1863                   object.flags = model.flags;
1864                   object.flags.ownMesh = false;
1865                   object.camera = model.camera;
1866                   info->rootObject.children.AddName(object);
1867                }
1868                delete block.dummyName;
1869             }
1870             object.parent = info->rootObject;
1871          }
1872          else
1873             object = info->rootObject.Find(targetName);
1874
1875          if(object)
1876          {
1877             object.flags.hierarchy = block.hierarchy + 1;
1878             if(block.parent != -1)
1879             {
1880                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1881                if(parent)
1882                {
1883                   object.parent.children.Remove(object);
1884                   parent.children.AddName(object);
1885                   object.parent = parent;
1886                }
1887             }
1888             object.pivot.x = block.pivot.x;
1889             object.pivot.y =-block.pivot.z;
1890             object.pivot.z = block.pivot.y;
1891
1892             object.tracks = block.tracks;
1893             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1894             object.endFrame = info->rootObject.endFrame;
1895          }
1896          /*else
1897             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1898          delete block.name;
1899          break;
1900       }
1901       case FRM_AMBIENT:
1902       {
1903          ObjectInfoBlock block { };
1904
1905          ReadChunks(ReadFrameInfoBlock, info, &block);
1906
1907          info->rootObject.tracks = block.tracks;
1908          break;
1909       }
1910       case FRM_OMNILIGHT:
1911       case FRM_SPOTLIGHT:
1912       {
1913          ObjectInfoBlock block { };
1914          Object object;
1915
1916          ReadChunks(ReadFrameInfoBlock, info, &block);
1917
1918          if(block.dummyName && block.dummyName[0])
1919          {
1920             if(!strcmp(block.name, "$$$DUMMY"))
1921             {
1922                object = Object { };
1923                object.name = block.dummyName;
1924                info->rootObject.children.AddName(object);
1925                object.transform.scaling = { 1, 1, 1 };
1926                object.flags.light = true;
1927             }
1928             else
1929             {
1930                Object model = info->rootObject.Find(block.name);
1931                if(model)
1932                {
1933                   object = Object { };
1934                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
1935                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
1936                   object.flags = model.flags;
1937                   object.flags.ownMesh = false;
1938                   object.light = model.light;
1939                   object.flags.light = true;
1940                   info->rootObject.children.AddName(object);
1941                }
1942                delete block.dummyName;
1943             }
1944             object.parent = info->rootObject;
1945          }
1946          else
1947             object = info->rootObject.Find(block.name);
1948
1949          if(object)
1950          {
1951             object.flags.hierarchy = block.hierarchy + 1;
1952             if(block.parent != -1)
1953             {
1954                Object parent = FindObjectID(info->rootObject, block.parent + 1);
1955                if(parent)
1956                {
1957                   object.parent.children.Remove(object);
1958                   parent.children.AddName(object);
1959                   object.parent = parent;
1960                }
1961             }
1962             object.pivot.x = block.pivot.x;
1963             object.pivot.y =-block.pivot.z;
1964             object.pivot.z = block.pivot.y;
1965
1966             object.tracks = block.tracks;
1967             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
1968             object.endFrame = info->rootObject.endFrame;
1969          }
1970          /*else
1971             Logf("Object not found while loading animation frame: %s\n", block.name);*/
1972          delete block.name;
1973          break;
1974       }
1975       case FRM_SPOTLIGHTTARGET:
1976       {
1977          ObjectInfoBlock block { };
1978          Object object;
1979          char targetName[MAXNAMELEN];
1980
1981          ReadChunks(ReadFrameInfoBlock, info, &block);
1982
1983          strcpy(targetName, block.name);
1984          strcat(targetName, ".target");
1985
1986          if(block.dummyName && block.dummyName[0])
1987          {
1988             if(!strcmp(block.name, "$$$DUMMY"))
1989             {
1990                object = Object { };
1991                object.name = block.dummyName;
1992                info->rootObject.children.AddName(object);
1993                object.transform.scaling = { 1,1,1 };
1994             }
1995             else
1996             {
1997                Object model = info->rootObject.Find(targetName);
1998                if(model)
1999                {
2000                   object = Object { };
2001                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
2002                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
2003                   object.flags = model.flags;
2004                   object.flags.ownMesh = false;
2005                   object.light = model.light;
2006                   info->rootObject.children.AddName(object);
2007                }
2008                delete block.dummyName;
2009             }
2010             object.parent = info->rootObject;
2011          }
2012          else
2013             object = info->rootObject.Find(targetName);
2014
2015          if(object)
2016          {
2017             object.flags.hierarchy = block.hierarchy + 1;
2018             if(block.parent != -1)
2019             {
2020                Object parent = FindObjectID(info->rootObject, block.parent + 1);
2021                if(parent)
2022                {
2023                   object.parent.children.Remove(object);
2024                   parent.children.AddName(object);
2025                   object.parent = parent;
2026                }
2027             }
2028             object.pivot.x = block.pivot.x;
2029             object.pivot.y =-block.pivot.z;
2030             object.pivot.z = block.pivot.y;
2031
2032             object.tracks = block.tracks;
2033             *&(object.frame) = object.startFrame = info->rootObject.startFrame;
2034             object.endFrame = info->rootObject.endFrame;
2035          }
2036          /*else
2037             Logf("Object not found while loading animation frame: %s\n", block.name);*/
2038          delete block.name;
2039          break;
2040       }
2041       case FRM_FRAMES:
2042       {
2043          info->rootObject.startFrame = ReadDWORD(info->f);
2044          info->rootObject.endFrame = ReadDWORD(info->f);
2045          *&(info->rootObject.frame) = info->rootObject.startFrame;
2046          break;
2047       }
2048    }
2049    return true;
2050 }
2051
2052 // Main Chunks
2053 static bool ReadMainChunks(FileInfo * info, void * data)
2054 {
2055    switch(info->chunkId)
2056    {
2057       case EDIT3DS:
2058          ReadChunks(ReadEditChunks, info, null);
2059          break;
2060       case KEYFRAME3DS:
2061       {
2062          Object object = data;
2063          if(!(object.flags.keysLoaded)) // Don't read key frames on reload
2064          {
2065             ReadChunks(ReadKeyFrameChunks, info, null);
2066             object.flags.keysLoaded = true;
2067          }
2068          break;
2069       }
2070    }
2071    return true;
2072 }
2073
2074 static bool ReadMain(FileInfo * info, void * data)
2075 {
2076    switch(info->chunkId)
2077    {
2078       case MAIN3DS:
2079          ReadChunks(ReadMainChunks, info, data);
2080          break;
2081    }
2082    return true;
2083 }
2084
2085 class Object3DSFormat : ObjectFormat
2086 {
2087    class_property(extension) = "3ds";
2088
2089    bool Load(Object object, char * fileName, DisplaySystem displaySystem)
2090    {
2091       bool result = false;
2092       if(fileName)
2093       {
2094          FileInfo info = {0};
2095          info.rootObject = object;
2096          info.displaySystem = displaySystem;
2097          info.pos = 0;
2098          info.end = 1;
2099          info.fileName = fileName;
2100          StripLastDirectory(fileName, info.textureDirectory);
2101          info.f = FileOpen(fileName, read);
2102          if(info.f)
2103          {
2104             if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
2105             {
2106                object.flags.root = true;
2107                object.SetMinMaxRadius(true);
2108                object._Animate(object.frame);
2109                object.UpdateTransform();
2110                result = true;
2111             }
2112             delete info.f;
2113          }
2114          if(info.matFaces)
2115             info.matFaces.Free();
2116          delete info.matFaces;
2117       }
2118       if(!result)
2119          object.Free(displaySystem);
2120       return result;
2121    }
2122 }