ecere/gfx/drivers/OpenGL (Android): Fixed blank textures
[sdk] / ecere / src / gfx / 3D / models / Object3DSFormat.ec
index 7d6d4bb..18afb6c 100644 (file)
@@ -2,160 +2,191 @@ namespace gfx3D::models;
 
 import "Object"
 
+#if defined(__EMSCRIPTEN__)
+#if !defined(_GLES2)
+#define _GLES2
+#endif
+#endif
+
+#if (defined(__ANDROID__) || defined(__ODROID__)) && !defined(_GLES)
+#define _GLES
+#endif
+
+#if !defined(_GLES) && !defined(_GLES2)
+#define USE_32_BIT_INDICES true
+#define indicesMember indices32
+#define uintindex uint32
+#else
+#define USE_32_BIT_INDICES false
+#define indicesMember indices
+#define uintindex uint16
+#endif
+
 #define MAXNAMELEN   64
 
-// RGB Chunks
-#define RGB_FLOAT             0x0010
-#define RGB_BYTE              0x0011
-#define RGB_BYTE_GAMMA        0x0012
-#define RGB_FLOAT_GAMMA       0x0013
-
-// Amount Of Chunks
-#define AMOUNT_OF             0x0030
-
-#define MAIN3DS               0x4D4D
-#define EDIT3DS               0x3D3D
-#define EDIT_AMBIENT          0x2100
-#define EDIT_MATERIAL         0xAFFF
-#define EDIT_OBJECT           0x4000
-#define OBJ_HIDDEN            0x4010
-#define OBJ_TRIMESH           0x4100
-#define OBJ_LIGHT             0x4600
-#define OBJ_CAMERA            0x4700
-
-// Triangular Mesh Chunks
-#define TRI_VERTEXL           0x4110
-#define TRI_FACEL1            0x4120
-#define TRI_MATERIAL          0x4130
-#define TRI_MAPPINGCOORS      0x4140
-#define TRI_SMOOTHING         0x4150
-#define TRI_LOCAL             0x4160
-
-// Light Chunks
-#define LIT_SPOT              0x4610
-#define LIT_ONOFF             0x4620
-#define LIT_ATTENUATION       0x4625
-#define LIT_START             0x4659
-#define LIT_END               0x465A
-#define LIT_MULTIPLIER        0x465B
-
-// Camera Chunks
-#define CAM_SEECONE           0x4710
-#define CAM_RANGES            0x4720
-
-// Material Chunks
-#define MAT_NAME              0xA000
-#define MAT_AMBIENT           0xA010
-#define MAT_DIFFUSE           0xA020
-#define MAT_SPECULAR          0xA030
-#define MAT_SHININESS         0xA040
-#define MAT_SHINSTRENGTH      0xA041
-#define MAT_SHIN3PC           0xA042
-#define MAT_TRANSPARENCY      0xA050
-#define MAT_XPFALL            0xA052
-#define MAT_REFBLUR           0xA053
-#define MAT_SELFILLUM         0xA080
-#define MAT_DOUBLESIDED       0xA081
-#define MAT_ADDITIVE          0xA083
-#define MAT_SELFILPCT         0xA084
-#define MAT_WIRE              0xA085
-#define MAT_SUPERSMP          0xA086
-#define MAT_WIRETHICKNESS     0xA087
-#define MAT_FACEMAP           0xA088
-#define MAT_XPFALLIN          0xA08A
-#define MAT_PHONG             0xA08C
-#define MAT_WIREABS           0xA08E
-#define MAT_SHADING           0xA100
-#define MAT_MAPTEXTURE1       0xA200
-#define MAT_SPECULARMAP       0xA204
-#define MAT_MAPOPACITY        0xA210
-#define MAT_REFLECTIONMAP     0xA220
-#define MAT_BUMPMAP           0xA230
-
-// Map Chunks
-#define MAP_FILENAME          0xA300
-
-#define MAT_SHININESSMAP      0xA33C
-#define MAT_EMISSIVEMAP       0xA33D
-#define MAP_OPTIONS           0xA351
-#define MAP_1_U_SCALE   0xA354
-#define MAP_1_V_SCALE   0xA356
-#define MAP_U_OFFSET    0xA358
-#define MAP_V_OFFSET    0xA35A
-#define MAP_ROTATION    0xA35C
-
-#define MAP_OPTIONS_DECAL           0x0001      // (related to MAP_OPTIONS_DONTTILE)
-#define MAP_OPTIONS_MIRROR          0x0002
-#define MAP_OPTIONS_NEGATIVE        0x0008
-#define MAP_OPTIONS_DONTTILE        0x0010
-#define MAP_OPTIONS_SUMMEDFILTERING 0x0020
-#define MAP_OPTIONS_USEALPHA        0x0040
-#define MAP_OPTIONS_LUMORALPHATINT  0x0080
-#define MAP_OPTIONS_IGNOREALPHA     0x0100
-#define MAP_OPTIONS_RGBTINT         0x0200
-
-#define MAP_FILTERBLUR        0xA353
-#define MAP_1_U_SCALE         0xA354
-#define MAP_1_V_SCALE         0xA356
-#define MAP_U_OFFSET          0xA358
-#define MAP_V_OFFSET          0xA35A
-#define MAP_ROTATION          0xA35C
-
-#define MAP_LUMTINT1          0xA360
-#define MAP_LUMTINT2          0xA362
-
-#define MAP_TINTR             0xA364
-#define MAP_TINTG             0xA366
-#define MAP_TINTB             0xA368
-
-// Keyframer Chunks
-#define KEYFRAME3DS           0xB000
-#define FRM_AMBIENT           0xB001
-#define FRM_MESHINFO          0xB002
-#define FRM_CAMERA            0xB003
-#define FRM_CAMERATARGET      0xB004
-#define FRM_OMNILIGHT         0xB005
-#define FRM_SPOTLIGHTTARGET   0xB006
-#define FRM_SPOTLIGHT         0xB007
-#define FRM_FRAMES            0xB008
-#define FRM_PARAM             0xB010
-#define FRM_DUMMYNAME         0xB011
-#define FRM_PIVOT             0xB013
-#define FRM_TRACKPOS          0xB020
-#define FRM_TRACKROT          0xB021
-#define FRM_TRACKSCALE        0xB022
-#define FRM_TRACKFOV          0xB023
-#define FRM_TRACKROLL         0xB024
-#define FRM_TRACKCOLOR        0xB025
-#define FRM_TRACKMORPH        0xB026   // What does this do?
-#define FRM_TRACKHOTSPOT      0xB027
-#define FRM_TRACKFALLOFF      0xB028
-#define FRM_TRACKHIDE         0xB029
-#define FRM_HIERARCHY         0xB030
+static enum ChunkID3DS : uint16
+{
+   // RGB Chunks
+   RGB_FLOAT             = 0x0010,
+   RGB_BYTE              = 0x0011,
+   RGB_BYTE_GAMMA        = 0x0012,
+   RGB_FLOAT_GAMMA       = 0x0013,
+
+   // Amount Of Chunks
+   AMOUNT_OF             = 0x0030,
+
+   MAIN3DS               = 0x4D4D,
+   EDIT3DS               = 0x3D3D,
+   EDIT_AMBIENT          = 0x2100,
+   EDIT_MATERIAL         = 0xAFFF,
+   EDIT_OBJECT           = 0x4000,
+   OBJ_HIDDEN            = 0x4010,
+   OBJ_TRIMESH           = 0x4100,
+   OBJ_LIGHT             = 0x4600,
+   OBJ_CAMERA            = 0x4700,
+
+   // Triangular Mesh Chunks
+   TRI_VERTEXL           = 0x4110,
+   TRI_FACEL1            = 0x4120,
+   TRI_MATERIAL          = 0x4130,
+   TRI_MAPPINGCOORS      = 0x4140,
+   TRI_SMOOTHING         = 0x4150,
+   TRI_LOCAL             = 0x4160,
+
+   // Light Chunks
+   LIT_SPOT              = 0x4610,
+   LIT_ONOFF             = 0x4620,
+   LIT_ATTENUATION       = 0x4625,
+   LIT_START             = 0x4659,
+   LIT_END               = 0x465A,
+   LIT_MULTIPLIER        = 0x465B,
+
+   // Camera Chunks
+   CAM_SEECONE           = 0x4710,
+   CAM_RANGES            = 0x4720,
+
+   // Material Chunks
+   MAT_NAME              = 0xA000,
+   MAT_AMBIENT           = 0xA010,
+   MAT_DIFFUSE           = 0xA020,
+   MAT_SPECULAR          = 0xA030,
+   MAT_SHININESS         = 0xA040,
+   MAT_SHINSTRENGTH      = 0xA041,
+   MAT_SHIN3PC           = 0xA042,
+   MAT_TRANSPARENCY      = 0xA050,
+   MAT_XPFALL            = 0xA052,
+   MAT_REFBLUR           = 0xA053,
+   MAT_SELFILLUM         = 0xA080,
+   MAT_DOUBLESIDED       = 0xA081,
+   MAT_ADDITIVE          = 0xA083,
+   MAT_SELFILPCT         = 0xA084,
+   MAT_WIRE              = 0xA085,
+   MAT_SUPERSMP          = 0xA086,
+   MAT_WIRETHICKNESS     = 0xA087,
+   MAT_FACEMAP           = 0xA088,
+   MAT_XPFALLIN          = 0xA08A,
+   MAT_PHONG             = 0xA08C,
+   MAT_WIREABS           = 0xA08E,
+   MAT_SHADING           = 0xA100,
+   MAT_MAPTEXTURE1       = 0xA200,
+   MAT_SPECULARMAP       = 0xA204,
+   MAT_MAPOPACITY        = 0xA210,
+   MAT_REFLECTIONMAP     = 0xA220,
+   MAT_BUMPMAP           = 0xA230,
+
+   // Map Chunks
+   MAP_FILENAME          = 0xA300,
+
+   MAT_SHININESSMAP      = 0xA33C,
+   MAT_EMISSIVEMAP       = 0xA33D,
+
+   MAP_OPTIONS           = 0xA351,
+   MAP_1_U_SCALE         = 0xA354,
+   MAP_1_V_SCALE         = 0xA356,
+   MAP_U_OFFSET          = 0xA358,
+   MAP_V_OFFSET          = 0xA35A,
+   MAP_ROTATION          = 0xA35C,
+
+   MAP_FILTERBLUR        = 0xA353,
+   MAP_1_U_SCALE         = 0xA354,
+   MAP_1_V_SCALE         = 0xA356,
+   MAP_U_OFFSET          = 0xA358,
+   MAP_V_OFFSET          = 0xA35A,
+   MAP_ROTATION          = 0xA35C,
+
+   MAP_LUMTINT1          = 0xA360,
+   MAP_LUMTINT2          = 0xA362,
+
+   MAP_TINTR             = 0xA364,
+   MAP_TINTG             = 0xA366,
+   MAP_TINTB             = 0xA368,
+
+   // Keyframer Chunks
+   KEYFRAME3DS           = 0xB000,
+   FRM_AMBIENT           = 0xB001,
+   FRM_MESHINFO          = 0xB002,
+   FRM_CAMERA            = 0xB003,
+   FRM_CAMERATARGET      = 0xB004,
+   FRM_OMNILIGHT         = 0xB005,
+   FRM_SPOTLIGHTTARGET   = 0xB006,
+   FRM_SPOTLIGHT         = 0xB007,
+   FRM_FRAMES            = 0xB008,
+   FRM_PARAM             = 0xB010,
+   FRM_DUMMYNAME         = 0xB011,
+   FRM_PIVOT             = 0xB013,
+   FRM_TRACKPOS          = 0xB020,
+   FRM_TRACKROT          = 0xB021,
+   FRM_TRACKSCALE        = 0xB022,
+   FRM_TRACKFOV          = 0xB023,
+   FRM_TRACKROLL         = 0xB024,
+   FRM_TRACKCOLOR        = 0xB025,
+   FRM_TRACKMORPH        = 0xB026,  // What does this do?
+   FRM_TRACKHOTSPOT      = 0xB027,
+   FRM_TRACKFALLOFF      = 0xB028,
+   FRM_TRACKHIDE         = 0xB029,
+   FRM_HIERARCHY         = 0xB030
+};
+
+static class MapOptions : uint32
+{
+   bool decal:1; // (related to dontTile)
+   bool mirror:1;
+   bool negative:1;
+   bool dontTile:1;
+   bool summedFiltering:1;
+   bool useAlpha:1;
+   bool lumOrAlphaTint:1;
+   bool ignoreAlpha:1;
+   bool rgbTint:1;
+};
 
 typedef struct FileInfo FileInfo;
 
 typedef struct
 {
-   uint16 indices[3];
-   uint16 oldIndices[3];
+   uint indices[3];
+   uint origIndices[3];
    uint smoothGroups;
+   Material material;
+   Vector3Df normal;
    bool done:1;
 } Face;
 
-struct FileInfo
+static struct FileInfo
 {
    File f;
    DisplaySystem displaySystem;
    Object rootObject;
+   const String fileName;
 
-   uint16 chunkId;
+   ChunkID3DS chunkId;
    uint pos, end;
 
    FileInfo * parent;
    int nFaces;
    Face * faces;
    char textureDirectory[MAX_DIRECTORY];
+   Map<uintptr, Array<int>> matFaces;
 };
 
 #define SWAP_WORD(word) (((unsigned short)(word) & 0x00ff) << 8) \
@@ -226,7 +257,7 @@ static bool ReadChunks(bool (* chunkParser)(FileInfo * info, void * data), FileI
       childInfo.parent = info;
 
       info->f.Seek(info->pos, start);
-      childInfo.chunkId = ReadWORD(info->f);
+      childInfo.chunkId = (ChunkID3DS)ReadWORD(info->f);
       length = ReadDWORD(info->f);
 
       childInfo.pos += sizeof(uint16) + sizeof(uint);
@@ -246,9 +277,9 @@ static bool ReadRGB(FileInfo * info, ColorRGB * rgb)
    if(info->chunkId == RGB_BYTE || info->chunkId == RGB_BYTE_GAMMA)
    {
       byte value;
-      info->f.Getc(&value); rgb->r = value / 255.0f;
-      info->f.Getc(&value); rgb->g = value / 255.0f;
-      info->f.Getc(&value); rgb->b = value / 255.0f;
+      info->f.Getc((char *)&value); rgb->r = value / 255.0f;
+      info->f.Getc((char *)&value); rgb->g = value / 255.0f;
+      info->f.Getc((char *)&value); rgb->b = value / 255.0f;
    }
    else if(info->chunkId == RGB_FLOAT || info->chunkId == RGB_FLOAT_GAMMA)
    {
@@ -274,80 +305,302 @@ static bool ReadAmountOf(FileInfo * info, uint16 * amountOf)
    return true;
 }
 
-typedef struct
+#define WELD_TRESHOLD        0.000001
+#define SMOOTH_CUTOFF   0  // 45
+
+struct SharedSourceVertexInfo
 {
-   uint smoothGroups;
    int index;
-} VertexConfig;
+   Vector3Df value;
+   uint unique;
+   Face * face;
 
-typedef struct
+   int OnCompare(SharedSourceVertexInfo b)
+   {
+      if(unique < b.unique) return -1;
+      if(unique > b.unique) return 1;
+
+      if(unique)
+      {
+         if(face < b.face) return -1;
+         if(face > b.face) return 1;
+      }
+      if(index == b.index) return 0;
+      if(WELD_TRESHOLD)
+      {
+         if(value.x < b.value.x - WELD_TRESHOLD) return -1;
+         if(value.x > b.value.x + WELD_TRESHOLD) return 1;
+         if(value.y < b.value.y - WELD_TRESHOLD) return -1;
+         if(value.y > b.value.y + WELD_TRESHOLD) return 1;
+         if(value.z < b.value.z - WELD_TRESHOLD) return -1;
+         if(value.z > b.value.z + WELD_TRESHOLD) return 1;
+      }
+      else
+      {
+         if(index < b.index) return -1;
+         if(index > b.index) return 1;
+      }
+      return 0;
+   }
+};
+
+class SharedDestVertexInfo
+{
+   Array<int> faces { };
+};
+
+struct SourceVertexInfo
+{
+   SharedSourceVertexInfo * shared;
+   Pointf texCoord;
+   uint smoothGroups;
+
+   int OnCompare(SourceVertexInfo b)
+   {
+      int r = (*shared).OnCompare(*b.shared);
+      if(!r) r = texCoord.OnCompare(b.texCoord);
+      if(!r) r = smoothGroups.OnCompare(b.smoothGroups);
+      return r;
+   }
+};
+
+class DestVertexInfo
 {
-   int numConfig;
-   VertexConfig * config;
-} VertexConfigList;
+   int index, copyFromIndex;
+   Vector3Df normal;
+};
 
 static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
 {
    int c;
    Face * faces = info->faces;
-   //int nFaces = info->nFaces;
    int nVertices = mesh.nVertices;
    int index;
    int nNewVertices;
-   int * numShared;
+   Vector3Df * mVertices;
+   double cutOff = cos(Degrees { SMOOTH_CUTOFF });
+
+   Map<SharedSourceVertexInfo, SharedDestVertexInfo> sharedVertices { };
+   Map<SourceVertexInfo, DestVertexInfo> vertexMap { };
+   Array<MapNode<SourceVertexInfo, DestVertexInfo>> vertices { size = nVertices };
 
-   VertexConfigList * configLists = new0 VertexConfigList[nVertices];
+   MapIterator<SharedSourceVertexInfo, SharedDestVertexInfo> itShared { map = sharedVertices };
+   MapIterator<SourceVertexInfo, DestVertexInfo> it { map = vertexMap };
 
    nNewVertices = nVertices;
+   mVertices = mesh->vertices;
+
    for(c = 0; c<info->nFaces; c++)
    {
       Face * face = &faces[c];
+      Plane plane;
+      plane.FromPointsf(mesh.vertices[face->indices[2]],
+                        mesh.vertices[face->indices[1]],
+                        mesh.vertices[face->indices[0]]);
+      face->normal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
+   }
+
+   for(c = 0; c < info->nFaces; c++)
+   {
+      Face * face = &faces[c];
       int i;
 
+      // Zero space points
+      if(!mVertices[face->indices[0]].OnCompare(mVertices[face->indices[1]]) &&
+         !mVertices[face->indices[0]].OnCompare(mVertices[face->indices[2]]))
+         continue;
+
       for(i = 0; i<3; i++)
       {
-         int index = face->indices[i];
-         int v;
-         VertexConfigList * configList = &configLists[index];
-         VertexConfig * config = null;
-         for(v = 0; v<configList->numConfig; v++)
+         SharedSourceVertexInfo * source;
+         SharedDestVertexInfo svInfo;
+         DestVertexInfo vInfo;
+
+         index = face->indices[i];
+
+         if(face->smoothGroups)
+            itShared.Index({ index = index, mVertices[index], face = face }, true);
+         else
+            itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
+         svInfo = itShared.data;
+         if(!svInfo) itShared.data = svInfo = { };
+         svInfo.faces.Add(c);
+
+         source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
+         // TODO: Allow obtaining address of MapIterator::key
+         // it.Index({ &itShared.key, mesh->texCoords[index] }, true);
+         it.Index({ source, mesh->texCoords ? mesh->texCoords[index] : { }, face->smoothGroups }, true);
+         vInfo = it.data;
+         if(!vInfo)
          {
-            uint smoothGroups = configList->config[v].smoothGroups;
-            if(smoothGroups == face->smoothGroups)
-            {
-               config = &configList->config[v];
-               break;
-            }
-            else if(smoothGroups && face->smoothGroups)
+            vInfo = { };
+            it.data = vInfo;
+            vInfo.copyFromIndex = index;
+            vInfo.index = index;
+         }
+
+         if(!vertices[index])
+            vertices[index] = (void *)it.pointer;
+         else if(vertices[index] != it.pointer)
+         {
+            // If it's a different smoothing group, we'll need extra vertices
+            index = vertices.size;
+            vInfo.index = index;
+            vertices.Add((void *)it.pointer);
+            nNewVertices++;
+         }
+         face->indices[i] = vInfo.index;
+      }
+   }
+
+   for(index = 0; index < nNewVertices; index++)
+   {
+      int numShared = 0;
+      it.pointer = vertices[index];
+      if(it.pointer)
+      {
+         DestVertexInfo vInfo = it.data;
+         Vector3Df normal { };
+         SourceVertexInfo * inf = (SourceVertexInfo *)&(((AVLNode)it.pointer).key);
+         uint smoothing = inf->smoothGroups;
+         bool added = true;
+         SharedSourceVertexInfo * shared = inf->shared;
+         SharedDestVertexInfo svInfo = sharedVertices[*shared];
+         int origIndex;
+         if(!svInfo || vInfo.index != index)
+            continue;
+
+         for(i : svInfo.faces)
+         {
+            Face * face = &info->faces[i];
+            face->done = false;
+            if(smoothing & face->smoothGroups)
+               smoothing |= face->smoothGroups;
+         }
+
+         // Optional code to compensate auto-welding with a limit angle cutoff between faces of same smoothing group
+         if(SMOOTH_CUTOFF && WELD_TRESHOLD)
+         {
+            for(i : svInfo.faces)
             {
-               int g;
-               for(g = 0; g<32; g++)
-                  if(smoothGroups & (1<<g) && face->smoothGroups & (1<<g))
+               Face * face = &info->faces[i];
+               if((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups))
+               {
+                  int j;
+                  for(j = 0; j < 3; j++)
                   {
-                     config = &configList->config[v];
-                     config->smoothGroups |= face->smoothGroups;
-                     break;
+                     if(face->indices[j] == vInfo.index)
+                     {
+                        origIndex = face->origIndices[j];
+                        face->done = true;
+                        normal.x += face->normal.x;
+                        normal.y += face->normal.y;
+                        normal.z += face->normal.z;
+                        numShared++;
+                        break;
+                     }
                   }
+               }
+               if(numShared) break;
             }
          }
 
-         if(!config || !face->smoothGroups)
+         while(added)
          {
-            // Duplicate the vertex and make the face use it
-            if(configList->numConfig)
-               index = nNewVertices++;
-            face->indices[i] = (uint16)index;
-            if(!config)
+            added = false;
+            for(i : svInfo.faces)
             {
-               configList->config = renew configList->config VertexConfig[configList->numConfig + 1];
-               config = &configList->config[configList->numConfig++];
-               config->index = index;
-               config->smoothGroups = face->smoothGroups;
+               Face * face = &info->faces[i];
+               if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
+               {
+                  bool valid = true;
+
+                  if(SMOOTH_CUTOFF && WELD_TRESHOLD)
+                  {
+                     int origIndexB = -1;
+                     int k;
+
+                     for(k = 0; k < 3; k++)
+                     {
+                        if(face->indices[k] == vInfo.index)
+                        {
+                           origIndexB = face->origIndices[k];
+                           break;
+                        }
+                     }
+                     valid = origIndex == origIndexB;
+                     if(!valid)
+                     {
+                        for(j : svInfo.faces)
+                        {
+                           if(info->faces[j].done)
+                           {
+                              double dot = info->faces[j].normal.DotProduct(face->normal);
+                              if(dot > 1) dot = 1; else if(dot < -1) dot = -1;
+                              valid = fabs(dot) > cutOff;
+                              if(valid) break;
+                           }
+                        }
+                     }
+                  }
+
+                  if(valid)
+                  {
+                     normal.x += face->normal.x;
+                     normal.y += face->normal.y;
+                     normal.z += face->normal.z;
+                     numShared++;
+                     added = true;
+                     face->done = true;
+                  }
+               }
             }
+            if(!SMOOTH_CUTOFF || !WELD_TRESHOLD) break;
          }
-         else
+         normal.Scale(normal, 1.0f / numShared);
+         if(vInfo.index == index)
+            vInfo.normal.Normalize(normal);
+
+         // Auto welding/smoothing requires extra vertices because angle is too steep
+         if(SMOOTH_CUTOFF && WELD_TRESHOLD)
          {
-            face->indices[i] = (uint16)config->index;
+            SharedDestVertexInfo newSharedInfo = null;
+            int index;
+            for(i : svInfo.faces)
+            {
+               Face * face = &info->faces[i];
+               if(!face->done && ((smoothing & face->smoothGroups) || (!smoothing && !face->smoothGroups)))
+               {
+                  int j;
+                  for(j = 0; j < 3; j++)
+                  {
+                     if(face->indices[j] == vInfo.index)
+                     {
+                        if(!newSharedInfo)
+                        {
+                           DestVertexInfo newVert;
+                           SharedSourceVertexInfo * source;
+
+                           index = nNewVertices++;
+                           itShared.Index({ index = index, { }, unique = index + 1, face = face }, true);
+                           source = (SharedSourceVertexInfo *)&(((AVLNode)itShared.pointer).key);
+                           itShared.data = newSharedInfo = { };
+
+                           it.Index({ source, mesh->texCoords ? mesh->texCoords[vInfo.copyFromIndex] : { }, face->smoothGroups }, true);
+                           newVert = { };
+                           it.data = newVert;
+                           newVert.copyFromIndex = vInfo.copyFromIndex;
+                           newVert.index = index;
+
+                           vertices.Add((void *)it.pointer);
+                        }
+                        face->indices[j] = index;
+                        newSharedInfo.faces.Add(i);
+                        break;
+                     }
+                  }
+               }
+            }
          }
       }
    }
@@ -357,6 +610,7 @@ static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
       Vector3Df * oldVertices = mesh.vertices;
       Pointf * oldTexCoords = mesh.texCoords;
 
+      // TODO: Support reallocation?
       *((void **)&mesh.vertices) = null;
       *((void **)&mesh.texCoords) = null;
       *((int *)&mesh.nVertices) = 0;
@@ -364,105 +618,43 @@ static void ComputeNormals(Mesh mesh, FileInfo * info, Object object)
       mesh.Allocate( { vertices = true, normals = true, texCoords1 = oldTexCoords ? true : false }, nNewVertices, info->displaySystem);
 
       // Fill in the new vertices
-      for(index = 0; index<nVertices; index++)
+      for(index = 0; index < nNewVertices; index++)
       {
-         int v;
-         VertexConfigList * configList = &configLists[index];
-         for(v = 0; v<configList->numConfig; v++)
-         {
-            VertexConfig * config = &configList->config[v];
-            Vector3Df * normal;
-            if(config->smoothGroups)
-            {
-               //if(v > 0)
-               {
-                  // Duplicate vertex
-                  mesh.vertices[config->index] = oldVertices[index]; //mesh.vertices[index];
-                  if(mesh.texCoords)
-                     mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
-               }
-            }
-            else
-            {
-               mesh.vertices[config->index] = oldVertices[index];
-               if(mesh.texCoords)
-                  mesh.texCoords[config->index] = oldTexCoords[index]; //mesh.texCoords[index];
-            }
-            normal = &mesh.normals[config->index];
-            *normal = { 0,0,0 };
-         }
+         DestVertexInfo vInfo;
+         it.pointer = vertices[index];
+         vInfo = it.data;
+
+         // Duplicate vertex
+         mesh.normals[index] = vInfo ? vInfo.normal : { };
+         mesh.vertices[index] = oldVertices[vInfo ? vInfo.copyFromIndex : index];
+         if(mesh.texCoords)
+            mesh.texCoords[index] = oldTexCoords[vInfo ? vInfo.copyFromIndex : index];
       }
 
       delete oldVertices;
       delete oldTexCoords;
    }
 
-   numShared = new0 int[nNewVertices];
-
-   for(c = 0; c<info->nFaces; c++)
    {
-      Face * face = &faces[c];
       int i;
-      Plane plane;
-      Vector3Df planeNormal;
-      plane.FromPointsf(mesh.vertices[face->oldIndices[2]],
-                       mesh.vertices[face->oldIndices[1]],
-                       mesh.vertices[face->oldIndices[0]]);
-      planeNormal = { (float)plane.normal.x, (float)plane.normal.y, (float)plane.normal.z };
-      if(face->smoothGroups)
-      {
-         for(i = 0; i<3; i++)
-         {
-            int index = face->indices[i];
-            Vector3Df * normal = &mesh.normals[index];
-            normal->Add(normal, planeNormal);
-            numShared[index]++;
-         }
-      }
-      else
-      {
-         for(i = 0; i<3; i++)
-         {
-            int index = face->oldIndices[i];
-            int newIndex = face->indices[i];
-            mesh.normals[newIndex] = planeNormal;
-            // Duplicate vertex
-            if(index != newIndex)
-            {
-               mesh.vertices[newIndex] = mesh.vertices[index];
-               if(mesh.texCoords)
-                  mesh.texCoords[newIndex] = mesh.texCoords[index];
-            }
-            numShared[newIndex]++;
-         }
-      }
-   }
-   for(index = 0; index<nNewVertices; index++)
-   {
-      Vector3Df * normal = &mesh.normals[index];
-      normal->Scale(normal, 1.0f / numShared[index]);
-      normal->Normalize(normal);
+      for(i = 0; i < info->nFaces; i++)
+         info->faces[i].done = false;
    }
 
    mesh.Unlock({ normals = true });
 
    // Free all the temporary stuff
-   if(configLists)
-   {
-      for(index = 0; index < nVertices; index++)
-      {
-         VertexConfigList * configList = &configLists[index];
-         if(configList->config) delete configList->config;
-      }
-      delete configLists;
-   }
-   delete numShared;
+
+   delete vertices;
+   vertexMap.Free();
+   delete vertexMap;
+   sharedVertices.Free();
+   delete sharedVertices;
 }
 
 // Meshes
 static bool ReadSmoothing(FileInfo * info, Object object)
 {
-   Mesh mesh = object.mesh;
    switch(info->chunkId)
    {
       case TRI_SMOOTHING:
@@ -479,81 +671,34 @@ static bool ReadSmoothing(FileInfo * info, Object object)
 static bool ReadFacesListChunks(FileInfo * info, Object object)
 {
    DisplaySystem displaySystem = info->displaySystem;
-   Mesh mesh = object.mesh;
    switch(info->chunkId)
    {
       case TRI_MATERIAL:
       {
          char * name;
          Material mat;
+         int i, c;
+         int count;
+         Array<int> faces;
+         char matName[MAX_LOCATION + 100];
 
+         strcpy(matName, info->fileName);
          ReadASCIIZ(info->f, &name);
-         mat = displaySystem.GetMaterial(name);
-         if(mat)
-         {
-            if(mat.flags.translucent)
-            {
-               int c;
-               uint16 count = ReadWORD(info->f);
-               mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + count];
-               for(c = 0; c<count; c++)
-               {
-                  uint16 face = ReadWORD(info->f);
-                  PrimitiveSingle * triangle = &mesh.primitives[mesh.nPrimitives++];
+         count = ReadWORD(info->f);
+         strcat(matName, name);
 
-                  if(mesh.AllocatePrimitive(triangle, triangles, 3))
-                  {
-                     triangle->indices[0] = info->faces[face].indices[0];
-                     triangle->indices[1] = info->faces[face].indices[1];
-                     triangle->indices[2] = info->faces[face].indices[2];
-                     triangle->middle.Add(mesh.vertices[triangle->indices[0]], mesh.vertices[triangle->indices[1]]);
-                     triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indices[2]]);
-                     triangle->plane.FromPointsf(
-                        mesh.vertices[triangle->indices[2]],
-                        mesh.vertices[triangle->indices[1]],
-                        mesh.vertices[triangle->indices[0]]);
+         mat = displaySystem.GetMaterial(matName);
+         faces = info->matFaces[(uintptr)mat];
+         if(!faces)
+            info->matFaces[(uintptr)mat] = faces = { };
+         i = faces.size;
+         faces.size += count;
 
-                     mesh.UnlockPrimitive(triangle);
-                  }
-                  triangle->middle.x /= 3;
-                  triangle->middle.y /= 3;
-                  triangle->middle.z /= 3;
-
-                  triangle->material = mat;
-
-                  info->faces[face].done = (byte)bool::true;
-               }
-               object.flags.translucent = true;
-            }
-            else
-            {
-               PrimitiveGroup group;
-               uint16 count = ReadWORD(info->f);
-               group = mesh.AddPrimitiveGroup(triangles, count * 3);
-               if(group)
-               {
-                  int c;
-                  group.material = mat;
-                  for(c = 0; c<count; c++)
-                  {
-                     uint16 face = ReadWORD(info->f);
-                     if(object.flags.flipWindings)
-                     {
-                        group.indices[c*3]   = info->faces[face].indices[2];
-                        group.indices[c*3+1] = info->faces[face].indices[1];
-                        group.indices[c*3+2] = info->faces[face].indices[0];
-                     }
-                     else
-                     {
-                        group.indices[c*3]   = info->faces[face].indices[0];
-                        group.indices[c*3+1] = info->faces[face].indices[1];
-                        group.indices[c*3+2] = info->faces[face].indices[2];
-                     }
-                     info->faces[face].done = (byte)bool::true;
-                  }
-                  mesh.UnlockPrimitiveGroup(group);
-               }
-            }
+         for(c = 0; c<count; c++)
+         {
+            uint16 face = ReadWORD(info->f);
+            faces[i + c] = face;
+            info->faces[face].material = mat;
          }
          delete name;
          break;
@@ -614,7 +759,7 @@ static bool ReadTriMesh(FileInfo * info, Object object)
       {
          int c;
          uint16 nFaces = 0;
-         int count;
+         uint count;
          uint pos;
 
          info->nFaces = nFaces = ReadWORD(info->f);
@@ -625,10 +770,8 @@ static bool ReadTriMesh(FileInfo * info, Object object)
          {
             int i;
             for(i = 0; i<3; i++)
-            {
-               info->faces[c].oldIndices[i] =
+               info->faces[c].origIndices[i] =
                info->faces[c].indices[i] = ReadWORD(info->f);
-            }
             ReadWORD(info->f);
             info->pos += 4*sizeof(uint16);
          }
@@ -636,10 +779,83 @@ static bool ReadTriMesh(FileInfo * info, Object object)
          ReadChunks(ReadSmoothing, info, object);
          info->pos = pos;
 
-         ComputeNormals(mesh, info, object);
+         if(info->matFaces)
+            info->matFaces.Free();
+         info->matFaces = { };
 
          ReadChunks(ReadFacesListChunks, info, object);
 
+         ComputeNormals(mesh, info, object);
+
+         // Create Groups
+         for(m : info->matFaces)
+         {
+            Material mat = (Material)&m;
+            Array<int> faces = m;
+            if(mat.flags.translucent)
+            {
+               mesh.primitives = renew mesh.primitives PrimitiveSingle[mesh.nPrimitives + faces.count];
+               for(i : faces)
+               {
+                  Face * face = &info->faces[i];
+                  PrimitiveSingle * triangle;
+
+                  triangle = &mesh.primitives[mesh.nPrimitives++];
+                  if(mesh.AllocatePrimitive(triangle, { triangles, indices32bit = USE_32_BIT_INDICES }, 3))
+                  {
+                     triangle->indicesMember[0] = (uintindex)face->indices[0];
+                     triangle->indicesMember[1] = (uintindex)face->indices[1];
+                     triangle->indicesMember[2] = (uintindex)face->indices[2];
+                     triangle->middle.Add(mesh.vertices[triangle->indicesMember[0]], mesh.vertices[triangle->indicesMember[1]]);
+                     triangle->middle.Add(triangle->middle, mesh.vertices[triangle->indicesMember[2]]);
+                     triangle->plane.FromPointsf(
+                        mesh.vertices[triangle->indicesMember[2]],
+                        mesh.vertices[triangle->indicesMember[1]],
+                        mesh.vertices[triangle->indicesMember[0]]);
+
+                     mesh.UnlockPrimitive(triangle);
+                  }
+                  triangle->middle.x /= 3;
+                  triangle->middle.y /= 3;
+                  triangle->middle.z /= 3;
+
+                  triangle->material = mat;
+
+                  face->done = true;
+                  object.flags.translucent = true;
+               }
+            }
+            else
+            {
+               PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, faces.count * 3);
+               if(group)
+               {
+                  c = 0;
+                  group.material = mat;
+                  for(i : faces)
+                  {
+                     Face * face = &info->faces[i];
+
+                     if(object.flags.flipWindings)
+                     {
+                        group.indicesMember[c*3]   = (uintindex)face->indices[2];
+                        group.indicesMember[c*3+1] = (uintindex)face->indices[1];
+                        group.indicesMember[c*3+2] = (uintindex)face->indices[0];
+                     }
+                     else
+                     {
+                        group.indicesMember[c*3]   = (uintindex)face->indices[0];
+                        group.indicesMember[c*3+1] = (uintindex)face->indices[1];
+                        group.indicesMember[c*3+2] = (uintindex)face->indices[2];
+                     }
+                     face->done = true;
+                     c++;
+                  }
+                  mesh.UnlockPrimitiveGroup(group);
+               }
+            }
+         }
+
          // Add faces without a material all together
          count = 0;
          for(c = 0; c<nFaces; c++)
@@ -647,40 +863,29 @@ static bool ReadTriMesh(FileInfo * info, Object object)
                count++;
          if(count)
          {
-            PrimitiveGroup group = mesh.AddPrimitiveGroup(triangles, count * 3);
+            PrimitiveGroup group = mesh.AddPrimitiveGroup({ triangles, indices32bit = USE_32_BIT_INDICES }, count * 3);
             if(group)
             {
                for(c = 0; c<nFaces; c++)
-                  if(!info->faces[c].done)
+               {
+                  Face * face = &info->faces[c];
+                  if(!face->done)
                   {
-                     group.indices[c*3]   = info->faces[c].indices[0];
-                     group.indices[c*3+1] = info->faces[c].indices[1];
-                     group.indices[c*3+2] = info->faces[c].indices[2];
+                     group.indicesMember[c*3]   = (uintindex)face->indices[0];
+                     group.indicesMember[c*3+1] = (uintindex)face->indices[1];
+                     group.indicesMember[c*3+2] = (uintindex)face->indices[2];
                   }
+               }
                mesh.UnlockPrimitiveGroup(group);
             }
          }
 
          delete info->faces;
-
-         /*
-         mesh.ComputeNormals();
-
-         if(object.flags.flipWindings)
+         if(info->matFaces)
          {
-            if(mesh.Lock({ normals = true }))
-            {
-               for(c = 0; c<mesh.nVertices; c++)
-               {
-                  mesh.normals[c].x *= -1;
-                  mesh.normals[c].y *= -1;
-                  mesh.normals[c].z *= -1;
-               }
-               mesh.Unlock({ normals = true });
-            }
-         }*/
-
-         // could use this instead? : mesh.ApplyTranslucency(object);
+            info->matFaces.Free();
+            delete info->matFaces;
+         }
          break;
       }
       case TRI_LOCAL:
@@ -815,35 +1020,48 @@ static bool ReadMap(FileInfo * info, Material mat)
          char location[MAX_LOCATION];
 
          ReadASCIIZ(info->f, &name);
-         strlwr(name);
 
          strcpy(location, info->textureDirectory);
          PathCat(location, name);
+         if(!FileExists(location))
+         {
+            // Attempt all lowercase if original case does not exist
+            strlwr(name);
+            strcpy(location, info->textureDirectory);
+            PathCat(location, name);
+         }
 
          if(info->parent->chunkId == MAT_BUMPMAP)
          {
-            mat.bumpMap = displaySystem.GetTexture(name);
+            // To avoid messing up the diffuse texture if same bitmap is specified by mistake...
+            char bumpName[MAX_LOCATION+5];
+            strcpy(bumpName, "BUMP:");
+            strcat(bumpName, location);
             if(!mat.bumpMap)
             {
-               mat.bumpMap = Bitmap { };
-               if(!mat.bumpMap.Load(location, null, null) ||
-                  !mat.bumpMap.Convert(null, pixelFormat888, null) ||
-                  !displaySystem.AddTexture(name, mat.bumpMap))
-                  delete mat.bumpMap;
-            }
-            if(mat.bumpMap)
-            {
-               ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
-               int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
-               int y, x;
-
-               for(y = 0; y < bh; y++)
-                  for(x = 0; x < bw; x++)
+               mat.bumpMap = displaySystem.GetTexture(bumpName);
+               if(!mat.bumpMap)
+               {
+                  mat.bumpMap = Bitmap { };
+                  if(!mat.bumpMap.Load(location, null, null) ||
+                     !mat.bumpMap.Convert(null, pixelFormat888, null) ||
+                     !displaySystem.AddTexture(bumpName, mat.bumpMap))
+                     delete mat.bumpMap;
+                  if(mat.bumpMap)
                   {
-                     uint bc = y * bw + x;
-                     Color color = picture[bc].color;
-                     picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
+                     ColorAlpha * picture = (ColorAlpha *)mat.bumpMap.picture;
+                     int bw = mat.bumpMap.width, bh = mat.bumpMap.height;
+                     int y, x;
+
+                     for(y = 0; y < bh; y++)
+                        for(x = 0; x < bw; x++)
+                        {
+                           uint bc = y * bw + x;
+                           Color color = picture[bc].color;
+                           picture[bc] = { 255, { color.r, 255 - color.b, color.g } };
+                        }
                   }
+               }
             }
          }
          else
@@ -853,13 +1071,13 @@ static bool ReadMap(FileInfo * info, Material mat)
             bool translucent = false;
             if(!mat.baseMap)
             {
-               mat.baseMap = displaySystem.GetTexture(name);
+               mat.baseMap = displaySystem.GetTexture(location);
                if(!mat.baseMap)
                {
                   mat.baseMap = Bitmap { };
                   if(!mat.baseMap.Load(location, null, null) ||
                      !mat.baseMap.Convert(null, pixelFormat888, null) ||
-                     !displaySystem.AddTexture(name, mat.baseMap))
+                     !displaySystem.AddTexture(location, mat.baseMap))
                   {
                      delete mat.baseMap;
                   }
@@ -912,8 +1130,8 @@ static bool ReadMap(FileInfo * info, Material mat)
       }
       case MAP_OPTIONS:
       {
-         uint16 options = ReadWORD(info->f);
-         if(!(options & MAP_OPTIONS_DONTTILE)) mat.flags.tile = true;
+         MapOptions options = (MapOptions)ReadWORD(info->f);
+         if(!options.dontTile) mat.flags.tile = true;
          break;
       }
       case MAP_1_U_SCALE:
@@ -939,7 +1157,13 @@ static bool ReadMaterial(FileInfo * info, Material mat)
    {
       case MAT_NAME:
       {
-         ReadASCIIZ(info->f, &mat.name);
+         String name;
+         char matName[MAX_LOCATION + 100];
+         strcpy(matName, info->fileName);
+         ReadASCIIZ(info->f, &name);
+         strcat(matName, name);
+         mat.name = CopyString(matName);
+         delete name;
          break;
       }
       case MAT_TRANSPARENCY:
@@ -1017,7 +1241,6 @@ static bool ReadMaterial(FileInfo * info, Material mat)
 // Lights
 static bool ReadLight(FileInfo * info, Object object)
 {
-   Mesh mesh = object.mesh;
    Light * light = &object.light;
    switch(info->chunkId)
    {
@@ -1105,7 +1328,6 @@ static bool ReadLight(FileInfo * info, Object object)
 // Cameras
 static bool ReadCamera(FileInfo * info, Object object)
 {
-   Mesh mesh = object.mesh;
    switch(info->chunkId)
    {
       case CAM_SEECONE:
@@ -1114,9 +1336,9 @@ static bool ReadCamera(FileInfo * info, Object object)
       }
       case CAM_RANGES:
       {
-         Camera camera = object.camera;
-         float nearRange = ReadFloat(info->f);
-         float farRange = ReadFloat(info->f);
+         //Camera camera = object.camera;
+         /*float nearRange = */ReadFloat(info->f);
+         /*float farRange = */ReadFloat(info->f);
          /*
          camera.zMin = Max(0.1, nearRange);
          camera.zMax = farRange;
@@ -1139,12 +1361,10 @@ static bool ReadEditObject(FileInfo * info, char * name)
          if(!object)
          {
             object = Object { };
-            object.name = name;
+            object.name = CopyString(name);
             info->rootObject.children.AddName(object);
             object.parent = info->rootObject;
          }
-         else
-            delete name;
          object.InitializeMesh(displaySystem);
          ReadChunks(ReadTriMesh, info, object);
          object.flags.mesh = true;
@@ -1160,12 +1380,10 @@ static bool ReadEditObject(FileInfo * info, char * name)
          if(!object)
          {
             object = Object { };
-            object.name = name;
+            object.name = CopyString(name);
             info->rootObject.children.AddName(object);
             object.parent = info->rootObject;
          }
-         else
-            delete name;
          object.flags.light = true;
 
          light = &object.light;
@@ -1187,29 +1405,31 @@ static bool ReadEditObject(FileInfo * info, char * name)
          Object object = info->rootObject.Find(name);
          Object target;
          Camera camera;
-         float bankAngle, focus;
+         float /*bankAngle, */focus;
          double mm;
 
          strcpy(targetName, name);
          strcat(targetName, ".target");
+         target = info->rootObject.Find(targetName);
 
          if(!object)
          {
             object = Object { };
-            object.name = name;
+            object.name = CopyString(name);
             info->rootObject.children.AddName(object);
 
             object.parent = info->rootObject;
             object.camera = Camera { };
             object.camera.type = lookAtObject;
+         }
 
+         if(!target)
+         {
             target = Object { };
             target.name = CopyString(targetName);
             info->rootObject.children.AddName(target);
             target.parent = info->rootObject;
          }
-         else
-            delete name;
 
          object.flags.camera = true;
          object.cameraTarget = target;
@@ -1230,7 +1450,7 @@ static bool ReadEditObject(FileInfo * info, char * name)
          target.transform.position.y =-ReadFloat(info->f);
 
          info->pos += sizeof(float) * 3;
-         bankAngle = ReadFloat(info->f);
+         /*bankAngle = */ReadFloat(info->f);
          info->pos += sizeof(float);
          focus = ReadFloat(info->f);
          info->pos += sizeof(float);
@@ -1242,8 +1462,6 @@ static bool ReadEditObject(FileInfo * info, char * name)
          break;
       }
       case OBJ_HIDDEN: break;
-      default:
-         delete name;
    }
    return true;
 }
@@ -1262,7 +1480,7 @@ static bool ReadEditChunks(FileInfo * info, void * data)
       }
       case EDIT_MATERIAL:
       {
-         Material material { };
+         Material material { /*flags = { singleSideLight = true }*/ };
          Material mat;
          ReadChunks(ReadMaterial, info, material);
 
@@ -1289,6 +1507,7 @@ static bool ReadEditChunks(FileInfo * info, void * data)
          char * name;
          info->pos += ReadASCIIZ(info->f, &name);
          ReadChunks(ReadEditObject, info, name);
+         delete name;
          break;
       }
    }
@@ -1306,12 +1525,10 @@ struct ObjectInfoBlock
 };
 
 // Key Framer Chunks
-
-#define ACCFLAG_TENSION    0x00000001
-#define ACCFLAG_CONTINUITY 0x00000002
-#define ACCFLAG_BIAS       0x00000004
-#define ACCFLAG_EASETO     0x00000008
-#define ACCFLAG_EASEFROM   0x00000010
+static class AccelerationFlags : uint32
+{
+   bool tension:1, continuity:1, bias:1, easeTo:1, easeFrom:1;
+};
 
 static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
 {
@@ -1319,10 +1536,10 @@ static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
    {
       case FRM_PARAM:
       {
-         uint16 flags1, flags2;
+         //uint16 flags1, flags2;
          ReadASCIIZ(info->f, &block->name);
-         flags1 = ReadWORD(info->f);
-         flags2 = ReadWORD(info->f);
+         /*flags1 = */ReadWORD(info->f);
+         /*flags2 = */ReadWORD(info->f);
          block->parent = ReadWORD(info->f);
          break;
       }
@@ -1376,21 +1593,21 @@ static bool ReadFrameInfoBlock(FileInfo * info, ObjectInfoBlock * block)
             track.keys = new0 FrameKey[track.numKeys];
             for(c = 0; c<track.numKeys; c++)
             {
-               uint16 accelerationFlags;
+               AccelerationFlags accelerationFlags;
                FrameKey * key = track.keys + c;
 
                key->frame = ReadDWORD(info->f);
-               accelerationFlags = ReadWORD(info->f);
+               accelerationFlags = (AccelerationFlags)ReadWORD(info->f);
 
-               if(accelerationFlags & ACCFLAG_TENSION)
+               if(accelerationFlags.tension)
                   key->tension = ReadFloat(info->f);
-               if(accelerationFlags & ACCFLAG_CONTINUITY)
+               if(accelerationFlags.continuity)
                   key->continuity = ReadFloat(info->f);
-               if(accelerationFlags & ACCFLAG_BIAS)
+               if(accelerationFlags.bias)
                   key->bias = ReadFloat(info->f);
-               if(accelerationFlags & ACCFLAG_EASETO)
+               if(accelerationFlags.easeTo)
                   key->easeTo = ReadFloat(info->f);
-               if(accelerationFlags & ACCFLAG_EASEFROM)
+               if(accelerationFlags.easeFrom)
                   key->easeFrom = ReadFloat(info->f);
 
                switch(info->chunkId)
@@ -1493,7 +1710,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
       case FRM_MESHINFO:
       {
          ObjectInfoBlock block { };
-         Object object;
+         Object object = null;
 
          ReadChunks(ReadFrameInfoBlock, info, &block);
 
@@ -1527,7 +1744,8 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                }
                delete block.dummyName;
             }
-            object.parent = info->rootObject;
+            if(object)
+               object.parent = info->rootObject;
          }
          else
             object = info->rootObject.Find(block.name);
@@ -1578,7 +1796,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
       case FRM_CAMERA:
       {
          ObjectInfoBlock block { };
-         Object object;
+         Object object = null;
 
          ReadChunks(ReadFrameInfoBlock, info, &block);
 
@@ -1608,7 +1826,8 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                }
                delete block.dummyName;
             }
-            object.parent = info->rootObject;
+            if(object)
+               object.parent = info->rootObject;
          }
          else
             object = info->rootObject.Find(block.name);
@@ -1642,7 +1861,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
       case FRM_CAMERATARGET:
       {
          ObjectInfoBlock block { };
-         Object object;
+         Object object = null;
          char targetName[MAXNAMELEN];
 
          ReadChunks(ReadFrameInfoBlock, info, &block);
@@ -1674,7 +1893,8 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                }
                delete block.dummyName;
             }
-            object.parent = info->rootObject;
+            if(object)
+               object.parent = info->rootObject;
          }
          else
             object = info->rootObject.Find(targetName);
@@ -1718,7 +1938,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
       case FRM_SPOTLIGHT:
       {
          ObjectInfoBlock block { };
-         Object object;
+         Object object = null;
 
          ReadChunks(ReadFrameInfoBlock, info, &block);
 
@@ -1748,7 +1968,8 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                }
                delete block.dummyName;
             }
-            object.parent = info->rootObject;
+            if(object)
+               object.parent = info->rootObject;
          }
          else
             object = info->rootObject.Find(block.name);
@@ -1782,7 +2003,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
       case FRM_SPOTLIGHTTARGET:
       {
          ObjectInfoBlock block { };
-         Object object;
+         Object object = null;
          char targetName[MAXNAMELEN];
 
          ReadChunks(ReadFrameInfoBlock, info, &block);
@@ -1806,6 +2027,7 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                {
                   object = Object { };
                   object.name  = new char[strlen(block.dummyName) + strlen(model.name) + 2];
+                  // TODO: When passing a String to a const String, use member if property is const String but member is String
                   sprintf(object.name, "%s.%s", model.name, block.dummyName);
                   object.flags = model.flags;
                   object.flags.ownMesh = false;
@@ -1814,7 +2036,8 @@ static bool ReadKeyFrameChunks(FileInfo * info, void * data)
                }
                delete block.dummyName;
             }
-            object.parent = info->rootObject;
+            if(object)
+               object.parent = info->rootObject;
          }
          else
             object = info->rootObject.Find(targetName);
@@ -1893,7 +2116,7 @@ class Object3DSFormat : ObjectFormat
 {
    class_property(extension) = "3ds";
 
-   bool Load(Object object, char * fileName, DisplaySystem displaySystem)
+   bool Load(Object object, const char * fileName, DisplaySystem displaySystem)
    {
       bool result = false;
       if(fileName)
@@ -1903,11 +2126,14 @@ class Object3DSFormat : ObjectFormat
          info.displaySystem = displaySystem;
          info.pos = 0;
          info.end = 1;
+         info.fileName = fileName;
          StripLastDirectory(fileName, info.textureDirectory);
          info.f = FileOpen(fileName, read);
          if(info.f)
          {
-            if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
+            // TOFIX: eC reorders that badly
+            // if(ReadChunks(ReadMain, &info, object) && info.rootObject.children.first)
+            if(ReadChunks(ReadMain, &info, object) && info.rootObject.firstChild)
             {
                object.flags.root = true;
                object.SetMinMaxRadius(true);
@@ -1917,6 +2143,9 @@ class Object3DSFormat : ObjectFormat
             }
             delete info.f;
          }
+         if(info.matFaces)
+            info.matFaces.Free();
+         delete info.matFaces;
       }
       if(!result)
          object.Free(displaySystem);