Beispiel #1
0
        public static RrfModel LoadModel(string filepath)
        {
            var result = new RrfModel();

            if (!File.Exists(filepath))
            {
                throw new FileNotFoundException();
            }

            using (var fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
            {
                try
                {
                    fileStream.Seek(0x0, SeekOrigin.Begin);

                    result.UnknownInt  = fileStream.ReadInt32();
                    result.MeshCount   = fileStream.ReadInt32();
                    result.VertexTotal = fileStream.ReadInt32();
                    result.UnknownInt2 = fileStream.ReadInt32();
                    result.UnknownInt3 = fileStream.ReadInt32();

                    result.Meshes = new List <RrfMesh>();

                    LoadMesh(ref result, fileStream);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception at {0:X8}:\n{1}\n", fileStream.Position, e);
                }
            }

            return(result);
        }
Beispiel #2
0
        private static void LoadMesh(ref RrfModel rrfModel, FileStream fileStream)
        {
            // Load header info
            LoadMeshHeaders(ref rrfModel, ref fileStream);

            // Retrieve face information
            LoadMeshFaces(ref rrfModel, ref fileStream);

            // Read remaining unknown bytes
            rrfModel.UnknownAddressRange.Start = fileStream.Position;

            do
            {
                var intBuffer = new byte[4];

                fileStream.Read(intBuffer);

                if ((intBuffer[0] == 0x0 &&
                     intBuffer[1] == 0xFF &&
                     intBuffer[2] == 0x0 &&
                     intBuffer[3] == 0xFF) ||
                    (intBuffer[0] == 0xFF &&
                     intBuffer[1] == 0x0 &&
                     intBuffer[2] == 0xFF &&
                     intBuffer[3] == 0x0))
                {
                    break;
                }

                rrfModel.UnknownEnding.Add(BitConverter.ToInt32(intBuffer));
            } while (fileStream.Position < fileStream.Length);

            rrfModel.UnknownAddressRange.End = fileStream.Position;
        }
        /// <summary>
        /// Sum all the origin offsets in the parent/child tree of the model
        /// </summary>
        /// <param name="index"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        private static int3 GetParentOffset(int index, RrfModel model)
        {
            var  sumOffset    = new int3();
            var  currentIndex = index;
            bool foundParentAtIndex;

            do
            {
                foundParentAtIndex = false;

                for (var i = 0; i < model.Meshes.Count; i++)
                {
                    var mesh = model.Meshes[i];

                    if (!mesh.ChildMeshes.Contains(currentIndex))
                    {
                        continue;
                    }

                    foundParentAtIndex = true;
                    sumOffset         += mesh.Origin;
                    currentIndex       = i;
                    break;
                }
            } while (foundParentAtIndex);

            return(sumOffset);
        }
        public static void Export(RrfModel model, string exportPath)
        {
            var dirPath = Path.GetDirectoryName(exportPath);

            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }

            var outputStr = ModelToString(model, true);

            File.WriteAllText(exportPath, outputStr);
        }
        private static void OutputFaces(RrfModel model, StringBuilder sb, RrfMesh mesh, int index)
        {
            sb.AppendLine($"# {mesh.FaceCount} Faces");
            sb.AppendLine($"# {mesh.FaceAddressRange.Start}");
            foreach (var face in mesh.Faces)
            {
                var vertexIndexOffset = 1 + model.Meshes.Take(index).Sum(f => f.VertexCount);

                sb.AppendLine(
                    $"f {face.VertexIndexes[0] + vertexIndexOffset}" +
                    $" {face.VertexIndexes[1] + vertexIndexOffset}" +
                    $" {face.VertexIndexes[2] + vertexIndexOffset}" +
                    (face.IsQuad ? $" {face.VertexIndexes[3] + vertexIndexOffset}" : string.Empty));
            }

            sb.AppendLine($"# {mesh.FaceAddressRange.End}");
        }
        // Code lifted from https://wiki.unity3d.com/index.php/ExportOBJ
        public static string ModelToString(RrfModel model, bool scaleMesh = true)
        {
            var sb = new StringBuilder();

            for (var index = 0; index < model.Meshes.Count; index++)
            {
                var mesh   = model.Meshes[index];
                var parent = model.Meshes.FirstOrDefault(f => f.ChildMeshes.Contains(index));

                sb.AppendLine($"o {mesh.Name}");
                sb.AppendLine($"# Header Range {mesh.HeaderAddressRange.Start} to {mesh.HeaderAddressRange.End}");

                OutputVertices(scaleMesh, sb, mesh, GetParentOffset(index, model));

                OutputFaces(model, sb, mesh, index);

                sb.AppendLine($"# End of {mesh.Name}");

                sb.AppendLine();
            }

            return(sb.ToString());
        }
Beispiel #7
0
        private static void LoadMeshHeaders(ref RrfModel rrfModel, ref FileStream fileStream)
        {
            for (var meshIndex = 0; meshIndex < rrfModel.MeshCount; meshIndex++)
            {
                var mesh = new RrfMesh
                {
                    HeaderAddressRange = new AddressRange
                    {
                        Start = fileStream.Position
                    }
                };

                // Read mesh name at 0x14
                const int maxNameLength = 0xC;

                for (var i = 0; i < maxNameLength; i++)
                {
                    var nByte = fileStream.ReadByte();

                    // Escape on first 0 byte
                    if (nByte == 0x0)
                    {
                        break;
                    }

                    mesh.Name += (char)nByte;
                }

                // Skip mesh name field
                var nameOffset = maxNameLength - mesh.Name.Length - 1; // Minus one because we read a null byte
                fileStream.Seek(nameOffset, SeekOrigin.Current);
                var nameEnd = fileStream.Position;

                mesh.Origin = new int3(fileStream.ReadInt32(), fileStream.ReadInt32(), fileStream.ReadInt32());

                // Read bounds
                mesh.BoundingBox         = new int3(fileStream.ReadInt32());
                mesh.BoundingBoxOffset   = new int3(fileStream.ReadInt32());
                mesh.BoundingBox.Y       = fileStream.ReadInt32();
                mesh.BoundingBoxOffset.Y = fileStream.ReadInt32();
                mesh.BoundingBox.Z       = fileStream.ReadInt32();
                mesh.BoundingBoxOffset.Z = fileStream.ReadInt32();

                // Read unknown values
                const int unknownNumbersOffset = 0x44;
                while (fileStream.Position < nameEnd + unknownNumbersOffset)
                {
                    mesh.UnknownPreTypeBytes.Add(fileStream.ReadInt32());
                }

                // Mesh type byte at 0x64
                mesh.Type = fileStream.ReadByte();

                // Read unknown type bytes
                for (var i = 0; i < 0x3; i++)
                {
                    mesh.UnknownTypeBytes.Add(fileStream.ReadByte());
                }

                mesh.VertexCount = fileStream.ReadInt32();

                // Skip terminating(?) FF FF FF FF bytes
                fileStream.Seek(0x4, SeekOrigin.Current);

                // Read child elements
                mesh.ChildCount = fileStream.ReadInt32();

                for (var i = 0; i < mesh.ChildCount && mesh.ChildCount > 0; i++)
                {
                    mesh.ChildMeshes.Add(fileStream.ReadInt32());
                }

                // Skip remaining child elements range
                var unknownIntCount = (32 - mesh.ChildCount) * 4;   // 4x to skip by int size
                fileStream.Seek(unknownIntCount, SeekOrigin.Current);

                // Read address ints
                mesh.UnknownZeroValue = fileStream.ReadInt32(); // RolloverIndex

                mesh.FaceCount        = fileStream.ReadInt32();
                mesh.FaceAddressRange = new AddressRange(fileStream.ReadInt32(), fileStream.ReadInt32());

                mesh.DuplicateVertexValue = fileStream.ReadInt32(); // Duplicate(?) vertex count?

                mesh.VertexAddressRange  = new AddressRange(fileStream.ReadInt32(), fileStream.ReadInt32());
                mesh.UnknownAddressRange = new AddressRange(fileStream.ReadInt32(), fileStream.ReadInt32());

                mesh.HeaderAddressRange.End = fileStream.Position;

                // Skip duplicate address patterns
                fileStream.Seek(0xFC, SeekOrigin.Current);

                rrfModel.Meshes.Add(mesh);
            }
        }
Beispiel #8
0
        private static void LoadMeshFaces(ref RrfModel rrfModel, ref FileStream fileStream)
        {
            for (var i = 0; i < rrfModel.MeshCount; i++)
            {
                // Retrieve face vertex information
                fileStream.Seek(rrfModel.Meshes[i].FaceAddressRange.Start, SeekOrigin.Begin);

                for (var j = 0; j < rrfModel.Meshes[i].FaceCount; j++)
                {
                    // Get face information
                    var face = new RrfFace
                    {
                        AddressRange = new AddressRange
                        {
                            Start = fileStream.Position
                        },
                        VertexIndexes =
                        {
                            [0] = fileStream.ReadInt32(),
                            [1] = fileStream.ReadInt32(),
                            [2] = fileStream.ReadInt32(),
                            [3] = -1    // Read later
                        },
                        TextureIndex = fileStream.ReadByte(),
                    };

                    var textureBits = fileStream.Read4BitByte();

                    face.TextureFileIndex     = textureBits.Item1;
                    face.TextureRolloverIndex = textureBits.Item2;
                    face.TextureProperties    = new[]
                    {
                        fileStream.ReadByte(),
                            fileStream.ReadByte(),
                    };

                    face.VertexIndexes[3] = fileStream.ReadInt32(); // 4th vertex index is part of a properties set

                    // Get render properties
                    var faceRenderProperties = (FaceRenderProperties)fileStream.ReadByte();

                    face.IsQuad        = (faceRenderProperties & FaceRenderProperties.IsQuad) == FaceRenderProperties.IsQuad;
                    face.IsDoubleSided = (faceRenderProperties & FaceRenderProperties.IsDouble) == FaceRenderProperties.IsDouble;
                    face.IsSprite      = (faceRenderProperties & FaceRenderProperties.IsSprite) == FaceRenderProperties.IsSprite;
                    face.IsUnknown8    = (faceRenderProperties & FaceRenderProperties.Unknown8) == FaceRenderProperties.Unknown8;

                    // Get face shading value
                    var faceShading      = FaceShading.None;
                    var wireframeShading = FaceRenderProperties.FlatShading | FaceRenderProperties.PhongShading;

                    if ((faceRenderProperties & wireframeShading) == wireframeShading)
                    {
                        faceShading = FaceShading.Wireframe;
                    }
                    else if ((faceRenderProperties & FaceRenderProperties.FlatShading) == FaceRenderProperties.FlatShading)
                    {
                        faceShading = FaceShading.Flat;
                    }
                    else if ((faceRenderProperties & FaceRenderProperties.PhongShading) == FaceRenderProperties.PhongShading)
                    {
                        faceShading = FaceShading.Phong;
                    }

                    face.Shading = faceShading;

                    // Read unknown
                    face.UnknownRenderProperties = new[]
                    {
                        fileStream.ReadByte(),
                            fileStream.ReadByte(),
                            fileStream.ReadByte()
                    };

                    face.AddressRange.End = fileStream.Position;

                    rrfModel.Meshes[i].Faces.Add(face);
                }

                // Retrieve unknown face ints
                for (var j = 0; j < rrfModel.Meshes[i].FaceCount; j++)
                {
                    const int unknownIntCount = 3;
                    for (var k = 0; k < unknownIntCount; k++)
                    {
                        rrfModel.Meshes[i].Faces[j].Unknown2.Add(fileStream.ReadInt32());
                    }
                }

                // Retrieve vertex information
                fileStream.Seek(rrfModel.Meshes[i].VertexAddressRange.Start, SeekOrigin.Begin);

                for (var j = 0; j < rrfModel.Meshes[i].VertexCount; j++)
                {
                    var vertex = new int3(fileStream.ReadInt32(), fileStream.ReadInt32(), fileStream.ReadInt32());

                    rrfModel.Meshes[i].Vertices.Add(new RrfVertex(vertex));
                }

                // Read unknown range values
                fileStream.Seek(rrfModel.Meshes[i].UnknownAddressRange.Start, SeekOrigin.Begin);

                // Read unknown values before next faces as a 3xn grid
                var gridIndex = 0;
                var tempList  = new List <int>();

                while (fileStream.Position < rrfModel.Meshes[i].UnknownAddressRange.End)
                {
                    var value = fileStream.ReadByte();

                    if (gridIndex > 5)
                    {
                        gridIndex = 1;
                        rrfModel.Meshes[i].UnknownPostFace.Add(tempList);
                        tempList = new List <int> {
                            value
                        };
                    }
                    else
                    {
                        tempList.Add(value);
                        gridIndex++;
                    }
                }

                rrfModel.Meshes[i].UnknownPostFaceCount = rrfModel.Meshes[i].UnknownPostFace.Count;

                rrfModel.Meshes[i].VertexAttrAddressRange.Start = fileStream.Position;

                // Load vertex attributes
                for (var k = 0; k < rrfModel.Meshes[i].VertexCount; k++)
                {
                    rrfModel.Meshes[i].Vertices[k].AttributeId = fileStream.ReadByte();

                    var posLevel = fileStream.Read4BitByte();   // First bit stores position(?), second stores level(?)

                    rrfModel.Meshes[i].Vertices[k].Position = posLevel.Item1;
                    rrfModel.Meshes[i].Vertices[k].Level    = posLevel.Item2;
                }

                rrfModel.Meshes[i].VertexAttrAddressRange.End = fileStream.Position;
            }
        }