Exemple #1
0
        private static void ParseBlock(Block block, ref MsTsShape shape)
        {
            Vertex v = null;             //Crappy, but there we go.....

            int[] t = null;
            ParseBlock(block, ref shape, ref v, ref t);
        }
Exemple #2
0
        private static void ParseBlock(Block block, ref MsTsShape shape, ref Vertex vertex, ref int[] intArray)
        {
            float       x, y, z;
            Vector3     point;
            KujuTokenID currentToken = KujuTokenID.error;
            Block       newBlock;
            uint        flags;

            switch (block.Token)
            {
            case KujuTokenID.shape:
                newBlock = block.ReadSubBlock(KujuTokenID.shape_header);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.volumes);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.shader_names);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.texture_filter_names);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.points);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.uv_points);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.normals);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.sort_vectors);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.colours);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.matrices);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.images);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.textures);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.light_materials);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.light_model_cfgs);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.vtx_states);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.prim_states);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.lod_controls);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.shape_header:
            case KujuTokenID.volumes:
            case KujuTokenID.texture_filter_names:
            case KujuTokenID.sort_vectors:
            case KujuTokenID.colours:
            case KujuTokenID.light_materials:
            case KujuTokenID.light_model_cfgs:
                //Unsupported stuff, so just read to the end at the minute
                block.Skip((int)block.Length());
                break;

            case KujuTokenID.vtx_state:
                flags = block.ReadUInt32();
                int  matrix1          = block.ReadInt32();
                int  lightMaterialIdx = block.ReadInt32();
                int  lightStateCfgIdx = block.ReadInt32();
                uint lightFlags       = block.ReadUInt32();
                int  matrix2          = -1;
                if ((block is BinaryBlock && block.Length() - block.Position() > 1) || (!(block is BinaryBlock) && block.Length() - block.Position() > 2))
                {
                    matrix2 = block.ReadInt32();
                }

                VertexStates vs = new VertexStates();
                vs.flags             = flags;
                vs.hierarchyID       = matrix1;
                vs.lightingMatrixID  = lightMaterialIdx;
                vs.lightingConfigIdx = lightStateCfgIdx;
                vs.lightingFlags     = lightFlags;
                vs.matrix2ID         = matrix2;
                shape.vtx_states.Add(vs);
                break;

            case KujuTokenID.vtx_states:
                int vtxStateCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (vtxStateCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.vtx_state);
                    ParseBlock(newBlock, ref shape);
                    vtxStateCount--;
                }

                break;

            case KujuTokenID.prim_state:
                flags = block.ReadUInt32();
                int   shader  = block.ReadInt32();
                int[] texIdxs = {};
                newBlock = block.ReadSubBlock(KujuTokenID.tex_idxs);
                ParseBlock(newBlock, ref shape, ref texIdxs);

                float          zBias         = block.ReadSingle();
                int            vertexStates  = block.ReadInt32();
                int            alphaTestMode = block.ReadInt32();
                int            lightCfgIdx   = block.ReadInt32();
                int            zBufferMode   = block.ReadInt32();
                PrimitiveState p             = new PrimitiveState();
                p.Name          = block.Label;
                p.Flags         = flags;
                p.Shader        = shader;
                p.Textures      = texIdxs;
                p.ZBias         = zBias;
                p.vertexStates  = vertexStates;
                p.alphaTestMode = alphaTestMode;
                p.lightCfgIdx   = lightCfgIdx;
                p.zBufferMode   = zBufferMode;
                shape.prim_states.Add(p);
                break;

            case KujuTokenID.tex_idxs:
                int texIdxCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                Array.Resize(ref intArray, texIdxCount);
                int idx = 0;
                while (texIdxCount > 0)
                {
                    intArray[idx] = block.ReadUInt16();
                    idx++;
                    texIdxCount--;
                }
                break;

            case KujuTokenID.prim_states:
                int primStateCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (primStateCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.prim_state);
                    ParseBlock(newBlock, ref shape);
                    primStateCount--;
                }

                break;

            case KujuTokenID.texture:
                int   imageIDX      = block.ReadInt32();
                int   filterMode    = (int)block.ReadUInt32();
                float mipmapLODBias = block.ReadSingle();
                uint  borderColor   = 0xff000000U;
                if (block.Length() - block.Position() > 1)
                {
                    borderColor = block.ReadUInt32();
                }

                //Unpack border color
                float r, g, b, a;
                r = borderColor % 256;
                g = (borderColor / 256) % 256;
                b = (borderColor / 256 / 256) % 256;
                a = (borderColor / 256 / 256 / 256) % 256;
                Texture t = new Texture();
                t.fileName      = shape.images[imageIDX];
                t.filterMode    = filterMode;
                t.mipmapLODBias = (int)mipmapLODBias;
                t.borderColor   = new Color32((byte)r, (byte)g, (byte)b, (byte)a);
                shape.textures.Add(t);
                break;

            case KujuTokenID.textures:
                int textureCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (textureCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.texture);
                    ParseBlock(newBlock, ref shape);
                    textureCount--;
                }

                break;

            case KujuTokenID.image:
                shape.images.Add(block.ReadString());
                break;

            case KujuTokenID.images:
                int imageCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (imageCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.image);
                    ParseBlock(newBlock, ref shape);
                    imageCount--;
                }

                break;

            case KujuTokenID.cullable_prims:
                int numPrims        = block.ReadInt32();
                int numFlatSections = block.ReadInt32();
                int numPrimIdxs     = block.ReadInt32();
                break;

            case KujuTokenID.geometry_info:
                int faceNormals      = block.ReadInt32();
                int txLightCommands  = block.ReadInt32();
                int nodeXTrilistIdxs = block.ReadInt32();
                int trilistIdxs      = block.ReadInt32();
                int lineListIdxs     = block.ReadInt32();
                nodeXTrilistIdxs = block.ReadInt32();                         //Duped, or is the first one actually something else?
                int trilists      = block.ReadInt32();
                int lineLists     = block.ReadInt32();
                int pointLists    = block.ReadInt32();
                int nodeXTrilists = block.ReadInt32();
                newBlock = block.ReadSubBlock(KujuTokenID.geometry_nodes);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.geometry_node_map);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.geometry_node_map:
                int[] geometryNodes = new int[block.ReadInt32()];
                for (int i = 0; i < geometryNodes.Length; i++)
                {
                    geometryNodes[i] = block.ReadInt32();
                }

                break;

            case KujuTokenID.geometry_node:
                int n_txLightCommands  = block.ReadInt32();
                int n_nodeXTxLightCmds = block.ReadInt32();
                int n_trilists         = block.ReadInt32();
                int n_lineLists        = block.ReadInt32();
                int n_pointLists       = block.ReadInt32();
                newBlock = block.ReadSubBlock(KujuTokenID.cullable_prims);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.geometry_nodes:
                int geometryNodeCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (geometryNodeCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.geometry_node);
                    ParseBlock(newBlock, ref shape);
                    geometryNodeCount--;
                }

                break;

            case KujuTokenID.point:
                x     = block.ReadSingle();
                y     = block.ReadSingle();
                z     = block.ReadSingle();
                point = new Vector3(x, y, z);
                shape.points.Add(point);
                break;

            case KujuTokenID.vector:
                x     = block.ReadSingle();
                y     = block.ReadSingle();
                z     = block.ReadSingle();
                point = new Vector3(x, y, z);
                shape.normals.Add(point);
                break;

            case KujuTokenID.points:
                int pointCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (pointCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.point);
                    ParseBlock(newBlock, ref shape);
                    pointCount--;
                }

                break;

            case KujuTokenID.uv_point:
                x = block.ReadSingle();
                y = block.ReadSingle();
                var uv_point = new Vector2(x, y);
                shape.uv_points.Add(uv_point);
                break;

            case KujuTokenID.uv_points:
                int uvPointCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (uvPointCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.uv_point);
                    ParseBlock(newBlock, ref shape);
                    uvPointCount--;
                }

                break;

            case KujuTokenID.matrices:
                int matrixCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (matrixCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.matrix);
                    ParseBlock(newBlock, ref shape);
                    matrixCount--;
                }
                break;

            case KujuTokenID.matrix:
                Matrix currentMatrix = new Matrix();
                currentMatrix.Name = block.Label;
                currentMatrix.A    = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle());
                currentMatrix.B    = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle());
                currentMatrix.C    = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle());
                currentMatrix.D    = new Vector3(block.ReadSingle(), block.ReadSingle(), block.ReadSingle());
                shape.matrices.Add(currentMatrix);
                break;

            case KujuTokenID.normals:
                int normalCount = block.ReadUInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (normalCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.vector);
                    ParseBlock(newBlock, ref shape);
                    normalCount--;
                }

                break;

            case KujuTokenID.distance_levels_header:
                int DLevBias = block.ReadInt16();
                break;

            case KujuTokenID.distance_levels:
                int distanceLevelCount = block.ReadInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (distanceLevelCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.distance_level);
                    ParseBlock(newBlock, ref shape);
                    distanceLevelCount--;
                }

                break;

            case KujuTokenID.distance_level_header:
                newBlock = block.ReadSubBlock(KujuTokenID.dlevel_selection);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.hierarchy);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.distance_level:
                newBlock = block.ReadSubBlock(KujuTokenID.distance_level_header);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.sub_objects);
                ParseBlock(newBlock, ref shape);
                shape.LODs.Add(currentLOD);
                break;

            case KujuTokenID.dlevel_selection:
                currentLOD = new LOD(block.ReadSingle());
                break;

            case KujuTokenID.hierarchy:
                currentLOD.hierarchy = new int[block.ReadInt32()];
                for (int i = 0; i < currentLOD.hierarchy.Length; i++)
                {
                    currentLOD.hierarchy[i] = block.ReadInt32();
                }

                break;

            case KujuTokenID.lod_control:
                newBlock = block.ReadSubBlock(KujuTokenID.distance_levels_header);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.distance_levels);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.lod_controls:
                int lodCount = block.ReadInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (lodCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.lod_control);
                    ParseBlock(newBlock, ref shape);
                    lodCount--;
                }

                break;

            case KujuTokenID.primitives:
                int capacity = block.ReadInt32();                         //Count of the number of entries in the block, not the number of primitives
                while (capacity > 0)
                {
                    newBlock = block.ReadSubBlock(new KujuTokenID[] { KujuTokenID.prim_state_idx, KujuTokenID.indexed_trilist });
                    switch (newBlock.Token)
                    {
                    case KujuTokenID.prim_state_idx:
                        ParseBlock(newBlock, ref shape);
                        string txF = null;
                        try
                        {
                            txF = OpenBveApi.Path.CombineFile(currentFolder, shape.textures[shape.prim_states[shape.currentPrimitiveState].Textures[0]].fileName);
                            if (!File.Exists(txF))
                            {
                                Interface.AddMessage(MessageType.Warning, true, "Texture file " + shape.textures[shape.prim_states[shape.currentPrimitiveState].Textures[0]].fileName + " was not found.");
                                txF = null;
                            }
                        }
                        catch
                        {
                            Interface.AddMessage(MessageType.Warning, true, "Texture file path " + shape.textures[shape.prim_states[shape.currentPrimitiveState].Textures[0]].fileName + " was invalid.");
                        }
                        currentLOD.subObjects[currentLOD.subObjects.Count - 1].materials.Add(new Material(txF));
                        break;

                    case KujuTokenID.indexed_trilist:
                        ParseBlock(newBlock, ref shape);
                        break;

                    default:
                        throw new Exception("Unexpected primitive type, got " + currentToken);
                    }

                    capacity--;
                }

                break;

            case KujuTokenID.prim_state_idx:
                shape.currentPrimitiveState = block.ReadInt32();
                break;

            case KujuTokenID.indexed_trilist:
                newBlock = block.ReadSubBlock(KujuTokenID.vertex_idxs);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.normal_idxs);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.sub_object:
                newBlock = block.ReadSubBlock(KujuTokenID.sub_object_header);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.vertices);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.vertex_sets);
                ParseBlock(newBlock, ref shape);
                newBlock = block.ReadSubBlock(KujuTokenID.primitives);
                ParseBlock(newBlock, ref shape);
                break;

            case KujuTokenID.sub_objects:
                int subObjectCount = block.ReadInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (subObjectCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.sub_object);
                    ParseBlock(newBlock, ref shape);
                    subObjectCount--;
                }

                break;

            case KujuTokenID.sub_object_header:
                currentLOD.subObjects.Add(new SubObject());
                shape.totalObjects++;
                flags = block.ReadUInt32();
                int  sortVectorIdx                = block.ReadInt32();
                int  volIdx                       = block.ReadInt32();
                uint sourceVertexFormatFlags      = block.ReadUInt32();
                uint destinationVertexFormatFlags = block.ReadUInt32();
                newBlock = block.ReadSubBlock(KujuTokenID.geometry_info);
                ParseBlock(newBlock, ref shape);

                /*
                 * Optional stuff, need to check if we're running off the end of the stream before reading each block
                 */
                if (block.Length() - block.Position() > 1)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.subobject_shaders);
                    ParseBlock(newBlock, ref shape);
                }

                if (block.Length() - block.Position() > 1)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.subobject_light_cfgs);
                    ParseBlock(newBlock, ref shape);
                }

                if (block.Length() - block.Position() > 1)
                {
                    int subObjectID = block.ReadInt32();
                }

                break;

            case KujuTokenID.subobject_light_cfgs:
                int[] subobject_light_cfgs = new int[block.ReadInt32()];
                for (int i = 0; i < subobject_light_cfgs.Length; i++)
                {
                    subobject_light_cfgs[i] = block.ReadInt32();
                }

                break;

            case KujuTokenID.subobject_shaders:
                int[] subobject_shaders = new int[block.ReadInt32()];
                for (int i = 0; i < subobject_shaders.Length; i++)
                {
                    subobject_shaders[i] = block.ReadInt32();
                }

                break;

            case KujuTokenID.vertices:
                int vertexCount = block.ReadInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (vertexCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.vertex);
                    ParseBlock(newBlock, ref shape);
                    vertexCount--;
                }

                break;

            case KujuTokenID.vertex:
                flags = block.ReadUInt32();                         //Various control variables, not supported
                int myPoint  = block.ReadInt32();                   //Index to points array
                int myNormal = block.ReadInt32();                   //Index to normals array

                Vertex v = new Vertex(shape.points[myPoint], shape.normals[myNormal]);

                uint Color1 = block.ReadUInt32();
                uint Color2 = block.ReadUInt32();
                newBlock = block.ReadSubBlock(KujuTokenID.vertex_uvs);
                ParseBlock(newBlock, ref shape, ref v);
                currentLOD.subObjects[currentLOD.subObjects.Count - 1].verticies.Add(v);
                break;

            case KujuTokenID.vertex_idxs:
                int remainingVertex = block.ReadInt32() / 3;
                while (remainingVertex > 0)
                {
                    int v1 = block.ReadInt32();
                    int v2 = block.ReadInt32();
                    int v3 = block.ReadInt32();

                    currentLOD.subObjects[currentLOD.subObjects.Count - 1].faces.Add(new Face(new int[] { v1, v2, v3 }, currentLOD.subObjects[currentLOD.subObjects.Count - 1].materials.Count - 1));
                    remainingVertex--;
                }

                break;

            case KujuTokenID.vertex_set:
                int       vertexStateIndex    = block.ReadInt32();                              //Index to the vtx_states member
                int       hierarchy           = shape.vtx_states[vertexStateIndex].hierarchyID; //Now pull the hierachy ID out
                int       setStartVertexIndex = block.ReadInt32();                              //First vertex
                int       setVertexCount      = block.ReadInt32();                              //Total number of vert
                VertexSet vts = new VertexSet();
                vts.hierarchyIndex = hierarchy;
                vts.startVertex    = setStartVertexIndex;
                vts.numVerticies   = setVertexCount;
                currentLOD.subObjects[currentLOD.subObjects.Count - 1].vertexSets.Add(vts);
                break;

            case KujuTokenID.vertex_sets:
                int vertexSetCount = block.ReadInt16();
                if (block is BinaryBlock)
                {
                    block.ReadUInt16();
                }
                while (vertexSetCount > 0)
                {
                    newBlock = block.ReadSubBlock(KujuTokenID.vertex_set);
                    ParseBlock(newBlock, ref shape);
                    vertexSetCount--;
                }

                //We now need to transform our verticies
                currentLOD.subObjects[currentLOD.subObjects.Count - 1].TransformVerticies(shape.matrices);
                break;

            case KujuTokenID.vertex_uvs:
                int[] vertex_uvs = new int[block.ReadInt32()];
                for (int i = 0; i < vertex_uvs.Length; i++)
                {
                    vertex_uvs[i] = block.ReadInt32();
                }

                //Looks as if vertex_uvs should always be of length 1, thus:
                vertex.TextureCoordinates = shape.uv_points[vertex_uvs[0]];
                break;
            }
        }
Exemple #3
0
 private static void ParseBlock(Block block, ref MsTsShape shape, ref Vertex v)
 {
     int[] t = null;
     ParseBlock(block, ref shape, ref v, ref t);
 }
Exemple #4
0
        private static void ParseBlock(Block block, ref MsTsShape shape, ref int[] array)
        {
            Vertex v = null;             //Crappy, but there we go.....

            ParseBlock(block, ref shape, ref v, ref array);
        }
Exemple #5
0
        internal static ObjectManager.AnimatedObjectCollection ReadObject(string fileName)
        {
            MsTsShape shape = new MsTsShape();

            ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection
            {
                Objects = new ObjectManager.AnimatedObject[4]
            };

            currentFolder = Path.GetDirectoryName(fileName);
            Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read);

            byte[] buffer = new byte[34];
            fb.Read(buffer, 0, 2);

            bool unicode = (buffer[0] == 0xFF && buffer[1] == 0xFE);

            string headerString;

            if (unicode)
            {
                fb.Read(buffer, 0, 32);
                headerString = Encoding.Unicode.GetString(buffer, 0, 16);
            }
            else
            {
                fb.Read(buffer, 2, 14);
                headerString = Encoding.ASCII.GetString(buffer, 0, 8);
            }

            // SIMISA@F  means compressed
            // SIMISA@@  means uncompressed
            if (headerString.StartsWith("SIMISA@F"))
            {
                fb = new ZlibStream(fb, CompressionMode.Decompress);
            }
            else if (headerString.StartsWith("\r\nSIMISA"))
            {
                // ie us1rd2l1000r10d.s, we are going to allow this but warn
                Console.Error.WriteLine("Improper header in " + fileName);
                fb.Read(buffer, 0, 4);
            }
            else if (!headerString.StartsWith("SIMISA@@"))
            {
                throw new Exception("Unrecognized shape file header " + headerString + " in " + fileName);
            }

            string subHeader;

            if (unicode)
            {
                fb.Read(buffer, 0, 32);
                subHeader = Encoding.Unicode.GetString(buffer, 0, 16);
            }
            else
            {
                fb.Read(buffer, 0, 16);
                subHeader = Encoding.ASCII.GetString(buffer, 0, 8);
            }
            if (subHeader[7] == 't')
            {
                using (BinaryReader reader = new BinaryReader(fb))
                {
                    byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position));
                    string s;
                    if (unicode)
                    {
                        s = Encoding.Unicode.GetString(newBytes);
                    }
                    else
                    {
                        s = Encoding.ASCII.GetString(newBytes);
                    }

                    s = s.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ").Trim();
                    if (!s.StartsWith("shape", StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new Exception();                         //Shape definition
                    }
                    TextualBlock block = new TextualBlock(s, KujuTokenID.shape);
                    ParseBlock(block, ref shape);
                }
            }
            else if (subHeader[7] != 'b')
            {
                throw new Exception("Unrecognized subHeader \"" + subHeader + "\" in " + fileName);
            }
            else
            {
                using (BinaryReader reader = new BinaryReader(fb))
                {
                    KujuTokenID currentToken = (KujuTokenID)reader.ReadUInt16();
                    if (currentToken != KujuTokenID.shape)
                    {
                        throw new Exception();                         //Shape definition
                    }
                    reader.ReadUInt16();
                    uint        remainingBytes = reader.ReadUInt32();
                    byte[]      newBytes       = reader.ReadBytes((int)remainingBytes);
                    BinaryBlock block          = new BinaryBlock(newBytes, KujuTokenID.shape);
                    ParseBlock(block, ref shape);
                }
            }
            Array.Resize(ref Result.Objects, shape.totalObjects);
            int idx = 0;

            double[] previousLODs = new double[shape.totalObjects];
            for (int i = 0; i < shape.LODs.Count; i++)
            {
                for (int j = 0; j < shape.LODs[i].subObjects.Count; j++)
                {
                    Result.Objects[idx]        = new ObjectManager.AnimatedObject();
                    Result.Objects[idx].States = new AnimatedObjectState[1];
                    AnimatedObjectState aos = new AnimatedObjectState(null, Vector3.Zero);
                    shape.LODs[i].subObjects[j].Apply(out aos.Object);
                    aos.Position = new Vector3(0, 0, 0);
                    Result.Objects[idx].States[0] = aos;
                    previousLODs[idx]             = shape.LODs[i].viewingDistance;
                    int k = idx;
                    while (k > 0)
                    {
                        if (previousLODs[k] < shape.LODs[i].viewingDistance)
                        {
                            break;
                        }

                        k--;
                    }

                    if (k != 0)
                    {
                        Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",if[cameraDistance >" + previousLODs[k] + ",0,-1],-1]", true);
                    }
                    else
                    {
                        Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",0,-1]", true);
                    }

                    idx++;
                }
            }
            return(Result);
        }