예제 #1
0
        public VisualMaterial Clone()
        {
            VisualMaterial vm = (VisualMaterial)MemberwiseClone();

            vm.textures     = new List <VisualMaterialTexture>(textures);
            vm.animTextures = new List <AnimatedTexture>(animTextures);
            vm.Reset();
            return(vm);
        }
예제 #2
0
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                VisualMaterial          vmt       = (VisualMaterial)value;
                string                  hash      = HashUtils.MD5Hash(vmt.ToJSON());
                VisualMaterialReference reference = new VisualMaterialReference()
                {
                    Hash = hash
                };

                var jt = JToken.FromObject(reference);

                jt.WriteTo(writer);
            }
예제 #3
0
        public static VisualMaterial FromOffsetOrRead(Pointer offset, Reader reader)
        {
            if (offset == null)
            {
                return(null);
            }
            VisualMaterial vm = FromOffset(offset);

            if (vm == null)
            {
                Pointer.DoAt(ref reader, offset, () => {
                    vm = VisualMaterial.Read(reader, offset);
                    MapLoader.Loader.visualMaterials.Add(vm);
                });
            }
            return(vm);
        }
예제 #4
0
        public static VisualMaterial Read(Reader reader, Pointer offset)
        {
            MapLoader      l = MapLoader.Loader;
            VisualMaterial m = new VisualMaterial(offset);

            // Material struct = 0x188
            //l.print("Material @ " + offset);
            m.flags = reader.ReadUInt32(); // After this: 0x4
            if (Settings.s.game != Settings.Game.R2Revolution && Settings.s.game != Settings.Game.LargoWinch)
            {
                if (Settings.s.platform == Settings.Platform.DC)
                {
                    reader.ReadUInt32();
                }
                m.ambientCoef  = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                m.diffuseCoef  = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                m.specularCoef = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                m.color        = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());          // 0x44
            }
            else if (Settings.s.game == Settings.Game.R2Revolution)
            {
                // Fill in light info for Revolution
                m.ambientCoef = new Vector4(0, 0, 0, 1f);
                m.diffuseCoef = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                //m.diffuseCoef = new Vector4(1, 1, 1, 1);
                reader.ReadInt32();                 // current refresh number for scrolling/animated textures
                m.off_animTextures_first   = Pointer.Read(reader);
                m.off_animTextures_current = Pointer.Read(reader);
                reader.ReadInt32();
                m.num_animTextures = reader.ReadUInt16();
                reader.ReadUInt16();                 // 0x70
            }
            else if (Settings.s.game == Settings.Game.LargoWinch)
            {
                m.ambientCoef = new Vector4(0, 0, 0, 1f);
                m.color       = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());           // 0x44
                m.diffuseCoef = new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                //m.ambientCoef = m.diffuseCoef;
                reader.ReadInt32();                 // current refresh number for scrolling/animated textures
                m.off_animTextures_first   = Pointer.Read(reader);
                m.off_animTextures_current = Pointer.Read(reader);
                reader.ReadInt32();
                m.num_animTextures = reader.ReadUInt16();
                reader.ReadUInt16();
            }
            if (Settings.s.game == Settings.Game.LargoWinch)
            {
                m.num_textures = 1;
                VisualMaterialTexture t = new VisualMaterialTexture();
                t.offset      = Pointer.Current(reader);
                t.off_texture = Pointer.Read(reader);                 // 0x4c
                t.texture     = TextureInfo.FromOffset(t.off_texture);
                t.textureOp   = reader.ReadByte();
                t.shadingMode = reader.ReadByte();
                t.uvFunction  = reader.ReadByte();
                t.scrollByte  = reader.ReadByte();
                t.scrollX     = reader.ReadSingle();
                t.scrollY     = reader.ReadSingle();
                reader.ReadSingle();

                new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                m.textures.Add(t);
            }
            else if (Settings.s.game == Settings.Game.R2Revolution)
            {
                m.num_textures = 1;
                VisualMaterialTexture t = new VisualMaterialTexture();
                t.offset         = Pointer.Current(reader);
                t.off_texture    = Pointer.Read(reader);              // 0x4c
                t.texture        = TextureInfo.FromOffset(t.off_texture);
                t.scrollMode     = reader.ReadUInt32();
                t.scrollX        = reader.ReadSingle();
                t.scrollY        = reader.ReadSingle();
                t.currentScrollX = reader.ReadSingle();
                t.currentScrollY = reader.ReadSingle();
                m.textures.Add(t);

                /*reader.ReadInt32(); // current refresh number for scrolling/animated textures, 0x64
                 * m.off_animTextures_first = Pointer.Read(reader); // 0x68
                 * m.off_animTextures_current = Pointer.Read(reader); // 0x6c
                 * m.num_animTextures = reader.ReadUInt16();
                 * reader.ReadUInt16(); // 0x70
                 * reader.ReadUInt32();
                 * reader.ReadByte();
                 * reader.ReadByte();
                 * m.properties = reader.ReadByte();
                 * reader.ReadByte();
                 * reader.ReadUInt32();
                 * reader.ReadUInt32();*/
            }
            else if (Settings.s.engineVersion < Settings.EngineVersion.R3)
            {
                m.num_textures = 1;
                reader.ReadUInt32(); // 0x48
                VisualMaterialTexture t = new VisualMaterialTexture();
                t.offset      = Pointer.Current(reader);
                t.off_texture = Pointer.Read(reader); // 0x4c
                t.texture     = TextureInfo.FromOffset(t.off_texture);
                if (Settings.s.game == Settings.Game.TT)
                {
                    /*m.off_animTextures_first = Pointer.Read(reader); // 0x68
                     * m.off_animTextures_current = Pointer.Read(reader); // 0x6c
                     * m.num_animTextures = reader.ReadUInt16();*/
                    Pointer.Read(reader); // detail texture
                    t.currentScrollX = reader.ReadSingle();
                    t.currentScrollY = reader.ReadSingle();
                    t.scrollX        = reader.ReadSingle(); // 0x58
                    t.scrollY        = reader.ReadSingle(); // 0x5c
                    t.scrollMode     = reader.ReadUInt32(); //0x60
                    m.textures.Add(t);

                    reader.ReadInt32(); // current refresh number for scrolling/animated textures, 0x64
                }
                else
                {
                    if (Settings.s.platform == Settings.Platform.DC)
                    {
                        // For some reason there's a huge gap here
                        reader.ReadBytes(0xD0);
                    }
                    t.currentScrollX = reader.ReadSingle();
                    t.currentScrollY = reader.ReadSingle();
                    t.scrollX        = reader.ReadSingle();            // 0x58
                    t.scrollY        = reader.ReadSingle();            // 0x5c
                    t.scrollMode     = reader.ReadUInt32();            //0x60
                    m.textures.Add(t);
                    reader.ReadInt32();                                // current refresh number for scrolling/animated textures, 0x64
                    m.off_animTextures_first   = Pointer.Read(reader); // 0x68
                    m.off_animTextures_current = Pointer.Read(reader); // 0x6c
                    m.num_animTextures         = reader.ReadUInt16();
                    reader.ReadUInt16();                               // 0x70
                }
                reader.ReadUInt32();                                   // 0x74
                m.properties = reader.ReadByte();                      // whole byte for texture scroll lock in R2, no bitmasks
                reader.ReadByte();
                reader.ReadByte();                                     // padding, not in DC
                reader.ReadByte();                                     // padding, not in DC
            }
            else                                                       // EngineVersion >= R3
            {
                reader.ReadUInt32();                                   // current refresh number for scrolling/animated textures, 0x48
                if (Settings.s.game == Settings.Game.Dinosaur)
                {
                    reader.ReadBytes(0x1C);
                }
                m.off_animTextures_first   = Pointer.Read(reader);
                m.off_animTextures_current = Pointer.Read(reader);
                m.num_animTextures         = reader.ReadUInt16();
                reader.ReadUInt16();
                reader.ReadUInt32();
                reader.ReadByte();
                reader.ReadByte();
                m.properties = reader.ReadByte();
                reader.ReadByte();
                reader.ReadUInt32();
                reader.ReadUInt32();
                /* m.num_textures = */ reader.ReadUInt32();
                for (int i = 0; i < 4; i++)
                {
                    VisualMaterialTexture t = new VisualMaterialTexture();
                    t.offset      = Pointer.Current(reader);
                    t.off_texture = Pointer.Read(reader);
                    if (t.off_texture == null)
                    {
                        break;
                    }

                    /*if (Settings.s.game == Settings.Game.Dinosaur) {
                     *      Pointer.DoAt(ref reader, t.off_texture, () => {
                     *              Pointer off_tex = Pointer.Read(reader);
                     *              t.texture = TextureInfo.FromOffset(off_tex);
                     *      });
                     * } else {*/
                    t.texture = TextureInfo.FromOffset(t.off_texture);
                    //}

                    t.textureOp   = reader.ReadByte();
                    t.shadingMode = reader.ReadByte();
                    t.uvFunction  = reader.ReadByte();
                    t.scrollByte  = reader.ReadByte();

                    if (Settings.s.game == Settings.Game.Dinosaur)
                    {
                        t.properties = reader.ReadInt32();
                        new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                        new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                        new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                        new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
                        t.currentScrollX = reader.ReadSingle();
                        t.currentScrollY = reader.ReadSingle();
                        t.scrollX        = reader.ReadSingle();
                        t.scrollY        = reader.ReadSingle();
                        new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        new Vector2(reader.ReadSingle(), reader.ReadSingle());
                        new Vector2(reader.ReadSingle(), reader.ReadSingle());
                    }
                    else
                    {
                        t.properties = reader.ReadInt32();
                        reader.ReadInt32();
                        reader.ReadInt32();
                        t.scrollX         = reader.ReadSingle();
                        t.scrollY         = reader.ReadSingle();
                        t.rotateSpeed     = reader.ReadSingle();
                        t.rotateDirection = reader.ReadSingle();
                        reader.ReadInt32();
                        reader.ReadInt32();
                        t.currentScrollX = reader.ReadSingle();
                        t.currentScrollY = reader.ReadSingle();
                        reader.ReadInt32();
                        reader.ReadInt32();
                        reader.ReadInt32();
                        reader.ReadInt32();
                        t.blendIndex = reader.ReadUInt32();
                    }

                    m.textures.Add(t);
                }
                m.num_textures = (uint)m.textures.Count;
            }
            if (m.num_animTextures > 0 && m.off_animTextures_first != null)
            {
                Pointer off_currentAnimTexture = m.off_animTextures_first;
                Pointer.Goto(ref reader, m.off_animTextures_first);
                for (int i = 0; i < m.num_animTextures; i++)
                {
                    if (off_currentAnimTexture == m.off_animTextures_current)
                    {
                        m.currentAnimTexture = i;
                    }
                    Pointer off_animTexture = Pointer.Read(reader);
                    float   time            = reader.ReadSingle();
                    m.animTextures.Add(new AnimatedTexture(off_animTexture, time));
                    Pointer off_nextAnimTexture = Pointer.Read(reader);
                    if (off_nextAnimTexture != null)
                    {
                        off_currentAnimTexture = off_nextAnimTexture;
                        Pointer.Goto(ref reader, off_nextAnimTexture);
                    }
                }
            }

            return(m);
        }
예제 #5
0
        public static SpriteElement Read(EndianBinaryReader reader, Pointer offset, MeshObject m)
        {
            MapLoader     l = MapLoader.Loader;
            SpriteElement s = new SpriteElement(offset, m);

            s.name = "Sprite @ pos " + offset;

            s.off_sprites = Pointer.Read(reader);
            s.num_sprites = reader.ReadUInt16();
            reader.ReadInt16(); // -1
            reader.ReadUInt32();
            reader.ReadUInt32();

            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();
                    s.sprites[i].off_info    = Pointer.Read(reader);
                    s.sprites[i].size        = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                    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 (l.mode == MapLoader.Mode.Rayman2PC)
                    {
                        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_uv1 = Pointer.Read(reader);
                        Pointer off_info_uv2 = Pointer.Read(reader);
                        s.sprites[i].off_material_pointer = Pointer.Read(reader);
                        Pointer.Goto(ref reader, off_current);

                        if (off_info_uv1 != null)
                        {
                            off_current           = Pointer.Goto(ref reader, off_info_uv1);
                            s.sprites[i].info_uv1 = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                            Pointer.Goto(ref reader, off_current);
                        }
                        if (off_info_uv2 != null)
                        {
                            off_current           = Pointer.Goto(ref reader, off_info_uv2);
                            s.sprites[i].info_uv2 = new Vector2(reader.ReadSingle(), reader.ReadSingle());
                            Pointer.Goto(ref reader, off_current);
                        }
                        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 (l.mode == MapLoader.Mode.Rayman2PC && s.sprites[i].off_material != null)
                            {
                                Pointer.Goto(ref reader, s.sprites[i].off_material);
                                s.sprites[i].off_material = Pointer.Read(reader);
                            }
                            if (s.sprites[i].off_material != null)
                            {
                                Pointer.Goto(ref reader, s.sprites[i].off_material);
                                s.sprites[i].r3mat = VisualMaterial.FromOffset(s.sprites[i].off_material, createIfNull: true);
                            }
                            else
                            {
                                s.sprites[i].r3mat = null;
                            }
                            Pointer.Goto(ref reader, off_current);
                        }
                    }
                }
            }

            return(s);
        }
예제 #6
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);
        }
예제 #7
0
        public static MeshElement Read(EndianBinaryReader reader, Pointer offset, MeshObject m)
        {
            MapLoader   l  = MapLoader.Loader;
            MeshElement sm = new MeshElement(offset, m);

            sm.name            = "Submesh @ pos " + offset;
            sm.backfaceCulling = !l.forceDisplayBackfaces;
            sm.off_material    = Pointer.Read(reader);
            if (l.mode != MapLoader.Mode.Rayman2PC)
            {
                sm.r3mat = VisualMaterial.FromOffset(sm.off_material);
            }
            else
            {
                Pointer off_current = Pointer.Goto(ref reader, sm.off_material);
                sm.off_material = Pointer.Read(reader);
                if (sm.off_material != null)
                {
                    Pointer.Goto(ref reader, sm.off_material);
                    sm.r3mat = VisualMaterial.FromOffset(sm.off_material, createIfNull: true);
                }
                else
                {
                    sm.r3mat = null;
                }
                Pointer.Goto(ref reader, off_current);
            }
            if (sm.r3mat != null)
            {
                sm.backfaceCulling = ((sm.r3mat.flags & VisualMaterial.flags_backfaceCulling) != 0) && !l.forceDisplayBackfaces;
            }
            sm.num_disconnected_triangles_spe = reader.ReadUInt16();
            sm.num_uvs = reader.ReadUInt16();
            if (l.mode != MapLoader.Mode.Rayman2PC)
            {
                sm.num_uvMaps = reader.ReadUInt16();
                reader.ReadUInt16();
            }
            sm.off_disconnected_triangles_spe = Pointer.Read(reader); // 1 entry = 3 shorts. Max: num_vertices
            if (l.mode == MapLoader.Mode.Rayman3GC)
            {
                reader.ReadUInt32();
            }
            sm.off_mapping_uvs_spe = Pointer.Read(reader); // 1 entry = 3 shorts. Max: num_weights
            sm.off_weights_spe     = Pointer.Read(reader); // 1 entry = 3 floats
            sm.off_uvs             = Pointer.Read(reader); // 1 entry = 2 floats
            if (l.mode != MapLoader.Mode.Rayman2PC)
            {
                reader.ReadUInt32();
                reader.ReadUInt32();
            }
            sm.off_vertex_indices = Pointer.Read(reader);
            sm.num_vertex_indices = reader.ReadUInt16();
            reader.ReadInt16();
            reader.ReadUInt32();
            if (l.mode != MapLoader.Mode.Rayman2PC)
            {
                reader.ReadUInt16();
                sm.num_mapping_entries        = reader.ReadUInt16();  // num_shorts
                sm.off_mapping_vertices       = Pointer.Read(reader); // shorts_offset1 (1st array of size num_shorts, max_num_vertices)
                sm.off_mapping_uvs            = Pointer.Read(reader); // shorts_offset2 (2nd array of size num_shorts, max: num_weights)
                sm.num_connected_vertices     = reader.ReadUInt16();  // num_shorts2
                sm.num_disconnected_triangles = reader.ReadUInt16();
                sm.off_connected_vertices     = Pointer.Read(reader); // shorts2_offset (array of size num_shorts2)
                sm.off_disconnected_triangles = Pointer.Read(reader);
                if (l.mode == MapLoader.Mode.Rayman3GC)
                {
                    sm.name = new string(reader.ReadChars(0x34)).TrimEnd('\0');
                }
            }
            else
            {
                // Defaults for Rayman 2
                sm.num_uvMaps                 = 1;
                sm.num_mapping_entries        = 0;
                sm.off_mapping_vertices       = null;
                sm.off_mapping_uvs            = null;
                sm.num_connected_vertices     = 0;
                sm.num_disconnected_triangles = 0;
                sm.off_connected_vertices     = null;
                sm.off_disconnected_triangles = null;
            }

            // Read mapping tables
            sm.mapping_uvs = new int[sm.num_uvMaps][];
            if (sm.num_mapping_entries > 0)
            {
                Pointer.Goto(ref reader, sm.off_mapping_vertices);
                //print("Mapping offset: " + String.Format("0x{0:X}", fs.Position));
                sm.mapping_vertices = new int[sm.num_mapping_entries];
                for (int j = 0; j < sm.num_mapping_entries; j++)
                {
                    sm.mapping_vertices[j] = reader.ReadInt16();
                }
                Pointer.Goto(ref reader, sm.off_mapping_uvs);
                for (int j = 0; j < sm.num_uvMaps; j++)
                {
                    sm.mapping_uvs[j] = new int[sm.num_mapping_entries];
                }
                for (int j = 0; j < sm.num_mapping_entries; j++)
                {
                    for (int um = 0; um < sm.num_uvMaps; um++)
                    {
                        sm.mapping_uvs[um][j] = reader.ReadInt16();
                    }
                }
            }
            if (sm.num_disconnected_triangles_spe > 0)
            {
                Pointer.Goto(ref reader, sm.off_mapping_uvs_spe);
                sm.mapping_uvs_spe = new int[sm.num_uvMaps][];
                for (int j = 0; j < sm.num_uvMaps; j++)
                {
                    sm.mapping_uvs_spe[j] = new int[sm.num_disconnected_triangles_spe * 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_disconnected_triangles_spe * 3; j++)
                    {
                        sm.mapping_uvs_spe[um][j] = reader.ReadInt16();
                    }
                }
            }

            // Read UVs
            Pointer.Goto(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.Goto(ref reader, sm.off_connected_vertices);
            //print("Creating triangles from connected vertices at " + String.Format("0x{0:X}", fs.Position));
            sm.connected_vertices = new int[sm.num_connected_vertices];
            for (int j = 0; j < sm.num_connected_vertices; j++)
            {
                sm.connected_vertices[j] = reader.ReadInt16();
            }
            Pointer.Goto(ref reader, sm.off_disconnected_triangles);
            sm.disconnected_triangles = new int[sm.num_disconnected_triangles * 3];
            //print("Loading disconnected triangles at " + String.Format("0x{0:X}", fs.Position));
            for (int j = 0; j < sm.num_disconnected_triangles; j++)
            {
                sm.disconnected_triangles[(j * 3) + 0] = reader.ReadInt16();
                sm.disconnected_triangles[(j * 3) + 1] = reader.ReadInt16();
                sm.disconnected_triangles[(j * 3) + 2] = reader.ReadInt16();
            }
            if (sm.num_disconnected_triangles_spe > 0)
            {
                Pointer.Goto(ref reader, sm.off_disconnected_triangles_spe);
                sm.disconnected_triangles_spe = new int[sm.num_disconnected_triangles_spe * 3];
                //print("Loading disconnected triangles at " + String.Format("0x{0:X}", fs.Position));
                for (int j = 0; j < sm.num_disconnected_triangles_spe; j++)
                {
                    sm.disconnected_triangles_spe[(j * 3) + 0] = reader.ReadInt16();
                    sm.disconnected_triangles_spe[(j * 3) + 1] = reader.ReadInt16();
                    sm.disconnected_triangles_spe[(j * 3) + 2] = reader.ReadInt16();
                }
            }
            return(sm);
        }
        public void AddLightmap(Texture2D lightmap, Vector2[] lightmapUVs)
        {
            this.lightmap    = lightmap;
            this.lightmapUVs = lightmapUVs;


            // Bad hack
            Array.Resize(ref uvs, num_uvs + lightmapUVs.Length);
            if (OPT_mapping_uvs != null)
            {
                Array.Resize(ref OPT_mapping_uvs, num_uvMaps + 1);
                OPT_mapping_uvs[OPT_mapping_uvs.Length - 1] = Enumerable.Range(num_uvs, lightmapUVs.Length).ToArray();
            }
            if (Settings.s.game == Settings.Game.LargoWinch)
            {
                if (mapping_uvs != null && mapping_lightmap != null)
                {
                    Array.Resize(ref mapping_uvs, num_uvMaps + 1);
                    mapping_uvs[mapping_uvs.Length - 1] = new int[num_triangles * 3];
                    for (int i = 0; i < num_triangles * 3; i++)
                    {
                        int search     = triangles[i];
                        int lightmapUV = Array.IndexOf(mapping_lightmap, search);
                        if (lightmapUV != -1)
                        {
                            mapping_uvs[mapping_uvs.Length - 1][i] = num_uvs + lightmapUV;
                        }
                        else
                        {
                            MapLoader.Loader.print("not found");
                            mapping_uvs[mapping_uvs.Length - 1][i] = mapping_uvs[mapping_uvs.Length - 2][i];
                        }
                        //mapping_uvs_spe[mapping_uvs_spe.Length - 1][i] = num_uvs + disconnected_triangles_spe[i];
                    }

                    //Enumerable.Range(num_uvs, lightmapUVs.Length).ToArray();
                }
            }
            Array.Copy(lightmapUVs, 0, uvs, num_uvs, lightmapUVs.Length);

            /*for (int j = 0; j < lightmapUVs.Length; j++) {
             *      uvs[num_uvs + j] = lightmapuv;
             * }*/
            num_uvs += (ushort)lightmapUVs.Length;
            num_uvMaps++;

            if (visualMaterial != null)
            {
                visualMaterial = visualMaterial.Clone();
                visualMaterial.num_textures += 1;
                visualMaterial.textures.Add(new VisualMaterialTexture()
                {
                    texture = new TextureInfo(null)
                    {
                        width   = (ushort)lightmap.width,
                        height  = (ushort)lightmap.height,
                        Texture = lightmap
                    },
                    textureOp  = 50,
                    uvFunction = 1
                });
            }
        }
        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);
        }