private void loadModelData(BlenderFile file)
        {
            models            = new List <BlenderModel>();
            transparentModels = new List <BlenderModel>();
            currentLayer      = 1;

            Structure curscene = file.GetStructuresOfType("FileGlobal")[0]["curscene"].Dereference()[0];
            ulong     next     = curscene["base.first"].Value;

            while (next != 0)
            {
                Structure objBase   = file.GetStructuresByAddress(next)[0];
                Structure obj       = objBase["object"].Dereference()[0];
                IField    data      = obj["data"];
                int       SDNAIndex = file.GetBlockByAddress((data as Field <ulong>).Value).SDNAIndex;
                while (file.StructureDNA.StructureList[SDNAIndex].StructureTypeName != "Mesh")
                {
                    ulong nextPointer = (objBase["next"] as Field <ulong>).Value;
                    if (nextPointer == 0)
                    {
                        return; // we've run out of objects in the list, and haven't found any meshes
                    }
                    objBase   = file.GetStructuresByAddress(nextPointer)[0];
                    obj       = objBase["object"].Dereference()[0];
                    data      = obj["data"];
                    SDNAIndex = file.GetBlockByAddress((data as Field <ulong>).Value).SDNAIndex;
                }

                Structure    mesh  = data.Dereference()[0];
                BlenderModel model = new BlenderModel(mesh, obj, GraphicsDevice, file);
                if (model.TextureHasTransparency)
                {
                    transparentModels.Add(model);
                }
                else
                {
                    models.Add(model);
                }

                next = (objBase["next"] as Field <ulong>).Value;
            }
        }
Example #2
0
        internal Scene(BlenderFile file, PopulatedStructure scene)
        {
            Name = new string(scene["id.name"].GetValueAsCharArray()).Split('\0')[0].Substring(2);

            // todo: add lamp importing here
            string[]             validTypeNames = new[] { "Mesh" };
            List <BlenderObject> objects        = new List <BlenderObject>();
            int i = 0;

            ulong next   = scene["base.first"].GetValueAsPointer();
            ulong basact = scene["basact"].GetValueAsPointer(); // this is the address of the Base object

            while (next != 0)
            {
                if (next == basact)
                {
                    activeObjectIndex = i;
                }

                PopulatedStructure objBase = file.GetStructuresByAddress(next)[0];
                PopulatedStructure obj     = file.GetStructuresByAddress(objBase["object"].GetValueAsPointer())[0];
                Field data = obj["data"];
                ulong ptr  = data.GetValueAsPointer(); // this will be 0 for objects of the Empty type
                if (ptr == 0)
                {
                    objects.Add(new BlenderObject(file, obj));
                }
                else
                {
                    int SDNAIndex = file.GetBlockByAddress(ptr).SDNAIndex;
                    if (validTypeNames.Contains(file.StructureDNA.StructureList[SDNAIndex].StructureTypeName))
                    {
                        objects.Add(new BlenderObject(file, obj));
                    }
                }

                next = objBase["next"].GetValueAsPointer();
                i++;
            }
        }
Example #3
0
        internal Mesh(PopulatedStructure mesh, BlenderFile file)
        {
            FileBlock materialArray;

            int pointerSize = mesh["mat"].Size;

            Name = new string(mesh["id.name"].GetValueAsCharArray()).Split('\0')[0].Substring(2);
            ulong mat = mesh["mat"].GetValueAsPointer();

            if (mat == 0 || (materialArray = file.GetBlockByAddress(mat)).Size % pointerSize != 0)
            {
                Materials = new Material[0];
            }
            else
            {
                int count = materialArray.Size % pointerSize;
                Materials = new Material[count];
                for (int i = 0; i < count; i++)
                {
                    Materials[i] = Material.GetOrCreateMaterial(
                        file,
                        file.GetStructuresByAddress(
                            pointerSize == 4 ? BitConverter.ToUInt32(materialArray.Data, count * pointerSize) :
                            BitConverter.ToUInt64(materialArray.Data, count * pointerSize)
                            )[0]
                        );
                }
            }
            float[] vectorTemp = mesh["loc"].GetValueAsFloatArray();
            Location   = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);
            vectorTemp = mesh["rot"].GetValueAsFloatArray();
            Rotation   = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);
            vectorTemp = mesh["size"].GetValueAsFloatArray();
            Size       = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);

            MeshBuilder primordialMesh = MeshBuilder.StartMesh(Name);

            // both structures use the same vertex structure
            List <Vector3> verts = new List <Vector3>();
            List <short[]> unconvertedNormals = new List <short[]>();

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mvert"].GetValueAsPointer()))
            {
                float[] vector = s["co"].GetValueAsFloatArray();
                unconvertedNormals.Add(s["no"].GetValueAsShortArray());
                verts.Add(new Vector3(vector[0], vector[1], vector[2]));
            }
            List <Vector3> normals = convertNormals(unconvertedNormals);

            VertexPositionNormalTexture[] vertices;
            BasicMaterialContent          bmc;

            // todo: not yet sure which format versions of Blender between 2.62 and 2.65 use.
            if (float.Parse(file.VersionNumber) >= 2.66f) // uses edges, loops, and polys (Blender 2.66+)
            {
                vertices = loadNewModel(file, mesh, verts, normals, out bmc);
            }
            else // uses MFace (Blender 2.49-2.61)
            {
                vertices = loadOldModel(file, mesh, verts, normals, out bmc);
            }

            MeshBuilder mb = MeshBuilder.StartMesh(Name);

            foreach (VertexPositionNormalTexture v in vertices)
            {
                mb.CreatePosition(v.Position);
            }
            int uvChannel     = mb.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0));
            int normalChannel = mb.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());
            int j             = 0;

            foreach (VertexPositionNormalTexture v in vertices)
            {
                mb.SetVertexChannelData(uvChannel, v.TextureCoordinate);
                mb.SetVertexChannelData(normalChannel, v.Normal);

                mb.AddTriangleVertex(j++);
            }
        }
Example #4
0
        private VertexPositionNormalTexture[] loadNewModel(BlenderFile file, PopulatedStructure mesh, List <Vector3> verts, List <Vector3> normals, out BasicMaterialContent bmc)
        {
            List <VertexPositionNormalTexture> output = new List <VertexPositionNormalTexture>();

            List <Vector2> edges = new List <Vector2>(); // using x as index1 and y as index2

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["medge"].GetValueAsPointer()))
            {
                edges.Add(new Vector2(s["v1"].GetValueAsInt(), s["v2"].GetValueAsInt()));
            }
            // a "loop" is a vertex index and an edge index. Groups of these are used to define a "poly", which is a face.
            List <Vector2> loops = new List <Vector2>(); // using x as "v" and y as "e"

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mloop"].GetValueAsPointer()))
            {
                loops.Add(new Vector2(s["v"].GetValueAsInt(), s["e"].GetValueAsInt()));
            }
            List <Vector2> uvLoops = null;                                                                              // using x as u and y as v

            Vector2[] backupUVs = new[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) }; // in case uvLoops is null
            if (mesh["mloopuv"].GetValueAsPointer() != 0)
            {
                uvLoops = new List <Vector2>();
                foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mloopuv"].GetValueAsPointer()))
                {
                    float[] uv = s["uv"].GetValueAsFloatArray();
                    uvLoops.Add(new Vector2(uv[0], uv[1]));
                }
            }
            List <Vector2> polys = new List <Vector2>(); // using x as "loopstart" and y as "totloop" (loop length)

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mpoly"].GetValueAsPointer()))
            {
                polys.Add(new Vector2(s["loopstart"].GetValueAsInt(), s["totloop"].GetValueAsInt()));
            }
            // assume all faces use same texture for now
            //if(mesh["mtpoly"].GetValueAsPointer() != 0)
            //{
            //    try
            //    {
            //        // todo: sometimes this line fails, probably due to "assume all faces use same texture"
            //        PopulatedStructure image = file.GetStructuresByAddress(file.GetStructuresByAddress(mesh["mtpoly"].GetValueAsPointer())[0]["tpage"].GetValueAsPointer())[0];
            //        if(image["packedfile"].GetValueAsPointer() != 0)
            //        {
            //            byte[] rawImage = file.GetBlockByAddress(file.GetStructuresByAddress(image["packedfile"].GetValueAsPointer())[0]["data"].GetValueAsPointer()).Data;
            //            using(Stream s = new MemoryStream(rawImage))
            //                texture = Texture2D.FromStream(GraphicsDevice, s);
            //        }
            //        else
            //        {
            //            string texturePath = image["name"].ToString().Split('\0')[0].Replace("/", "\\").Replace("\\\\", "\\");
            //            string filePath = file.GetStructuresOfType("FileGlobal")[0]["filename"].ToString();
            //            filePath = filePath.Substring(0, filePath.LastIndexOf('\\'));
            //            using(Stream s = File.Open((filePath + texturePath).Replace("\'", ""), FileMode.Open, FileAccess.Read))
            //                texture = Texture2D.FromStream(GraphicsDevice, s);
            //        }
            //    }
            //    catch
            //    {
            //        texture = defaultTex;
            //    }
            //}
            //else
            //{
            //    texture = new Texture2D(GraphicsDevice, 1, 1);
            //    texture.SetData(new Color[] { Color.Gray });
            //}
            // loops of length 3 are triangles and can be directly added to the vertex list. loops of length 4
            // are quads, and have to be split into two triangles.
            foreach (Vector2 poly in polys)
            {
                Vector2[] faceEdges  = new Vector2[(int)poly.Y];
                Vector2[] faceUVs    = new Vector2[faceEdges.Length];
                int       j          = 0;
                int       loopOffset = (int)poly.X;
                for (int i = loopOffset; i < (int)poly.Y + loopOffset; i++)
                {
                    faceEdges[j] = edges[(int)loops[i].Y];
                    faceUVs[j]   = uvLoops == null ? backupUVs[i - loopOffset] : uvLoops[i];
                    j++;
                }
                Vector3[] faceVerts       = new Vector3[faceEdges.Length];
                Vector3[] faceVertNormals = new Vector3[faceEdges.Length];
                for (int i = 0; i < faceEdges.Length; i++)
                {
                    int index = (int)(loops[loopOffset + i].X == faceEdges[i].X ? faceEdges[i].Y : faceEdges[i].X);
                    faceVerts[i]       = verts[index];
                    faceVertNormals[i] = normals[index];
                }
                if (faceVerts.Length == 3) // already a triangle
                {
                    // push 0 to the end
                    Vector2 temp = faceUVs[0];
                    faceUVs[0] = faceUVs[1];
                    faceUVs[1] = temp;
                    temp       = faceUVs[1];
                    faceUVs[1] = faceUVs[2];
                    faceUVs[2] = temp;

                    for (int i = 2; i >= 0; i--) // 2, 1, 0
                    {
                        output.Add(new VertexPositionNormalTexture(faceVerts[i], faceVertNormals[i], faceUVs[i]));
                    }
                }
                else if (faceVerts.Length == 4) // quad, split into tris
                {
                    // swap 3 with 1 and 2 with 3
                    Vector2 temp = faceUVs[1];
                    faceUVs[1] = faceUVs[3];
                    faceUVs[3] = temp;
                    temp       = faceUVs[2];
                    faceUVs[2] = faceUVs[3];
                    faceUVs[3] = temp;

                    // 2, 1, 0
                    for (int i = 2; i >= 0; i--)
                    {
                        output.Add(new VertexPositionNormalTexture(faceVerts[i], faceVertNormals[i], faceUVs[i]));
                    }

                    // 3, 2, 0
                    for (int i = 3; i >= 1; i--)
                    {
                        output.Add(new VertexPositionNormalTexture(faceVerts[i == 1 ? 0 : i], faceVertNormals[i == 1 ? 0 : i], faceUVs[i == 1 ? 0 : i]));
                    }
                }
            }
            bmc = new BasicMaterialContent();
            return(output.ToArray());
        }
Example #5
0
        private VertexPositionNormalTexture[] loadOldModel(BlenderFile file, PopulatedStructure mesh, List <Vector3> verts, List <Vector3> normals, out BasicMaterialContent bmc)
        {
            // I believe this function has a bug when used on a mesh that has unconnected chunks of vertices;
            // however the only file I currently have that exhibits this problem decompresses to 240MB when I use the HTML
            // renderer tool, so I can't feasibly poke through the data to see what's going wrong.

            List <VertexPositionNormalTexture> output = new List <VertexPositionNormalTexture>();

            bmc = new BasicMaterialContent();

            List <int[]>     faces  = new List <int[]>();
            List <float[, ]> tFaces = new List <float[, ]>();

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mface"].GetValueAsPointer()))
            {
                faces.Add(new[] { s["v1"].GetValueAsInt(), s["v2"].GetValueAsInt(), s["v3"].GetValueAsInt(), s["v4"].GetValueAsInt() });
            }
            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mtface"].GetValueAsPointer()))
            {
                tFaces.Add((float[, ])s["uv"].GetValueAsMultidimensionalArray());
            }

            // assume all faces use same texture
            PopulatedStructure image = file.GetStructuresByAddress(file.GetStructuresByAddress(mesh["mtface"].GetValueAsPointer())[0]["tpage"].GetValueAsPointer())[0];

            if (image["packedfile"].GetValueAsPointer() != 0)
            {
                byte[] rawImage = file.GetBlockByAddress(file.GetStructuresByAddress(image["packedfile"].GetValueAsPointer())[0]["data"].GetValueAsPointer()).Data;
                string filename = Name + "_" + new string(image["id.name"].GetValueAsCharArray()).Split('\0')[0].Substring(2);
                using (BinaryWriter s = new BinaryWriter(File.Open(filename, FileMode.Create)))
                    s.Write(rawImage);
                bmc.Texture = new ExternalReference <TextureContent>(filename);
            }
            else
            {
                try
                {
                    string texturePath = image["name"].ToString().Split('\0')[0].Replace("/", "\\").Replace("\\\\", "\\");
                    string filePath    = file.GetStructuresOfType("FileGlobal")[0]["filename"].ToString();
                    filePath = filePath.Substring(0, filePath.LastIndexOf('\\'));
                    File.Copy((filePath + texturePath).Replace("\'", ""), Name + "_" + new string(image["id.name"].GetValueAsCharArray()).Split('\0')[0].Substring(2));
                }
                catch
                {
                    //texture = defaultTex;
                }
            }

            int j = 0;

            foreach (int[] face in faces)
            {
                Vector3[] faceVerts       = new Vector3[face.Length];
                Vector3[] faceVertNormals = new Vector3[face.Length];
                Vector2[] faceUVs         = new Vector2[face.Length];
                for (int i = 0; i < face.Length; i++)
                {
                    faceVerts[i]       = verts[face[i]];
                    faceVertNormals[i] = normals[face[i]];
                    faceUVs[i]         = new Vector2(tFaces[j][i, 0], tFaces[j][i, 1]);
                }
                j++;

                // 2, 1, 0
                for (int i = 2; i >= 0; i--)
                {
                    output.Add(new VertexPositionNormalTexture(faceVerts[i], faceVertNormals[i], faceUVs[i]));
                }

                // 3, 2, 0
                for (int i = 3; i >= 1; i--)
                {
                    output.Add(new VertexPositionNormalTexture(faceVerts[i == 1 ? 0 : i], faceVertNormals[i == 1 ? 0 : i], faceUVs[i == 1 ? 0 : i]));
                }
            }

            return(output.ToArray());
        }
Example #6
0
        public BlenderModel(Structure mesh, Structure obj, GraphicsDevice GraphicsDevice, BlenderFile file)
        {
            // If I was less sloppy, I would have used casts to generics instead of the raw dynamic Value on IField,
            // but I'm lazy and this code is soon to go away anyway.
            if(defaultTex == null)
            {
                defaultTex = new Texture2D(GraphicsDevice, 1, 1);
                defaultTex.SetData(new Color[] { Color.Gray });
            }

            this.GraphicsDevice = GraphicsDevice;

            // both structures use the same vertex structure
            List<Vector3> verts = new List<Vector3>();
            List<short[]> unconvertedNormals = new List<short[]>();
            foreach(Structure s in mesh["mvert"].Dereference())
            {
                float[] vector = s["co"].Value;
                unconvertedNormals.Add(s["no"].Value);
                verts.Add(new Vector3(vector[0], vector[1], vector[2]));
            }

            List<Vector3> normals = convertNormals(unconvertedNormals);
            VertexPositionNormalTexture[] vertices;
            Texture2D texture;
            // todo: not yet sure which format versions of Blender between 2.62 and 2.65 use.
            if(float.Parse(file.VersionNumber) >= 2.66f) // uses edges, loops, and polys (Blender 2.66+)
                vertices = loadNewModel(file, mesh, verts, normals, out texture);
            else // uses MFace (Blender 2.49-2.61)
                vertices = loadOldModel(file, mesh, verts, normals, out texture);

            VertexPositionColor[] normalVerts = new VertexPositionColor[normals.Count * 2];
            for(int i = 0; i < verts.Count * 2; i += 2)
            {
                normalVerts[i] = new VertexPositionColor(verts[i / 2], Color.MidnightBlue);
                normalVerts[i + 1] = new VertexPositionColor(verts[i / 2] + normals[i / 2] * 0.25f, Color.MidnightBlue);
            }

            float[] posVector = obj["loc"].Value;
            this.Position = new Vector3(posVector[0], posVector[1], posVector[2]);
            float[] scaleVector = obj["size"].Value;
            this.Scale = new Vector3(scaleVector[0], scaleVector[1], scaleVector[2]);
            float[] rotVector = obj["rot"].Value;
            this.Rotation = Quaternion.CreateFromYawPitchRoll(rotVector[1], rotVector[0], rotVector[2]);
            this.Vertices = vertices;
            this.NormalVerts = normalVerts;
            this.VertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionNormalTexture.VertexDeclaration, this.Vertices.Length, BufferUsage.None);
            this.NormalBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.VertexDeclaration, this.NormalVerts.Length, BufferUsage.None);
            this.Texture = texture;
            this.Name = new string(obj["id.name"].Value).Split('\0')[0].Substring(2); // remove null term, remove first two characters

            // LSB on represents layer 1, next bit is layer 2, etc
            this.Layer = obj["lay"].Value;

            // the "mat" field is a pointer to a pointer (technically, a pointer to an array of pointers)
            // I'm not sure what to do with multiple materials, so just use the first one
            ulong blockaddr = mesh["mat"].Value;
            if(blockaddr != 0)
            {
                Structure mat = file.GetStructuresByAddress(BitConverter.ToUInt32(file.GetBlockByAddress(blockaddr).Data, 0))[0];
                this.TextureHasTransparency = mat["game.alpha_blend"].Value != 0;

                int mode = mat["mode"].Value;
                this.LightingEnabled = (mode & 4) == 0; // as far as I can tell, this is where "shadeless" is stored.
            }
            else
                this.LightingEnabled = true;
        }