Exemple #1
0
        //mxd. Normals calculation algorithm taken from OpenGl wiki
        private void CalculateNormals()
        {
            if (triangles == 0)
            {
                return;
            }

            BoundingBoxSizes bbs = new BoundingBoxSizes(vertices[0]);

            for (int i = 0; i < triangles; i++)
            {
                int         startindex = i * 3;
                WorldVertex p1         = vertices[startindex];
                WorldVertex p2         = vertices[startindex + 1];
                WorldVertex p3         = vertices[startindex + 2];

                Vector3f U = new Vector3f(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);
                Vector3f V = new Vector3f(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z);

                p1.nx = p2.nx = p3.nx = -(U.Y * V.Z - U.Z * V.Y);
                p1.ny = p2.ny = p3.ny = -(U.Z * V.X - U.X * V.Z);
                p1.nz = p2.nz = p3.nz = -(U.X * V.Y - U.Y * V.X);

                vertices[startindex]     = p1;
                vertices[startindex + 1] = p2;
                vertices[startindex + 2] = p3;

                BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p1);
                BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p2);
                BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p3);
            }

            boundingBox = BoundingBoxTools.CalculateBoundingPlane(bbs);
        }
Exemple #2
0
        private static void LoadModel(ModelData mde, List <DataReader> containers, Device device)
        {
            mde.Model = new GZModel();
            BoundingBoxSizes bbs    = new BoundingBoxSizes();
            MD3LoadResult    result = new MD3LoadResult();

            //load models and textures
            for (int i = 0; i < mde.ModelNames.Count; i++)
            {
                // Use model skins?
                // INFO: Skin MODELDEF property overrides both embedded surface names and ones set using SurfaceSkin MODELDEF property
                Dictionary <int, string> skins = null;
                if (string.IsNullOrEmpty(mde.SkinNames[i]))
                {
                    skins = (mde.SurfaceSkinNames[i].Count > 0 ? mde.SurfaceSkinNames[i] : new Dictionary <int, string>());
                }

                // Load mesh
                MemoryStream ms = LoadFile(containers, mde.ModelNames[i], true);
                if (ms == null)
                {
                    General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": unable to find file.");
                    continue;
                }

                string ext = Path.GetExtension(mde.ModelNames[i]);
                switch (ext)
                {
                case ".md3":
                    if (!string.IsNullOrEmpty(mde.FrameNames[i]))
                    {
                        General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": frame names are not supported for MD3 models!");
                        continue;
                    }
                    result = ReadMD3Model(ref bbs, skins, ms, device, mde.FrameIndices[i]);
                    break;

                case ".md2":
                    result = ReadMD2Model(ref bbs, ms, device, mde.FrameIndices[i], mde.FrameNames[i]);
                    break;

                default:
                    result.Errors = "model format is not supported";
                    break;
                }

                ms.Close();

                //got errors?
                if (!String.IsNullOrEmpty(result.Errors))
                {
                    General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + result.Errors);
                }
                else
                {
                    //add loaded data to ModeldefEntry
                    mde.Model.Meshes.AddRange(result.Meshes);

                    //prepare UnknownTexture3D... just in case :)
                    if (General.Map.Data.UnknownTexture3D.Texture == null || General.Map.Data.UnknownTexture3D.Texture.Disposed)
                    {
                        General.Map.Data.UnknownTexture3D.CreateTexture();
                    }

                    //load texture
                    List <string> errors = new List <string>();

                    // Texture not defined in MODELDEF?
                    if (skins != null)
                    {
                        //try to use model's own skins
                        for (int m = 0; m < result.Meshes.Count; m++)
                        {
                            if (string.IsNullOrEmpty(result.Skins[m]))
                            {
                                mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
                                errors.Add("texture not found in MODELDEF or model skin.");
                                continue;
                            }

                            string path = result.Skins[m].Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
                            ext = Path.GetExtension(path);

                            if (Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1)
                            {
                                mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
                                errors.Add("image format \"" + ext + "\" is not supported!");
                                continue;
                            }

                            //relative path?
                            if (path.IndexOf(Path.DirectorySeparatorChar) == -1)
                            {
                                path = Path.Combine(Path.GetDirectoryName(mde.ModelNames[i]), path);
                            }

                            Texture t = LoadTexture(containers, path, device);

                            if (t == null)
                            {
                                mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
                                errors.Add("unable to load skin \"" + result.Skins[m] + "\"");
                                continue;
                            }

                            mde.Model.Textures.Add(t);
                        }
                    }
                    //Try to use texture loaded from MODELDEFS
                    else
                    {
                        Texture t = LoadTexture(containers, mde.SkinNames[i], device);
                        if (t == null)
                        {
                            mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
                            errors.Add("unable to load texture \"" + mde.SkinNames[i] + "\"");
                        }
                        else
                        {
                            mde.Model.Textures.Add(t);
                        }
                    }

                    //report errors
                    if (errors.Count > 0)
                    {
                        foreach (string e in errors)
                        {
                            General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + e);
                        }
                    }
                }
            }

            //clear unneeded data
            mde.SkinNames  = null;
            mde.ModelNames = null;

            if (mde.Model.Meshes == null || mde.Model.Meshes.Count == 0)
            {
                mde.Model = null;
                return;
            }

            //scale bbs
            bbs.MaxX = (int)(bbs.MaxX * mde.Scale.X);
            bbs.MinX = (int)(bbs.MinX * mde.Scale.X);
            bbs.MaxY = (int)(bbs.MaxY * mde.Scale.Y);
            bbs.MinY = (int)(bbs.MinY * mde.Scale.Y);

            //calculate model radius
            mde.Model.Radius = Math.Max(Math.Max(Math.Abs(bbs.MinY), Math.Abs(bbs.MaxY)), Math.Max(Math.Abs(bbs.MinX), Math.Abs(bbs.MaxX)));
        }
Exemple #3
0
        private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, Stream s, Device device, int frame, string framename)
        {
            long          start  = s.Position;
            MD3LoadResult result = new MD3LoadResult();

            using (var br = new BinaryReader(s, Encoding.ASCII))
            {
                string magic = ReadString(br, 4);
                if (magic != "IDP2")                //magic number: "IDP2"
                {
                    result.Errors = "unknown header: expected \"IDP2\", but got \"" + magic + "\"";
                    return(result);
                }

                int modelVersion = br.ReadInt32();
                if (modelVersion != 8)                //MD2 version. Must be equal to 8
                {
                    result.Errors = "expected MD3 version 15, but got " + modelVersion;
                    return(result);
                }

                int texWidth  = br.ReadInt32();
                int texHeight = br.ReadInt32();
                int framesize = br.ReadInt32();  // Size of one frame in bytes
                s.Position += 4;                 //Number of textures
                int num_verts = br.ReadInt32();  //Number of vertices
                int num_uv    = br.ReadInt32();  //The number of UV coordinates in the model
                int num_tris  = br.ReadInt32();  //Number of triangles
                s.Position += 4;                 //Number of OpenGL commands
                int num_frames = br.ReadInt32(); //Total number of frames

                // Sanity checks
                if (frame < 0 || frame >= num_frames)
                {
                    result.Errors = "frame " + frame + " is outside of model's frame range [0.." + (num_frames - 1) + "]";
                    return(result);
                }

                s.Position += 4;                    //Offset to skin names (each skin name is an unsigned char[64] and are null terminated)
                int ofs_uv        = br.ReadInt32(); //Offset to s-t texture coordinates
                int ofs_tris      = br.ReadInt32(); //Offset to triangles
                int ofs_animFrame = br.ReadInt32(); //An offset to the first animation frame

                List <int>         polyIndecesList = new List <int>();
                List <int>         uvIndecesList   = new List <int>();
                List <Vector2>     uvCoordsList    = new List <Vector2>();
                List <WorldVertex> vertList        = new List <WorldVertex>();

                // Polygons
                s.Position = ofs_tris + start;

                for (int i = 0; i < num_tris; i++)
                {
                    polyIndecesList.Add(br.ReadUInt16());
                    polyIndecesList.Add(br.ReadUInt16());
                    polyIndecesList.Add(br.ReadUInt16());

                    uvIndecesList.Add(br.ReadUInt16());
                    uvIndecesList.Add(br.ReadUInt16());
                    uvIndecesList.Add(br.ReadUInt16());
                }

                // UV coords
                s.Position = ofs_uv + start;

                for (int i = 0; i < num_uv; i++)
                {
                    uvCoordsList.Add(new Vector2((float)br.ReadInt16() / texWidth, (float)br.ReadInt16() / texHeight));
                }

                // Frames
                // Find correct frame
                if (!string.IsNullOrEmpty(framename))
                {
                    // Skip frames untill frame name matches
                    bool framefound = false;
                    for (int i = 0; i < num_frames; i++)
                    {
                        s.Position  = ofs_animFrame + start + i * framesize;
                        s.Position += 24;                         // Skip scale and translate
                        string curframename = ReadString(br, 16).ToLowerInvariant();

                        if (curframename == framename)
                        {
                            // Step back so scale and translate can be read
                            s.Position -= 40;
                            framefound  = true;
                            break;
                        }
                    }

                    // No dice? Bail out!
                    if (!framefound)
                    {
                        result.Errors = "unable to find frame \"" + framename + "\"!";
                        return(result);
                    }
                }
                else
                {
                    // If we have frame number, we can go directly to target frame
                    s.Position = ofs_animFrame + start + frame * framesize;
                }

                Vector3 scale     = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
                Vector3 translate = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());

                s.Position += 16;                 // Skip frame name

                // Prepare to fix rotation angle
                float angleOfsetCos = (float)Math.Cos(-Angle2D.PIHALF);
                float angleOfsetSin = (float)Math.Sin(-Angle2D.PIHALF);

                //verts
                for (int i = 0; i < num_verts; i++)
                {
                    WorldVertex v = new WorldVertex();

                    v.x = (br.ReadByte() * scale.X + translate.X);
                    v.y = (br.ReadByte() * scale.Y + translate.Y);
                    v.z = (br.ReadByte() * scale.Z + translate.Z);

                    // Fix rotation angle
                    float rx = angleOfsetCos * v.x - angleOfsetSin * v.y;
                    float ry = angleOfsetSin * v.x + angleOfsetCos * v.y;
                    v.y = ry;
                    v.x = rx;

                    vertList.Add(v);
                    s.Position += 1;                     //vertex normal
                }

                for (int i = 0; i < polyIndecesList.Count; i++)
                {
                    WorldVertex v = vertList[polyIndecesList[i]];

                    //bounding box
                    BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, new WorldVertex(v.y, v.x, v.z));

                    //uv
                    float tu = uvCoordsList[uvIndecesList[i]].X;
                    float tv = uvCoordsList[uvIndecesList[i]].Y;

                    //uv-coordinates already set?
                    if (v.c == -1 && (v.u != tu || v.v != tv))
                    {
                        //add a new vertex
                        vertList.Add(new WorldVertex(v.x, v.y, v.z, -1, tu, tv));
                        polyIndecesList[i] = vertList.Count - 1;
                    }
                    else
                    {
                        v.u = tu;
                        v.v = tv;
                        v.c = -1;                         //set color to white

                        //return to proper place
                        vertList[polyIndecesList[i]] = v;
                    }
                }

                //mesh
                Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements);

                using (DataStream stream = mesh.LockVertexBuffer(LockFlags.None))
                {
                    stream.WriteRange(vertList.ToArray());
                }
                mesh.UnlockVertexBuffer();

                using (DataStream stream = mesh.LockIndexBuffer(LockFlags.None))
                {
                    stream.WriteRange(polyIndecesList.ToArray());
                }
                mesh.UnlockIndexBuffer();

                mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort);

                //store in result
                result.Meshes.Add(mesh);
                result.Skins.Add("");                 //no skin support for MD2
            }

            return(result);
        }
Exemple #4
0
        private static string ReadSurface(ref BoundingBoxSizes bbs, ref string skin, BinaryReader br, List <int> polyIndecesList, List <WorldVertex> vertList, int frame)
        {
            int  vertexOffset = vertList.Count;
            long start        = br.BaseStream.Position;

            string magic = ReadString(br, 4);

            if (magic != "IDP3")
            {
                return("error while reading surface. Unknown header: expected \"IDP3\", but got \"" + magic + "\"");
            }

            string name         = ReadString(br, 64);
            int    flags        = br.ReadInt32();
            int    numFrames    = br.ReadInt32();   //Number of animation frames. This should match NUM_FRAMES in the MD3 header.
            int    numShaders   = br.ReadInt32();   //Number of Shader objects defined in this Surface, with a limit of MD3_MAX_SHADERS. Current value of MD3_MAX_SHADERS is 256.
            int    numVerts     = br.ReadInt32();   //Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096.
            int    numTriangles = br.ReadInt32();   //Number of Triangle objects defined in this Surface, maximum of MD3_MAX_TRIANGLES. Current value of MD3_MAX_TRIANGLES is 8192.
            int    ofsTriangles = br.ReadInt32();   //Relative offset from SURFACE_START where the list of Triangle objects starts.
            int    ofsShaders   = br.ReadInt32();
            int    ofsST        = br.ReadInt32();   //Relative offset from SURFACE_START where the list of ST objects (s-t texture coordinates) starts.
            int    ofsNormal    = br.ReadInt32();   //Relative offset from SURFACE_START where the list of Vertex objects (X-Y-Z-N vertices) starts.
            int    ofsEnd       = br.ReadInt32();   //Relative offset from SURFACE_START to where the Surface object ends.

            // Sanity check
            if (frame < 0 || frame >= numFrames)
            {
                return("frame " + frame + " is outside of model's frame range [0.." + (numFrames - 1) + "]");
            }

            // Polygons
            if (start + ofsTriangles != br.BaseStream.Position)
            {
                br.BaseStream.Position = start + ofsTriangles;
            }

            for (int i = 0; i < numTriangles * 3; i++)
            {
                polyIndecesList.Add(vertexOffset + br.ReadInt32());
            }

            // Shaders
            if (start + ofsShaders != br.BaseStream.Position)
            {
                br.BaseStream.Position = start + ofsShaders;
            }

            skin = ReadString(br, 64);             //we are interested only in the first one

            // Vertices
            if (start + ofsST != br.BaseStream.Position)
            {
                br.BaseStream.Position = start + ofsST;
            }

            for (int i = 0; i < numVerts; i++)
            {
                WorldVertex v = new WorldVertex();
                v.c = -1;                 //white
                v.u = br.ReadSingle();
                v.v = br.ReadSingle();

                vertList.Add(v);
            }

            // Positions and normals
            long vertoffset = start + ofsNormal + numVerts * 8 * frame;             // The length of Vertex struct is 8 bytes

            if (br.BaseStream.Position != vertoffset)
            {
                br.BaseStream.Position = vertoffset;
            }

            for (int i = vertexOffset; i < vertexOffset + numVerts; i++)
            {
                WorldVertex v = vertList[i];

                //read vertex
                v.y = -(float)br.ReadInt16() / 64;
                v.x = (float)br.ReadInt16() / 64;
                v.z = (float)br.ReadInt16() / 64;

                //bounding box
                BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, v);

                var lat = br.ReadByte() * (2 * Math.PI) / 255.0;
                var lng = br.ReadByte() * (2 * Math.PI) / 255.0;

                v.nx = (float)(Math.Sin(lng) * Math.Sin(lat));
                v.ny = -(float)(Math.Cos(lng) * Math.Sin(lat));
                v.nz = (float)(Math.Cos(lat));

                vertList[i] = v;
            }

            if (start + ofsEnd != br.BaseStream.Position)
            {
                br.BaseStream.Position = start + ofsEnd;
            }
            return("");
        }
Exemple #5
0
        internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary <int, string> skins, Stream s, Device device, int frame)
        {
            long          start  = s.Position;
            MD3LoadResult result = new MD3LoadResult();

            using (var br = new BinaryReader(s, Encoding.ASCII))
            {
                string magic = ReadString(br, 4);
                if (magic != "IDP3")
                {
                    result.Errors = "unknown header: expected \"IDP3\", but got \"" + magic + "\"";
                    return(result);
                }

                int modelVersion = br.ReadInt32();
                if (modelVersion != 15)                //MD3 version. Must be equal to 15
                {
                    result.Errors = "expected MD3 version 15, but got " + modelVersion;
                    return(result);
                }

                s.Position += 76;
                int numSurfaces = br.ReadInt32();
                s.Position += 12;
                int ofsSurfaces = br.ReadInt32();

                s.Position = ofsSurfaces + start;

                List <int>         polyIndecesList = new List <int>();
                List <WorldVertex> vertList        = new List <WorldVertex>();

                Dictionary <string, List <List <int> > > polyIndecesListsPerTexture = new Dictionary <string, List <List <int> > >(StringComparer.Ordinal);
                Dictionary <string, List <WorldVertex> > vertListsPerTexture        = new Dictionary <string, List <WorldVertex> >(StringComparer.Ordinal);
                Dictionary <string, List <int> >         vertexOffsets = new Dictionary <string, List <int> >(StringComparer.Ordinal);
                bool useskins = false;

                for (int c = 0; c < numSurfaces; c++)
                {
                    string skin  = "";
                    string error = ReadSurface(ref bbs, ref skin, br, polyIndecesList, vertList, frame);

                    if (!string.IsNullOrEmpty(error))
                    {
                        result.Errors = error;
                        return(result);
                    }

                    // Pick a skin to use
                    if (skins == null)
                    {
                        // skins is null when Skin MODELDEF property is set
                        skin = string.Empty;
                    }
                    else if (skins.ContainsKey(c))
                    {
                        // Overrtide surface skin with SurfaceSkin MODELDEF property
                        skin = skins[c];
                    }

                    if (!string.IsNullOrEmpty(skin))
                    {
                        useskins = true;

                        if (polyIndecesListsPerTexture.ContainsKey(skin))
                        {
                            polyIndecesListsPerTexture[skin].Add(polyIndecesList);
                            vertListsPerTexture[skin].AddRange(vertList.ToArray());
                            vertexOffsets[skin].Add(vertList.Count);
                        }
                        else
                        {
                            polyIndecesListsPerTexture.Add(skin, new List <List <int> > {
                                polyIndecesList
                            });
                            vertListsPerTexture.Add(skin, vertList);
                            vertexOffsets.Add(skin, new List <int> {
                                vertList.Count
                            });
                        }

                        //reset lists
                        polyIndecesList = new List <int>();
                        vertList        = new List <WorldVertex>();
                    }
                }

                if (!useskins)
                {
                    //create mesh
                    CreateMesh(device, ref result, vertList, polyIndecesList);
                    result.Skins.Add("");
                }
                else
                {
                    //create a mesh for each surface texture
                    foreach (KeyValuePair <string, List <List <int> > > group in polyIndecesListsPerTexture)
                    {
                        polyIndecesList = new List <int>();
                        int offset = 0;

                        //collect indices, fix vertex offsets
                        for (int i = 0; i < group.Value.Count; i++)
                        {
                            if (i > 0)
                            {
                                //TODO: Damn I need to rewrite all of this stuff from scratch...
                                offset += vertexOffsets[group.Key][i - 1];
                                for (int c = 0; c < group.Value[i].Count; c++)
                                {
                                    group.Value[i][c] += offset;
                                }
                            }
                            polyIndecesList.AddRange(group.Value[i].ToArray());
                        }

                        CreateMesh(device, ref result, vertListsPerTexture[group.Key], polyIndecesList);
                        result.Skins.Add(group.Key.ToLowerInvariant());
                    }
                }
            }

            return(result);
        }