Exemple #1
0
        public IGeometricObjectElement Clone(GeometricObject mesh)
        {
            GeometricObjectElementSprites sm = (GeometricObjectElementSprites)MemberwiseClone();

            sm.geo = mesh;
            sm.Reset();
            return(sm);
        }
        public IGeometricObjectElement Clone(GeometricObject geo)
        {
            GeometricObjectElementTriangles sm = (GeometricObjectElementTriangles)MemberwiseClone();

            sm.geo = geo;
            sm.Reset();
            return(sm);
        }
        public IGeometricObject Clone()
        {
            GeometricObject m = (GeometricObject)MemberwiseClone();

            m.Reset();
            m.elements = new IGeometricObjectElement[num_elements];
            for (uint i = 0; i < m.num_elements; i++)
            {
                if (elements[i] != null)
                {
                    m.elements[i] = elements[i].Clone(m);
                    if (m.elements[i] is DeformSet)
                    {
                        m.bones = (DeformSet)m.elements[i];
                    }
                }
            }
            return(m);
        }
Exemple #4
0
        public static GeometricShadowObject Read(Reader reader, Pointer offset, SuperObject so)
        {
            MapLoader             l   = MapLoader.Loader;
            GeometricShadowObject igo = new GeometricShadowObject(offset, so);

            igo.off_data = Pointer.Read(reader);
            igo.unk4     = reader.ReadUInt32();
            igo.unk8     = reader.ReadUInt32();

            Pointer.DoAt(ref reader, igo.off_data, () => {
                igo.data = GeometricObject.Read(reader, igo.off_data);
                if (igo.data != null)
                {
                    igo.data.Gao.transform.parent = igo.Gao.transform;
                    igo.Gao.name = "[Shadow] " + igo.data.Gao.name;
                    igo.Gao.SetActive(false); // Shadows don't draw well right now
                }
            });
            return(igo);
        }
 private static void ReadMeshFromATO(Reader reader, GeometricObject m)
 {
     // Revolution only: Before creating the gameobject, read the actual model data from the ATO
     if (Settings.s.game == Settings.Game.R2Revolution)
     {
         MapLoader l = MapLoader.Loader;
         List <GeometricObject> meshObjects = new List <GeometricObject>();
         for (uint i = 0; i < m.num_elements; i++)
         {
             if (m.element_types[i] == 1)
             {
                 R2PS2Loader ps2l = (R2PS2Loader)l;
                 meshObjects.Add(ps2l.ato.meshes[ps2l.ato.meshes.Length - 1 - ps2l.meshesRead - meshObjects.Count]);
             }
         }
         if (meshObjects.Count > 0)
         {
             int  currentSubblock = 0;
             int  curNumVertices  = 0;
             bool tryMapping      = true;
             if (m.vertices == null)
             {
                 m.num_vertices = (ushort)meshObjects.Sum(mesh => mesh.num_vertices);
                 m.vertices     = new Vector3[m.num_vertices];
                 m.normals      = new Vector3[m.num_vertices];
                 tryMapping     = false;
             }
             for (int i = 0; i < meshObjects.Count; i++)
             {
                 GeometricObject mo = meshObjects[i];
                 while (currentSubblock < m.num_elements && m.element_types[currentSubblock] != 1)
                 {
                     currentSubblock++;
                 }
                 GeometricObjectElementTriangles me  = (GeometricObjectElementTriangles)m.elements[currentSubblock];
                 GeometricObjectElementTriangles moe = ((GeometricObjectElementTriangles)mo.elements[0]);
                 if (!tryMapping)
                 {
                     Array.Copy(mo.vertices, 0, m.vertices, curNumVertices, mo.num_vertices);
                     if (mo.normals != null)
                     {
                         Array.Copy(mo.normals, 0, m.normals, curNumVertices, mo.num_vertices);
                     }
                     me.OPT_mapping_vertices = Enumerable.Range(curNumVertices, mo.num_vertices).ToArray();
                     curNumVertices         += mo.num_vertices;
                 }
                 else
                 {
                     me.OPT_mapping_vertices = new int[moe.OPT_num_mapping_entries];
                     for (int j = 0; j < mo.vertices.Length; j++)
                     {
                         me.OPT_mapping_vertices[j] = Array.IndexOf(m.vertices, mo.vertices[j]);
                         if (me.OPT_mapping_vertices[j] == -1 || me.OPT_mapping_vertices[j] != Array.IndexOf(m.vertices, mo.vertices[j]))
                         {
                             Debug.LogError("Failed matching vertices between Renderware and OpenSpace");
                         }
                     }
                 }
                 me.OPT_disconnectedTriangles     = moe.OPT_disconnectedTriangles;
                 me.OPT_num_disconnectedTriangles = moe.OPT_num_disconnectedTriangles;
                 me.triangles               = null;
                 me.num_triangles           = 0;
                 me.num_uvMaps              = moe.num_uvMaps;
                 me.num_uvs                 = moe.num_uvs;
                 me.uvs                     = moe.uvs;
                 me.OPT_mapping_uvs         = moe.OPT_mapping_uvs;
                 me.OPT_num_mapping_entries = moe.OPT_num_mapping_entries;
                 me.vertexColors            = moe.vertexColors;
                 currentSubblock++;
                 if (me.lightmap_index != -1)
                 {
                     R2PS2Loader ps2l = ((R2PS2Loader)l);
                     string      id_r = me.lightmap_index.ToString("D3") + "." + 0;
                     //l.print(id_r + " - " + me.num_disconnected_triangles + " - " + m.num_vertices + " - " + mo.num_vertices);
                     Texture2D lm = ps2l.GetLightmap(id_r);
                     if (me.visualMaterial != null)
                     {
                         if (lm != null)
                         {
                             string    id_g = me.lightmap_index.ToString("D3") + "." + 1;
                             string    id_b = me.lightmap_index.ToString("D3") + "." + 2;
                             Texture2D lm_g = ps2l.GetLightmap(id_g);
                             Texture2D lm_b = ps2l.GetLightmap(id_b);
                             if (lm_g != null && lm_b != null)
                             {
                                 for (int j = 0; j < lm.width; j++)
                                 {
                                     for (int k = 0; k < lm.height; k++)
                                     {
                                         Color r = lm.GetPixel(j, k);
                                         Color g = lm_g.GetPixel(j, k);
                                         Color b = lm_b.GetPixel(j, k);
                                         lm.SetPixel(j, k, new Color(r.a, g.a, b.a, 1f));
                                     }
                                 }
                                 lm.Apply();
                             }
                         }
                         else
                         {
                             lm = new Texture2D(1, 1);
                             lm.SetPixel(0, 0, Color.white);
                             lm.wrapMode   = TextureWrapMode.Clamp;
                             lm.filterMode = FilterMode.Bilinear;
                             lm.Apply();
                         }
                         Vector2[] lightmapUVs = new Vector2[mo.num_vertices];
                         Pointer.DoAt(ref reader, ps2l.off_lightmapUV[me.lightmap_index], () => {
                             for (int j = 0; j < mo.num_vertices; j++)
                             {
                                 lightmapUVs[j] = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                             }
                         });
                         me.AddLightmap(lm, lightmapUVs);
                     }
                 }
             }
             ((R2PS2Loader)l).meshesRead += (uint)meshObjects.Count;
         }
     }
 }
        public static GeometricObject Read(Reader reader, Pointer offset)
        {
            MapLoader l = MapLoader.Loader;
            //l.print("Geometric Object: " + offset);
            GeometricObject m = new GeometricObject(offset);

            if (Settings.s.game == Settings.Game.LargoWinch)
            {
                uint flags = reader.ReadUInt32();
                m.num_vertices      = reader.ReadUInt16();
                m.num_elements      = reader.ReadUInt16();
                m.off_element_types = Pointer.Read(reader);
                m.off_elements      = Pointer.Read(reader);
                m.off_vertices      = Pointer.Read(reader);
                m.off_normals       = Pointer.Read(reader);
                reader.ReadSingle();
                reader.ReadSingle();
                reader.ReadSingle();
                reader.ReadSingle();
                m.lookAtMode = reader.ReadUInt32();
            }
            else if (Settings.s.game == Settings.Game.R2Revolution)
            {
                m.off_element_types = Pointer.Read(reader);
                m.off_elements      = Pointer.Read(reader);
                uint flags = reader.ReadUInt32();
                reader.ReadSingle();
                reader.ReadSingle();
                reader.ReadSingle();
                reader.ReadSingle();
                m.off_mapping  = Pointer.Read(reader);
                m.num_vertices = reader.ReadUInt16();
                m.num_elements = reader.ReadUInt16();
                m.off_vertices = Pointer.Read(reader);
                m.off_normals  = Pointer.Read(reader);
                m.lookAtMode   = flags & 3;
            }
            else
            {
                if (Settings.s.engineVersion <= Settings.EngineVersion.Montreal)
                {
                    m.num_vertices = (ushort)reader.ReadUInt32();
                }
                m.off_vertices = Pointer.Read(reader);
                m.off_normals  = Pointer.Read(reader);
                if (Settings.s.engineVersion < Settings.EngineVersion.R3)
                {
                    m.off_materials = Pointer.Read(reader);
                }
                else
                {
                    m.off_blendWeights = Pointer.Read(reader);
                }
                if (Settings.s.mode != Settings.Mode.RaymanArenaGC &&
                    Settings.s.mode != Settings.Mode.RaymanArenaGCDemo &&
                    Settings.s.game != Settings.Game.RM &&
                    Settings.s.mode != Settings.Mode.DonaldDuckPKGC &&
                    !(Settings.s.platform == Settings.Platform.PS2 && Settings.s.engineVersion == Settings.EngineVersion.R3))
                {
                    reader.ReadInt32();
                }
                if (Settings.s.engineVersion <= Settings.EngineVersion.Montreal)
                {
                    m.num_elements = (ushort)reader.ReadUInt32();
                }
                m.off_element_types = Pointer.Read(reader);
                m.off_elements      = Pointer.Read(reader);
                reader.ReadInt32();
                reader.ReadInt32();
                if (Settings.s.engineVersion == Settings.EngineVersion.R2)
                {
                    reader.ReadInt32();
                    reader.ReadInt32();
                }
                if (Settings.s.game == Settings.Game.Dinosaur)
                {
                    reader.ReadInt32();
                    reader.ReadInt32();
                    reader.ReadInt32();
                }
                if (Settings.s.engineVersion > Settings.EngineVersion.Montreal)
                {
                    m.lookAtMode = reader.ReadUInt32();
                    //if (m.lookAtMode != 0) l.print(m.lookAtMode);
                    m.num_vertices = reader.ReadUInt16();
                    m.num_elements = reader.ReadUInt16();
                    reader.ReadInt32();
                    reader.ReadSingle();                     // bounding volume radius
                    reader.ReadSingle();                     // x
                    reader.ReadSingle();                     // z
                    reader.ReadSingle();                     // y
                    reader.ReadInt32();
                    if (Settings.s.engineVersion == Settings.EngineVersion.R3)
                    {
                        reader.ReadInt32();
                        if (!(Settings.s.platform == Settings.Platform.PS2 && (Settings.s.game == Settings.Game.RM || Settings.s.game == Settings.Game.RA)))
                        {
                            reader.ReadInt16();
                            if (Settings.s.platform == Settings.Platform.PS2)
                            {
                                reader.ReadInt16();
                                reader.ReadUInt32();
                            }
                        }
                    }
                }
                else
                {
                    reader.ReadInt32();
                    reader.ReadInt32();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadSingle();
                }
            }
            m.name = "Mesh @ " + offset;
            if (Settings.s.hasNames)
            {
                m.name = reader.ReadString(0x32);
            }
            if (Settings.s.platform == Settings.Platform.PS2 && Settings.s.engineVersion >= Settings.EngineVersion.R3)
            {
                reader.Align(0x4);
                reader.ReadUInt32();
                reader.ReadUInt32();
                m.optimizedObject = new Pointer <PS2OptimizedSDCStructure>(reader, resolve: false);
                reader.ReadUInt32();
                reader.ReadUInt32();
                if (Settings.s.game == Settings.Game.R3)
                {
                    m.ps2IsSinus = reader.ReadUInt32();
                }
            }
            // Vertices
            Pointer.DoAt(ref reader, m.off_vertices, () => {
                m.vertices = new Vector3[m.num_vertices];
                for (int i = 0; i < m.num_vertices; i++)
                {
                    float x       = reader.ReadSingle();
                    float z       = reader.ReadSingle();
                    float y       = reader.ReadSingle();
                    m.vertices[i] = new Vector3(x, y, z);
                }
            });
            // Normals
            Pointer.DoAt(ref reader, m.off_normals, () => {
                m.normals = new Vector3[m.num_vertices];
                for (int i = 0; i < m.num_vertices; i++)
                {
                    float x      = reader.ReadSingle();
                    float z      = reader.ReadSingle();
                    float y      = reader.ReadSingle();
                    m.normals[i] = new Vector3(x, y, z);
                }
            });
            Pointer.DoAt(ref reader, m.off_blendWeights, () => {
                m.blendWeights = new float[4][];

                /*reader.ReadUInt32(); // 0
                 * R3Pointer off_blendWeightsStart = R3Pointer.Read(reader);
                 * R3Pointer.Goto(ref reader, off_blendWeightsStart);*/
                for (int i = 0; i < 4; i++)
                {
                    Pointer off_blendWeights = Pointer.Read(reader);
                    Pointer.DoAt(ref reader, off_blendWeights, () => {
                        m.blendWeights[i] = new float[m.num_vertices];
                        for (int j = 0; j < m.num_vertices; j++)
                        {
                            m.blendWeights[i][j] = reader.ReadSingle();
                        }
                    });
                }
                reader.ReadUInt32();
                reader.ReadUInt32();
                reader.ReadUInt32();
                reader.ReadUInt32();
            });
            Pointer.DoAt(ref reader, m.off_mapping, () => {
                // Revolution only
                reader.ReadUInt32();
                Pointer.Read(reader);
                Pointer off_mappingBlocks = Pointer.Read(reader);
                Pointer.Read(reader);
                Pointer.Read(reader);
                ushort num_mappingBlocks = reader.ReadUInt16();
                reader.ReadUInt16();
                Pointer.DoAt(ref reader, off_mappingBlocks, () => {
                    m.mapping = new int[num_mappingBlocks][];
                    for (int i = 0; i < num_mappingBlocks; i++)
                    {
                        Pointer off_mapping = Pointer.Read(reader);
                        Pointer.DoAt(ref reader, off_mapping, () => {
                            m.mapping[i] = new int[m.num_vertices];
                            for (int j = 0; j < m.num_vertices; j++)
                            {
                                m.mapping[i][j] = reader.ReadUInt16();
                                if (m.mapping[i][j] >= m.num_vertices)
                                {
                                    l.print(m.offset);
                                }
                            }
                        });
                    }
                });
            });
            // Read element types & initialize arrays
            Pointer.Goto(ref reader, m.off_element_types);
            m.element_types = new ushort[m.num_elements];
            m.elements      = new IGeometricObjectElement[m.num_elements];
            for (uint i = 0; i < m.num_elements; i++)
            {
                m.element_types[i] = reader.ReadUInt16();
            }
            // Process elements
            for (uint i = 0; i < m.num_elements; i++)
            {
                Pointer.Goto(ref reader, m.off_elements + (i * 4));
                Pointer block_offset = Pointer.Read(reader);
                Pointer.Goto(ref reader, block_offset);
                switch (m.element_types[i])
                {
                case 1:     // Material
                    m.elements[i] = GeometricObjectElementTriangles.Read(reader, block_offset, m);
                    break;

                case 3:     // Sprite
                    m.elements[i] = GeometricObjectElementSprites.Read(reader, block_offset, m);
                    break;

                case 13:
                case 15:
                    m.bones       = DeformSet.Read(reader, block_offset, m);
                    m.elements[i] = m.bones;
                    break;

                default:
                    m.elements[i] = null;

                    /*1 = indexedtriangles
                     * 2 = facemap
                     * 3 = sprite
                     * 4 = TMesh
                     * 5 = points
                     * 6 = lines
                     * 7 = spheres
                     * 8 = alignedboxes
                     * 9 = cones
                     * 13 = deformationsetinfo*/
                    l.print("Unknown geometric element type " + m.element_types[i] + " at offset " + block_offset);
                    break;
                }
            }
            ReadMeshFromATO(reader, m);
            if (Settings.s.platform == Settings.Platform.PS2 && Settings.s.engineVersion == Settings.EngineVersion.R3)
            {
                m.optimizedObject?.Resolve(reader, onPreRead: opt => opt.isSinus = m.ps2IsSinus);
                m.ReadMeshFromSDC();
            }
            m.InitGameObject();
            return(m);
        }
Exemple #7
0
 public GeometricObjectElementSprites(Pointer offset, GeometricObject geo)
 {
     this.geo    = geo;
     this.offset = offset;
 }
Exemple #8
0
        public static GeometricObjectElementSprites Read(Reader reader, Pointer offset, GeometricObject m)
        {
            MapLoader l = MapLoader.Loader;
            GeometricObjectElementSprites s = new GeometricObjectElementSprites(offset, m);

            s.name = "Sprite @ pos " + offset;
            //l.print(s.name);

            if (Settings.s.engineVersion > Settings.EngineVersion.Montreal)
            {
                if (Settings.s.platform == Settings.Platform.DC)
                {
                    s.off_sprites = offset;
                    s.num_sprites = 1;
                }
                else
                {
                    s.off_sprites = Pointer.Read(reader);
                    s.num_sprites = reader.ReadUInt16();
                    reader.ReadInt16(); // -1
                    if (Settings.s.game != Settings.Game.R2Revolution)
                    {
                        reader.ReadUInt32();
                        if (Settings.s.game != Settings.Game.LargoWinch)
                        {
                            reader.ReadUInt32();
                        }
                    }
                }
            }
            else
            {
                s.num_sprites = (ushort)reader.ReadUInt32();
                s.off_sprites = Pointer.Read(reader);
                reader.ReadUInt32();
            }
            if (Settings.s.game == Settings.Game.R2Revolution)
            {
                Pointer.DoAt(ref reader, s.off_sprites, () => {
                    s.sprites = new IndexedSprite[s.num_sprites];
                    for (uint i = 0; i < s.num_sprites; i++)
                    {
                        s.sprites[i]            = new IndexedSprite();
                        uint type               = reader.ReadUInt32();
                        s.sprites[i].info_scale = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        if (type == 0x20)
                        {
                            // Light cookie sprite
                            uint index                              = reader.ReadUInt32();
                            R2PS2Loader ps2l                        = MapLoader.Loader as R2PS2Loader;
                            s.sprites[i].visualMaterial             = ps2l.lightCookieMaterial.Clone();
                            s.sprites[i].visualMaterial.diffuseCoef = ps2l.lightCookieColors[index];
                        }
                        else
                        {
                            s.sprites[i].off_material = Pointer.Read(reader);
                            if (s.sprites[i].off_material != null)
                            {
                                s.sprites[i].visualMaterial = VisualMaterial.FromOffsetOrRead(s.sprites[0].off_material, reader);
                            }
                        }
                    }
                });
            }
            else if (Settings.s.platform == Settings.Platform.DC)
            {
                s.sprites    = new IndexedSprite[1];
                s.sprites[0] = new IndexedSprite();
                s.sprites[0].off_material = Pointer.Read(reader);
                if (s.sprites[0].off_material != null)
                {
                    s.sprites[0].visualMaterial = VisualMaterial.FromOffsetOrRead(s.sprites[0].off_material, reader);
                }
                s.sprites[0].info_scale = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                reader.ReadUInt16();
                s.sprites[0].centerPoint = reader.ReadUInt16();
                reader.ReadUInt16();
                reader.ReadUInt16();
                reader.ReadUInt16();
                reader.ReadUInt16();
            }
            else
            {
                if (s.off_sprites != null)
                {
                    Pointer.Goto(ref reader, s.off_sprites);
                    s.sprites = new IndexedSprite[s.num_sprites];
                    for (uint i = 0; i < s.num_sprites; i++)
                    {
                        s.sprites[i] = new IndexedSprite();
                        if (Settings.s.engineVersion <= Settings.EngineVersion.Montreal)
                        {
                            reader.ReadUInt32();
                        }
                        s.sprites[i].off_info = Pointer.Read(reader);
                        s.sprites[i].size     = new Vector2(reader.ReadSingle(), reader.ReadSingle());

                        if (Settings.s.engineVersion > Settings.EngineVersion.Montreal)
                        {
                            if (Settings.s.game != Settings.Game.LargoWinch)
                            {
                                s.sprites[i].constraint = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                                s.sprites[i].uv1        = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                                s.sprites[i].uv2        = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                            }
                            s.sprites[i].centerPoint = reader.ReadUInt16();
                            reader.ReadUInt16();
                            if (Settings.s.engineVersion < Settings.EngineVersion.R3)
                            {
                                reader.ReadUInt32();
                            }
                        }

                        if (s.sprites[i].off_info != null)
                        {
                            Pointer off_current = Pointer.Goto(ref reader, s.sprites[i].off_info);
                            reader.ReadUInt32();
                            Pointer.Read(reader);
                            Pointer.Read(reader);
                            Pointer off_info_scale   = Pointer.Read(reader);
                            Pointer off_info_unknown = Pointer.Read(reader);
                            s.sprites[i].off_material_pointer = Pointer.Read(reader);
                            Pointer.Goto(ref reader, off_current);

                            Pointer.DoAt(ref reader, off_info_scale, () => {
                                s.sprites[i].info_scale = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                            });
                            Pointer.DoAt(ref reader, off_info_unknown, () => {
                                s.sprites[i].info_unknown = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                            });
                            if (s.sprites[i].off_material_pointer != null)
                            {
                                off_current = Pointer.Goto(ref reader, s.sprites[i].off_material_pointer);
                                s.sprites[i].off_material = Pointer.Read(reader);
                                if (s.sprites[i].off_material != null)
                                {
                                    if (Settings.s.engineVersion < Settings.EngineVersion.R3)
                                    {
                                        s.sprites[i].gameMaterial   = GameMaterial.FromOffsetOrRead(s.sprites[i].off_material, reader);
                                        s.sprites[i].visualMaterial = s.sprites[i].gameMaterial.visualMaterial;
                                    }
                                    else
                                    {
                                        s.sprites[i].visualMaterial = VisualMaterial.FromOffsetOrRead(s.sprites[i].off_material, reader);
                                    }
                                }
                                Pointer.Goto(ref reader, off_current);
                            }
                        }
                    }
                }
            }

            return(s);
        }
        public static GeometricObjectElementTriangles Read(Reader reader, Pointer offset, GeometricObject geo)
        {
            MapLoader l = MapLoader.Loader;
            GeometricObjectElementTriangles sm = new GeometricObjectElementTriangles(offset, geo);

            sm.name = "Submesh @ pos " + offset;
            //l.print(sm.name);
            sm.backfaceCulling = !l.forceDisplayBackfaces;
            sm.off_material    = Pointer.Read(reader);
            if (Settings.s.game == Settings.Game.LargoWinch)
            {
                //sm.visualMaterial = VisualMaterial.FromOffset(sm.off_material);
                sm.visualMaterial = VisualMaterial.FromOffsetOrRead(sm.off_material, reader);
            }
            else if (Settings.s.engineVersion == Settings.EngineVersion.R3 || Settings.s.game == Settings.Game.R2Revolution)
            {
                sm.visualMaterial = VisualMaterial.FromOffset(sm.off_material);
            }
            else
            {
                sm.gameMaterial   = GameMaterial.FromOffsetOrRead(sm.off_material, reader);
                sm.visualMaterial = sm.gameMaterial.visualMaterial;
            }
            sm.visualMaterialOG = sm.visualMaterial;

            /*if (sm.visualMaterial != null && sm.visualMaterial.textures.Count > 0 && sm.visualMaterial.textures[0].off_texture != null) {
             *  sm.name += " - VisMatTex:" + sm.visualMaterial.textures[0].offset + " - TexInfo:" + sm.visualMaterial.textures[0].off_texture;
             * }*/
            if (sm.visualMaterial != null)
            {
                sm.backfaceCulling = ((sm.visualMaterial.flags & VisualMaterial.flags_backfaceCulling) != 0) && !l.forceDisplayBackfaces;
            }
            sm.num_triangles = reader.ReadUInt16();
            if (Settings.s.game == Settings.Game.R2Revolution)
            {
                sm.lightmap_index = reader.ReadInt16();
                sm.off_triangles  = Pointer.Read(reader);
            }
            else
            {
                sm.num_uvs = reader.ReadUInt16();
                if (Settings.s.engineVersion == Settings.EngineVersion.R3)
                {
                    sm.num_uvMaps     = reader.ReadUInt16();
                    sm.lightmap_index = reader.ReadInt16();
                }
                sm.off_triangles = Pointer.Read(reader);                 // 1 entry = 3 shorts. Max: num_vertices
                if (Settings.s.mode == Settings.Mode.Rayman3GC)
                {
                    reader.ReadUInt32();
                }
                sm.off_mapping_uvs = Pointer.Read(reader);         // 1 entry = 3 shorts. Max: num_weights
                sm.off_normals     = Pointer.Read(reader);         // 1 entry = 3 floats
                sm.off_uvs         = Pointer.Read(reader);         // 1 entry = 2 floats
                if (Settings.s.game == Settings.Game.LargoWinch)
                {
                    sm.off_mapping_lightmap = Pointer.Read(reader);
                    sm.num_mapping_lightmap = reader.ReadUInt16();
                    reader.ReadUInt16();
                }
                else if (Settings.s.engineVersion == Settings.EngineVersion.R3)
                {
                    reader.ReadUInt32();
                    reader.ReadUInt32();
                }
                else if (Settings.s.engineVersion == Settings.EngineVersion.Montreal)
                {
                    reader.ReadUInt32();
                }
                if (Settings.s.game != Settings.Game.TTSE)
                {
                    sm.off_vertex_indices = Pointer.Read(reader);
                    sm.num_vertex_indices = reader.ReadUInt16();
                    sm.parallelBox        = reader.ReadUInt16();
                    reader.ReadUInt32();
                }
            }
            if (Settings.s.engineVersion == Settings.EngineVersion.R3)
            {
                if (Settings.s.game != Settings.Game.Dinosaur &&
                    Settings.s.game != Settings.Game.LargoWinch &&
                    Settings.s.mode != Settings.Mode.RaymanArenaGCDemo)
                {
                    sm.isVisibleInPortal = reader.ReadByte();
                    reader.ReadByte();
                    sm.OPT_num_mapping_entries       = reader.ReadUInt16();            // num_shorts
                    sm.OPT_off_mapping_vertices      = Pointer.Read(reader);           // shorts_offset1 (1st array of size num_shorts, max_num_vertices)
                    sm.OPT_off_mapping_uvs           = Pointer.Read(reader);           // shorts_offset2 (2nd array of size num_shorts, max: num_weights)
                    sm.OPT_num_triangleStrip         = reader.ReadUInt16();            // num_shorts2
                    sm.OPT_num_disconnectedTriangles = reader.ReadUInt16();
                    sm.OPT_off_triangleStrip         = Pointer.Read(reader);           // shorts2_offset (array of size num_shorts2)
                    sm.OPT_off_disconnectedTriangles = Pointer.Read(reader);
                    if (Settings.s.hasNames)
                    {
                        sm.name += reader.ReadString(0x34);
                    }
                }
                else
                {
                    sm.OPT_num_mapping_entries       = 0;
                    sm.OPT_off_mapping_vertices      = null;
                    sm.OPT_off_mapping_uvs           = null;
                    sm.OPT_num_triangleStrip         = 0;
                    sm.OPT_num_disconnectedTriangles = 0;
                    sm.OPT_off_triangleStrip         = null;
                    sm.OPT_off_disconnectedTriangles = null;
                    sm.isVisibleInPortal             = 1;
                    if (Settings.s.mode == Settings.Mode.RaymanArenaGCDemo)
                    {
                        sm.isVisibleInPortal = reader.ReadByte();
                        reader.ReadByte();
                        sm.OPT_num_mapping_entries = reader.ReadUInt16();                         // num_shorts
                    }
                }
            }
            else
            {
                // Defaults for Rayman 2, no optimized mesh feature
                sm.num_uvMaps = 1;
                sm.OPT_num_mapping_entries       = 0;
                sm.OPT_off_mapping_vertices      = null;
                sm.OPT_off_mapping_uvs           = null;
                sm.OPT_num_triangleStrip         = 0;
                sm.OPT_num_disconnectedTriangles = 0;
                sm.OPT_off_triangleStrip         = null;
                sm.OPT_off_disconnectedTriangles = null;
                sm.isVisibleInPortal             = 1;
            }

            // Read mapping tables
            sm.OPT_mapping_uvs = new int[sm.num_uvMaps][];
            if (sm.OPT_num_mapping_entries > 0)
            {
                Pointer.Goto(ref reader, sm.OPT_off_mapping_vertices);
                //print("Mapping offset: " + String.Format("0x{0:X}", fs.Position));
                sm.OPT_mapping_vertices = new int[sm.OPT_num_mapping_entries];
                for (int j = 0; j < sm.OPT_num_mapping_entries; j++)
                {
                    sm.OPT_mapping_vertices[j] = reader.ReadInt16();
                }
                Pointer.Goto(ref reader, sm.OPT_off_mapping_uvs);
                for (int j = 0; j < sm.num_uvMaps; j++)
                {
                    sm.OPT_mapping_uvs[j] = new int[sm.OPT_num_mapping_entries];
                }
                for (int j = 0; j < sm.OPT_num_mapping_entries; j++)
                {
                    for (int um = 0; um < sm.num_uvMaps; um++)
                    {
                        sm.OPT_mapping_uvs[um][j] = reader.ReadInt16();
                    }
                }
            }
            if (sm.num_triangles > 0)
            {
                Pointer.Goto(ref reader, sm.off_mapping_uvs);
                sm.mapping_uvs = new int[sm.num_uvMaps][];
                for (int j = 0; j < sm.num_uvMaps; j++)
                {
                    sm.mapping_uvs[j] = new int[sm.num_triangles * 3];
                }
                // Why is uv maps here the outer loop instead of inner like the other thing?
                for (int um = 0; um < sm.num_uvMaps; um++)
                {
                    for (int j = 0; j < sm.num_triangles * 3; j++)
                    {
                        sm.mapping_uvs[um][j] = reader.ReadInt16();
                    }
                }
            }

            // Read UVs
            Pointer.DoAt(ref reader, sm.off_uvs, () => {
                sm.uvs = new Vector2[sm.num_uvs];
                for (int j = 0; j < sm.num_uvs; j++)
                {
                    sm.uvs[j] = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                }
            });
            // Read triangle data
            Pointer.DoAt(ref reader, sm.OPT_off_triangleStrip, () => {
                sm.OPT_triangleStrip = new int[sm.OPT_num_triangleStrip];
                for (int j = 0; j < sm.OPT_num_triangleStrip; j++)
                {
                    sm.OPT_triangleStrip[j] = reader.ReadInt16();
                }
            });
            Pointer.DoAt(ref reader, sm.OPT_off_disconnectedTriangles, () => {
                sm.OPT_disconnectedTriangles = new int[sm.OPT_num_disconnectedTriangles * 3];
                //print("Loading disconnected triangles at " + String.Format("0x{0:X}", fs.Position));
                for (int j = 0; j < sm.OPT_num_disconnectedTriangles; j++)
                {
                    sm.OPT_disconnectedTriangles[(j * 3) + 0] = reader.ReadInt16();
                    sm.OPT_disconnectedTriangles[(j * 3) + 1] = reader.ReadInt16();
                    sm.OPT_disconnectedTriangles[(j * 3) + 2] = reader.ReadInt16();
                }
            });
            if (sm.num_triangles > 0)
            {
                Pointer.Goto(ref reader, sm.off_triangles);
                sm.triangles = new int[sm.num_triangles * 3];
                //print("Loading disconnected triangles at " + String.Format("0x{0:X}", fs.Position));
                for (int j = 0; j < sm.num_triangles; j++)
                {
                    sm.triangles[(j * 3) + 0] = reader.ReadInt16();
                    sm.triangles[(j * 3) + 1] = reader.ReadInt16();
                    sm.triangles[(j * 3) + 2] = reader.ReadInt16();
                }
                if (sm.off_normals != null)
                {
                    Pointer.Goto(ref reader, sm.off_normals);
                    sm.normals = new Vector3[sm.num_triangles];
                    for (int j = 0; j < sm.num_triangles; j++)
                    {
                        float x = reader.ReadSingle();
                        float z = reader.ReadSingle();
                        float y = reader.ReadSingle();
                        sm.normals[j] = new Vector3(x, y, z);
                    }
                }
            }
            if (Settings.s.game == Settings.Game.LargoWinch && sm.lightmap_index != -1)
            {
                LWLoader lwl = MapLoader.Loader as LWLoader;
                if (lwl.lms != null && sm.lightmap_index >= 0 && sm.lightmap_index < lwl.lms.Count)
                {
                    /*if (sm.lightmap_index < l.off_lightmapUV.Length - 1) {
                     *      int amount = ((int)l.off_lightmapUV[sm.lightmap_index + 1].offset - (int)l.off_lightmapUV[sm.lightmap_index].offset);
                     *      amount = amount / 8;
                     *      l.print(offset + " - UVs: " + amount + " - " + sm.mesh.num_vertices + " - " + sm.num_mapping_entries + " - " + sm.num_uvs + " - " + sm.num_disconnected_triangles_spe + " - " + sm.num_mapping_lightmap);
                     * }*/
                    Vector2[] lightmapUVs = new Vector2[sm.num_mapping_lightmap];
                    Pointer.DoAt(ref reader, sm.off_mapping_lightmap, () => {
                        sm.mapping_lightmap = new int[sm.num_mapping_lightmap];
                        for (int i = 0; i < sm.num_mapping_lightmap; i++)
                        {
                            sm.mapping_lightmap[i] = reader.ReadInt16();
                        }
                    });
                    Pointer.DoAt(ref reader, l.off_lightmapUV[sm.lightmap_index], () => {
                        for (int j = 0; j < lightmapUVs.Length; j++)
                        {
                            lightmapUVs[j] = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        }
                    });
                    sm.AddLightmap(lwl.GetLightmap(sm.lightmap_index), lightmapUVs);
                }
            }
            return(sm);
        }