Exemplo n.º 1
0
        /// <summary>
        ///     Writes the skeleton hierarchy to the DAE.
        /// </summary>
        /// <param name="skeleton">The skeleton</param>
        /// <param name="index">Index of the current bone (root bone when it's not a recursive call)</param>
        /// <param name="nodes">List with the DAE nodes</param>
        private static void writeSkeleton(List <RenderBase.OBone> skeleton, int index, ref List <daeNode> nodes)
        {
            daeNode node = new daeNode();

            node.name = skeleton[index].name;
            node.id   = node.name + "_bone_id";
            node.sid  = node.name;
            node.type = "JOINT";

            RenderBase.OMatrix transform = new RenderBase.OMatrix();
            transform *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x);
            transform *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y);
            transform *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z);
            transform *= RenderBase.OMatrix.translate(skeleton[index].translation);

            node.matrix.set(transform);

            for (int i = 0; i < skeleton.Count; i++)
            {
                if (skeleton[i].parentId == index)
                {
                    if (node.childs == null)
                    {
                        node.childs = new List <daeNode>();
                    }
                    writeSkeleton(skeleton, i, ref node.childs);
                }
            }

            nodes.Add(node);
        }
Exemplo n.º 2
0
 /// <summary>
 ///     Transforms a Node from relative to absolute positions.
 /// </summary>
 /// <param name="nodes">A list with all nodes</param>
 /// <param name="index">Index of the node to convert</param>
 /// <param name="target">Target matrix to save node transformation</param>
 private static void transformNode(List <node> nodes, int index, ref RenderBase.OMatrix target)
 {
     target *= nodes[index].transform;
     if (nodes[index].parentId > -1)
     {
         transformNode(nodes, nodes[index].parentId, ref target);
     }
 }
Exemplo n.º 3
0
            public RenderBase.OMatrix get()
            {
                RenderBase.OMatrix output = new RenderBase.OMatrix();
                string[]           values = data.Split(Convert.ToChar(" "));
                int k = 0;

                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        output[j, i] = float.Parse(values[k++]);
                    }
                }
                return(output);
            }
Exemplo n.º 4
0
            public void set(RenderBase.OMatrix mtx)
            {
                StringBuilder strArray = new StringBuilder();

                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        if (i == 3 && j == 3)
                        {
                            strArray.Append(mtx[j, i].ToString(CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            strArray.Append(mtx[j, i].ToString(CultureInfo.InvariantCulture) + " ");
                        }
                    }
                }
                data = strArray.ToString();
            }
Exemplo n.º 5
0
        /// <summary>
        ///     Exports a Model to the Collada format.
        ///     See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information.
        /// </summary>
        /// <param name="model">The Model that will be exported</param>
        /// <param name="fileName">The output File Name</param>
        /// <param name="modelIndex">Index of the model to be exported</param>
        /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1)
        {
            RenderBase.OModel mdl = model.model[modelIndex];
            COLLADA dae = new COLLADA();
            COLLADA daeShiny = new COLLADA();

            dae.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ");
            dae.asset.modified = dae.asset.created;

            daeShiny.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ");
            daeShiny.asset.modified = dae.asset.created;

            bool hasShiny = false;

            // Attempt to detect packaged textures. See http://www.github.com/Quibilia/Ohana3DS-Transfigured for directions.
            try
            {
                string modelDir;
                string sDir;
                string[] texDirs;
                string[] texFiles;

                modelDir = Path.GetFileNameWithoutExtension(fileName).Remove(4);
                sDir = Path.GetDirectoryName(fileName);
                texDirs = Directory.GetDirectories(Path.Combine(sDir, "../../Textures/"));

                foreach (string texDir in texDirs)
                {
                    if (texDir.Contains(modelDir))
                    {
                        texFiles = Directory.GetFiles(texDir);

                        if (Directory.Exists(texDir.Replace("Textures", "Models/DAE")) == false && texDir.Contains("Xtra") == false)
                        {
                            Directory.CreateDirectory(texDir.Replace("Textures", "Models/DAE"));
                        }

                        bool hasIris = false;

                        foreach (string texFile in texFiles)
                        {
                            if (texFile.Contains("Iris1"))
                            {
                                hasIris = true;
                            }
                        }

                        foreach (string texFile in texFiles)
                        {
                            if (texFile.Contains("Xtra") == false)
                            {
                                Bitmap tempBMP = (Bitmap)Bitmap.FromFile(texFile);

                                if (texFile.Contains("Body") && texFile.Contains("Nor") == false)
                                {
                                    int w = tempBMP.Width / 2;

                                    Bitmap scaledBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height);
                                    Bitmap reverseBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height);
                                    reverseBMP.RotateFlip(RotateFlipType.RotateNoneFlipX);

                                    Bitmap finalBMP = new Bitmap(tempBMP.Width, tempBMP.Height);

                                    for (int x = 0; x < scaledBMP.Width; x++)
                                    {
                                        for (int y = 0; y < scaledBMP.Height; y++)
                                        {
                                            finalBMP.SetPixel(x, y, scaledBMP.GetPixel(x, y));
                                        }
                                    }

                                    for (int x = 0; x < reverseBMP.Width; x++)
                                    {
                                        for (int y = 0; y < reverseBMP.Height; y++)
                                        {
                                            finalBMP.SetPixel(x + reverseBMP.Width, y, reverseBMP.GetPixel(x, y));
                                        }
                                    }

                                    if (texFile.Contains("BodyA2") == false && texFile.Contains("BodyB2") == false && texFile.Contains("Body2") == false)
                                    {
                                        finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));

                                        model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                }
                                else if (texFile.Contains("Iris1"))
                                {
                                    Bitmap eyeBMP = (Bitmap)Bitmap.FromFile(texFile.Replace("Iris", "Eye"));

                                    foreach (string norFile in texFiles)
                                    {
                                        if (norFile.Contains("Xtra") == false && norFile.Contains("EyeNor"))
                                        {
                                            Bitmap thisEye = new Bitmap(eyeBMP.Width / 2, eyeBMP.Height / 4);
                                            Bitmap norBMP = (Bitmap)Bitmap.FromFile(norFile);
                                            Bitmap thisNor = new Bitmap(norBMP.Width / 4, norBMP.Height / 4);
                                            Bitmap finalBMP = new Bitmap(eyeBMP.Width * 2, eyeBMP.Height);

                                            int eye = 0;
                                            int half = 0;
                                            bool reflectEye = false;
                                            bool reflectIris = true;
                                            bool reflectNor = true;

                                            for (int eyeY = 0; eyeY < 4; eyeY++)
                                            {
                                                eye = 0;

                                                for (int eyeX = 0; eyeX < 2; eyeX++)
                                                {
                                                    reflectEye = !reflectEye;
                                                    reflectNor = !reflectNor;
                                                    reflectIris = !reflectIris;

                                                    for (half = 0; half < 2; half++)
                                                    {
                                                        for (int x = half * (eyeBMP.Width / 2); x < (half + 1) * (eyeBMP.Width / 2); x++)
                                                        {
                                                            for (int y = eyeY * (eyeBMP.Height / 4); y < (eyeY + 1) * (eyeBMP.Height / 4); y++)
                                                            {
                                                                thisEye.SetPixel(x - (half * (eyeBMP.Width / 2)), y - (eyeY * (eyeBMP.Height / 4)), eyeBMP.GetPixel(x, y));
                                                            }
                                                        }

                                                        for (int x = (half * 2) * (norBMP.Width / 4); x < ((half * 2) + 1) * (norBMP.Width / 4); x++)
                                                        {
                                                            for (int y = eyeY * (norBMP.Height / 4); y < (eyeY + 1) * (norBMP.Height / 4); y++)
                                                            {
                                                                thisNor.SetPixel(x - ((half * 2) * (norBMP.Width / 4)), y - (eyeY * (norBMP.Height / 4)), norBMP.GetPixel(x, y));
                                                            }
                                                        }

                                                        if (reflectEye == true)
                                                        {
                                                            thisEye.RotateFlip(RotateFlipType.RotateNoneFlipX);
                                                        }

                                                        reflectEye = !reflectEye;

                                                        if (reflectNor == true)
                                                        {
                                                            thisNor.RotateFlip(RotateFlipType.RotateNoneFlipX);
                                                        }

                                                        if (reflectIris == true)
                                                        {
                                                            tempBMP.RotateFlip(RotateFlipType.RotateNoneFlipX);
                                                            reflectIris = false;
                                                        }

                                                        // By now, tempBMP, thisEye, and thisNor should all be the same size.

                                                        for (int x = 0; x < tempBMP.Width; x++)
                                                        {
                                                            for (int y = 0; y < tempBMP.Height; y++)
                                                            {
                                                                if (tempBMP.GetPixel(x, y).A >= 0x7F && thisNor.GetPixel(x, y).A >= 0x7F)
                                                                {
                                                                    thisEye.SetPixel(x, y, tempBMP.GetPixel(x, y));
                                                                }
                                                            }
                                                        }

                                                        for (int x = eye * (norBMP.Width / 4); x < (eye + 1) * (norBMP.Width / 4); x++)
                                                        {
                                                            for (int y = eyeY * (norBMP.Height / 4); y < (eyeY + 1) * (norBMP.Height / 4); y++)
                                                            {
                                                                finalBMP.SetPixel(x, y, thisEye.GetPixel(x - (eye * (norBMP.Width / 4)), y - (eyeY * (norBMP.Height / 4))));
                                                            }
                                                        }

                                                        if (eye == 3)
                                                        {
                                                            eye--;
                                                        }
                                                        else if (eye == 1)
                                                        {
                                                            eye += 2;
                                                        }
                                                        else if (eye == 0)
                                                        {
                                                            eye++;
                                                        }
                                                        else if (eye == 2)
                                                        {
                                                            eye -= 2;
                                                        }
                                                    }
                                                }
                                            }

                                            finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                            model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                        }
                                    }
                                }
                                else if (texFile.Contains("Mouth1") && texFile.Contains("Nor") == false)
                                {
                                    bool hasNormals = false;

                                    foreach (string norFile in texFiles)
                                    {
                                        // If the mouth has a normal file, the UV coordinates expect it split across the image border.

                                        if (norFile.Contains("Mouth") && norFile.Contains("Nor"))
                                        {
                                            hasNormals = true;
                                        }
                                    }

                                    if (hasNormals == true)
                                    {
                                        Bitmap thisHalf = new Bitmap(tempBMP.Width / 2, tempBMP.Height / 4);
                                        Bitmap finalBMP = new Bitmap(tempBMP.Width * 2, tempBMP.Height);

                                        for (int halfY = 0; halfY < 4; halfY++)
                                        {
                                            for (int halfX = 0; halfX < 2; halfX++)
                                            {
                                                for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        thisHalf.SetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4)), tempBMP.GetPixel(x, y));
                                                    }
                                                }

                                                for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        finalBMP.SetPixel(x, y, thisHalf.GetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4))));
                                                    }
                                                }

                                                for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        finalBMP.SetPixel(finalBMP.Width - (x + 1), y, thisHalf.GetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4))));
                                                    }
                                                }
                                            }
                                        }

                                        finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                        model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                    else
                                    {
                                        // If the mouth doesn't have a normal file, the UV coordinates expect it as-is. Hooray!

                                        tempBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                        model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                }
                                else if (texFile.Contains("Eye1") && hasIris == false && texFile.Contains("AEye") == false && texFile.Contains("BEye") == false & texFile.Contains("CEye") == false)
                                {
                                    if (tempBMP.Width == tempBMP.Height)
                                    {
                                        tempBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                        model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                    else
                                    {
                                        Bitmap thisEye = new Bitmap(tempBMP.Width / 2, tempBMP.Height / 4);
                                        Bitmap finalBMP = new Bitmap(tempBMP.Width * 2, tempBMP.Height);

                                        for (int eyeY = 0; eyeY < 4; eyeY++)
                                        {
                                            for (int eyeX = 0; eyeX < 2; eyeX++)
                                            {
                                                for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        thisEye.SetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4)), tempBMP.GetPixel(x, y));
                                                    }
                                                }

                                                for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        finalBMP.SetPixel(x, y, thisEye.GetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4))));
                                                    }
                                                }

                                                for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++)
                                                {
                                                    for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++)
                                                    {
                                                        finalBMP.SetPixel(finalBMP.Width - (x + 1), y, thisEye.GetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4))));
                                                    }
                                                }
                                            }
                                        }

                                        finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                        model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                }
                                else if (texFile.Contains("AEye") || texFile.Contains("BEye") || texFile.Contains("CEye"))
                                {
                                    Bitmap finalBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height);

                                    finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                    model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                }
                                else if (texFile.Contains("Fire"))
                                {
                                    Bitmap finalBMP = new Bitmap(tempBMP);

                                    foreach (string bodyFile in texFiles)
                                    {
                                        if ((bodyFile.Contains("Body1") || bodyFile.Contains("BodyA1")) && bodyFile.Contains("Nor") == false)
                                        {
                                            Bitmap bodyBMP = (Bitmap)Bitmap.FromFile(bodyFile);

                                            byte r, g, b;

                                            // Most-Frequent Body Color
                                            Color MFBC = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00), LFBC = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00);
                                            int LF = 0, SF = 65536;

                                            List<Color> colors = new List<Color>();
                                            List<int> freqs = new List<int>();

                                            for (int x = 0; x < bodyBMP.Width; x++)
                                            {
                                                for (int y = 0; y < bodyBMP.Height; y++)
                                                {
                                                    r = bodyBMP.GetPixel(x, y).R;
                                                    g = bodyBMP.GetPixel(x, y).G;
                                                    b = bodyBMP.GetPixel(x, y).B;

                                                    if (bodyBMP.GetPixel(x, y).A != 0x00)
                                                    {
                                                        bool matchFound = false;

                                                        foreach (Color c in colors)
                                                        {
                                                            if (c.R == r && c.G == g && c.B == b)
                                                            {
                                                                matchFound = true;
                                                                int freq = freqs[colors.IndexOf(c)];
                                                                freq++;

                                                                freqs[colors.IndexOf(c)] = freq;
                                                            }
                                                        }

                                                        if (matchFound == false)
                                                        {
                                                            colors.Add(Color.FromArgb(r, g, b));
                                                            freqs.Add(1);
                                                        }
                                                    }
                                                }
                                            }

                                            int tolerance = 18;

                                            foreach (Color c in colors)
                                            {
                                                if (freqs[colors.IndexOf(c)] > LF)
                                                {
                                                    LF = freqs[colors.IndexOf(c)];
                                                    MFBC = c;
                                                }
                                                else if (freqs[colors.IndexOf(c)] + 150 < SF)
                                                {
                                                    if (c.R <= (c.G - tolerance) || c.R >= (c.G + tolerance))
                                                    {
                                                        if (c.B <= (c.G - tolerance) || c.B >= (c.G + tolerance))
                                                        {
                                                            SF = freqs[colors.IndexOf(c)];
                                                            LFBC = c;
                                                        }
                                                    }
                                                }
                                            }

                                            bool mfbcgray = false;
                                            bool lfbcgray = false;
                                            bool bothgray = false;
                                            bool neithergray = false;

                                            if (MFBC.R > (MFBC.G - tolerance) && MFBC.R < (MFBC.G + tolerance))
                                            {
                                                if (MFBC.B > (MFBC.G - tolerance) && MFBC.B < (MFBC.G + tolerance))
                                                {
                                                    mfbcgray = true;
                                                }
                                            }

                                            if (LFBC.R > (LFBC.G - tolerance) && LFBC.R < (LFBC.G + tolerance))
                                            {
                                                if (LFBC.B > (LFBC.G - tolerance) && LFBC.B < (LFBC.G + tolerance))
                                                {
                                                    lfbcgray = true;
                                                }
                                            }

                                            if (mfbcgray && lfbcgray)
                                            {
                                                bothgray = true;
                                            }

                                            if (!mfbcgray && !lfbcgray)
                                            {
                                                neithergray = true;
                                            }

                                            Color baseOperator;
                                            Color accentOperator;

                                            if (bothgray)
                                            {
                                                baseOperator = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00);
                                                accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00);
                                            }
                                            else if (neithergray)
                                            {
                                                baseOperator = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00);
                                                accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00);
                                            }
                                            else
                                            {
                                                if (mfbcgray)
                                                {
                                                    baseOperator = LFBC;
                                                    accentOperator = LFBC;
                                                }
                                                else if (lfbcgray)
                                                {
                                                    baseOperator = MFBC;
                                                    accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
                                                }
                                                else
                                                {
                                                    // Just to satisfy C#...
                                                    baseOperator = MFBC;
                                                    accentOperator = LFBC;
                                                }
                                            }

                                            for (int x = 0; x < finalBMP.Width; x++)
                                            {
                                                for (int y = 0; y < finalBMP.Height; y++)
                                                {
                                                    Color tempColor = tempBMP.GetPixel(x, y);
                                                    Color finalColor;

                                                    if (tempColor.R < 0x10 && tempColor.G < 0x10 && tempColor.B < 0x10)
                                                    {
                                                        finalColor = Color.FromArgb(0x1F, baseOperator.R, baseOperator.G, baseOperator.B);
                                                    }
                                                    else
                                                    {
                                                        finalColor = Color.FromArgb(0x1F, tempColor.R & accentOperator.R, tempColor.G & accentOperator.G, tempColor.B & accentOperator.B);
                                                    }

                                                    finalBMP.SetPixel(x, y, finalColor);
                                                }
                                            }

                                            finalBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                            model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE")));
                                        }
                                    }
                                }
                                else
                                {
                                    if (texFile.Contains("Iris2") == false && texFile.Contains("Eye2") == false && texFile.Contains("Mouth2") == false)
                                    {
                                        tempBMP.Save(texFile.Replace("Textures", "Models/DAE"));
                                        model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE")));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch // No packaged textures detected...
            {
            }

            foreach (RenderBase.OTexture tex in model.texture)
            {
                daeImage img = new daeImage();

                string n = Path.GetFileNameWithoutExtension(tex.name);

                img.id = n;
                img.name = n;

                if (tex.name.Contains("Shiny"))
                {
                    img.id += "_shiny";
                    img.name += "_shiny";

                    hasShiny = true;
                }

                img.id += "_id";

                img.init_from = tex.name;

                if (img.id.Contains("_shiny"))
                {
                    daeShiny.library_images.Add(img);
                }
                else
                {
                    dae.library_images.Add(img);
                }
            }

            #region Normal

            int currentMat = 0;

            foreach (RenderBase.OMaterial mat in mdl.material)
            {
                if (mat.name0 == null)
                {
                    mat.name0 = mat.name;
                }

                mdl.material[currentMat].name = mat.name;
                mdl.material[currentMat].name0 = mat.name0;

                currentMat++;

                daeMaterial mtl = new daeMaterial();
                mtl.name = mat.name + "_mat";
                mtl.id = mtl.name + "_id";
                mtl.instance_effect.url = "#eff_" + mtl.id;

                dae.library_materials.Add(mtl);

                daeEffect eff = new daeEffect();
                eff.id = "eff_" + mtl.id;
                eff.name = "eff_" + mtl.name;

                daeParam surface = new daeParam();
                surface.surface = new daeParamSurfaceElement();
                surface.sid = "img_surface";
                surface.surface.type = "2D";
                surface.surface.init_from = mat.name0 + "_id";
                surface.surface.format = "PNG";
                eff.profile_COMMON.newparam.Add(surface);

                bool fire = false;

                for (int i = 0; i < dae.library_images.Count; i++)
                {
                    if (dae.library_images[i].init_from.Contains(mat.name) || dae.library_images[i].id.Contains(mat.name) || dae.library_images[i].init_from.Contains(mat.name0) || dae.library_images[i].id.Contains(mat.name0))
                    {
                        if (dae.library_images[i].init_from.Contains("Nor") == false)
                        {
                            if (mat.name.Contains("Fire"))
                            {
                                fire = true;

                                dae.library_images[i].name = dae.library_images[i].name.Replace("Mask", "1");
                                dae.library_images[i].id = dae.library_images[i].id.Replace("Mask", "1");
                                dae.library_images[i].init_from = dae.library_images[i].init_from.Replace("Mask", "1");

                                mat.name = mat.name.Replace("Mask", "1");
                                mat.name0 = mat.name0.Replace("Mask", "1");
                            }

                            surface.surface.init_from = dae.library_images[i].id;
                        }
                    }
                }

                daeParam sampler = new daeParam();
                sampler.sampler2D = new daeParamSampler2DElement();
                sampler.sid = "img_sampler";
                sampler.sampler2D.source = "img_surface";

                switch (mat.textureMapper[0].wrapU)
                {
                    case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break;
                    case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break;
                    case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break;
                    case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break;
                    default: sampler.sampler2D.wrap_s = "WRAP"; break;
                }

                switch (mat.textureMapper[0].wrapV)
                {
                    case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break;
                    case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break;
                    case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break;
                    case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break;
                    default: sampler.sampler2D.wrap_t = "WRAP"; break;
                }

                switch (mat.textureMapper[0].minFilter)
                {
                    case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break;
                    case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break;
                    case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break;
                    case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break;
                    default: sampler.sampler2D.minfilter = "NONE"; break;
                }

                switch (mat.textureMapper[0].magFilter)
                {
                    case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break;
                    case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break;
                    default: sampler.sampler2D.magfilter = "NONE"; break;
                }

                sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter;

                eff.profile_COMMON.newparam.Add(sampler);

                eff.profile_COMMON.technique.sid = "img_technique";

                eff.profile_COMMON.technique.phong.emission.set(Color.Black);
                eff.profile_COMMON.technique.phong.ambient.set(Color.Black);
                eff.profile_COMMON.technique.phong.specular.set(Color.White);

                eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler";

                eff.profile_COMMON.technique.phong.transparency = new daePhongTransparent();

                if (fire)
                {
                    eff.profile_COMMON.technique.phong.transparency.value = "0.1";
                }
                else
                {
                    eff.profile_COMMON.technique.phong.transparency.value = "1.0";
                }

                dae.library_effects.Add(eff);
            }

            string jointNames = null;
            string invBindPoses = null;
            for (int index = 0; index < mdl.skeleton.Count; index++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformSkeleton(mdl.skeleton, index, ref transform);

                jointNames += mdl.skeleton[index].name;
                daeMatrix mtx = new daeMatrix();
                mtx.set(transform.invert());
                invBindPoses += mtx.data;
                if (index < mdl.skeleton.Count - 1)
                {
                    jointNames += " ";
                    invBindPoses += " ";
                }
            }

            int meshIndex = 0;
            daeVisualScene vs = new daeVisualScene();
            vs.name = "vs_" + mdl.name;
            vs.id = vs.name + "_id";
            if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node);

            bool rightIris = false;

            foreach (RenderBase.OMesh obj in mdl.mesh)
            {
                float largestUV = 0.0f;

                //Geometry
                daeGeometry geometry = new daeGeometry();

                string meshName = "mesh_" + meshIndex++ + "_" + obj.name;
                geometry.id = meshName + "_id";
                geometry.name = meshName;

                MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj);
                List<float> positions = new List<float>();
                List<float> normals = new List<float>();
                List<float> uv0 = new List<float>();
                List<float> uv1 = new List<float>();
                List<float> uv2 = new List<float>();
                List<float> colors = new List<float>();

                if (obj.name.Contains("Iris"))
                {
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        if (mesh.texUVCount > 0)
                        {
                            if (vtx.texture0.x > largestUV)
                            {
                                largestUV = vtx.texture0.x;
                            }
                        }
                    }

                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        if (mesh.texUVCount > 0)
                        {
                            vtx.texture0.x = largestUV - vtx.texture0.x;

                            if (rightIris)
                            {
                                vtx.texture0.x = 1.25f - vtx.texture0.x;
                            }
                        }

                        if (mesh.texUVCount > 1)
                        {
                            vtx.texture1.x = largestUV - vtx.texture1.x;

                            if (rightIris)
                            {
                                vtx.texture1.x = 1.25f - vtx.texture1.x;
                            }
                        }

                        if (mesh.texUVCount > 2)
                        {
                            vtx.texture2.x = largestUV - vtx.texture2.x;

                            if (rightIris)
                            {
                                vtx.texture2.x = 1.25f - vtx.texture2.x;
                            }
                        }
                    }

                    rightIris = !rightIris;
                }
                else if (obj.name.Contains("Eye"))
                {
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        if (mesh.texUVCount > 0)
                        {
                            if (vtx.texture0.x > largestUV)
                            {
                                largestUV = vtx.texture0.x;
                            }
                        }
                    }
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        if (mesh.texUVCount > 0)
                        {
                            vtx.texture0.x = largestUV - vtx.texture0.x;
                        }

                        if (mesh.texUVCount > 1)
                        {
                            vtx.texture1.x = largestUV - vtx.texture1.x;
                        }

                        if (mesh.texUVCount > 2)
                        {
                            vtx.texture2.x = largestUV - vtx.texture2.x;
                        }
                    }
                }

                foreach (RenderBase.OVertex vtx in mesh.vertices)
                {
                    positions.Add(vtx.position.x);
                    positions.Add(vtx.position.y);
                    positions.Add(vtx.position.z);

                    if (mesh.hasNormal)
                    {
                        normals.Add(vtx.normal.x);
                        normals.Add(vtx.normal.y);
                        normals.Add(vtx.normal.z);
                    }

                    if (mesh.texUVCount > 0)
                    {
                        uv0.Add(vtx.texture0.x);
                        uv0.Add(vtx.texture0.y);
                    }

                    if (mesh.texUVCount > 1)
                    {
                        uv1.Add(vtx.texture1.x);
                        uv1.Add(vtx.texture1.y);
                    }

                    if (mesh.texUVCount > 2)
                    {
                        uv2.Add(vtx.texture2.x);
                        uv2.Add(vtx.texture2.y);
                    }

                    if (mesh.hasColor)
                    {
                        colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f);
                        colors.Add((vtx.diffuseColor & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f);
                    }
                }

                daeSource position = new daeSource();
                position.name = meshName + "_position";
                position.id = position.name + "_id";
                position.float_array = new daeFloatArray();
                position.float_array.id = position.name + "_array_id";
                position.float_array.set(positions);
                position.technique_common.accessor.source = "#" + position.float_array.id;
                position.technique_common.accessor.count = (uint)mesh.vertices.Count;
                position.technique_common.accessor.stride = 3;
                position.technique_common.accessor.addParam("X", "float");
                position.technique_common.accessor.addParam("Y", "float");
                position.technique_common.accessor.addParam("Z", "float");

                geometry.mesh.source.Add(position);

                daeSource normal = new daeSource();
                if (mesh.hasNormal)
                {
                    normal.name = meshName + "_normal";
                    normal.id = normal.name + "_id";
                    normal.float_array = new daeFloatArray();
                    normal.float_array.id = normal.name + "_array_id";
                    normal.float_array.set(normals);
                    normal.technique_common.accessor.source = "#" + normal.float_array.id;
                    normal.technique_common.accessor.count = (uint)mesh.vertices.Count;
                    normal.technique_common.accessor.stride = 3;
                    normal.technique_common.accessor.addParam("X", "float");
                    normal.technique_common.accessor.addParam("Y", "float");
                    normal.technique_common.accessor.addParam("Z", "float");

                    geometry.mesh.source.Add(normal);
                }

                daeSource[] texUV = new daeSource[3];
                for (int i = 0; i < mesh.texUVCount; i++)
                {
                    texUV[i] = new daeSource();

                    texUV[i].name = meshName + "_uv" + i;
                    texUV[i].id = texUV[i].name + "_id";
                    texUV[i].float_array = new daeFloatArray();
                    texUV[i].float_array.id = texUV[i].name + "_array_id";
                    texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id;
                    texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count;
                    texUV[i].technique_common.accessor.stride = 2;
                    texUV[i].technique_common.accessor.addParam("S", "float");
                    texUV[i].technique_common.accessor.addParam("T", "float");

                    geometry.mesh.source.Add(texUV[i]);
                }

                daeSource color = new daeSource();
                if (mesh.hasColor)
                {
                    color.name = meshName + "_color";
                    color.id = color.name + "_id";
                    color.float_array = new daeFloatArray();
                    color.float_array.id = color.name + "_array_id";
                    color.float_array.set(colors);
                    color.technique_common.accessor.source = "#" + color.float_array.id;
                    color.technique_common.accessor.count = (uint)mesh.vertices.Count;
                    color.technique_common.accessor.stride = 4;
                    color.technique_common.accessor.addParam("R", "float");
                    color.technique_common.accessor.addParam("G", "float");
                    color.technique_common.accessor.addParam("B", "float");
                    color.technique_common.accessor.addParam("A", "float");

                    geometry.mesh.source.Add(color);
                }

                geometry.mesh.vertices.id = meshName + "_vertices_id";
                geometry.mesh.vertices.addInput("POSITION", "#" + position.id);

                geometry.mesh.triangles.material = mdl.material[obj.materialId].name + "_mat";
                geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id);
                if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id);
                if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id);
                if (mesh.texUVCount > 0)
                {
                    texUV[0].float_array.set(uv0);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id);
                }
                if (mesh.texUVCount > 1)
                {
                    texUV[1].float_array.set(uv1);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1);
                }
                if (mesh.texUVCount > 2)
                {
                    texUV[2].float_array.set(uv2);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2);
                }
                geometry.mesh.triangles.set(mesh.indices);

                dae.library_geometries.Add(geometry);

                bool hasNode = obj.vertices[0].node.Count > 0;
                bool hasWeight = obj.vertices[0].weight.Count > 0;
                bool hasController = hasNode && hasWeight;

                //Controller
                daeController controller = new daeController();
                if (hasController)
                {
                    controller.id = meshName + "_ctrl_id";

                    controller.skin.source = "#" + geometry.id;
                    controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix());

                    daeSource joints = new daeSource();
                    joints.id = meshName + "_ctrl_joint_names_id";
                    joints.Name_array = new daeNameArray();
                    joints.Name_array.id = meshName + "_ctrl_joint_names_array_id";
                    joints.Name_array.count = (uint)mdl.skeleton.Count;
                    joints.Name_array.data = jointNames;
                    joints.technique_common.accessor.source = "#" + joints.Name_array.id;
                    joints.technique_common.accessor.count = joints.Name_array.count;
                    joints.technique_common.accessor.stride = 1;
                    joints.technique_common.accessor.addParam("JOINT", "Name");

                    controller.skin.src.Add(joints);

                    daeSource bindPoses = new daeSource();
                    bindPoses.id = meshName + "_ctrl_inv_bind_poses_id";
                    bindPoses.float_array = new daeFloatArray();
                    bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id";
                    bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16);
                    bindPoses.float_array.data = invBindPoses;
                    bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id;
                    bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count;
                    bindPoses.technique_common.accessor.stride = 16;
                    bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4");

                    controller.skin.src.Add(bindPoses);

                    daeSource weights = new daeSource();
                    weights.id = meshName + "_ctrl_weights_id";
                    weights.float_array = new daeFloatArray();
                    weights.float_array.id = meshName + "_ctrl_weights_array_id";
                    weights.technique_common.accessor.source = "#" + weights.float_array.id;
                    weights.technique_common.accessor.stride = 1;
                    weights.technique_common.accessor.addParam("WEIGHT", "float");

                    StringBuilder w = new StringBuilder();
                    StringBuilder vcount = new StringBuilder();
                    StringBuilder v = new StringBuilder();

                    float[] wLookBack = new float[32];
                    uint wLookBackIndex = 0;
                    int buffLen = 0;

                    int wIndex = 0;
                    int wCount = 0;
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        int count = Math.Min(vtx.node.Count, vtx.weight.Count);

                        vcount.Append(count + " ");
                        for (int n = 0; n < count; n++)
                        {
                            v.Append(vtx.node[n] + " ");
                            bool found = false;
                            uint bPos = (wLookBackIndex - 1) & 0x1f;
                            for (int i = 0; i < buffLen; i++)
                            {
                                if (wLookBack[bPos] == vtx.weight[n])
                                {
                                    v.Append(wIndex - (i + 1) + " ");
                                    found = true;
                                    break;
                                }
                                bPos = (bPos - 1) & 0x1f;
                            }

                            if (!found)
                            {
                                v.Append(wIndex++ + " ");
                                w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " ");
                                wCount++;

                                wLookBack[wLookBackIndex] = vtx.weight[n];
                                wLookBackIndex = (wLookBackIndex + 1) & 0x1f;
                                if (buffLen < wLookBack.Length) buffLen++;
                            }
                        }
                    }

                    weights.float_array.data = w.ToString().TrimEnd();
                    weights.float_array.count = (uint)wCount;
                    weights.technique_common.accessor.count = (uint)wCount;

                    controller.skin.src.Add(weights);
                    controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd();
                    controller.skin.vertex_weights.v = v.ToString().TrimEnd();
                    controller.skin.vertex_weights.count = (uint)mesh.vertices.Count;
                    controller.skin.joints.addInput("JOINT", "#" + joints.id);
                    controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id);

                    controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id);
                    controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1);

                    if (dae.library_controllers == null) dae.library_controllers = new List<daeController>();
                    dae.library_controllers.Add(controller);
                }

                //Visual scene node
                daeNode node = new daeNode();
                node.name = "vsn_" + meshName;
                node.id = node.name + "_id";
                node.matrix.set(new RenderBase.OMatrix());
                if (hasController)
                {
                    node.instance_controller = new daeInstanceController();
                    node.instance_controller.url = "#" + controller.id;
                    node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id";
                    node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat";
                    node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }
                else
                {
                    node.instance_geometry = new daeInstanceGeometry();
                    node.instance_geometry.url = "#" + geometry.id;
                    node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat";
                    node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }

                vs.node.Add(node);
            }
            dae.library_visual_scenes.Add(vs);

            daeInstaceVisualScene scene = new daeInstaceVisualScene();
            scene.url = "#" + vs.id;
            dae.scene.Add(scene);

            XmlWriterSettings settings = new XmlWriterSettings
            {
                Encoding = Encoding.UTF8,
                Indent = true
            };

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "http://www.collada.org/2005/11/COLLADASchema");
            XmlSerializer serializer = new XmlSerializer(typeof(COLLADA));
            XmlWriter output = XmlWriter.Create(new FileStream(fileName.Replace(".bch", ".dae").Replace("BCH", "DAE"), FileMode.Create), settings);
            serializer.Serialize(output, dae, ns);
            output.Close();

            #endregion

            #region Shiny

            if (hasShiny)
            {
                currentMat = 0;

                foreach (RenderBase.OMaterial mat in mdl.material)
                {
                    if (mat.name0 == null)
                    {
                        mat.name0 = mat.name;
                    }

                    mat.name += "_shiny";
                    mat.name0 += "_shiny";

                    mdl.material[currentMat].name = mat.name;
                    mdl.material[currentMat].name0 = mat.name0;

                    currentMat++;

                    daeMaterial mtl = new daeMaterial();
                    mtl.name = mat.name + "_mat";
                    mtl.id = mtl.name + "_id";
                    mtl.instance_effect.url = "#eff_" + mtl.id;

                    daeShiny.library_materials.Add(mtl);

                    daeEffect eff = new daeEffect();
                    eff.id = "eff_" + mtl.id;
                    eff.name = "eff_" + mtl.name;

                    daeParam surface = new daeParam();
                    surface.surface = new daeParamSurfaceElement();
                    surface.sid = "img_surface";
                    surface.surface.type = "2D";
                    surface.surface.init_from = mat.name0 + "_id";
                    surface.surface.format = "PNG";
                    eff.profile_COMMON.newparam.Add(surface);

                    bool fire = false;

                    for (int i = 0; i < daeShiny.library_images.Count; i++)
                    {
                        if (daeShiny.library_images[i].init_from.Contains(mat.name) || daeShiny.library_images[i].id.Contains(mat.name) || daeShiny.library_images[i].init_from.Contains(mat.name0) || daeShiny.library_images[i].id.Contains(mat.name0))
                        {
                            if (daeShiny.library_images[i].init_from.Contains("Nor") == false)
                            {
                                if (mat.name.Contains("Fire"))
                                {
                                    fire = true;

                                    daeShiny.library_images[i].name = daeShiny.library_images[i].name.Replace("Mask", "1");
                                    daeShiny.library_images[i].id = daeShiny.library_images[i].id.Replace("Mask", "1");
                                    daeShiny.library_images[i].init_from = daeShiny.library_images[i].init_from.Replace("Mask", "1");

                                    mat.name = mat.name.Replace("Mask", "1");
                                    mat.name0 = mat.name0.Replace("Mask", "1");
                                }

                                surface.surface.init_from = daeShiny.library_images[i].id;
                            }
                        }
                    }

                    if (mat.name.Contains("Fire"))
                    {
                        fire = true;
                    }

                    daeParam sampler = new daeParam();
                    sampler.sampler2D = new daeParamSampler2DElement();
                    sampler.sid = "img_sampler";
                    sampler.sampler2D.source = "img_surface";

                    switch (mat.textureMapper[0].wrapU)
                    {
                        case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break;
                        case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break;
                        case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break;
                        case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break;
                        default: sampler.sampler2D.wrap_s = "WRAP"; break;
                    }

                    switch (mat.textureMapper[0].wrapV)
                    {
                        case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break;
                        case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break;
                        case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break;
                        case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break;
                        default: sampler.sampler2D.wrap_t = "WRAP"; break;
                    }

                    switch (mat.textureMapper[0].minFilter)
                    {
                        case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break;
                        case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break;
                        case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break;
                        case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break;
                        default: sampler.sampler2D.minfilter = "NONE"; break;
                    }

                    switch (mat.textureMapper[0].magFilter)
                    {
                        case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break;
                        case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break;
                        default: sampler.sampler2D.magfilter = "NONE"; break;
                    }

                    sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter;

                    eff.profile_COMMON.newparam.Add(sampler);

                    eff.profile_COMMON.technique.sid = "img_technique";

                    eff.profile_COMMON.technique.phong.emission.set(Color.Black);
                    eff.profile_COMMON.technique.phong.ambient.set(Color.Black);
                    eff.profile_COMMON.technique.phong.specular.set(Color.White);

                    eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler";

                    eff.profile_COMMON.technique.phong.transparency = new daePhongTransparent();

                    if (fire)
                    {
                        eff.profile_COMMON.technique.phong.transparency.value = "0.1";
                    }
                    else
                    {
                        eff.profile_COMMON.technique.phong.transparency.value = "1.0";
                    }

                    daeShiny.library_effects.Add(eff);
                }

                jointNames = null;
                invBindPoses = null;
                for (int index = 0; index < mdl.skeleton.Count; index++)
                {
                    RenderBase.OMatrix transform = new RenderBase.OMatrix();
                    transformSkeleton(mdl.skeleton, index, ref transform);

                    jointNames += mdl.skeleton[index].name;
                    daeMatrix mtx = new daeMatrix();
                    mtx.set(transform.invert());
                    invBindPoses += mtx.data;
                    if (index < mdl.skeleton.Count - 1)
                    {
                        jointNames += " ";
                        invBindPoses += " ";
                    }
                }

                meshIndex = 0;
                vs = new daeVisualScene();
                vs.name = "vs_" + mdl.name;
                vs.id = vs.name + "_id";
                if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node);

                rightIris = false;

                foreach (RenderBase.OMesh obj in mdl.mesh)
                {
                    float largestUV = 0.0f;

                    //Geometry
                    daeGeometry geometry = new daeGeometry();

                    string meshName = "mesh_" + meshIndex++ + "_" + obj.name;
                    geometry.id = meshName + "_id";
                    geometry.name = meshName;

                    MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj);
                    List<float> positions = new List<float>();
                    List<float> normals = new List<float>();
                    List<float> uv0 = new List<float>();
                    List<float> uv1 = new List<float>();
                    List<float> uv2 = new List<float>();
                    List<float> colors = new List<float>();

                    if (obj.name.Contains("Iris"))
                    {
                        foreach (RenderBase.OVertex vtx in mesh.vertices)
                        {
                            if (mesh.texUVCount > 0)
                            {
                                if (vtx.texture0.x > largestUV)
                                {
                                    largestUV = vtx.texture0.x;
                                }
                            }
                        }

                        foreach (RenderBase.OVertex vtx in mesh.vertices)
                        {
                            if (mesh.texUVCount > 0)
                            {
                                vtx.texture0.x = largestUV - vtx.texture0.x;

                                if (rightIris)
                                {
                                    vtx.texture0.x = 1.25f - vtx.texture0.x;
                                }
                            }

                            if (mesh.texUVCount > 1)
                            {
                                vtx.texture1.x = largestUV - vtx.texture1.x;

                                if (rightIris)
                                {
                                    vtx.texture1.x = 1.25f - vtx.texture1.x;
                                }
                            }

                            if (mesh.texUVCount > 2)
                            {
                                vtx.texture2.x = largestUV - vtx.texture2.x;

                                if (rightIris)
                                {
                                    vtx.texture2.x = 1.25f - vtx.texture2.x;
                                }
                            }
                        }

                        rightIris = !rightIris;
                    }
                    else if (obj.name.Contains("Eye"))
                    {
                        foreach (RenderBase.OVertex vtx in mesh.vertices)
                        {
                            if (mesh.texUVCount > 0)
                            {
                                if (vtx.texture0.x > largestUV)
                                {
                                    largestUV = vtx.texture0.x;
                                }
                            }
                        }

                        foreach (RenderBase.OVertex vtx in mesh.vertices)
                        {
                            if (mesh.texUVCount > 0)
                            {
                                vtx.texture0.x = largestUV - vtx.texture0.x;
                            }

                            if (mesh.texUVCount > 1)
                            {
                                vtx.texture1.x = largestUV - vtx.texture1.x;
                            }

                            if (mesh.texUVCount > 2)
                            {
                                vtx.texture2.x = largestUV - vtx.texture2.x;
                            }
                        }
                    }

                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        positions.Add(vtx.position.x);
                        positions.Add(vtx.position.y);
                        positions.Add(vtx.position.z);

                        if (mesh.hasNormal)
                        {
                            normals.Add(vtx.normal.x);
                            normals.Add(vtx.normal.y);
                            normals.Add(vtx.normal.z);
                        }

                        if (mesh.texUVCount > 0)
                        {
                            uv0.Add(vtx.texture0.x);
                            uv0.Add(vtx.texture0.y);
                        }

                        if (mesh.texUVCount > 1)
                        {
                            uv1.Add(vtx.texture1.x);
                            uv1.Add(vtx.texture1.y);
                        }

                        if (mesh.texUVCount > 2)
                        {
                            uv2.Add(vtx.texture2.x);
                            uv2.Add(vtx.texture2.y);
                        }

                        if (mesh.hasColor)
                        {
                            colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f);
                            colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f);
                            colors.Add((vtx.diffuseColor & 0xff) / 255f);
                            colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f);
                        }
                    }

                    daeSource position = new daeSource();
                    position.name = meshName + "_position";
                    position.id = position.name + "_id";
                    position.float_array = new daeFloatArray();
                    position.float_array.id = position.name + "_array_id";
                    position.float_array.set(positions);
                    position.technique_common.accessor.source = "#" + position.float_array.id;
                    position.technique_common.accessor.count = (uint)mesh.vertices.Count;
                    position.technique_common.accessor.stride = 3;
                    position.technique_common.accessor.addParam("X", "float");
                    position.technique_common.accessor.addParam("Y", "float");
                    position.technique_common.accessor.addParam("Z", "float");

                    geometry.mesh.source.Add(position);

                    daeSource normal = new daeSource();
                    if (mesh.hasNormal)
                    {
                        normal.name = meshName + "_normal";
                        normal.id = normal.name + "_id";
                        normal.float_array = new daeFloatArray();
                        normal.float_array.id = normal.name + "_array_id";
                        normal.float_array.set(normals);
                        normal.technique_common.accessor.source = "#" + normal.float_array.id;
                        normal.technique_common.accessor.count = (uint)mesh.vertices.Count;
                        normal.technique_common.accessor.stride = 3;
                        normal.technique_common.accessor.addParam("X", "float");
                        normal.technique_common.accessor.addParam("Y", "float");
                        normal.technique_common.accessor.addParam("Z", "float");

                        geometry.mesh.source.Add(normal);
                    }

                    daeSource[] texUV = new daeSource[3];
                    for (int i = 0; i < mesh.texUVCount; i++)
                    {
                        texUV[i] = new daeSource();

                        texUV[i].name = meshName + "_uv" + i;
                        texUV[i].id = texUV[i].name + "_id";
                        texUV[i].float_array = new daeFloatArray();
                        texUV[i].float_array.id = texUV[i].name + "_array_id";
                        texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id;
                        texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count;
                        texUV[i].technique_common.accessor.stride = 2;
                        texUV[i].technique_common.accessor.addParam("S", "float");
                        texUV[i].technique_common.accessor.addParam("T", "float");

                        geometry.mesh.source.Add(texUV[i]);
                    }

                    daeSource color = new daeSource();
                    if (mesh.hasColor)
                    {
                        color.name = meshName + "_color";
                        color.id = color.name + "_id";
                        color.float_array = new daeFloatArray();
                        color.float_array.id = color.name + "_array_id";
                        color.float_array.set(colors);
                        color.technique_common.accessor.source = "#" + color.float_array.id;
                        color.technique_common.accessor.count = (uint)mesh.vertices.Count;
                        color.technique_common.accessor.stride = 4;
                        color.technique_common.accessor.addParam("R", "float");
                        color.technique_common.accessor.addParam("G", "float");
                        color.technique_common.accessor.addParam("B", "float");
                        color.technique_common.accessor.addParam("A", "float");

                        geometry.mesh.source.Add(color);
                    }

                    geometry.mesh.vertices.id = meshName + "_vertices_id";
                    geometry.mesh.vertices.addInput("POSITION", "#" + position.id);

                    geometry.mesh.triangles.material = mdl.material[obj.materialId].name + "_mat";
                    geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id);
                    if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id);
                    if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id);
                    if (mesh.texUVCount > 0)
                    {
                        texUV[0].float_array.set(uv0);
                        geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id);
                    }
                    if (mesh.texUVCount > 1)
                    {
                        texUV[1].float_array.set(uv1);
                        geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1);
                    }
                    if (mesh.texUVCount > 2)
                    {
                        texUV[2].float_array.set(uv2);
                        geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2);
                    }
                    geometry.mesh.triangles.set(mesh.indices);

                    daeShiny.library_geometries.Add(geometry);

                    bool hasNode = obj.vertices[0].node.Count > 0;
                    bool hasWeight = obj.vertices[0].weight.Count > 0;
                    bool hasController = hasNode && hasWeight;

                    //Controller
                    daeController controller = new daeController();
                    if (hasController)
                    {
                        controller.id = meshName + "_ctrl_id";

                        controller.skin.source = "#" + geometry.id;
                        controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix());

                        daeSource joints = new daeSource();
                        joints.id = meshName + "_ctrl_joint_names_id";
                        joints.Name_array = new daeNameArray();
                        joints.Name_array.id = meshName + "_ctrl_joint_names_array_id";
                        joints.Name_array.count = (uint)mdl.skeleton.Count;
                        joints.Name_array.data = jointNames;
                        joints.technique_common.accessor.source = "#" + joints.Name_array.id;
                        joints.technique_common.accessor.count = joints.Name_array.count;
                        joints.technique_common.accessor.stride = 1;
                        joints.technique_common.accessor.addParam("JOINT", "Name");

                        controller.skin.src.Add(joints);

                        daeSource bindPoses = new daeSource();
                        bindPoses.id = meshName + "_ctrl_inv_bind_poses_id";
                        bindPoses.float_array = new daeFloatArray();
                        bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id";
                        bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16);
                        bindPoses.float_array.data = invBindPoses;
                        bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id;
                        bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count;
                        bindPoses.technique_common.accessor.stride = 16;
                        bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4");

                        controller.skin.src.Add(bindPoses);

                        daeSource weights = new daeSource();
                        weights.id = meshName + "_ctrl_weights_id";
                        weights.float_array = new daeFloatArray();
                        weights.float_array.id = meshName + "_ctrl_weights_array_id";
                        weights.technique_common.accessor.source = "#" + weights.float_array.id;
                        weights.technique_common.accessor.stride = 1;
                        weights.technique_common.accessor.addParam("WEIGHT", "float");

                        StringBuilder w = new StringBuilder();
                        StringBuilder vcount = new StringBuilder();
                        StringBuilder v = new StringBuilder();

                        float[] wLookBack = new float[32];
                        uint wLookBackIndex = 0;
                        int buffLen = 0;

                        int wIndex = 0;
                        int wCount = 0;
                        foreach (RenderBase.OVertex vtx in mesh.vertices)
                        {
                            int count = Math.Min(vtx.node.Count, vtx.weight.Count);

                            vcount.Append(count + " ");
                            for (int n = 0; n < count; n++)
                            {
                                v.Append(vtx.node[n] + " ");
                                bool found = false;
                                uint bPos = (wLookBackIndex - 1) & 0x1f;
                                for (int i = 0; i < buffLen; i++)
                                {
                                    if (wLookBack[bPos] == vtx.weight[n])
                                    {
                                        v.Append(wIndex - (i + 1) + " ");
                                        found = true;
                                        break;
                                    }
                                    bPos = (bPos - 1) & 0x1f;
                                }

                                if (!found)
                                {
                                    v.Append(wIndex++ + " ");
                                    w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " ");
                                    wCount++;

                                    wLookBack[wLookBackIndex] = vtx.weight[n];
                                    wLookBackIndex = (wLookBackIndex + 1) & 0x1f;
                                    if (buffLen < wLookBack.Length) buffLen++;
                                }
                            }
                        }

                        weights.float_array.data = w.ToString().TrimEnd();
                        weights.float_array.count = (uint)wCount;
                        weights.technique_common.accessor.count = (uint)wCount;

                        controller.skin.src.Add(weights);
                        controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd();
                        controller.skin.vertex_weights.v = v.ToString().TrimEnd();
                        controller.skin.vertex_weights.count = (uint)mesh.vertices.Count;
                        controller.skin.joints.addInput("JOINT", "#" + joints.id);
                        controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id);

                        controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id);
                        controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1);

                        if (daeShiny.library_controllers == null) daeShiny.library_controllers = new List<daeController>();
                        daeShiny.library_controllers.Add(controller);
                    }

                    //Visual scene node
                    daeNode node = new daeNode();
                    node.name = "vsn_" + meshName;
                    node.id = node.name + "_id";
                    node.matrix.set(new RenderBase.OMatrix());
                    if (hasController)
                    {
                        node.instance_controller = new daeInstanceController();
                        node.instance_controller.url = "#" + controller.id;
                        node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id";
                        node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat";
                        node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                    }
                    else
                    {
                        node.instance_geometry = new daeInstanceGeometry();
                        node.instance_geometry.url = "#" + geometry.id;
                        node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat";
                        node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                    }

                    vs.node.Add(node);
                }
                daeShiny.library_visual_scenes.Add(vs);

                scene = new daeInstaceVisualScene();
                scene.url = "#" + vs.id;
                daeShiny.scene.Add(scene);

                settings = new XmlWriterSettings
                {
                    Encoding = Encoding.UTF8,
                    Indent = true
                };

                ns = new XmlSerializerNamespaces();
                ns.Add("", "http://www.collada.org/2005/11/COLLADASchema");
                serializer = new XmlSerializer(typeof(COLLADA));
                output = XmlWriter.Create(new FileStream(fileName.Replace(".bch", "_Shiny.dae").Replace("BCH", "DAE"), FileMode.Create), settings);
                serializer.Serialize(output, daeShiny, ns);
                output.Close();
            }
            #endregion
        }
Exemplo n.º 6
0
            public RenderBase.OMatrix get()
            {
                RenderBase.OMatrix output = new RenderBase.OMatrix();
                string[] values = data.Split(Convert.ToChar(" "));
                int k = 0;
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        output[j, i] = float.Parse(values[k++]);
                    }

                }
                return output;
            }
Exemplo n.º 7
0
        /// <summary>
        ///     Writes the skeleton hierarchy to the DAE.
        /// </summary>
        /// <param name="skeleton">The skeleton</param>
        /// <param name="index">Index of the current bone (root bone when it's not a recursive call)</param>
        /// <param name="nodes">List with the DAE nodes</param>
        private static void writeSkeleton(List<RenderBase.OBone> skeleton, int index, ref List<daeNode> nodes)
        {
            daeNode node = new daeNode();
            node.name = skeleton[index].name;
            node.id = node.name + "_bone_id";
            node.sid = node.name;
            node.type = "JOINT";

            RenderBase.OMatrix transform = new RenderBase.OMatrix();
            transform *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x);
            transform *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y);
            transform *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z);
            transform *= RenderBase.OMatrix.translate(skeleton[index].translation);

            node.matrix.set(transform);

            for (int i = 0; i < skeleton.Count; i++)
            {
                if (skeleton[i].parentId == index)
                {
                    if (node.childs == null) node.childs = new List<daeNode>();
                    writeSkeleton(skeleton, i, ref node.childs);
                }
            }

            nodes.Add(node);
        }
Exemplo n.º 8
0
        /// <summary>
        ///     Reads a 4x3 Matrix from the file.
        /// </summary>
        /// <param name="input">BinaryReader of the CGFX file</param>
        /// <returns></returns>
        private static RenderBase.OMatrix getMatrix(BinaryReader input)
        {
            RenderBase.OMatrix output = new RenderBase.OMatrix();

            output.M11 = input.ReadSingle();
            output.M21 = input.ReadSingle();
            output.M31 = input.ReadSingle();
            output.M41 = input.ReadSingle();

            output.M12 = input.ReadSingle();
            output.M22 = input.ReadSingle();
            output.M32 = input.ReadSingle();
            output.M42 = input.ReadSingle();

            output.M13 = input.ReadSingle();
            output.M23 = input.ReadSingle();
            output.M33 = input.ReadSingle();
            output.M43 = input.ReadSingle();

            return output;
        }
Exemplo n.º 9
0
        /// <summary>
        ///     Loads a CGFX file.
        ///     Note that CGFX must start at offset 0x0 (don't try using it for CGFXs inside containers).
        /// </summary>
        /// <param name="data">Stream of the CGFX file.</param>
        /// <returns></returns>
        public static RenderBase.OModelGroup load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);

            RenderBase.OModelGroup models = new RenderBase.OModelGroup();

            cgfxHeader header = new cgfxHeader();
            header.magic = IOUtils.readString(input, 0, 4);
            header.endian = input.ReadUInt16();
            header.length = input.ReadUInt16();
            header.revision = input.ReadUInt32();
            header.fileLength = input.ReadUInt32();
            header.entries = input.ReadUInt32();

            data.Seek(header.length, SeekOrigin.Begin);
            dataHeader dataHeader = new dataHeader();
            dataHeader.magic = IOUtils.readString(input, (uint)data.Position, 4);
            dataHeader.length = input.ReadUInt32();
            dataHeader.models = getDictionary(input);
            dataHeader.textures = getDictionary(input);
            dataHeader.lookUpTables = getDictionary(input);
            dataHeader.materials = getDictionary(input);
            dataHeader.shaders = getDictionary(input);
            dataHeader.cameras = getDictionary(input);
            dataHeader.lights = getDictionary(input);
            dataHeader.fogs = getDictionary(input);
            dataHeader.scenes = getDictionary(input);
            dataHeader.skeletalAnimations = getDictionary(input);
            dataHeader.materialAnimations = getDictionary(input);
            dataHeader.visibilityAnimations = getDictionary(input);
            dataHeader.cameraAnimations = getDictionary(input);
            dataHeader.lightAnimations = getDictionary(input);
            dataHeader.emitters = getDictionary(input);

            //Textures
            foreach (dictEntry textureEntry in dataHeader.textures)
            {
                data.Seek(textureEntry.dataOffset, SeekOrigin.Begin);

                uint type = input.ReadUInt32();
                string txobMagic = IOUtils.readString(input, (uint)data.Position, 4);
                uint revision = input.ReadUInt32();
                string name = IOUtils.readString(input, getRelativeOffset(input));
                uint userDataEntries = input.ReadUInt32();
                uint userDataOffset = getRelativeOffset(input);
                int height = (int)input.ReadUInt32();
                int width = (int)input.ReadUInt32();
                uint openGLFormat = input.ReadUInt32();
                uint openGLType = input.ReadUInt32();
                uint mipmapLevels = input.ReadUInt32();
                uint textureObject = input.ReadUInt32();
                uint locationFlags = input.ReadUInt32();
                RenderBase.OTextureFormat format = (RenderBase.OTextureFormat)input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();
                uint dataLength = input.ReadUInt32();
                uint dataOffset = getRelativeOffset(input);
                uint dynamicAllocator = input.ReadUInt32();
                uint bitsPerPixel = input.ReadUInt32();
                uint locationAddress = input.ReadUInt32();
                uint memoryAddress = input.ReadUInt32();

                byte[] buffer = new byte[dataLength];
                data.Seek(dataOffset, SeekOrigin.Begin);
                input.Read(buffer, 0, buffer.Length);
                models.texture.Add(new RenderBase.OTexture(TextureCodec.decode(buffer, width, height, format), name));
            }

            //Skeletal animations
            foreach (dictEntry skeletalAnimationEntry in dataHeader.skeletalAnimations)
            {
                data.Seek(skeletalAnimationEntry.dataOffset, SeekOrigin.Begin);

                RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation();

                string canmMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4);
                uint revision = input.ReadUInt32();
                skeletalAnimation.name = IOUtils.readString(input, getRelativeOffset(input));
                string targetAnimationGroupName = IOUtils.readString(input, getRelativeOffset(input));
                skeletalAnimation.loopMode = (RenderBase.OLoopMode)input.ReadUInt32();
                skeletalAnimation.frameSize = input.ReadSingle();
                List<dictEntry> memberAnimationDataDictionary = getDictionary(input);
                uint userDataEntries = input.ReadUInt32();
                uint userDataOffset = getRelativeOffset(input);

                foreach (dictEntry entry in memberAnimationDataDictionary)
                {
                    RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone();
                    bone.name = IOUtils.readString(input, entry.nameOffset);
                    data.Seek(entry.dataOffset, SeekOrigin.Begin);

                    uint boneFlags = input.ReadUInt32();
                    string bonePath = IOUtils.readString(input, getRelativeOffset(input));
                    if ((revision >> 24) < 7) data.Seek(8, SeekOrigin.Current);
                    cgfxSegmentType segmentType = (cgfxSegmentType)input.ReadUInt32();

                    switch (segmentType)
                    {
                        case cgfxSegmentType.transform:
                            data.Seek(0xc, SeekOrigin.Current);
                            uint notExistMask = 0x80000;
                            uint constantMask = 0x200;

                            for (int j = 0; j < 2; j++)
                            {
                                for (int axis = 0; axis < 3; axis++)
                                {
                                    bool notExist = (boneFlags & notExistMask) > 0;
                                    bool constant = (boneFlags & constantMask) > 0;

                                    RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
                                    frame.exists = !notExist;
                                    if (frame.exists)
                                    {
                                        if (constant)
                                        {
                                            frame.interpolation = RenderBase.OInterpolationMode.linear;
                                            frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
                                        }
                                        else
                                        {
                                            uint frameOffset = getRelativeOffset(input);
                                            long position = data.Position;
                                            data.Seek(frameOffset, SeekOrigin.Begin);
                                            getAnimationKeyFrame(input, frame);
                                            data.Seek(position, SeekOrigin.Begin);
                                        }
                                    }
                                    else
                                        data.Seek(4, SeekOrigin.Current);

                                    if (j == 0)
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.rotationX = frame; break;
                                            case 1: bone.rotationY = frame; break;
                                            case 2: bone.rotationZ = frame; break;
                                        }
                                    }
                                    else
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.translationX = frame; break;
                                            case 1: bone.translationY = frame; break;
                                            case 2: bone.translationZ = frame; break;
                                        }
                                    }

                                    notExistMask <<= 1;
                                    constantMask <<= 1;
                                }

                                notExistMask <<= 1;
                                constantMask <<= 1;
                                data.Seek(4, SeekOrigin.Current);
                            }
                            break;
                        case cgfxSegmentType.transformQuaternion:
                            bone.isFrameFormat = true;
                            uint rotationOffset = getRelativeOffset(input);
                            uint translationOffset = getRelativeOffset(input);
                            uint scaleOffset = getRelativeOffset(input);

                            if ((boneFlags & 0x10) == 0)
                            {
                                bone.rotationQuaternion.exists = true;
                                data.Seek(rotationOffset, SeekOrigin.Begin);
                                bone.rotationQuaternion.startFrame = input.ReadSingle();
                                bone.rotationQuaternion.endFrame = input.ReadSingle();
                                input.ReadUInt32();
                                uint constantFlags = input.ReadUInt32();

                                if ((constantFlags & 1) > 0)
                                {
                                    bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle()));
                                }
                                else
                                {
                                    uint rotationEntries = (uint)(bone.rotationQuaternion.endFrame - bone.rotationQuaternion.startFrame);

                                    for (int j = 0; j < rotationEntries; j++)
                                    {
                                        bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle()));
                                        uint flags = input.ReadUInt32();
                                    }
                                }
                            }
                            else
                                data.Seek(4, SeekOrigin.Current);

                            if ((boneFlags & 8) == 0)
                            {
                                bone.translation.exists = true;
                                data.Seek(translationOffset, SeekOrigin.Begin);
                                bone.translation.startFrame = input.ReadSingle();
                                bone.translation.endFrame = input.ReadSingle();
                                input.ReadUInt32();
                                uint constantFlags = input.ReadUInt32();

                                if ((constantFlags & 1) > 0)
                                {
                                    bone.translation.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        0));
                                }
                                else
                                {
                                    uint translationEntries = (uint)(bone.rotationQuaternion.endFrame - bone.rotationQuaternion.startFrame);

                                    for (int j = 0; j < translationEntries; j++)
                                    {
                                        bone.translation.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            0));
                                        uint flags = input.ReadUInt32();
                                    }
                                }
                            }
                            else
                                data.Seek(4, SeekOrigin.Current);

                            break;
                    }

                    skeletalAnimation.bone.Add(bone);
                }

                models.skeletalAnimation.list.Add(skeletalAnimation);
            }

            //Models
            foreach (dictEntry modelEntry in dataHeader.models)
            {
                data.Seek(modelEntry.dataOffset, SeekOrigin.Begin);

                cmdlHeader cmdlHeader = new cmdlHeader();

                uint flags = input.ReadUInt32();
                cmdlHeader.hasSkeleton = (flags & 0x80) > 0;
                string cmdlMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4);
                uint revision = input.ReadUInt32();
                cmdlHeader.modelName = IOUtils.readString(input, getRelativeOffset(input));
                cmdlHeader.userDataEntries = input.ReadUInt32();
                cmdlHeader.userDataDictionaryOffset = getRelativeOffset(input);
                input.ReadUInt32();
                flags = input.ReadUInt32();
                cmdlHeader.isBranchVisible = (flags & 1) > 0;
                cmdlHeader.childCount = input.ReadUInt32();
                input.ReadUInt32(); //Unused
                cmdlHeader.animationGroupEntries = input.ReadUInt32();
                cmdlHeader.animationGroupDictionaryOffset = getRelativeOffset(input);
                cmdlHeader.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                cmdlHeader.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                cmdlHeader.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                cmdlHeader.localMatrix = getMatrix(input);
                cmdlHeader.worldMatrix = getMatrix(input);
                cmdlHeader.objectEntries = input.ReadUInt32();
                cmdlHeader.objectPointerTableOffset = getRelativeOffset(input);
                cmdlHeader.materials = getDictionary(input);
                cmdlHeader.shapeEntries = input.ReadUInt32();
                cmdlHeader.shapePointerTableOffset = getRelativeOffset(input);
                cmdlHeader.objectNodes = getDictionary(input);
                flags = input.ReadUInt32();
                cmdlHeader.isVisible = (flags & 1) > 0;
                cmdlHeader.isNonUniformScalable = (flags & 0x100) > 0;
                cmdlHeader.cullMode = (RenderBase.OModelCullingMode)input.ReadUInt32();
                cmdlHeader.layerId = input.ReadUInt32();
                if (cmdlHeader.hasSkeleton) cmdlHeader.skeletonOffset = getRelativeOffset(input);

                RenderBase.OModel model = new RenderBase.OModel();
                model.name = cmdlHeader.modelName;
                model.transform = cmdlHeader.worldMatrix;

                //Materials
                foreach (dictEntry materialEntry in cmdlHeader.materials)
                {
                    data.Seek(materialEntry.dataOffset, SeekOrigin.Begin);

                    RenderBase.OMaterial material = new RenderBase.OMaterial();

                    flags = input.ReadUInt32();
                    string mtobMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4);
                    revision = input.ReadUInt32();
                    material.name = IOUtils.readString(input, getRelativeOffset(input));
                    uint userDataEntries = input.ReadUInt32();
                    uint userDataOffset = getRelativeOffset(input);

                    flags = input.ReadUInt32();
                    material.isFragmentLightEnabled = (flags & 1) > 0;
                    material.isVertexLightEnabled = (flags & 2) > 0;
                    material.isHemiSphereLightEnabled = (flags & 4) > 0;
                    material.isHemiSphereOcclusionEnabled = (flags & 8) > 0;
                    material.isFogEnabled = (flags & 0x10) > 0;
                    material.rasterization.isPolygonOffsetEnabled = (flags & 0x20) > 0;

                    uint textureCoordinatesConfig = input.ReadUInt32();
                    uint translucencyKind = input.ReadUInt32();

                    /*
                     * Material color
                     */
                    MeshUtils.getColorFloat(input); //Emission (stored as float4)
                    MeshUtils.getColorFloat(input); //Ambient (stored as float4)
                    MeshUtils.getColorFloat(input); //Diffuse (stored as float4)
                    MeshUtils.getColorFloat(input); //Specular 0 (stored as float4)
                    MeshUtils.getColorFloat(input); //Specular 1 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 0 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 1 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 2 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 3 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 4 (stored as float4)
                    MeshUtils.getColorFloat(input); //Constant 5 (stored as float4)

                    material.materialColor.emission = MeshUtils.getColor(input);
                    material.materialColor.ambient = MeshUtils.getColor(input);
                    material.materialColor.diffuse = MeshUtils.getColor(input);
                    material.materialColor.specular0 = MeshUtils.getColor(input);
                    material.materialColor.specular1 = MeshUtils.getColor(input);
                    material.materialColor.constant0 = MeshUtils.getColor(input);
                    material.materialColor.constant1 = MeshUtils.getColor(input);
                    material.materialColor.constant2 = MeshUtils.getColor(input);
                    material.materialColor.constant3 = MeshUtils.getColor(input);
                    material.materialColor.constant4 = MeshUtils.getColor(input);
                    material.materialColor.constant5 = MeshUtils.getColor(input);

                    /*
                     * Rasterization
                     */
                    material.rasterization.isPolygonOffsetEnabled = (input.ReadUInt32() & 1) > 0;
                    material.rasterization.cullMode = (RenderBase.OCullMode)input.ReadUInt32();
                    material.rasterization.polygonOffsetUnit = input.ReadSingle();
                    data.Seek(0xc, SeekOrigin.Current);

                    /*
                     * Fragment operation
                     */

                    //Depth operation
                    flags = input.ReadUInt32();
                    PICACommandReader depthCommands = new PICACommandReader(data, 4, true);
                    material.fragmentOperation.depth = depthCommands.getDepthTest();
                    material.fragmentOperation.depth.isTestEnabled = (flags & 1) > 0;
                    material.fragmentOperation.depth.isMaskEnabled = (flags & 2) > 0;

                    //Blend operation
                    RenderBase.OBlendMode blendMode = RenderBase.OBlendMode.notUsed;
                    switch (input.ReadUInt32())
                    {
                        case 0: blendMode = RenderBase.OBlendMode.notUsed; break;
                        case 1: blendMode = RenderBase.OBlendMode.blend; break;
                        case 2: blendMode = RenderBase.OBlendMode.blend; break; //Separate blend
                        case 3: blendMode = RenderBase.OBlendMode.logical; break;
                    }
                    Color blendColor = MeshUtils.getColorFloat(input);
                    PICACommandReader blendCommands = new PICACommandReader(data, 5, true);
                    material.fragmentOperation.blend = blendCommands.getBlendOperation();
                    material.fragmentOperation.blend.mode = blendMode;
                    material.fragmentOperation.blend.blendColor = blendColor;

                    //Stencil operation
                    input.ReadUInt32();
                    PICACommandReader stencilCommands = new PICACommandReader(data, 4, true);
                    material.fragmentOperation.stencil = stencilCommands.getStencilTest();

                    /*
                     * Texture coordinates
                     */
                    uint usedTextureCoordinates = input.ReadUInt32();

                    for (int i = 0; i < 3; i++)
                    {
                        RenderBase.OTextureCoordinator coordinator = new RenderBase.OTextureCoordinator();

                        uint sourceCoordinate = input.ReadUInt32();
                        coordinator.projection = (RenderBase.OTextureProjection)input.ReadUInt32();
                        coordinator.referenceCamera = input.ReadUInt32();
                        uint matrixMode = input.ReadUInt32();
                        coordinator.scaleU = input.ReadSingle();
                        coordinator.scaleV = input.ReadSingle();
                        coordinator.rotate = input.ReadSingle();
                        coordinator.translateU = input.ReadSingle();
                        coordinator.translateV = input.ReadSingle();
                        bool isEnabled = (input.ReadUInt32() & 1) > 0;
                        RenderBase.OMatrix transformMatrix = getMatrix(input);

                        material.textureCoordinator[i] = coordinator;
                    }

                    /*
                     * Texture mappers
                     */
                    uint[] mapperOffsets = new uint[4];
                    mapperOffsets[0] = getRelativeOffset(input);
                    mapperOffsets[1] = getRelativeOffset(input);
                    mapperOffsets[2] = getRelativeOffset(input);
                    mapperOffsets[3] = getRelativeOffset(input);
                    long position = data.Position;

                    for (int i = 0; i < 3; i++)
                    {
                        if (mapperOffsets[i] != 0)
                        {
                            data.Seek(mapperOffsets[i], SeekOrigin.Begin);

                            flags = input.ReadUInt32();
                            uint dynamicAllocator = input.ReadUInt32();
                            uint textureHeaderOffset = getRelativeOffset(input);
                            uint samplerOffset = getRelativeOffset(input);
                            PICACommandReader textureCommands = new PICACommandReader(data, 13, true);

                            switch (i)
                            {
                                case 0:
                                    material.textureMapper[i] = textureCommands.getTexUnit0Mapper();
                                    material.textureMapper[i].borderColor = textureCommands.getTexUnit0BorderColor();
                                    data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin);
                                    material.name0 = IOUtils.readString(input, getRelativeOffset(input));
                                    break;
                                case 1:
                                    material.textureMapper[i] = textureCommands.getTexUnit1Mapper();
                                    material.textureMapper[i].borderColor = textureCommands.getTexUnit1BorderColor();
                                    data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin);
                                    material.name1 = IOUtils.readString(input, getRelativeOffset(input));
                                    break;
                                case 2:
                                    material.textureMapper[i] = textureCommands.getTexUnit2Mapper();
                                    material.textureMapper[i].borderColor = textureCommands.getTexUnit2BorderColor();
                                    data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin);
                                    material.name2 = IOUtils.readString(input, getRelativeOffset(input));
                                    break;
                            }

                            data.Seek(samplerOffset, SeekOrigin.Begin);
                            Color borderColor = MeshUtils.getColorFloat(input); //Not needed, we already got from Commands buffer
                            material.textureMapper[i].LODBias = input.ReadSingle();
                        }
                    }

                    data.Seek(position, SeekOrigin.Begin);
                    uint shaderOffset = getRelativeOffset(input);
                    uint fragmentShaderOffset = getRelativeOffset(input);
                    uint shaderProgramDescriptionIndex = input.ReadUInt32();
                    uint shaderParametersCount = input.ReadUInt32();
                    uint shaderParametersPointerTableOffset = getRelativeOffset(input);
                    material.lightSetIndex = input.ReadUInt32();
                    material.fogIndex = input.ReadUInt32();

                    uint shadingParametersHash = input.ReadUInt32();
                    uint shaderParametersHash = input.ReadUInt32();
                    uint textureCoordinatorsHash = input.ReadUInt32();
                    uint textureSamplersHash = input.ReadUInt32();
                    uint textureMappersHash = input.ReadUInt32();
                    uint materialColorHash = input.ReadUInt32();
                    uint rasterizationHash = input.ReadUInt32();
                    uint fragmentLightingHash = input.ReadUInt32();
                    uint fragmentLightingTableHash = input.ReadUInt32();
                    uint fragmentLightingTableParametersHash = input.ReadUInt32();
                    uint textureCombinersHash = input.ReadUInt32();
                    uint alphaTestHash = input.ReadUInt32();
                    uint fragmentOperationHash = input.ReadUInt32();
                    uint materialId = input.ReadUInt32();

                    /*
                     * Shader
                     */
                    if (shaderOffset != 0)
                    {
                        data.Seek(shaderOffset, SeekOrigin.Begin);

                        flags = input.ReadUInt32();
                        string shdrMagic = IOUtils.readString(input, (uint)data.Position, 4);
                        revision = input.ReadUInt32();
                        string shaderName = IOUtils.readString(input, getRelativeOffset(input));
                        userDataEntries = input.ReadUInt32();
                        userDataOffset = getRelativeOffset(input);
                        string referenceShaderName = IOUtils.readString(input, getRelativeOffset(input));
                        input.ReadUInt32();

                        material.shaderReference = new RenderBase.OReference(shaderName, referenceShaderName);
                    }

                    /*
                     * Fragment shader
                     */
                    if (fragmentShaderOffset != 0)
                    {
                        data.Seek(fragmentShaderOffset, SeekOrigin.Begin);

                        material.fragmentShader.bufferColor = MeshUtils.getColorFloat(input);

                        flags = input.ReadUInt32();
                        material.fragmentShader.lighting.isClampHighLight = (flags & 1) > 0;
                        material.fragmentShader.lighting.isDistribution0Enabled = (flags & 2) > 0;
                        material.fragmentShader.lighting.isDistribution1Enabled = (flags & 4) > 0;
                        material.fragmentShader.lighting.isGeometryFactor0Enabled = (flags & 8) > 0;
                        material.fragmentShader.lighting.isGeometryFactor1Enabled = (flags & 0x10) > 0;
                        material.fragmentShader.lighting.isReflectionEnabled = (flags & 0x20) > 0;

                        material.fragmentShader.layerConfig = input.ReadUInt32();
                        material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)input.ReadUInt32();
                        material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)input.ReadUInt32();
                        material.fragmentShader.bump.mode = (RenderBase.OBumpMode)input.ReadUInt32();

                        flags = input.ReadUInt32();
                        material.fragmentShader.bump.isBumpRenormalize = (flags & 1) > 0;

                        uint fragmentLightingTableOffset = getRelativeOffset(input);
                        position = data.Position;

                        data.Seek(fragmentLightingTableOffset, SeekOrigin.Begin);
                        material.fragmentShader.lighting.reflectanceRSampler = getFragmentSampler(input, getRelativeOffset(input));
                        material.fragmentShader.lighting.reflectanceGSampler = getFragmentSampler(input, getRelativeOffset(input));
                        material.fragmentShader.lighting.reflectanceBSampler = getFragmentSampler(input, getRelativeOffset(input));
                        material.fragmentShader.lighting.distribution0Sampler = getFragmentSampler(input, getRelativeOffset(input));
                        material.fragmentShader.lighting.distribution1Sampler = getFragmentSampler(input, getRelativeOffset(input));
                        material.fragmentShader.lighting.fresnelSampler = getFragmentSampler(input, getRelativeOffset(input));

                        data.Seek(position, SeekOrigin.Begin);
                        input.ReadUInt32();

                        PICACommandReader combiner0Commands = new PICACommandReader(data, 6, true); input.ReadUInt32();
                        PICACommandReader combiner1Commands = new PICACommandReader(data, 6, true); input.ReadUInt32();
                        PICACommandReader combiner2Commands = new PICACommandReader(data, 6, true); input.ReadUInt32();
                        PICACommandReader combiner3Commands = new PICACommandReader(data, 6, true); input.ReadUInt32();
                        PICACommandReader combiner4Commands = new PICACommandReader(data, 6, true); input.ReadUInt32();
                        PICACommandReader combiner5Commands = new PICACommandReader(data, 6, true);

                        material.fragmentShader.textureCombiner[0] = combiner0Commands.getTevStage(0);
                        material.fragmentShader.textureCombiner[1] = combiner1Commands.getTevStage(1);
                        material.fragmentShader.textureCombiner[2] = combiner2Commands.getTevStage(2);
                        material.fragmentShader.textureCombiner[3] = combiner3Commands.getTevStage(3);
                        material.fragmentShader.textureCombiner[4] = combiner4Commands.getTevStage(4);
                        material.fragmentShader.textureCombiner[5] = combiner5Commands.getTevStage(5);

                        PICACommandReader alphaCommands = new PICACommandReader(data, 2, true);
                        material.fragmentShader.alphaTest = alphaCommands.getAlphaTest();
                    }

                    model.material.Add(material);
                }

                //Skeleton
                bool isSkeletonTranslateAnimationEnabled;
                if (cmdlHeader.hasSkeleton)
                {
                    data.Seek(cmdlHeader.skeletonOffset, SeekOrigin.Begin);

                    flags = input.ReadUInt32();
                    string skeletonMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); //SOBJ
                    revision = input.ReadUInt32();
                    string name = IOUtils.readString(input, getRelativeOffset(input));
                    input.ReadUInt32();
                    input.ReadUInt32();
                    List<dictEntry> skeletonDictionary = getDictionary(input);
                    uint rootBoneOffset = getRelativeOffset(input);
                    cgfxSkeletonScalingRule scalingRule = (cgfxSkeletonScalingRule)input.ReadUInt32();
                    isSkeletonTranslateAnimationEnabled = (input.ReadUInt32() & 1) > 0;

                    foreach (dictEntry boneEntry in skeletonDictionary)
                    {
                        data.Seek(boneEntry.dataOffset, SeekOrigin.Begin);

                        RenderBase.OBone bone = new RenderBase.OBone();

                        bone.name = IOUtils.readString(input, getRelativeOffset(input));
                        uint boneFlags = input.ReadUInt32();

                        bone.isSegmentScaleCompensate = (boneFlags & 0x20) > 0;

                        uint boneId = input.ReadUInt32();
                        bone.parentId = (short)input.ReadInt32();
                        int parentOffset = input.ReadInt32();
                        int childOffset = input.ReadInt32();
                        int previousSiblingOffset = input.ReadInt32();
                        int nextSiblingOffset = input.ReadInt32();
                        bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        bone.absoluteScale = new RenderBase.OVector3(bone.scale);
                        RenderBase.OMatrix localMatrix = getMatrix(input);
                        RenderBase.OMatrix worldMatrix = getMatrix(input);
                        RenderBase.OMatrix invBaseMatrix = getMatrix(input);
                        bone.billboardMode = (RenderBase.OBillboardMode)input.ReadInt32();

                        uint userDataEntries = input.ReadUInt32();
                        uint userDataOffset = getRelativeOffset(input);

                        model.skeleton.Add(bone);
                    }
                }

                List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>();
                for (int index = 0; index < model.skeleton.Count; index++)
                {
                    RenderBase.OMatrix transform = new RenderBase.OMatrix();
                    transformSkeleton(model.skeleton, index, ref transform);
                    skeletonTransform.Add(transform);
                }

                //Shapes
                List<cgfxShapeEntry> shapeHeader = new List<cgfxShapeEntry>();
                for (int i = 0; i < cmdlHeader.shapeEntries; i++)
                {
                    data.Seek(cmdlHeader.shapePointerTableOffset + (i * 4), SeekOrigin.Begin);
                    data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                    cgfxShapeEntry shape = new cgfxShapeEntry();

                    flags = input.ReadUInt32();
                    string sobjMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4);
                    revision = input.ReadUInt32();
                    shape.name = IOUtils.readString(input, getRelativeOffset(input));
                    shape.userDataEntries = input.ReadUInt32();
                    shape.userDataDictionaryOffset = getRelativeOffset(input);
                    flags = input.ReadUInt32();
                    shape.boundingBoxOffset = getRelativeOffset(input);
                    shape.positionOffset = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    shape.facesGroupEntries = input.ReadUInt32();
                    shape.facesGroupOffset = getRelativeOffset(input);
                    input.ReadUInt32();
                    shape.vertexGroupEntries = input.ReadUInt32();
                    shape.vertexGroupOffset = getRelativeOffset(input);
                    shape.blendShapeOffset = getRelativeOffset(input);

                    shapeHeader.Add(shape);
                }

                List<RenderBase.OMesh> shapes = new List<RenderBase.OMesh>();
                foreach (cgfxShapeEntry shapeEntry in shapeHeader)
                {
                    RenderBase.OMesh shape = new RenderBase.OMesh();

                    data.Seek(shapeEntry.vertexGroupOffset, SeekOrigin.Begin);
                    data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                    input.ReadUInt32(); //Useless name offset
                    input.ReadUInt32(); //Useless User Data entries
                    input.ReadUInt32(); //Useless User Data offset
                    uint bufferObject = input.ReadUInt32();
                    uint locationFlag = input.ReadUInt32();
                    uint vshAttributesBufferLength = input.ReadUInt32();
                    uint vshAttributesBufferOffset = getRelativeOffset(input);
                    uint locationAddress = input.ReadUInt32();
                    uint memoryArea = input.ReadUInt32();
                    uint vshAttributesBufferStride = input.ReadUInt32();
                    uint vshAttributesBufferComponentsEntries = input.ReadUInt32();
                    uint vshAttributesBufferComponentsOffset = getRelativeOffset(input);

                    List<attributeFormat> vshAttributeFormats = new List<attributeFormat>();
                    for (int i = 0; i < vshAttributesBufferComponentsEntries; i++)
                    {
                        data.Seek(vshAttributesBufferComponentsOffset + (i * 4), SeekOrigin.Begin);
                        data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                        attributeFormat format = new attributeFormat();

                        flags = input.ReadUInt32();
                        format.attribute = (PICACommand.vshAttribute)input.ReadUInt32();
                        format.isInterleaved = input.ReadUInt32() == 2;
                        bufferObject = input.ReadUInt32();
                        locationFlag = input.ReadUInt32();
                        uint attributesStreamLength = input.ReadUInt32();
                        uint attributesStreamOffset = getRelativeOffset(input);
                        locationAddress = input.ReadUInt32();
                        memoryArea = input.ReadUInt32();
                        format.type = (attributeFormatType)(input.ReadUInt32() & 0xf);
                        format.attributeLength = input.ReadUInt32();
                        format.scale = input.ReadSingle();
                        format.offset = input.ReadUInt32();

                        vshAttributeFormats.Add(format);
                    }

                    //Faces
                    for (int faceIndex = 0; faceIndex < shapeEntry.facesGroupEntries; faceIndex++)
                    {
                        data.Seek(shapeEntry.facesGroupOffset + (faceIndex * 4), SeekOrigin.Begin);
                        data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                        uint nodeListEntries = input.ReadUInt32();
                        uint nodeListOffset = getRelativeOffset(input);
                        RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none;
                        switch (input.ReadUInt32()) //Skinning Mode
                        {
                            case 0: skinningMode = RenderBase.OSkinningMode.none; break;
                            case 1: skinningMode = RenderBase.OSkinningMode.rigidSkinning; break;
                            case 2: skinningMode = RenderBase.OSkinningMode.smoothSkinning; break;
                        }
                        uint faceMainHeaderEntries = input.ReadUInt32();
                        uint faceMainHeaderOffset = getRelativeOffset(input);

                        //Bone nodes
                        List<uint> nodeList = new List<uint>();
                        data.Seek(nodeListOffset, SeekOrigin.Begin);
                        for (int i = 0; i < nodeListEntries; i++) nodeList.Add(input.ReadUInt32());

                        //Face-related stuff
                        data.Seek(faceMainHeaderOffset, SeekOrigin.Begin);
                        data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                        uint faceDescriptorEntries = input.ReadUInt32();
                        uint faceDescriptorOffset = getRelativeOffset(input);
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();

                        data.Seek(faceDescriptorOffset, SeekOrigin.Begin);
                        data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                        PICACommand.indexBufferFormat idxBufferFormat = (PICACommand.indexBufferFormat)((input.ReadUInt32() & 2) >> 1);
                        input.ReadUInt32();
                        uint idxBufferLength = input.ReadUInt32();
                        uint idxBufferOffset = getRelativeOffset(input);

                        for (int attribute = 0; attribute < vshAttributeFormats.Count; attribute++)
                        {
                            attributeFormat format = vshAttributeFormats[attribute];

                            switch (format.attribute)
                            {
                                case PICACommand.vshAttribute.normal: shape.hasNormal = true; break;
                                case PICACommand.vshAttribute.tangent: shape.hasTangent = true; break;
                                case PICACommand.vshAttribute.color: shape.hasColor = true; break;
                                case PICACommand.vshAttribute.textureCoordinate0: shape.texUVCount = Math.Max(shape.texUVCount, 1); break;
                                case PICACommand.vshAttribute.textureCoordinate1: shape.texUVCount = Math.Max(shape.texUVCount, 2); break;
                                case PICACommand.vshAttribute.textureCoordinate2: shape.texUVCount = Math.Max(shape.texUVCount, 3); break;
                            }
                        }

                        if (nodeList.Count > 0)
                        {
                            shape.hasNode = true;
                            shape.hasWeight = true;
                        }

                        data.Seek(idxBufferOffset, SeekOrigin.Begin);
                        for (int i = 0; i < idxBufferLength; i++)
                        {
                            ushort index = 0;

                            switch (idxBufferFormat)
                            {
                                case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); i++; break;
                                case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break;
                            }

                            long dataPosition = data.Position;
                            long vertexOffset = vshAttributesBufferOffset + (index * vshAttributesBufferStride);

                            RenderBase.OVertex vertex = new RenderBase.OVertex();
                            vertex.diffuseColor = 0xffffffff;

                            for (int attribute = 0; attribute < vshAttributeFormats.Count; attribute++)
                            {
                                attributeFormat format = vshAttributeFormats[attribute];
                                if (format.attribute == PICACommand.vshAttribute.boneWeight) format.type = attributeFormatType.unsignedByte;
                                data.Seek(vertexOffset + format.offset, SeekOrigin.Begin);
                                RenderBase.OVector4 vector =  getVector(input, format);

                                switch (format.attribute)
                                {
                                    case PICACommand.vshAttribute.position:
                                        float x = (vector.x * format.scale) + shapeEntry.positionOffset.x;
                                        float y = (vector.y * format.scale) + shapeEntry.positionOffset.y;
                                        float z = (vector.z * format.scale) + shapeEntry.positionOffset.z;
                                        vertex.position = new RenderBase.OVector3(x, y, z);
                                        break;
                                    case PICACommand.vshAttribute.normal:
                                        vertex.normal = new RenderBase.OVector3(vector.x * format.scale, vector.y * format.scale, vector.z * format.scale);
                                        break;
                                    case PICACommand.vshAttribute.tangent:
                                        vertex.tangent = new RenderBase.OVector3(vector.x * format.scale, vector.y * format.scale, vector.z * format.scale);
                                        break;
                                    case PICACommand.vshAttribute.color:
                                        uint r = MeshUtils.saturate((vector.x * format.scale) * 0xff);
                                        uint g = MeshUtils.saturate((vector.y * format.scale) * 0xff);
                                        uint b = MeshUtils.saturate((vector.z * format.scale) * 0xff);
                                        uint a = MeshUtils.saturate((vector.w * format.scale) * 0xff);
                                        vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate0:
                                        vertex.texture0 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate1:
                                        vertex.texture1 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate2:
                                        vertex.texture2 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale);
                                        break;
                                    case PICACommand.vshAttribute.boneIndex:
                                        int b0 = (int)vector.x;
                                        int b1 = (int)vector.y;
                                        int b2 = (int)vector.z;
                                        int b3 = (int)vector.w;

                                        if (b0 < nodeList.Count && format.attributeLength > 0) vertex.node.Add((int)nodeList[b0]);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (b1 < nodeList.Count && format.attributeLength > 1) vertex.node.Add((int)nodeList[b1]);
                                            if (b2 < nodeList.Count && format.attributeLength > 2) vertex.node.Add((int)nodeList[b2]);
                                            if (b3 < nodeList.Count && format.attributeLength > 3) vertex.node.Add((int)nodeList[b3]);
                                        }

                                        break;
                                    case PICACommand.vshAttribute.boneWeight:
                                        if (format.attributeLength > 0) vertex.weight.Add(vector.x * format.scale);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (format.attributeLength > 1) vertex.weight.Add(vector.y * format.scale);
                                            if (format.attributeLength > 2) vertex.weight.Add(vector.z * format.scale);
                                            if (format.attributeLength > 3) vertex.weight.Add(vector.w * format.scale);
                                        }
                                        break;
                                }
                            }

                            //If the node list have 4 or less bones, then there is no need to store the indices per vertex
                            //Instead, the entire list is used, since it supports up to 4 bones.
                            if (vertex.node.Count == 0 && nodeList.Count <= 4)
                            {
                                for (int n = 0; n < nodeList.Count; n++) vertex.node.Add((int)nodeList[n]);
                                if (vertex.weight.Count == 0) vertex.weight.Add(1);
                            }

                            if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0)
                            {
                                //Note: Rigid skinning can have only one bone per vertex
                                //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton
                                if (vertex.node[0] < skeletonTransform.Count)
                                {
                                    if (vertex.weight.Count == 0) vertex.weight.Add(1);
                                    vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]);
                                }
                            }

                            MeshUtils.calculateBounds(model, vertex);
                            shape.vertices.Add(vertex);

                            data.Seek(dataPosition, SeekOrigin.Begin);
                        }
                    }

                    shapes.Add(shape);
                }

                //Objects
                List<cgfxObjectEntry> objectHeader = new List<cgfxObjectEntry>();
                for (int i = 0; i < cmdlHeader.objectEntries; i++)
                {
                    data.Seek(cmdlHeader.objectPointerTableOffset + (i * 4), SeekOrigin.Begin);
                    data.Seek(getRelativeOffset(input), SeekOrigin.Begin);

                    cgfxObjectEntry obj = new cgfxObjectEntry();

                    flags = input.ReadUInt32();
                    string msobMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4);
                    revision = input.ReadUInt32();
                    obj.name = IOUtils.readString(input, getRelativeOffset(input));
                    obj.userDataEntries = input.ReadUInt32();
                    obj.userDataDictionaryOffset = getRelativeOffset(input);
                    obj.shapeIndex = input.ReadUInt32();
                    obj.materialId = input.ReadUInt32();
                    obj.ownerModelOffset = getRelativeOffset(input);
                    obj.isVisible = (input.ReadByte() & 1) > 0;
                    obj.renderPriority = input.ReadByte();
                    obj.objectNodeVisibilityIndex = input.ReadUInt16();
                    obj.currentPrimitiveIndex = input.ReadUInt16();
                    //Theres still a bunch of stuff after this, but isn't really needed

                    objectHeader.Add(obj);
                }

                List<objectNode> objectNodeList = new List<objectNode>();
                foreach (dictEntry objectNodeEntry in cmdlHeader.objectNodes)
                {
                    objectNode node = new objectNode();

                    data.Seek(objectNodeEntry.dataOffset, SeekOrigin.Begin);
                    node.name = IOUtils.readString(input, getRelativeOffset(input));
                    node.isVisible = input.ReadUInt32() == 1;

                    objectNodeList.Add(node);
                }

                foreach (cgfxObjectEntry obj in objectHeader)
                {
                    RenderBase.OMesh modelObject = shapes[(int)obj.shapeIndex];

                    if (objectNodeList.Count > 0)
                    {
                        modelObject.name = objectNodeList[obj.objectNodeVisibilityIndex].name ;
                        modelObject.isVisible = objectNodeList[obj.objectNodeVisibilityIndex].isVisible;
                    }
                    modelObject.materialId = (ushort)obj.materialId;
                    modelObject.renderPriority = obj.renderPriority;

                    model.mesh.Add(modelObject);
                }

                models.model.Add(model);
            }

            data.Close();

            return models;
        }
Exemplo n.º 10
0
        /// <summary>
        ///     Loads a BCH file.
        ///     Note that BCH must start at offset 0x0 (don't try using it for BCHs inside containers).
        /// </summary>
        /// <param name="data">Memory Stream of the BCH file. The Stream will not be usable after</param>
        /// <returns></returns>
        public static RenderBase.OModelGroup load(MemoryStream data)
        {
            BinaryReader input = new BinaryReader(data);
            BinaryWriter writer = new BinaryWriter(data);

            RenderBase.OModelGroup models = new RenderBase.OModelGroup();

            //Primary header
            bchHeader header = new bchHeader();
            header.magic = IOUtils.readString(input, 0);
            data.Seek(4, SeekOrigin.Current);
            header.backwardCompatibility = input.ReadByte();
            header.forwardCompatibility = input.ReadByte();
            header.version = input.ReadUInt16();

            header.mainHeaderOffset = input.ReadUInt32();
            header.stringTableOffset = input.ReadUInt32();
            header.gpuCommandsOffset = input.ReadUInt32();
            header.dataOffset = input.ReadUInt32();
            if (header.backwardCompatibility > 0x20) header.dataExtendedOffset = input.ReadUInt32();
            header.relocationTableOffset = input.ReadUInt32();

            header.mainHeaderLength = input.ReadUInt32();
            header.stringTableLength = input.ReadUInt32();
            header.gpuCommandsLength = input.ReadUInt32();
            header.dataLength = input.ReadUInt32();
            if (header.backwardCompatibility > 0x20) header.dataExtendedLength = input.ReadUInt32();
            header.relocationTableLength = input.ReadUInt32();

            header.uninitializedDataSectionLength = input.ReadUInt32();
            header.uninitializedDescriptionSectionLength = input.ReadUInt32();

            if (header.backwardCompatibility > 7)
            {
                header.flags = input.ReadUInt16();
                header.addressCount = input.ReadUInt16();
            }

            //Transform relative offsets to absolute offsets, also add extra bits if necessary.
            //The game does this on RAM after the BCH is loaded, so offsets to data is absolute and points to VRAM.
            for (uint o = header.relocationTableOffset; o < header.relocationTableOffset + header.relocationTableLength; o += 4)
            {
                data.Seek(o, SeekOrigin.Begin);
                uint value = input.ReadUInt32();
                uint offset = value & 0x1ffffff;
                byte flags = (byte)(value >> 25);

                switch (flags)
                {
                    case 0:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.mainHeaderOffset);
                        break;

                    case 1:
                        data.Seek(offset + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.stringTableOffset);
                        break;

                    case 2:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.gpuCommandsOffset);
                        break;

                    case 7:
                    case 0xc:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.dataOffset);
                        break;
                }

                //The moron that designed the format used different flags on different versions, instead of keeping compatibility.
                data.Seek((offset * 4) + header.gpuCommandsOffset, SeekOrigin.Begin);
                if (header.backwardCompatibility < 6)
                {
                    switch (flags)
                    {
                        case 0x23: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x26: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 8)
                {
                    switch (flags)
                    {
                        case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 0x21)
                {
                    switch (flags)
                    {
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else
                {
                    switch (flags)
                    {
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset
                        case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset
                        case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset
                        case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset
                        case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset
                        case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset
                    }
                }
            }

            //Content header
            data.Seek(header.mainHeaderOffset, SeekOrigin.Begin);
            bchContentHeader contentHeader = new bchContentHeader
            {
                modelsPointerTableOffset = input.ReadUInt32(),
                modelsPointerTableEntries = input.ReadUInt32(),
                modelsNameOffset = input.ReadUInt32(),
                materialsPointerTableOffset = input.ReadUInt32(),
                materialsPointerTableEntries = input.ReadUInt32(),
                materialsNameOffset = input.ReadUInt32(),
                shadersPointerTableOffset = input.ReadUInt32(),
                shadersPointerTableEntries = input.ReadUInt32(),
                shadersNameOffset = input.ReadUInt32(),
                texturesPointerTableOffset = input.ReadUInt32(),
                texturesPointerTableEntries = input.ReadUInt32(),
                texturesNameOffset = input.ReadUInt32(),
                materialsLUTPointerTableOffset = input.ReadUInt32(),
                materialsLUTPointerTableEntries = input.ReadUInt32(),
                materialsLUTNameOffset = input.ReadUInt32(),
                lightsPointerTableOffset = input.ReadUInt32(),
                lightsPointerTableEntries = input.ReadUInt32(),
                lightsNameOffset = input.ReadUInt32(),
                camerasPointerTableOffset = input.ReadUInt32(),
                camerasPointerTableEntries = input.ReadUInt32(),
                camerasNameOffset = input.ReadUInt32(),
                fogsPointerTableOffset = input.ReadUInt32(),
                fogsPointerTableEntries = input.ReadUInt32(),
                fogsNameOffset = input.ReadUInt32(),
                skeletalAnimationsPointerTableOffset = input.ReadUInt32(),
                skeletalAnimationsPointerTableEntries = input.ReadUInt32(),
                skeletalAnimationsNameOffset = input.ReadUInt32(),
                materialAnimationsPointerTableOffset = input.ReadUInt32(),
                materialAnimationsPointerTableEntries = input.ReadUInt32(),
                materialAnimationsNameOffset = input.ReadUInt32(),
                visibilityAnimationsPointerTableOffset = input.ReadUInt32(),
                visibilityAnimationsPointerTableEntries = input.ReadUInt32(),
                visibilityAnimationsNameOffset = input.ReadUInt32(),
                lightAnimationsPointerTableOffset = input.ReadUInt32(),
                lightAnimationsPointerTableEntries = input.ReadUInt32(),
                lightAnimationsNameOffset = input.ReadUInt32(),
                cameraAnimationsPointerTableOffset = input.ReadUInt32(),
                cameraAnimationsPointerTableEntries = input.ReadUInt32(),
                cameraAnimationsNameOffset = input.ReadUInt32(),
                fogAnimationsPointerTableOffset = input.ReadUInt32(),
                fogAnimationsPointerTableEntries = input.ReadUInt32(),
                fogAnimationsNameOffset = input.ReadUInt32(),
                scenePointerTableOffset = input.ReadUInt32(),
                scenePointerTableEntries = input.ReadUInt32(),
                sceneNameOffset = input.ReadUInt32()
            };
            //Note: NameOffset are PATRICIA trees

            //Shaders
            for (int index = 0; index < contentHeader.shadersPointerTableEntries; index++)
            {
                data.Seek(contentHeader.shadersPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                uint shaderDataOffset = input.ReadUInt32();
                uint shaderDataLength = input.ReadUInt32();
            }

            //Textures
            for (int index = 0; index < contentHeader.texturesPointerTableEntries; index++)
            {
                data.Seek(contentHeader.texturesPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                uint textureCommandsOffset = input.ReadUInt32();
                uint textureCommandsWordCount = input.ReadUInt32();
                data.Seek(0x14, SeekOrigin.Current);
                string textureName = readString(input);

                data.Seek(textureCommandsOffset, SeekOrigin.Begin);
                PICACommandReader textureCommands = new PICACommandReader(data, textureCommandsWordCount);

                //Note: It have textures for the 3 texture units.
                //The other texture units are used with textureCoordinate1 and 2.
                data.Seek(textureCommands.getTexUnit0Address(), SeekOrigin.Begin);
                Size textureSize = textureCommands.getTexUnit0Size();
                byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4];
                input.Read(buffer, 0, buffer.Length);
                Bitmap texture = TextureCodec.decode(
                    buffer,
                    textureSize.Width,
                    textureSize.Height,
                    textureCommands.getTexUnit0Format());

                models.texture.Add(new RenderBase.OTexture(texture, textureName));
            }

            //LookUp Tables
            for (int index = 0; index < contentHeader.materialsLUTPointerTableEntries; index++)
            {
                data.Seek(contentHeader.materialsLUTPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                input.ReadUInt32();
                uint samplersCount = input.ReadUInt32();
                string name = readString(input);

                RenderBase.OLookUpTable table = new RenderBase.OLookUpTable();
                table.name = name;
                for (int i = 0; i < samplersCount; i++)
                {
                    RenderBase.OLookUpTableSampler sampler = new RenderBase.OLookUpTableSampler();

                    input.ReadUInt32();
                    uint tableOffset = input.ReadUInt32();
                    uint tableWordCount = input.ReadUInt32();
                    sampler.name = readString(input);

                    long dataPosition = data.Position;
                    data.Seek(tableOffset, SeekOrigin.Begin);
                    PICACommandReader lutCommands = new PICACommandReader(data, tableWordCount);
                    sampler.table = lutCommands.getFSHLookUpTable();
                    table.sampler.Add(sampler);

                    data.Seek(dataPosition, SeekOrigin.Begin);
                }

                models.lookUpTable.Add(table);
            }

            //Lights
            for (int index = 0; index < contentHeader.lightsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.lightsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OLight light = new RenderBase.OLight();
                light.name = readString(input);
                light.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                light.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                light.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint lightFlags = input.ReadUInt32();
                switch (lightFlags & 0xf)
                {
                    case 1: light.lightUse = RenderBase.OLightUse.hemiSphere; break;
                    case 2: light.lightUse = RenderBase.OLightUse.ambient; break;
                    case 5:
                    case 6:
                    case 7:
                        light.lightUse = RenderBase.OLightUse.vertex;
                        switch (lightFlags & 0xf)
                        {
                            case 5: light.lightType = RenderBase.OLightType.directional; break;
                            case 6: light.lightType = RenderBase.OLightType.point; break;
                            case 7: light.lightType = RenderBase.OLightType.spot; break;
                        }
                        break;
                    case 9:
                    case 0xa:
                    case 0xb:
                        light.lightUse = RenderBase.OLightUse.fragment;
                        switch (lightFlags & 0xf)
                        {
                            case 9: light.lightType = RenderBase.OLightType.directional; break;
                            case 0xa: light.lightType = RenderBase.OLightType.point; break;
                            case 0xb: light.lightType = RenderBase.OLightType.spot; break;
                        }
                        break;
                    default: Debug.WriteLine(string.Format("BCH: Warning - Unknow Light Flags {0}", lightFlags.ToString("X8"))); break;
                }
                light.isLightEnabled = (lightFlags & 0x100) > 0;
                light.isTwoSideDiffuse = (lightFlags & 0x10000) > 0;
                light.isDistanceAttenuationEnabled = (lightFlags & 0x20000) > 0;
                light.angleSampler.input = (RenderBase.OFragmentSamplerInput)((lightFlags >> 24) & 0xf);
                light.angleSampler.scale = (RenderBase.OFragmentSamplerScale)((lightFlags >> 28) & 0xf);

                input.ReadUInt32();
                switch (light.lightUse)
                {
                    case RenderBase.OLightUse.hemiSphere:
                        light.groundColor = MeshUtils.getColorFloat(input);
                        light.skyColor = MeshUtils.getColorFloat(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        light.lerpFactor = input.ReadSingle();
                        break;
                    case RenderBase.OLightUse.ambient: light.ambient = MeshUtils.getColor(input); break;
                    case RenderBase.OLightUse.vertex:
                        light.ambient = MeshUtils.getColorFloat(input);
                        light.diffuse = MeshUtils.getColorFloat(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        light.distanceAttenuationConstant = input.ReadSingle();
                        light.distanceAttenuationLinear = input.ReadSingle();
                        light.distanceAttenuationQuadratic = input.ReadSingle();
                        light.spotExponent = input.ReadSingle();
                        light.spotCutoffAngle = input.ReadSingle();
                        break;
                    case RenderBase.OLightUse.fragment:
                        light.ambient = MeshUtils.getColor(input);
                        light.diffuse = MeshUtils.getColor(input);
                        light.specular0 = MeshUtils.getColor(input);
                        light.specular1 = MeshUtils.getColor(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        input.ReadUInt32();
                        input.ReadUInt32();
                        light.attenuationStart = input.ReadSingle();
                        light.attenuationEnd = input.ReadSingle();

                        light.distanceSampler.tableName = readString(input);
                        light.distanceSampler.samplerName = readString(input);

                        light.angleSampler.tableName = readString(input);
                        light.angleSampler.samplerName = readString(input);
                        break;
                }

                models.light.Add(light);
            }

            //Cameras
            for (int index = 0; index < contentHeader.camerasPointerTableEntries; index++)
            {
                data.Seek(contentHeader.camerasPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OCamera camera = new RenderBase.OCamera();
                camera.name = readString(input);
                camera.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                camera.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                camera.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint cameraFlags = input.ReadUInt32();
                camera.isInheritingTargetRotate = (cameraFlags & 0x10000) > 0;
                camera.isInheritingTargetTranslate = (cameraFlags & 0x20000) > 0;
                camera.isInheritingUpRotate = (cameraFlags & 0x40000) > 0;
                camera.view = (RenderBase.OCameraView)(cameraFlags & 0xf);
                camera.projection = (RenderBase.OCameraProjection)((cameraFlags >> 8) & 0xf);

                input.ReadSingle();
                uint viewOffset = input.ReadUInt32();
                uint projectionOffset = input.ReadUInt32();

                data.Seek(viewOffset, SeekOrigin.Begin);
                camera.target = new RenderBase.OVector3();
                camera.rotation = new RenderBase.OVector3();
                camera.upVector = new RenderBase.OVector3();
                RenderBase.OVector3 target = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                switch (camera.view)
                {
                    case RenderBase.OCameraView.aimTarget: camera.target = target; camera.twist = input.ReadSingle(); break;
                    case RenderBase.OCameraView.lookAtTarget: camera.target = target; camera.upVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); break;
                    case RenderBase.OCameraView.rotate: camera.rotation = target; break;
                }

                data.Seek(projectionOffset, SeekOrigin.Begin);
                camera.zNear = input.ReadSingle();
                camera.zFar = input.ReadSingle();
                camera.aspectRatio = input.ReadSingle();
                switch (camera.projection)
                {
                    case RenderBase.OCameraProjection.perspective: camera.fieldOfViewY = input.ReadSingle(); break;
                    case RenderBase.OCameraProjection.orthogonal: camera.height = input.ReadSingle(); break;
                }

                models.camera.Add(camera);
            }

            //Fogs
            for (int index = 0; index < contentHeader.fogsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.fogsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OFog fog = new RenderBase.OFog();
                fog.name = readString(input);
                fog.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                fog.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                fog.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint fogFlags = input.ReadUInt32();
                fog.fogUpdater = (RenderBase.OFogUpdater)(fogFlags & 0xf);
                fog.isZFlip = (fogFlags & 0x100) > 0;
                fog.isAttenuateDistance = (fogFlags & 0x200) > 0;

                fog.fogColor = MeshUtils.getColor(input);

                fog.minFogDepth = input.ReadSingle();
                fog.maxFogDepth = input.ReadSingle();
                fog.fogDensity = input.ReadSingle();

                models.fog.Add(fog);
            }

            //Skeletal Animations
            for (int index = 0; index < contentHeader.skeletalAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.skeletalAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation();

                skeletalAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                skeletalAnimation.frameSize = input.ReadSingle();
                uint boneTableOffset = input.ReadUInt32();
                uint boneTableEntries = input.ReadUInt32();
                uint metaDataPointerOffset = input.ReadUInt32();

                if (metaDataPointerOffset != 0)
                {
                    data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                    skeletalAnimation.userData = getMetaData(input);
                }

                for (int i = 0; i < boneTableEntries; i++)
                {
                    data.Seek(boneTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone();

                    data.Seek(offset, SeekOrigin.Begin);
                    bone.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();
                    input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform:
                            data.Seek(offset + 0x18, SeekOrigin.Begin);

                            uint notExistMask = 0x80000;
                            uint constantMask = 0x200;

                            for (int j = 0; j < 2; j++)
                            {
                                for (int axis = 0; axis < 3; axis++)
                                {
                                    bool notExist = (flags & notExistMask) > 0;
                                    bool constant = (flags & constantMask) > 0;

                                    RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
                                    frame.exists = !notExist;
                                    if (frame.exists)
                                    {
                                        if (constant)
                                        {
                                            frame.interpolation = RenderBase.OInterpolationMode.linear;
                                            frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
                                        }
                                        else
                                        {
                                            uint frameOffset = input.ReadUInt32();
                                            long position = data.Position;
                                            data.Seek(frameOffset, SeekOrigin.Begin);
                                            getAnimationKeyFrame(input, frame);
                                            data.Seek(position, SeekOrigin.Begin);
                                        }
                                    }
                                    else
                                        data.Seek(4, SeekOrigin.Current);

                                    if (j == 0)
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.rotationX = frame; break;
                                            case 1: bone.rotationY = frame; break;
                                            case 2: bone.rotationZ = frame; break;
                                        }
                                    }
                                    else
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.translationX = frame; break;
                                            case 1: bone.translationY = frame; break;
                                            case 2: bone.translationZ = frame; break;
                                        }
                                    }

                                    notExistMask <<= 1;
                                    constantMask <<= 1;
                                }

                                constantMask <<= 1;
                            }

                            break;
                        case RenderBase.OSegmentType.transformQuaternion:
                            bone.isFrameFormat = true;

                            long originalPos = data.Position;
                            uint rotationOffset = input.ReadUInt32();
                            uint translationOffset = input.ReadUInt32();

                            if ((flags & 0x10) == 0)
                            {
                                bone.rotationQuaternion.exists = true;
                                data.Seek(rotationOffset, SeekOrigin.Begin);

                                if ((flags & 2) > 0)
                                {
                                    bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle()));
                                }
                                else
                                {
                                    bone.rotationQuaternion.startFrame = input.ReadSingle();
                                    bone.rotationQuaternion.endFrame = input.ReadSingle();

                                    uint rotationFlags = input.ReadUInt32();
                                    uint rotationDataOffset = input.ReadUInt32();
                                    uint rotationEntries = input.ReadUInt32();

                                    data.Seek(rotationDataOffset, SeekOrigin.Begin);
                                    for (int j = 0; j < rotationEntries; j++)
                                    {
                                        bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle()));
                                    }
                                }
                            }

                            if ((flags & 8) == 0)
                            {
                                bone.translation.exists = true;
                                data.Seek(translationOffset, SeekOrigin.Begin);

                                if ((flags & 1) > 0)
                                {
                                    bone.translation.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        0));
                                }
                                else
                                {
                                    bone.translation.startFrame = input.ReadSingle();
                                    bone.translation.endFrame = input.ReadSingle();

                                    uint translationFlags = input.ReadUInt32();
                                    uint translationDataOffset = input.ReadUInt32();
                                    uint translationEntries = input.ReadUInt32();

                                    data.Seek(translationDataOffset, SeekOrigin.Begin);
                                    for (int j = 0; j < translationEntries; j++)
                                    {
                                        bone.translation.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            0));
                                    }
                                }
                            }

                            break;
                        case RenderBase.OSegmentType.transformMatrix:
                            bone.isFullBakedFormat = true;

                            input.ReadUInt32();
                            uint matrixOffset = input.ReadUInt32();
                            uint entries = input.ReadUInt32();

                            data.Seek(matrixOffset, SeekOrigin.Begin);
                            for (int j = 0; j < entries; j++)
                            {
                                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                                transform.M11 = input.ReadSingle();
                                transform.M21 = input.ReadSingle();
                                transform.M31 = input.ReadSingle();
                                transform.M41 = input.ReadSingle();

                                transform.M12 = input.ReadSingle();
                                transform.M22 = input.ReadSingle();
                                transform.M32 = input.ReadSingle();
                                transform.M42 = input.ReadSingle();

                                transform.M13 = input.ReadSingle();
                                transform.M23 = input.ReadSingle();
                                transform.M33 = input.ReadSingle();
                                transform.M43 = input.ReadSingle();

                                bone.transform.Add(transform);
                            }

                            break;
                        default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name));
                    }

                    skeletalAnimation.bone.Add(bone);
                }

                models.skeletalAnimation.list.Add(skeletalAnimation);
            }

            //Material Animations
            for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation();

                materialAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                materialAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint textureNameTableOffset = input.ReadUInt32();
                uint textureNameTableEntries = input.ReadUInt32();

                data.Seek(textureNameTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < textureNameTableEntries; i++)
                {
                    string name = readString(input);
                    materialAnimation.textureName.Add(name);
                }

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break;
                        case RenderBase.OSegmentType.vector2: segmentCount = 2; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                        case RenderBase.OSegmentType.integer: segmentCount = 1; break;
                    }

                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = (flags & (0x100 << j)) == 0;
                        bool constant = (flags & (1 << j)) > 0;

                        if (frame.exists)
                        {
                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    materialAnimation.data.Add(animationData);
                }

                models.materialAnimation.list.Add(materialAnimation);
            }

            //Visibility Animations
            for (int index = 0; index < contentHeader.visibilityAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.visibilityAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OVisibilityAnimation visibilityAnimation = new RenderBase.OVisibilityAnimation();

                visibilityAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                visibilityAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                visibilityAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OVisibilityAnimationData animationData = new RenderBase.OVisibilityAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    if (segmentType == RenderBase.OSegmentType.boolean)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
                        if (segmentType == RenderBase.OSegmentType.boolean) frame = getAnimationKeyFrameBool(input);
                        animationData.visibilityList = frame;
                    }

                    visibilityAnimation.data.Add(animationData);
                }

                models.visibilityAnimation.list.Add(visibilityAnimation);
            }

            //Light Animations
            for (int index = 0; index < contentHeader.lightAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.lightAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OLightAnimation lightAnimation = new RenderBase.OLightAnimation();

                lightAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                lightAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                lightAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint typeFlags = input.ReadUInt32();
                lightAnimation.lightType = (RenderBase.OLightType)((typeFlags & 3) - 1);
                lightAnimation.lightUse = (RenderBase.OLightUse)((typeFlags >> 2) & 3);

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OLightAnimationData animationData = new RenderBase.OLightAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OLightAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform: segmentCount = 9; break;
                        case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break;
                        case RenderBase.OSegmentType.vector3: segmentCount = 3; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                        case RenderBase.OSegmentType.boolean: segmentCount = 1; break;
                    }

                    uint constantMask = 0x40;
                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0;

                        if (frame.exists)
                        {
                            if (segmentType == RenderBase.OSegmentType.boolean)
                            {
                                frame = getAnimationKeyFrameBool(input);
                            }
                            else
                            {
                                bool constant;
                                if (segmentType == RenderBase.OSegmentType.transform)
                                {
                                    constant = (flags & constantMask) > 0;
                                    if (j == 5)
                                        constantMask <<= 2;
                                    else
                                        constantMask <<= 1;
                                }
                                else
                                    constant = (flags & (1 << j)) > 0;

                                if (constant)
                                {
                                    frame.interpolation = RenderBase.OInterpolationMode.linear;
                                    frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                                }
                                else
                                {
                                    uint frameOffset = input.ReadUInt32();
                                    data.Seek(frameOffset, SeekOrigin.Begin);
                                    getAnimationKeyFrame(input, frame);
                                }
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    lightAnimation.data.Add(animationData);
                }

                models.lightAnimation.list.Add(lightAnimation);
            }

            //Camera Animations
            for (int index = 0; index < contentHeader.cameraAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.cameraAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OCameraAnimation cameraAnimation = new RenderBase.OCameraAnimation();

                cameraAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                cameraAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                cameraAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint modeFlags = input.ReadUInt32();
                cameraAnimation.viewMode = (RenderBase.OCameraView)(modeFlags & 0xf);
                cameraAnimation.projectionMode = (RenderBase.OCameraProjection)((modeFlags >> 8) & 0xf);

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OCameraAnimationData animationData = new RenderBase.OCameraAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OCameraAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform: segmentCount = 9; break;
                        case RenderBase.OSegmentType.vector3: segmentCount = 3; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                    }

                    uint constantMask = 0x40;
                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0;
                        bool constant;
                        if (segmentType == RenderBase.OSegmentType.transform)
                        {
                            constant = (flags & constantMask) > 0;
                            if (j == 5)
                                constantMask <<= 2;
                            else
                                constantMask <<= 1;
                        }
                        else
                            constant = (flags & (1 << j)) > 0;

                        if (frame.exists)
                        {
                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    cameraAnimation.data.Add(animationData);
                }

                models.cameraAnimation.list.Add(cameraAnimation);
            }

            //Fog Animations
            for (int index = 0; index < contentHeader.fogAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.fogAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OFogAnimation fogAnimation = new RenderBase.OFogAnimation();

                fogAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                fogAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                fogAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OFogAnimationData animationData = new RenderBase.OFogAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    int segmentCount = segmentType == RenderBase.OSegmentType.rgbaColor ? 4 : 0;

                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> 8) & (1 << j)) == 0;

                        if (frame.exists)
                        {
                            bool constant = (flags & (1 << j)) > 0;

                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.colorList.Add(frame);
                    }

                    fogAnimation.data.Add(animationData);
                }

                models.fogAnimation.list.Add(fogAnimation);
            }

            //Scene Environment
            for (int index = 0; index < contentHeader.scenePointerTableEntries; index++)
            {
                data.Seek(contentHeader.scenePointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OScene scene = new RenderBase.OScene();
                scene.name = readString(input);

                uint cameraReferenceOffset = input.ReadUInt32();
                uint cameraReferenceEntries = input.ReadUInt32();
                uint lightReferenceOffset = input.ReadUInt32();
                uint lightReferenceEntries = input.ReadUInt32();
                uint fogReferenceOffset = input.ReadUInt32();
                uint fogReferenceEntries = input.ReadUInt32();

                data.Seek(cameraReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < cameraReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.cameras.Add(reference);
                }

                data.Seek(lightReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < lightReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.lights.Add(reference);
                }

                data.Seek(fogReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < fogReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.fogs.Add(reference);
                }
            }

            //Models
            for (int modelIndex = 0; modelIndex < contentHeader.modelsPointerTableEntries; modelIndex++)
            {
                RenderBase.OModel model = new RenderBase.OModel();

                data.Seek(contentHeader.modelsPointerTableOffset + (modelIndex * 4), SeekOrigin.Begin);
                uint objectsHeaderOffset = input.ReadUInt32();

                //Objects header
                data.Seek(objectsHeaderOffset, SeekOrigin.Begin);
                bchModelHeader modelHeader;
                modelHeader.flags = input.ReadByte();
                modelHeader.skeletonScalingType = input.ReadByte();
                modelHeader.silhouetteMaterialEntries = input.ReadUInt16();

                modelHeader.worldTransform = new RenderBase.OMatrix();
                modelHeader.worldTransform.M11 = input.ReadSingle();
                modelHeader.worldTransform.M21 = input.ReadSingle();
                modelHeader.worldTransform.M31 = input.ReadSingle();
                modelHeader.worldTransform.M41 = input.ReadSingle();

                modelHeader.worldTransform.M12 = input.ReadSingle();
                modelHeader.worldTransform.M22 = input.ReadSingle();
                modelHeader.worldTransform.M32 = input.ReadSingle();
                modelHeader.worldTransform.M42 = input.ReadSingle();

                modelHeader.worldTransform.M13 = input.ReadSingle();
                modelHeader.worldTransform.M23 = input.ReadSingle();
                modelHeader.worldTransform.M33 = input.ReadSingle();
                modelHeader.worldTransform.M43 = input.ReadSingle();

                modelHeader.materialsTableOffset = input.ReadUInt32();
                modelHeader.materialsTableEntries = input.ReadUInt32();
                modelHeader.materialsNameOffset = input.ReadUInt32();
                modelHeader.verticesTableOffset = input.ReadUInt32();
                modelHeader.verticesTableEntries = input.ReadUInt32();
                data.Seek(header.backwardCompatibility > 6 ? 0x28 : 0x20, SeekOrigin.Current);
                modelHeader.skeletonOffset = input.ReadUInt32();
                modelHeader.skeletonEntries = input.ReadUInt32();
                modelHeader.skeletonNameOffset = input.ReadUInt32();
                modelHeader.objectsNodeVisibilityOffset = input.ReadUInt32();
                modelHeader.objectsNodeCount = input.ReadUInt32();
                modelHeader.modelName = readString(input);
                modelHeader.objectsNodeNameEntries = input.ReadUInt32();
                modelHeader.objectsNodeNameOffset = input.ReadUInt32();
                input.ReadUInt32(); //0x0
                modelHeader.metaDataPointerOffset = input.ReadUInt32();

                model.transform = modelHeader.worldTransform;
                model.name = modelHeader.modelName;

                string[] objectName = new string[modelHeader.objectsNodeNameEntries];
                data.Seek(modelHeader.objectsNodeNameOffset, SeekOrigin.Begin);
                int rootReferenceBit = input.ReadInt32(); //Radix tree
                uint rootLeftNode = input.ReadUInt16();
                uint rootRightNode = input.ReadUInt16();
                uint rootNameOffset = input.ReadUInt32();
                for (int i = 0; i < modelHeader.objectsNodeNameEntries; i++)
                {
                    int referenceBit = input.ReadInt32();
                    ushort leftNode = input.ReadUInt16();
                    ushort rightNode = input.ReadUInt16();
                    objectName[i] = readString(input);
                }

                //Materials
                for (int index = 0; index < modelHeader.materialsTableEntries; index++)
                {
                    //Nota: As versões mais antigas tinham o Coordinator já no header do material.
                    //As versões mais recentes tem uma seção reservada só pra ele, por isso possuem tamanho do header menor.
                    if (header.backwardCompatibility < 0x21)
                        data.Seek(modelHeader.materialsTableOffset + (index * 0x58), SeekOrigin.Begin);
                    else
                        data.Seek(modelHeader.materialsTableOffset + (index * 0x2c), SeekOrigin.Begin);

                    RenderBase.OMaterial material = new RenderBase.OMaterial();

                    uint materialParametersOffset = input.ReadUInt32();
                    input.ReadUInt32(); //TODO
                    input.ReadUInt32();
                    input.ReadUInt32();
                    uint textureCommandsOffset = input.ReadUInt32();
                    uint textureCommandsWordCount = input.ReadUInt32();

                    uint materialMapperOffset = 0;
                    if (header.backwardCompatibility < 0x21)
                    {
                        materialMapperOffset = (uint)data.Position;
                        data.Seek(0x30, SeekOrigin.Current);
                    }
                    else
                        materialMapperOffset = input.ReadUInt32();

                    material.name0 = readString(input);
                    material.name1 = readString(input);
                    material.name2 = readString(input);
                    material.name = readString(input);

                    //Parameters
                    //Same pointer of Materials section. Why?
                    if (materialParametersOffset != 0)
                    {
                        data.Seek(materialParametersOffset, SeekOrigin.Begin);
                        uint hash = input.ReadUInt32();

                        ushort materialFlags = input.ReadUInt16();
                        material.isFragmentLightEnabled = (materialFlags & 1) > 0;
                        material.isVertexLightEnabled = (materialFlags & 2) > 0;
                        material.isHemiSphereLightEnabled = (materialFlags & 4) > 0;
                        material.isHemiSphereOcclusionEnabled = (materialFlags & 8) > 0;
                        material.isFogEnabled = (materialFlags & 0x10) > 0;
                        material.rasterization.isPolygonOffsetEnabled = (materialFlags & 0x20) > 0;

                        ushort fragmentFlags = input.ReadUInt16();
                        material.fragmentShader.bump.isBumpRenormalize = (fragmentFlags & 1) > 0;
                        material.fragmentShader.lighting.isClampHighLight = (fragmentFlags & 2) > 0;
                        material.fragmentShader.lighting.isDistribution0Enabled = (fragmentFlags & 4) > 0;
                        material.fragmentShader.lighting.isDistribution1Enabled = (fragmentFlags & 8) > 0;
                        material.fragmentShader.lighting.isGeometryFactor0Enabled = (fragmentFlags & 0x10) > 0;
                        material.fragmentShader.lighting.isGeometryFactor1Enabled = (fragmentFlags & 0x20) > 0;
                        material.fragmentShader.lighting.isReflectionEnabled = (fragmentFlags & 0x40) > 0;
                        RenderBase.OBlendMode blendMode = (RenderBase.OBlendMode)((fragmentFlags >> 10) & 3);

                        input.ReadUInt32();
                        for (int i = 0; i < 3; i++)
                        {
                            RenderBase.OTextureCoordinator coordinator;
                            uint projectionAndCamera = input.ReadUInt32();
                            coordinator.projection = (RenderBase.OTextureProjection)((projectionAndCamera >> 16) & 0xff);
                            coordinator.referenceCamera = projectionAndCamera >> 24;
                            coordinator.scaleU = input.ReadSingle();
                            coordinator.scaleV = input.ReadSingle();
                            coordinator.rotate = input.ReadSingle();
                            coordinator.translateU = input.ReadSingle();
                            coordinator.translateV = input.ReadSingle();

                            material.textureCoordinator[i] = coordinator;
                        }

                        material.lightSetIndex = input.ReadUInt16();
                        material.fogIndex = input.ReadUInt16();

                        material.materialColor.emission = MeshUtils.getColor(input);
                        material.materialColor.ambient = MeshUtils.getColor(input);
                        material.materialColor.diffuse = MeshUtils.getColor(input);
                        material.materialColor.specular0 = MeshUtils.getColor(input);
                        material.materialColor.specular1 = MeshUtils.getColor(input);
                        material.materialColor.constant0 = MeshUtils.getColor(input);
                        material.materialColor.constant1 = MeshUtils.getColor(input);
                        material.materialColor.constant2 = MeshUtils.getColor(input);
                        material.materialColor.constant3 = MeshUtils.getColor(input);
                        material.materialColor.constant4 = MeshUtils.getColor(input);
                        material.materialColor.constant5 = MeshUtils.getColor(input);
                        material.fragmentOperation.blend.blendColor = MeshUtils.getColor(input);
                        material.materialColor.colorScale = input.ReadSingle();

                        input.ReadUInt32(); //TODO: Figure out
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();

                        uint fragmentData = input.ReadUInt32();
                        material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)(fragmentData >> 24);
                        material.fragmentShader.bump.mode = (RenderBase.OBumpMode)((fragmentData >> 16) & 0xff);
                        material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)((fragmentData >> 8) & 0xff);
                        material.fragmentShader.layerConfig = fragmentData & 0xff;

                        //Some Fragment Lighting related commands... This seems a bit out of place.
                        long position = data.Position;
                        PICACommandReader fshLightingCommands = new PICACommandReader(data, 6, true);

                        PICACommand.fragmentSamplerAbsolute sAbs = fshLightingCommands.getReflectanceSamplerAbsolute();
                        material.fragmentShader.lighting.reflectanceRSampler.isAbsolute = sAbs.r;
                        material.fragmentShader.lighting.reflectanceGSampler.isAbsolute = sAbs.g;
                        material.fragmentShader.lighting.reflectanceBSampler.isAbsolute = sAbs.b;
                        material.fragmentShader.lighting.distribution0Sampler.isAbsolute = sAbs.d0;
                        material.fragmentShader.lighting.distribution1Sampler.isAbsolute = sAbs.d1;
                        material.fragmentShader.lighting.fresnelSampler.isAbsolute = sAbs.fresnel;

                        PICACommand.fragmentSamplerInput sInput = fshLightingCommands.getReflectanceSamplerInput();
                        material.fragmentShader.lighting.reflectanceRSampler.input = sInput.r;
                        material.fragmentShader.lighting.reflectanceGSampler.input = sInput.g;
                        material.fragmentShader.lighting.reflectanceBSampler.input = sInput.b;
                        material.fragmentShader.lighting.distribution0Sampler.input = sInput.d0;
                        material.fragmentShader.lighting.distribution1Sampler.input = sInput.d1;
                        material.fragmentShader.lighting.fresnelSampler.input = sInput.fresnel;

                        PICACommand.fragmentSamplerScale sScale = fshLightingCommands.getReflectanceSamplerScale();
                        material.fragmentShader.lighting.reflectanceRSampler.scale = sScale.r;
                        material.fragmentShader.lighting.reflectanceGSampler.scale = sScale.g;
                        material.fragmentShader.lighting.reflectanceBSampler.scale = sScale.b;
                        material.fragmentShader.lighting.distribution0Sampler.scale = sScale.d0;
                        material.fragmentShader.lighting.distribution1Sampler.scale = sScale.d1;
                        material.fragmentShader.lighting.fresnelSampler.scale = sScale.fresnel;
                        data.Seek(position + (4 * 6), SeekOrigin.Begin); //Just to be sure ;)

                        RenderBase.OConstantColor[] constantList = new RenderBase.OConstantColor[6];
                        uint constantColor = input.ReadUInt32();
                        for (int i = 0; i < 6; i++) constantList[i] = (RenderBase.OConstantColor)((constantColor >> (i * 4)) & 0xf);
                        material.rasterization.polygonOffsetUnit = input.ReadSingle();
                        uint fshCommandsOffset = input.ReadUInt32();
                        uint fshCommandsWordCount = input.ReadUInt32();
                        input.ReadUInt32();

                        material.fragmentShader.lighting.distribution0Sampler.tableName = readString(input);
                        material.fragmentShader.lighting.distribution1Sampler.tableName = readString(input);
                        material.fragmentShader.lighting.fresnelSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceRSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceGSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceBSampler.tableName = readString(input);

                        material.fragmentShader.lighting.distribution0Sampler.samplerName = readString(input);
                        material.fragmentShader.lighting.distribution1Sampler.samplerName = readString(input);
                        material.fragmentShader.lighting.fresnelSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceRSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceGSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceBSampler.samplerName = readString(input);

                        material.shaderReference = new RenderBase.OReference(readString(input));
                        material.modelReference = new RenderBase.OReference(readString(input));

                        //User Data
                        if (header.backwardCompatibility > 6)
                        {
                            uint metaDataPointerOffset = input.ReadUInt32();
                            if (metaDataPointerOffset != 0)
                            {
                                data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                                material.userData = getMetaData(input);
                            }
                        }

                        //Mapper
                        data.Seek(materialMapperOffset, SeekOrigin.Begin);
                        for (int i = 0; i < 3; i++)
                        {
                            RenderBase.OTextureMapper mapper;
                            uint wrapAndMagFilter = input.ReadUInt32();
                            uint levelOfDetailAndMinFilter = input.ReadUInt32();
                            mapper.wrapU = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 8) & 0xff);
                            mapper.wrapV = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 16) & 0xff);
                            mapper.magFilter = (RenderBase.OTextureMagFilter)(wrapAndMagFilter >> 24);
                            mapper.minFilter = (RenderBase.OTextureMinFilter)(levelOfDetailAndMinFilter & 0xff);
                            mapper.minLOD = (levelOfDetailAndMinFilter >> 8) & 0xff; //max 232
                            mapper.LODBias = input.ReadSingle();
                            mapper.borderColor = MeshUtils.getColor(input);

                            material.textureMapper[i] = mapper;
                        }

                        //Fragment Shader commands
                        data.Seek(fshCommandsOffset, SeekOrigin.Begin);
                        PICACommandReader fshCommands = new PICACommandReader(data, fshCommandsWordCount);
                        for (byte stage = 0; stage < 6; stage++) material.fragmentShader.textureCombiner[stage] = fshCommands.getTevStage(stage);
                        material.fragmentShader.bufferColor = fshCommands.getFragmentBufferColor();
                        material.fragmentOperation.blend = fshCommands.getBlendOperation();
                        material.fragmentOperation.blend.logicalOperation = fshCommands.getColorLogicOperation();
                        material.fragmentShader.alphaTest = fshCommands.getAlphaTest();
                        material.fragmentOperation.stencil = fshCommands.getStencilTest();
                        material.fragmentOperation.depth = fshCommands.getDepthTest();
                        material.rasterization.cullMode = fshCommands.getCullMode();
                        material.fragmentOperation.blend.mode = blendMode;
                    }

                    model.material.Add(material);
                }

                //Skeleton
                data.Seek(modelHeader.skeletonOffset, SeekOrigin.Begin);
                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    RenderBase.OBone bone = new RenderBase.OBone();

                    uint boneFlags = input.ReadUInt32();
                    bone.billboardMode = (RenderBase.OBillboardMode)((boneFlags >> 16) & 0xf);
                    bone.isSegmentScaleCompensate = (boneFlags & 0x00400000) > 0;
                    bone.parentId = input.ReadInt16();
                    ushort boneSpacer = input.ReadUInt16();
                    bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    bone.absoluteScale = new RenderBase.OVector3(bone.scale);

                    RenderBase.OMatrix boneMatrix = new RenderBase.OMatrix();
                    boneMatrix.M11 = input.ReadSingle();
                    boneMatrix.M21 = input.ReadSingle();
                    boneMatrix.M31 = input.ReadSingle();
                    boneMatrix.M41 = input.ReadSingle();

                    boneMatrix.M12 = input.ReadSingle();
                    boneMatrix.M22 = input.ReadSingle();
                    boneMatrix.M32 = input.ReadSingle();
                    boneMatrix.M42 = input.ReadSingle();

                    boneMatrix.M13 = input.ReadSingle();
                    boneMatrix.M23 = input.ReadSingle();
                    boneMatrix.M33 = input.ReadSingle();
                    boneMatrix.M43 = input.ReadSingle();

                    bone.name = readString(input);

                    uint metaDataPointerOffset = input.ReadUInt32();
                    if (metaDataPointerOffset != 0)
                    {
                        long position = data.Position;
                        data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                        bone.userData = getMetaData(input);
                        data.Seek(position, SeekOrigin.Begin);
                    }

                    model.skeleton.Add(bone);
                }

                List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>();
                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    RenderBase.OMatrix transform = new RenderBase.OMatrix();
                    transformSkeleton(model.skeleton, index, ref transform);
                    skeletonTransform.Add(transform);
                }

                data.Seek(modelHeader.objectsNodeVisibilityOffset, SeekOrigin.Begin);
                uint nodeVisibility = input.ReadUInt32();

                //Vertices header
                data.Seek(modelHeader.verticesTableOffset, SeekOrigin.Begin);
                List<bchObjectEntry> objects = new List<bchObjectEntry>();

                for (int index = 0; index < modelHeader.verticesTableEntries; index++)
                {
                    bchObjectEntry objectEntry = new bchObjectEntry();
                    objectEntry.materialId = input.ReadUInt16();
                    ushort flags = input.ReadUInt16();

                    if (header.backwardCompatibility != 8) objectEntry.isSilhouette = (flags & 1) > 0;
                    objectEntry.nodeId = input.ReadUInt16();
                    objectEntry.renderPriority = input.ReadUInt16();
                    objectEntry.vshAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffer 0
                    objectEntry.vshAttributesBufferCommandsWordCount = input.ReadUInt32();
                    objectEntry.facesHeaderOffset = input.ReadUInt32();
                    objectEntry.facesHeaderEntries = input.ReadUInt32();
                    objectEntry.vshExtraAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffers 1-11
                    objectEntry.vshExtraAttributesBufferCommandsWordCount = input.ReadUInt32();
                    objectEntry.centerVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    objectEntry.flagsOffset = input.ReadUInt32();
                    input.ReadUInt32(); //ex: 0x0 fixo
                    objectEntry.boundingBoxOffset = input.ReadUInt32();

                    objects.Add(objectEntry);
                }

                for (int objIndex = 0; objIndex < objects.Count; objIndex++)
                {
                    if (objects[objIndex].isSilhouette) continue; //TODO: Figure out for what "Silhouette" is used.

                    RenderBase.OMesh obj = new RenderBase.OMesh();
                    obj.materialId = objects[objIndex].materialId;
                    obj.renderPriority = objects[objIndex].renderPriority;
                    if (objects[objIndex].nodeId < objectName.Length) obj.name = objectName[objects[objIndex].nodeId]; else obj.name = "mesh" + objIndex.ToString();
                    obj.isVisible = (nodeVisibility & (1 << objects[objIndex].nodeId)) > 0;

                    //Vertices
                    data.Seek(objects[objIndex].vshAttributesBufferCommandsOffset, SeekOrigin.Begin);
                    PICACommandReader vshCommands = new PICACommandReader(data, objects[objIndex].vshAttributesBufferCommandsWordCount);

                    Stack<float> vshAttributesUniformReg6 = vshCommands.getVSHFloatUniformData(6);
                    Stack<float> vshAttributesUniformReg7 = vshCommands.getVSHFloatUniformData(7);
                    RenderBase.OVector4 positionOffset = new RenderBase.OVector4(
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop());
                    float texture0Scale = vshAttributesUniformReg7.Pop();
                    float texture1Scale = vshAttributesUniformReg7.Pop();
                    float texture2Scale = vshAttributesUniformReg7.Pop();
                    float boneWeightScale = vshAttributesUniformReg7.Pop();
                    float positionScale = vshAttributesUniformReg7.Pop();
                    float normalScale = vshAttributesUniformReg7.Pop();
                    float tangentScale = vshAttributesUniformReg7.Pop();
                    float colorScale = vshAttributesUniformReg7.Pop();

                    //Faces
                    uint facesCount = objects[objIndex].facesHeaderEntries;
                    bool hasFaces = facesCount > 0;
                    uint facesTableOffset = 0;
                    if (!hasFaces)
                    {
                        data.Seek(modelHeader.verticesTableOffset + modelHeader.verticesTableEntries * 0x38, SeekOrigin.Begin);
                        data.Seek(objIndex * 0x1c + 0x10, SeekOrigin.Current);

                        facesTableOffset = input.ReadUInt32();
                        facesCount = input.ReadUInt32();
                    }

                    for (uint f = 0; f < facesCount; f++)
                    {
                        RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none;
                        List<ushort> nodeList = new List<ushort>();
                        uint idxBufferOffset;
                        PICACommand.indexBufferFormat idxBufferFormat;
                        uint idxBufferTotalVertices;

                        if (hasFaces)
                        {
                            uint baseOffset = objects[objIndex].facesHeaderOffset + f * 0x34;
                            data.Seek(baseOffset, SeekOrigin.Begin);
                            skinningMode = (RenderBase.OSkinningMode)input.ReadUInt16();
                            ushort nodeIdEntries = input.ReadUInt16();
                            for (int n = 0; n < nodeIdEntries; n++) nodeList.Add(input.ReadUInt16());

                            data.Seek(baseOffset + 0x2c, SeekOrigin.Begin);
                            uint faceHeaderOffset = input.ReadUInt32();
                            uint faceHeaderWordCount = input.ReadUInt32();
                            data.Seek(faceHeaderOffset, SeekOrigin.Begin);
                            PICACommandReader idxCommands = new PICACommandReader(data, faceHeaderWordCount);
                            idxBufferOffset = idxCommands.getIndexBufferAddress();
                            idxBufferFormat = idxCommands.getIndexBufferFormat();
                            idxBufferTotalVertices = idxCommands.getIndexBufferTotalVertices();
                        }
                        else
                        {
                            data.Seek(facesTableOffset + f * 8, SeekOrigin.Begin);
                            
                            idxBufferOffset = input.ReadUInt32();
                            idxBufferFormat = PICACommand.indexBufferFormat.unsignedShort;
                            idxBufferTotalVertices = input.ReadUInt32();
                        }

                        //Carregamento de dados relacionados ao Vertex Shader
                        uint vshAttributesBufferOffset = vshCommands.getVSHAttributesBufferAddress(0);
                        uint vshAttributesBufferStride = vshCommands.getVSHAttributesBufferStride(0);
                        uint vshTotalAttributes = vshCommands.getVSHTotalAttributes(0);
                        PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation();
                        uint[] vshAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(0);
                        PICACommand.attributeFormat[] vshAttributesBufferFormat = vshCommands.getVSHAttributesBufferFormat();

                        for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                        {
                            switch (vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]])
                            {
                                case PICACommand.vshAttribute.normal: obj.hasNormal = true; break;
                                case PICACommand.vshAttribute.tangent: obj.hasTangent = true; break;
                                case PICACommand.vshAttribute.color: obj.hasColor = true; break;
                                case PICACommand.vshAttribute.textureCoordinate0: obj.texUVCount = Math.Max(obj.texUVCount, 1); break;
                                case PICACommand.vshAttribute.textureCoordinate1: obj.texUVCount = Math.Max(obj.texUVCount, 2); break;
                                case PICACommand.vshAttribute.textureCoordinate2: obj.texUVCount = Math.Max(obj.texUVCount, 3); break;
                            }
                        }

                        if (nodeList.Count > 0)
                        {
                            obj.hasNode = true;
                            obj.hasWeight = true;
                        }

                        data.Seek(idxBufferOffset, SeekOrigin.Begin);
                        for (int faceIndex = 0; faceIndex < idxBufferTotalVertices; faceIndex++)
                        {
                            ushort index = 0;

                            switch (idxBufferFormat)
                            {
                                case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); break;
                                case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break;
                            }

                            long dataPosition = data.Position;
                            long vertexOffset = vshAttributesBufferOffset + (index * vshAttributesBufferStride);
                            data.Seek(vertexOffset, SeekOrigin.Begin);

                            RenderBase.OVertex vertex = new RenderBase.OVertex();
                            vertex.diffuseColor = 0xffffffff;
                            for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                            {
                                //gdkchan self note: The Attribute type flags are used for something else on Bone Weight (and bone index?)
                                PICACommand.vshAttribute att = vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]];
                                PICACommand.attributeFormat format = vshAttributesBufferFormat[vshAttributesBufferPermutation[attribute]];
                                if (att == PICACommand.vshAttribute.boneWeight) format.type = PICACommand.attributeFormatType.unsignedByte;
                                RenderBase.OVector4 vector = getVector(input, format);

                                switch (att)
                                {
                                    case PICACommand.vshAttribute.position:
                                        float x = (vector.x * positionScale) + positionOffset.x;
                                        float y = (vector.y * positionScale) + positionOffset.y;
                                        float z = (vector.z * positionScale) + positionOffset.z;
                                        vertex.position = new RenderBase.OVector3(x, y, z);
                                        break;
                                    case PICACommand.vshAttribute.normal:
                                        vertex.normal = new RenderBase.OVector3(vector.x * normalScale, vector.y * normalScale, vector.z * normalScale);
                                        break;
                                    case PICACommand.vshAttribute.tangent:
                                        vertex.tangent = new RenderBase.OVector3(vector.x * tangentScale, vector.y * tangentScale, vector.z * tangentScale);
                                        break;
                                    case PICACommand.vshAttribute.color:
                                        uint r = MeshUtils.saturate((vector.x * colorScale) * 0xff);
                                        uint g = MeshUtils.saturate((vector.y * colorScale) * 0xff);
                                        uint b = MeshUtils.saturate((vector.z * colorScale) * 0xff);
                                        uint a = MeshUtils.saturate((vector.w * colorScale) * 0xff);
                                        vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate0:
                                        vertex.texture0 = new RenderBase.OVector2(vector.x * texture0Scale, vector.y * texture0Scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate1:
                                        vertex.texture1 = new RenderBase.OVector2(vector.x * texture1Scale, vector.y * texture1Scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate2:
                                        vertex.texture2 = new RenderBase.OVector2(vector.x * texture2Scale, vector.y * texture2Scale);
                                        break;
                                    case PICACommand.vshAttribute.boneIndex:
                                        vertex.node.Add(nodeList[(int)vector.x]);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (format.attributeLength > 0) vertex.node.Add(nodeList[(int)vector.y]);
                                            if (format.attributeLength > 1) vertex.node.Add(nodeList[(int)vector.z]);
                                            if (format.attributeLength > 2) vertex.node.Add(nodeList[(int)vector.w]);
                                        }
                                        break;
                                    case PICACommand.vshAttribute.boneWeight:
                                        vertex.weight.Add(vector.x * boneWeightScale);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (format.attributeLength > 0) vertex.weight.Add(vector.y * boneWeightScale);
                                            if (format.attributeLength > 1) vertex.weight.Add(vector.z * boneWeightScale);
                                            if (format.attributeLength > 2) vertex.weight.Add(vector.w * boneWeightScale);
                                        }
                                        break;
                                }
                            }

                            //If the node list have 4 or less bones, then there is no need to store the indices per vertex
                            //Instead, the entire list is used, since it supports up to 4 bones.
                            if (vertex.node.Count == 0 && nodeList.Count <= 4)
                            {
                                for (int n = 0; n < nodeList.Count; n++) vertex.node.Add(nodeList[n]);
                                if (vertex.weight.Count == 0) vertex.weight.Add(1);
                            }

                            if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0)
                            {
                                //Note: Rigid skinning can have only one bone per vertex
                                //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton
                                if (vertex.weight.Count == 0) vertex.weight.Add(1);
                                vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]);
                            }

                            MeshUtils.calculateBounds(model, vertex);
                            obj.vertices.Add(vertex);

                            data.Seek(dataPosition, SeekOrigin.Begin);
                        }
                    }

                    //Bounding box
                    if (objects[objIndex].boundingBoxOffset != 0)
                    {
                        data.Seek(objects[objIndex].boundingBoxOffset, SeekOrigin.Begin);
                        uint bBoxDataOffset = input.ReadUInt32();
                        uint bBoxEntries = input.ReadUInt32();
                        uint bBoxNameOffset = input.ReadUInt32();

                        for (int index = 0; index < bBoxEntries; index++)
                        {
                            data.Seek(bBoxDataOffset + (index * 0xc), SeekOrigin.Begin);

                            RenderBase.OOrientedBoundingBox bBox = new RenderBase.OOrientedBoundingBox();

                            bBox.name = readString(input);
                            uint flags = input.ReadUInt32();
                            uint dataOffset = input.ReadUInt32();

                            data.Seek(dataOffset, SeekOrigin.Begin);
                            bBox.centerPosition = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                            bBox.orientationMatrix.M11 = input.ReadSingle();
                            bBox.orientationMatrix.M21 = input.ReadSingle();
                            bBox.orientationMatrix.M31 = input.ReadSingle();

                            bBox.orientationMatrix.M12 = input.ReadSingle();
                            bBox.orientationMatrix.M22 = input.ReadSingle();
                            bBox.orientationMatrix.M32 = input.ReadSingle();

                            bBox.orientationMatrix.M13 = input.ReadSingle();
                            bBox.orientationMatrix.M23 = input.ReadSingle();
                            bBox.orientationMatrix.M33 = input.ReadSingle();

                            bBox.size = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                            obj.boundingBox.Add(bBox);
                        }
                    }
    
                    model.mesh.Add(obj);
                }

                if (modelHeader.metaDataPointerOffset != 0)
                {
                    data.Seek(modelHeader.metaDataPointerOffset, SeekOrigin.Begin);
                    model.userData = getMetaData(input);
                }

                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    scaleSkeleton(model.skeleton, index, index);
                }

                models.model.Add(model);
            }

            data.Close();

            return models;
        }
Exemplo n.º 11
0
        /// <summary>
        ///     Reads the Model PACKage from Dragon Quest VII.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns></returns>
        public static OContainer load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);
            OContainer output = new OContainer();

            List<sectionEntry> mainSection = getSection(input);

            //World nodes section
            data.Seek(mainSection[0].offset, SeekOrigin.Begin);
            List<node> nodes = new List<node>();
            List<sectionEntry> worldNodesSection = getSection(input);
            foreach (sectionEntry entry in worldNodesSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                node n = new node();

                //Geometry node
                input.ReadUInt32(); //GNOD magic number
                input.ReadUInt32();
                input.ReadUInt32();
                n.parentId = input.ReadInt32();
                n.name = IOUtils.readString(input, (uint)data.Position);

                data.Seek(entry.offset + 0x20, SeekOrigin.Begin);
                n.transform = new RenderBase.OMatrix();
                RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z));
                n.transform *= RenderBase.OMatrix.rotateX(r.x);
                n.transform *= RenderBase.OMatrix.rotateY(r.y);
                n.transform *= RenderBase.OMatrix.rotateZ(r.z);
                n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z));

                nodes.Add(n);
            }

            RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count];
            for (int i = 0; i < nodes.Count; i++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformNode(nodes, i, ref transform);
                nodesTransform[i] = transform;
            }

            //Models section
            data.Seek(mainSection[1].offset, SeekOrigin.Begin);
            List<sectionEntry> modelsSection = getSection(input);
            foreach (sectionEntry entry in modelsSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                //Field Data section
                /*
                 * Usually have 3 entries.
                 * 1st entry: Model CGFX
                 * 2nd entry: Unknow CGFX, possibly animations
                 * 3rd entry: Another FieldData section, possibly child object
                 */

                List<sectionEntry> fieldDataSection = getSection(input);
                data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin);
                uint length = fieldDataSection[0].length;
                while ((length & 0x7f) != 0) length++; //Align
                byte[] buffer = new byte[length];
                input.Read(buffer, 0, buffer.Length);

                OContainer.fileEntry file = new OContainer.fileEntry();
                file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl";
                file.data = buffer;

                output.content.Add(file);
            }

            //FILE section
            data.Seek(mainSection[2].offset, SeekOrigin.Begin);
            //TODO

            //Collision section
            data.Seek(mainSection[3].offset, SeekOrigin.Begin);
            //TODO

            //PARM(???) section
            data.Seek(mainSection[4].offset, SeekOrigin.Begin);
            //TODO

            //Textures CGFX
            data.Seek(mainSection[5].offset, SeekOrigin.Begin);
            byte[] texBuffer = new byte[mainSection[5].length];
            input.Read(texBuffer, 0, texBuffer.Length);

            OContainer.fileEntry texFile = new OContainer.fileEntry();
            texFile.name = "textures.bctex";
            texFile.data = texBuffer;

            output.content.Add(texFile);

            data.Close();

            return output;
        }
Exemplo n.º 12
0
        /// <summary>
        ///     Exports a Model to the Collada format.
        ///     See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information.
        /// </summary>
        /// <param name="model">The Model that will be exported</param>
        /// <param name="fileName">The output File Name</param>
        /// <param name="modelIndex">Index of the model to be exported</param>
        /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1)
        {
            RenderBase.OModel mdl = model.model[modelIndex];
            COLLADA           dae = new COLLADA();

            dae.asset.created  = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ");
            dae.asset.modified = dae.asset.created;

            foreach (RenderBase.OTexture tex in model.texture)
            {
                daeImage img = new daeImage();
                img.id        = tex.name + "_id";
                img.name      = tex.name;
                img.init_from = "./" + tex.name + ".png";

                dae.library_images.Add(img);
            }

            foreach (RenderBase.OMaterial mat in mdl.material)
            {
                daeMaterial mtl = new daeMaterial();
                mtl.name = mat.name + "_mat";
                mtl.id   = mtl.name + "_id";
                mtl.instance_effect.url = "#eff_" + mat.name + "_id";

                dae.library_materials.Add(mtl);

                daeEffect eff = new daeEffect();
                eff.id   = "eff_" + mat.name + "_id";
                eff.name = "eff_" + mat.name;

                daeParam surface = new daeParam();
                surface.surface           = new daeParamSurfaceElement();
                surface.sid               = "img_surface";
                surface.surface.type      = "2D";
                surface.surface.init_from = mat.name0 + "_id";
                surface.surface.format    = "PNG";
                eff.profile_COMMON.newparam.Add(surface);

                daeParam sampler = new daeParam();
                sampler.sampler2D        = new daeParamSampler2DElement();
                sampler.sid              = "img_sampler";
                sampler.sampler2D.source = "img_surface";

                switch (mat.textureMapper[0].wrapU)
                {
                case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break;

                case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break;

                case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break;

                case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break;

                default: sampler.sampler2D.wrap_s = "NONE"; break;
                }

                switch (mat.textureMapper[0].wrapV)
                {
                case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break;

                case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break;

                case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break;

                case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break;

                default: sampler.sampler2D.wrap_t = "NONE"; break;
                }

                switch (mat.textureMapper[0].minFilter)
                {
                case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break;

                case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break;

                case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break;

                case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break;

                default: sampler.sampler2D.minfilter = "NONE"; break;
                }

                switch (mat.textureMapper[0].magFilter)
                {
                case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break;

                case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break;

                default: sampler.sampler2D.magfilter = "NONE"; break;
                }

                sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter;

                eff.profile_COMMON.newparam.Add(sampler);

                eff.profile_COMMON.technique.sid = "img_technique";
                eff.profile_COMMON.technique.phong.emission.set(Color.Black);
                eff.profile_COMMON.technique.phong.ambient.set(Color.Black);
                eff.profile_COMMON.technique.phong.specular.set(Color.White);
                eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler";

                dae.library_effects.Add(eff);
            }

            string jointNames   = null;
            string invBindPoses = null;

            for (int index = 0; index < mdl.skeleton.Count; index++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformSkeleton(mdl.skeleton, index, ref transform);

                jointNames += mdl.skeleton[index].name;
                daeMatrix mtx = new daeMatrix();
                mtx.set(transform.invert());
                invBindPoses += mtx.data;
                if (index < mdl.skeleton.Count - 1)
                {
                    jointNames   += " ";
                    invBindPoses += " ";
                }
            }

            int            meshIndex = 0;
            daeVisualScene vs        = new daeVisualScene();

            vs.name = "vs_" + mdl.name;
            vs.id   = vs.name + "_id";
            if (mdl.skeleton.Count > 0)
            {
                writeSkeleton(mdl.skeleton, 0, ref vs.node);
            }
            foreach (RenderBase.OMesh obj in mdl.mesh)
            {
                //Geometry
                daeGeometry geometry = new daeGeometry();

                string meshName = "mesh_" + meshIndex++ + "_" + obj.name;
                geometry.id   = meshName + "_id";
                geometry.name = meshName;

                MeshUtils.optimizedMesh mesh      = MeshUtils.optimizeMesh(obj);
                List <float>            positions = new List <float>();
                List <float>            normals   = new List <float>();
                List <float>            uv0       = new List <float>();
                List <float>            uv1       = new List <float>();
                List <float>            uv2       = new List <float>();
                List <float>            colors    = new List <float>();
                foreach (RenderBase.OVertex vtx in mesh.vertices)
                {
                    positions.Add(vtx.position.x);
                    positions.Add(vtx.position.y);
                    positions.Add(vtx.position.z);

                    if (mesh.hasNormal)
                    {
                        normals.Add(vtx.normal.x);
                        normals.Add(vtx.normal.y);
                        normals.Add(vtx.normal.z);
                    }

                    if (mesh.texUVCount > 0)
                    {
                        uv0.Add(vtx.texture0.x);
                        uv0.Add(vtx.texture0.y);
                    }

                    if (mesh.texUVCount > 1)
                    {
                        uv1.Add(vtx.texture1.x);
                        uv1.Add(vtx.texture1.y);
                    }

                    if (mesh.texUVCount > 2)
                    {
                        uv2.Add(vtx.texture2.x);
                        uv2.Add(vtx.texture2.y);
                    }

                    if (mesh.hasColor)
                    {
                        colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f);
                        colors.Add((vtx.diffuseColor & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f);
                    }
                }

                daeSource position = new daeSource();
                position.name           = meshName + "_position";
                position.id             = position.name + "_id";
                position.float_array    = new daeFloatArray();
                position.float_array.id = position.name + "_array_id";
                position.float_array.set(positions);
                position.technique_common.accessor.source = "#" + position.float_array.id;
                position.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                position.technique_common.accessor.stride = 3;
                position.technique_common.accessor.addParam("X", "float");
                position.technique_common.accessor.addParam("Y", "float");
                position.technique_common.accessor.addParam("Z", "float");

                geometry.mesh.source.Add(position);

                daeSource normal = new daeSource();
                if (mesh.hasNormal)
                {
                    normal.name           = meshName + "_normal";
                    normal.id             = normal.name + "_id";
                    normal.float_array    = new daeFloatArray();
                    normal.float_array.id = normal.name + "_array_id";
                    normal.float_array.set(normals);
                    normal.technique_common.accessor.source = "#" + normal.float_array.id;
                    normal.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    normal.technique_common.accessor.stride = 3;
                    normal.technique_common.accessor.addParam("X", "float");
                    normal.technique_common.accessor.addParam("Y", "float");
                    normal.technique_common.accessor.addParam("Z", "float");

                    geometry.mesh.source.Add(normal);
                }

                daeSource[] texUV = new daeSource[3];
                for (int i = 0; i < mesh.texUVCount; i++)
                {
                    texUV[i] = new daeSource();

                    texUV[i].name           = meshName + "_uv" + i;
                    texUV[i].id             = texUV[i].name + "_id";
                    texUV[i].float_array    = new daeFloatArray();
                    texUV[i].float_array.id = texUV[i].name + "_array_id";
                    texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id;
                    texUV[i].technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    texUV[i].technique_common.accessor.stride = 2;
                    texUV[i].technique_common.accessor.addParam("S", "float");
                    texUV[i].technique_common.accessor.addParam("T", "float");

                    geometry.mesh.source.Add(texUV[i]);
                }

                daeSource color = new daeSource();
                if (mesh.hasColor)
                {
                    color.name           = meshName + "_color";
                    color.id             = color.name + "_id";
                    color.float_array    = new daeFloatArray();
                    color.float_array.id = color.name + "_array_id";
                    color.float_array.set(colors);
                    color.technique_common.accessor.source = "#" + color.float_array.id;
                    color.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    color.technique_common.accessor.stride = 4;
                    color.technique_common.accessor.addParam("R", "float");
                    color.technique_common.accessor.addParam("G", "float");
                    color.technique_common.accessor.addParam("B", "float");
                    color.technique_common.accessor.addParam("A", "float");

                    geometry.mesh.source.Add(color);
                }

                geometry.mesh.vertices.id = meshName + "_vertices_id";
                geometry.mesh.vertices.addInput("POSITION", "#" + position.id);


                geometry.mesh.triangles.material = mdl.material[obj.materialId].name;
                geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id);
                if (mesh.hasNormal)
                {
                    geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id);
                }
                if (mesh.hasColor)
                {
                    geometry.mesh.triangles.addInput("COLOR", "#" + color.id);
                }
                if (mesh.texUVCount > 0)
                {
                    texUV[0].float_array.set(uv0);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id);
                }
                if (mesh.texUVCount > 1)
                {
                    texUV[1].float_array.set(uv1);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1);
                }
                if (mesh.texUVCount > 2)
                {
                    texUV[2].float_array.set(uv2);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2);
                }
                geometry.mesh.triangles.set(mesh.indices);

                dae.library_geometries.Add(geometry);

                bool hasNode       = obj.vertices[0].node.Count > 0;
                bool hasWeight     = obj.vertices[0].weight.Count > 0;
                bool hasController = hasNode && hasWeight;

                //Controller
                daeController controller = new daeController();
                if (hasController)
                {
                    controller.id = meshName + "_ctrl_id";

                    controller.skin.source = "#" + geometry.id;
                    controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix());

                    daeSource joints = new daeSource();
                    joints.id               = meshName + "_ctrl_joint_names_id";
                    joints.Name_array       = new daeNameArray();
                    joints.Name_array.id    = meshName + "_ctrl_joint_names_array_id";
                    joints.Name_array.count = (uint)mdl.skeleton.Count;
                    joints.Name_array.data  = jointNames;
                    joints.technique_common.accessor.source = "#" + joints.Name_array.id;
                    joints.technique_common.accessor.count  = joints.Name_array.count;
                    joints.technique_common.accessor.stride = 1;
                    joints.technique_common.accessor.addParam("JOINT", "Name");

                    controller.skin.src.Add(joints);

                    daeSource bindPoses = new daeSource();
                    bindPoses.id                = meshName + "_ctrl_inv_bind_poses_id";
                    bindPoses.float_array       = new daeFloatArray();
                    bindPoses.float_array.id    = meshName + "_ctrl_inv_bind_poses_array_id";
                    bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16);
                    bindPoses.float_array.data  = invBindPoses;
                    bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id;
                    bindPoses.technique_common.accessor.count  = (uint)mdl.skeleton.Count;
                    bindPoses.technique_common.accessor.stride = 16;
                    bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4");

                    controller.skin.src.Add(bindPoses);

                    daeSource weights = new daeSource();
                    weights.id             = meshName + "_ctrl_weights_id";
                    weights.float_array    = new daeFloatArray();
                    weights.float_array.id = meshName + "_ctrl_weights_array_id";
                    weights.technique_common.accessor.source = "#" + weights.float_array.id;
                    weights.technique_common.accessor.stride = 1;
                    weights.technique_common.accessor.addParam("WEIGHT", "float");

                    StringBuilder w      = new StringBuilder();
                    StringBuilder vcount = new StringBuilder();
                    StringBuilder v      = new StringBuilder();

                    float[] wLookBack      = new float[32];
                    uint    wLookBackIndex = 0;
                    int     buffLen        = 0;

                    int wIndex = 0;
                    int wCount = 0;
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        int count = Math.Min(vtx.node.Count, vtx.weight.Count);

                        vcount.Append(count + " ");
                        for (int n = 0; n < count; n++)
                        {
                            v.Append(vtx.node[n] + " ");
                            bool found = false;
                            uint bPos  = (wLookBackIndex - 1) & 0x1f;
                            for (int i = 0; i < buffLen; i++)
                            {
                                if (wLookBack[bPos] == vtx.weight[n])
                                {
                                    v.Append(wIndex - (i + 1) + " ");
                                    found = true;
                                    break;
                                }
                                bPos = (bPos - 1) & 0x1f;
                            }

                            if (!found)
                            {
                                v.Append(wIndex++ + " ");
                                w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " ");
                                wCount++;

                                wLookBack[wLookBackIndex] = vtx.weight[n];
                                wLookBackIndex            = (wLookBackIndex + 1) & 0x1f;
                                if (buffLen < wLookBack.Length)
                                {
                                    buffLen++;
                                }
                            }
                        }
                    }

                    weights.float_array.data  = w.ToString().TrimEnd();
                    weights.float_array.count = (uint)wCount;
                    weights.technique_common.accessor.count = (uint)wCount;

                    controller.skin.src.Add(weights);
                    controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd();
                    controller.skin.vertex_weights.v      = v.ToString().TrimEnd();
                    controller.skin.vertex_weights.count  = (uint)mesh.vertices.Count;
                    controller.skin.joints.addInput("JOINT", "#" + joints.id);
                    controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id);

                    controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id);
                    controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1);

                    if (dae.library_controllers == null)
                    {
                        dae.library_controllers = new List <daeController>();
                    }
                    dae.library_controllers.Add(controller);
                }

                //Visual scene node
                daeNode node = new daeNode();
                node.name = "vsn_" + meshName;
                node.id   = node.name + "_id";
                node.matrix.set(new RenderBase.OMatrix());
                if (hasController)
                {
                    node.instance_controller          = new daeInstanceController();
                    node.instance_controller.url      = "#" + controller.id;
                    node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id";
                    node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }
                else
                {
                    node.instance_geometry     = new daeInstanceGeometry();
                    node.instance_geometry.url = "#" + geometry.id;
                    node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }

                vs.node.Add(node);
            }
            dae.library_visual_scenes.Add(vs);

            daeInstaceVisualScene scene = new daeInstaceVisualScene();

            scene.url = "#" + vs.id;
            dae.scene.Add(scene);

            XmlWriterSettings settings = new XmlWriterSettings
            {
                Encoding = Encoding.UTF8,
                Indent   = true
            };

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

            ns.Add("", "http://www.collada.org/2005/11/COLLADASchema");
            XmlSerializer serializer = new XmlSerializer(typeof(COLLADA));
            XmlWriter     output     = XmlWriter.Create(new FileStream(fileName, FileMode.Create), settings);

            serializer.Serialize(output, dae, ns);
            output.Close();
        }
Exemplo n.º 13
0
 /// <summary>
 ///     Transforms a Skeleton from relative to absolute positions.
 /// </summary>
 /// <param name="skeleton">The skeleton</param>
 /// <param name="index">Index of the bone to convert</param>
 /// <param name="target">Target matrix to save bone transformation</param>
 private static void transformSkeleton(List <RenderBase.OBone> skeleton, int index, ref RenderBase.OMatrix target)
 {
     target *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x);
     target *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y);
     target *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z);
     target *= RenderBase.OMatrix.translate(skeleton[index].translation);
     if (skeleton[index].parentId > -1)
     {
         transformSkeleton(skeleton, skeleton[index].parentId, ref target);
     }
 }
Exemplo n.º 14
0
        /// <summary>
        ///     Exports a Model to the Collada format.
        ///     See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information.
        /// </summary>
        /// <param name="model">The Model that will be exported</param>
        /// <param name="fileName">The output File Name</param>
        /// <param name="modelIndex">Index of the model to be exported</param>
        /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1)
        {
            RenderBase.OModel mdl = model.model[modelIndex];
            COLLADA dae = new COLLADA();

            dae.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ");
            dae.asset.modified = dae.asset.created;

            foreach (RenderBase.OTexture tex in model.texture)
            {
                daeImage img = new daeImage();
                img.id = tex.name + "_id";
                img.name = tex.name;
                img.init_from = "./" + tex.name + ".png";

                dae.library_images.Add(img);
            }

            foreach (RenderBase.OMaterial mat in mdl.material)
            {
                daeMaterial mtl = new daeMaterial();
                mtl.name = mat.name + "_mat";
                mtl.id = mtl.name + "_id";
                mtl.instance_effect.url = "#eff_" + mat.name + "_id";

                dae.library_materials.Add(mtl);

                daeEffect eff = new daeEffect();
                eff.id = "eff_" + mat.name + "_id";
                eff.name = "eff_" + mat.name;

                daeParam surface = new daeParam();
                surface.surface = new daeParamSurfaceElement();
                surface.sid = "img_surface";
                surface.surface.type = "2D";
                surface.surface.init_from = mat.name0 + "_id";
                surface.surface.format = "PNG";
                eff.profile_COMMON.newparam.Add(surface);

                daeParam sampler = new daeParam();
                sampler.sampler2D = new daeParamSampler2DElement();
                sampler.sid = "img_sampler";
                sampler.sampler2D.source = "img_surface";

                switch (mat.textureMapper[0].wrapU)
                {
                    case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break;
                    case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break;
                    case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break;
                    case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break;
                    default: sampler.sampler2D.wrap_s = "NONE"; break;
                }

                switch (mat.textureMapper[0].wrapV)
                {
                    case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break;
                    case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break;
                    case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break;
                    case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break;
                    default: sampler.sampler2D.wrap_t = "NONE"; break;
                }

                switch (mat.textureMapper[0].minFilter)
                {
                    case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break;
                    case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break;
                    case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break;
                    case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break;
                    default: sampler.sampler2D.minfilter = "NONE"; break;
                }

                switch (mat.textureMapper[0].magFilter)
                {
                    case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break;
                    case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break;
                    default: sampler.sampler2D.magfilter = "NONE"; break;
                }

                sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter;

                eff.profile_COMMON.newparam.Add(sampler);

                eff.profile_COMMON.technique.sid = "img_technique";
                eff.profile_COMMON.technique.phong.emission.set(Color.Black);
                eff.profile_COMMON.technique.phong.ambient.set(Color.Black);
                eff.profile_COMMON.technique.phong.specular.set(Color.White);
                eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler";

                dae.library_effects.Add(eff);
            }

            string jointNames = null;
            string invBindPoses = null;
            for (int index = 0; index < mdl.skeleton.Count; index++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformSkeleton(mdl.skeleton, index, ref transform);

                jointNames += mdl.skeleton[index].name;
                daeMatrix mtx = new daeMatrix();
                mtx.set(transform.invert());
                invBindPoses += mtx.data;
                if (index < mdl.skeleton.Count - 1)
                {
                    jointNames += " ";
                    invBindPoses += " ";
                }
            }

            int meshIndex = 0;
            daeVisualScene vs = new daeVisualScene();
            vs.name = "vs_" + mdl.name;
            vs.id = vs.name + "_id";
            if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node);
            foreach (RenderBase.OMesh obj in mdl.mesh)
            {
                //Geometry
                daeGeometry geometry = new daeGeometry();

                string meshName = "mesh_" + meshIndex++ + "_" + obj.name;
                geometry.id = meshName + "_id";
                geometry.name = meshName;

                MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj);
                List<float> positions = new List<float>();
                List<float> normals = new List<float>();
                List<float> uv0 = new List<float>();
                List<float> uv1 = new List<float>();
                List<float> uv2 = new List<float>();
                List<float> colors = new List<float>();
                foreach (RenderBase.OVertex vtx in mesh.vertices)
                {
                    positions.Add(vtx.position.x);
                    positions.Add(vtx.position.y);
                    positions.Add(vtx.position.z);

                    if (mesh.hasNormal)
                    {
                        normals.Add(vtx.normal.x);
                        normals.Add(vtx.normal.y);
                        normals.Add(vtx.normal.z);
                    }

                    if (mesh.texUVCount > 0)
                    {
                        uv0.Add(vtx.texture0.x);
                        uv0.Add(vtx.texture0.y);
                    }

                    if (mesh.texUVCount > 1)
                    {
                        uv1.Add(vtx.texture1.x);
                        uv1.Add(vtx.texture1.y);
                    }

                    if (mesh.texUVCount > 2)
                    {
                        uv2.Add(vtx.texture2.x);
                        uv2.Add(vtx.texture2.y);
                    }

                    if (mesh.hasColor)
                    {
                        colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f);
                        colors.Add((vtx.diffuseColor & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f);
                    }
                }

                daeSource position = new daeSource();
                position.name = meshName + "_position";
                position.id = position.name + "_id";
                position.float_array = new daeFloatArray();
                position.float_array.id = position.name + "_array_id";
                position.float_array.set(positions);
                position.technique_common.accessor.source = "#" + position.float_array.id;
                position.technique_common.accessor.count = (uint)mesh.vertices.Count;
                position.technique_common.accessor.stride = 3;
                position.technique_common.accessor.addParam("X", "float");
                position.technique_common.accessor.addParam("Y", "float");
                position.technique_common.accessor.addParam("Z", "float");

                geometry.mesh.source.Add(position);

                daeSource normal = new daeSource();
                if (mesh.hasNormal)
                {
                    normal.name = meshName + "_normal";
                    normal.id = normal.name + "_id";
                    normal.float_array = new daeFloatArray();
                    normal.float_array.id = normal.name + "_array_id";
                    normal.float_array.set(normals);
                    normal.technique_common.accessor.source = "#" + normal.float_array.id;
                    normal.technique_common.accessor.count = (uint)mesh.vertices.Count;
                    normal.technique_common.accessor.stride = 3;
                    normal.technique_common.accessor.addParam("X", "float");
                    normal.technique_common.accessor.addParam("Y", "float");
                    normal.technique_common.accessor.addParam("Z", "float");

                    geometry.mesh.source.Add(normal);
                }

                daeSource[] texUV = new daeSource[3];
                for (int i = 0; i < mesh.texUVCount; i++)
                {
                    texUV[i] = new daeSource();

                    texUV[i].name = meshName + "_uv" + i;
                    texUV[i].id = texUV[i].name + "_id";
                    texUV[i].float_array = new daeFloatArray();
                    texUV[i].float_array.id = texUV[i].name + "_array_id";
                    texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id;
                    texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count;
                    texUV[i].technique_common.accessor.stride = 2;
                    texUV[i].technique_common.accessor.addParam("S", "float");
                    texUV[i].technique_common.accessor.addParam("T", "float");

                    geometry.mesh.source.Add(texUV[i]);
                }

                daeSource color = new daeSource();
                if (mesh.hasColor)
                {
                    color.name = meshName + "_color";
                    color.id = color.name + "_id";
                    color.float_array = new daeFloatArray();
                    color.float_array.id = color.name + "_array_id";
                    color.float_array.set(colors);
                    color.technique_common.accessor.source = "#" + color.float_array.id;
                    color.technique_common.accessor.count = (uint)mesh.vertices.Count;
                    color.technique_common.accessor.stride = 4;
                    color.technique_common.accessor.addParam("R", "float");
                    color.technique_common.accessor.addParam("G", "float");
                    color.technique_common.accessor.addParam("B", "float");
                    color.technique_common.accessor.addParam("A", "float");

                    geometry.mesh.source.Add(color);
                }

                geometry.mesh.vertices.id = meshName + "_vertices_id";
                geometry.mesh.vertices.addInput("POSITION", "#" + position.id);


                geometry.mesh.triangles.material = mdl.material[obj.materialId].name;
                geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id);
                if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id);
                if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id);
                if (mesh.texUVCount > 0)
                {
                    texUV[0].float_array.set(uv0);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id);
                }
                if (mesh.texUVCount > 1)
                {
                    texUV[1].float_array.set(uv1);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1);
                }
                if (mesh.texUVCount > 2)
                {
                    texUV[2].float_array.set(uv2);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2);
                }
                geometry.mesh.triangles.set(mesh.indices);

                dae.library_geometries.Add(geometry);

                bool hasNode = obj.vertices[0].node.Count > 0;
                bool hasWeight = obj.vertices[0].weight.Count > 0;
                bool hasController = hasNode && hasWeight;

                //Controller
                daeController controller = new daeController();
                if (hasController)
                {
                    controller.id = meshName + "_ctrl_id";

                    controller.skin.source = "#" + geometry.id;
                    controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix());

                    daeSource joints = new daeSource();
                    joints.id = meshName + "_ctrl_joint_names_id";
                    joints.Name_array = new daeNameArray();
                    joints.Name_array.id = meshName + "_ctrl_joint_names_array_id";
                    joints.Name_array.count = (uint)mdl.skeleton.Count;
                    joints.Name_array.data = jointNames;
                    joints.technique_common.accessor.source = "#" + joints.Name_array.id;
                    joints.technique_common.accessor.count = joints.Name_array.count;
                    joints.technique_common.accessor.stride = 1;
                    joints.technique_common.accessor.addParam("JOINT", "Name");

                    controller.skin.src.Add(joints);

                    daeSource bindPoses = new daeSource();
                    bindPoses.id = meshName +"_ctrl_inv_bind_poses_id";
                    bindPoses.float_array = new daeFloatArray();
                    bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id";
                    bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16);
                    bindPoses.float_array.data = invBindPoses;
                    bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id;
                    bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count;
                    bindPoses.technique_common.accessor.stride = 16;
                    bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4");

                    controller.skin.src.Add(bindPoses);

                    daeSource weights = new daeSource();
                    weights.id = meshName + "_ctrl_weights_id";
                    weights.float_array = new daeFloatArray();
                    weights.float_array.id = meshName + "_ctrl_weights_array_id";
                    weights.technique_common.accessor.source = "#" + weights.float_array.id;
                    weights.technique_common.accessor.stride = 1;
                    weights.technique_common.accessor.addParam("WEIGHT", "float");

                    StringBuilder w = new StringBuilder();
                    StringBuilder vcount = new StringBuilder();
                    StringBuilder v = new StringBuilder();

                    float[] wLookBack = new float[32];
                    uint wLookBackIndex = 0;
                    int buffLen = 0;

                    int wIndex = 0;
                    int wCount = 0;
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        int count = Math.Min(vtx.node.Count, vtx.weight.Count);

                        vcount.Append(count + " ");
                        for (int n = 0; n < count; n++)
                        {
                            v.Append(vtx.node[n] + " ");
                            bool found = false;
                            uint bPos = (wLookBackIndex - 1) & 0x1f;
                            for (int i = 0; i < buffLen; i++)
                            {
                                if (wLookBack[bPos] == vtx.weight[n])
                                {
                                    v.Append(wIndex - (i + 1) + " ");
                                    found = true;
                                    break;
                                }
                                bPos = (bPos - 1) & 0x1f;
                            }

                            if (!found)
                            {
                                v.Append(wIndex++ + " ");
                                w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " ");
                                wCount++;

                                wLookBack[wLookBackIndex] = vtx.weight[n];
                                wLookBackIndex = (wLookBackIndex + 1) & 0x1f;
                                if (buffLen < wLookBack.Length) buffLen++;
                            }
                        }
                    }

                    weights.float_array.data = w.ToString().TrimEnd();
                    weights.float_array.count = (uint)wCount;
                    weights.technique_common.accessor.count = (uint)wCount;

                    controller.skin.src.Add(weights);
                    controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd();
                    controller.skin.vertex_weights.v = v.ToString().TrimEnd();
                    controller.skin.vertex_weights.count = (uint)mesh.vertices.Count;
                    controller.skin.joints.addInput("JOINT", "#" + joints.id);
                    controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id);

                    controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id);
                    controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1);

                    if (dae.library_controllers == null) dae.library_controllers = new List<daeController>();
                    dae.library_controllers.Add(controller);
                }

                //Visual scene node
                daeNode node = new daeNode();
                node.name = "vsn_" + meshName;
                node.id = node.name + "_id";
                node.matrix.set(new RenderBase.OMatrix());
                if (hasController)
                {
                    node.instance_controller = new daeInstanceController();
                    node.instance_controller.url = "#" + controller.id;
                    node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id";
                    node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }
                else
                {
                    node.instance_geometry = new daeInstanceGeometry();
                    node.instance_geometry.url = "#" + geometry.id;
                    node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }

                vs.node.Add(node);
            }
            dae.library_visual_scenes.Add(vs);

            daeInstaceVisualScene scene = new daeInstaceVisualScene();
            scene.url = "#" + vs.id;
            dae.scene.Add(scene);

            XmlWriterSettings settings = new XmlWriterSettings
            {
                Encoding = Encoding.UTF8,
                Indent = true
            };

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "http://www.collada.org/2005/11/COLLADASchema");
            XmlSerializer serializer = new XmlSerializer(typeof(COLLADA));
            XmlWriter output = XmlWriter.Create(new FileStream(fileName, FileMode.Create), settings);
            serializer.Serialize(output, dae, ns);
            output.Close();
        }
Exemplo n.º 15
0
        /// <summary>
        ///     Reads the Model PACKage from Dragon Quest VII.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns></returns>
        public static OContainer load(Stream data)
        {
            BinaryReader input  = new BinaryReader(data);
            OContainer   output = new OContainer();

            List <sectionEntry> mainSection = getSection(input);

            //World nodes section
            data.Seek(mainSection[0].offset, SeekOrigin.Begin);
            List <node>         nodes             = new List <node>();
            List <sectionEntry> worldNodesSection = getSection(input);

            foreach (sectionEntry entry in worldNodesSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                node n = new node();

                //Geometry node
                input.ReadUInt32(); //GNOD magic number
                input.ReadUInt32();
                input.ReadUInt32();
                n.parentId = input.ReadInt32();
                n.name     = IOUtils.readString(input, (uint)data.Position);

                data.Seek(entry.offset + 0x20, SeekOrigin.Begin);
                n.transform = new RenderBase.OMatrix();
                RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z));
                n.transform *= RenderBase.OMatrix.rotateX(r.x);
                n.transform *= RenderBase.OMatrix.rotateY(r.y);
                n.transform *= RenderBase.OMatrix.rotateZ(r.z);
                n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z));

                nodes.Add(n);
            }

            RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count];
            for (int i = 0; i < nodes.Count; i++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformNode(nodes, i, ref transform);
                nodesTransform[i] = transform;
            }

            //Models section
            data.Seek(mainSection[1].offset, SeekOrigin.Begin);
            List <sectionEntry> modelsSection = getSection(input);

            foreach (sectionEntry entry in modelsSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                //Field Data section

                /*
                 * Usually have 3 entries.
                 * 1st entry: Model CGFX
                 * 2nd entry: Unknow CGFX, possibly animations
                 * 3rd entry: Another FieldData section, possibly child object
                 */

                List <sectionEntry> fieldDataSection = getSection(input);
                data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin);
                uint length = fieldDataSection[0].length;
                while ((length & 0x7f) != 0)
                {
                    length++;                          //Align
                }
                byte[] buffer = new byte[length];
                input.Read(buffer, 0, buffer.Length);

                OContainer.fileEntry file = new OContainer.fileEntry();
                file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl";
                file.data = buffer;

                output.content.Add(file);
            }

            //FILE section
            data.Seek(mainSection[2].offset, SeekOrigin.Begin);
            //TODO

            //Collision section
            data.Seek(mainSection[3].offset, SeekOrigin.Begin);
            //TODO

            //PARM(???) section
            data.Seek(mainSection[4].offset, SeekOrigin.Begin);
            //TODO

            //Textures CGFX
            data.Seek(mainSection[5].offset, SeekOrigin.Begin);
            byte[] texBuffer = new byte[mainSection[5].length];
            input.Read(texBuffer, 0, texBuffer.Length);

            OContainer.fileEntry texFile = new OContainer.fileEntry();
            texFile.name = "textures.bctex";
            texFile.data = texBuffer;

            output.content.Add(texFile);

            data.Close();

            return(output);
        }