Example #1
0
            public void AddBlendShapesToMesh(Mesh mesh)
            {
                Dictionary <string, BlendShape> map = new Dictionary <string, BlendShape>();

                foreach (var x in BlendShapes)
                {
                    BlendShape bs = null;
                    if (!map.TryGetValue(x.Name, out bs))
                    {
                        bs             = new BlendShape();
                        bs.Positions   = Positions.ToArray();
                        bs.Normals     = Normals.ToArray();
                        bs.Tangents    = Tangents.Select(y => (Vector3)y).ToArray();
                        bs.Name        = x.Name;
                        bs.FrameWeight = x.FrameWeight;
                        map.Add(x.Name, bs);
                    }

                    var j = x.VertexOffset;
                    for (int i = 0; i < x.Positions.Length; ++i, ++j)
                    {
                        bs.Positions[j] = x.Positions[i];
                        bs.Normals[j]   = x.Normals[i];
                        bs.Tangents[j]  = x.Tangents[i];
                    }
                }

                foreach (var kv in map)
                {
                    //Debug.LogFormat("AddBlendShapeFrame: {0}", kv.Key);
                    mesh.AddBlendShapeFrame(kv.Key, kv.Value.FrameWeight,
                                            kv.Value.Positions, kv.Value.Normals, kv.Value.Tangents);
                }
            }
Example #2
0
 /// <summary>
 /// Get normals for this object
 /// </summary>
 /// <returns>normals for object</returns>
 public Vector3[] GetNormals()
 {
     if (_recalculateNormals)
     {
         CalculateNormals();
     }
     return(Normals.ToArray());
 }
Example #3
0
 public IFileGeometry3D ApplyMatrix(ref Matrix4x4 matrix)
 {
     return(new GeometryData(Name,
                             Positions.ToArray().Transform(ref matrix).ToList(),
                             Normals.ToArray().Transform(ref matrix).ToList(),
                             Colors.ToList(),
                             Indices.ToList(),
                             TextureCoors.ToList(),
                             Topology
                             ));
 }
Example #4
0
 public Primitive Clone()
 {
     return(new Primitive(LocalVertices.ToArray(),
                          GlobalVertices.ToArray(),
                          Normals.ToArray(),
                          TextureCoords.ToArray(),
                          Indexes.ToArray(),
                          NormalIndexes.ToArray(),
                          TextureCoordsIndexes.ToArray(),
                          Pivot.Clone()));
 }
        public ImmutableGeometryData Transform(Matrix4x4 matrix)
        {
            var p = Positions.ToArray().Transform(ref matrix);
            var n = Normals.ToArray().Transform(ref matrix);

            return(new ImmutableGeometryData(p.AsReadOnly(), n.AsReadOnly(), Indices)
            {
                Colors = Colors,
                TexCoor = TexCoor,
                Topology = Topology,
                IsModified = true,
            });
        }
Example #6
0
    /*public void Clear()
     * {
     *  this.Triangles.Clear();
     *  this.Vertices.Clear();
     *  this.Normals.Clear();
     *  this.UVs.Clear();
     * }*/

    public Mesh Build()
    {
        //RemoveCollapsedTriangles();

        Mesh mesh = new Mesh();

        mesh.vertices  = Vertices.ToArray();
        mesh.triangles = Triangles.ToArray();
        mesh.normals   = Normals.ToArray();
        mesh.uv        = UVs.ToArray();
        mesh.uv2       = UVs.ToArray();
        mesh.uv3       = UVs.ToArray();
        //mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        mesh.Optimize();

        return(mesh);
    }
Example #7
0
    public Mesh CreateMesh()
    {
        Mesh mesh = new Mesh();


        mesh.vertices  = Vertices.ToArray();
        mesh.triangles = indices.ToArray();

        if (Normals.Count == Vertices.Count)
        {
            mesh.normals = Normals.ToArray();
        }

        if (UVs.Count == Vertices.Count)
        {
            mesh.uv = UVs.ToArray();
        }

        mesh.RecalculateBounds();
        return(mesh);
    }
        public override void Upload()
        {
            if (_faceInfos == null || _faceInfos.Count <= 0)
            {
                UploadedCount = 0;
                return;
            }

            GL.BindVertexArray(VaoId);

            if (UploadedCount == 0)
            {
                //0 positions
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[0]);
                GL.BufferData(BufferTarget.ArrayBuffer, Positions.Count * Vector3.SizeInBytes, Positions.ToArray(),
                              BufferUsageHint.StaticDraw);
                GL.EnableVertexAttribArray(0);
                GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);
                //1 texCoords
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[1]);
                GL.BufferData(BufferTarget.ArrayBuffer, TexCoords.Count * Vector4.SizeInBytes, TexCoords.ToArray(),
                              BufferUsageHint.StaticDraw);
                GL.EnableVertexAttribArray(1);
                GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 0, 0);
                //2 normals
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[2]);
                GL.BufferData(BufferTarget.ArrayBuffer, Normals.Count * Vector4.SizeInBytes, Normals.ToArray(),
                              BufferUsageHint.StaticDraw);
                GL.EnableVertexAttribArray(2);
                GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, 0, 0);
                //3 color
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[3]);
                GL.BufferData(BufferTarget.ArrayBuffer, Colors.Count * Vector3.SizeInBytes, Colors.ToArray(),
                              BufferUsageHint.StaticDraw);
                GL.EnableVertexAttribArray(3);
                GL.VertexAttribPointer(3, 3, VertexAttribPointerType.Float, false, 0, 0);
                //4 light
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[4]);
                GL.BufferData(BufferTarget.ArrayBuffer, Lights.Count * Vector3.SizeInBytes, Lights.ToArray(),
                              BufferUsageHint.StaticDraw);
                GL.EnableVertexAttribArray(4);
                GL.VertexAttribPointer(4, 3, VertexAttribPointerType.Float, false, 0, 0);
            }
            else
            {
                //0 positions
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[0]);
                GL.BufferData(BufferTarget.ArrayBuffer, Positions.Count * Vector3.SizeInBytes, Positions.ToArray(),
                              BufferUsageHint.StaticDraw);
                //1 texCoords
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[1]);
                GL.BufferData(BufferTarget.ArrayBuffer, TexCoords.Count * Vector4.SizeInBytes, TexCoords.ToArray(),
                              BufferUsageHint.StaticDraw);
                //2 normals
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[2]);
                GL.BufferData(BufferTarget.ArrayBuffer, Normals.Count * Vector4.SizeInBytes, Normals.ToArray(),
                              BufferUsageHint.StaticDraw);
                //3 color
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[3]);
                GL.BufferData(BufferTarget.ArrayBuffer, Colors.Count * Vector3.SizeInBytes, Colors.ToArray(),
                              BufferUsageHint.StaticDraw);
                //4 light
                GL.BindBuffer(BufferTarget.ArrayBuffer, BufferIds[4]);
                GL.BufferData(BufferTarget.ArrayBuffer, Lights.Count * Vector3.SizeInBytes, Lights.ToArray(),
                              BufferUsageHint.StaticDraw);
            }

            UploadedCount  = _faceInfos.Count * 6;
            _uploadedFaces = _faceInfos.ToArray();

            var indices = new List <uint>();

            foreach (var face in _uploadedFaces)
            {
                indices.AddRange(face.Indices);
            }

            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndicesId);
            GL.BufferData(BufferTarget.ElementArrayBuffer, UploadedCount * sizeof(uint), indices.ToArray(), BufferUsageHint.DynamicDraw);
        }
Example #9
0
        public MeshIntegrationResult Integrate(MeshEnumerateOption onlyBlendShapeRenderers)
        {
            var mesh = new Mesh();

            if (Positions.Count > ushort.MaxValue)
            {
                Debug.LogFormat("exceed 65535 vertices: {0}", Positions.Count);
                mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            }

            mesh.vertices     = Positions.ToArray();
            mesh.normals      = Normals.ToArray();
            mesh.uv           = UV.ToArray();
            mesh.tangents     = Tangents.ToArray();
            mesh.boneWeights  = BoneWeights.ToArray();
            mesh.subMeshCount = SubMeshes.Count;
            for (var i = 0; i < SubMeshes.Count; ++i)
            {
                mesh.SetIndices(SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
            }
            mesh.bindposes = BindPoses.ToArray();

            // blendshape
            switch (onlyBlendShapeRenderers)
            {
            case MeshEnumerateOption.OnlyWithBlendShape:
            {
                AddBlendShapesToMesh(mesh);
                mesh.name = INTEGRATED_MESH_WITH_BLENDSHAPE_NAME;
                break;
            }

            case MeshEnumerateOption.All:
            {
                AddBlendShapesToMesh(mesh);
                mesh.name = INTEGRATED_MESH_ALL_NAME;
                break;
            }

            case MeshEnumerateOption.OnlyWithoutBlendShape:
            {
                mesh.name = INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME;
                break;
            }
            }

            // meshName
            var meshNode = new GameObject();

            switch (onlyBlendShapeRenderers)
            {
            case MeshEnumerateOption.OnlyWithBlendShape:
            {
                meshNode.name = INTEGRATED_MESH_WITH_BLENDSHAPE_NAME;
                break;
            }

            case MeshEnumerateOption.OnlyWithoutBlendShape:
            {
                meshNode.name = INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME;
                break;
            }

            case MeshEnumerateOption.All:
            {
                meshNode.name = INTEGRATED_MESH_ALL_NAME;
                break;
            }
            }

            var integrated = meshNode.AddComponent <SkinnedMeshRenderer>();

            integrated.sharedMesh      = mesh;
            integrated.sharedMaterials = SubMeshes.Select(x => x.Material).ToArray();
            integrated.bones           = Bones.ToArray();
            Result.IntegratedRenderer  = integrated;
            Result.MeshMap.Integrated  = mesh;
            return(Result);
        }
Example #10
0
        private List <Vertex> BuildVertList(ModelRoot Root, out List <mesh> meshes)
        {
            List <Vertex> verts = new List <Vertex>();

            meshes = new List <mesh>();



            foreach (var Node  in Root.LogicalNodes)
            {
                if (Node.Mesh == null)
                {
                    continue;
                }

                Mesh Mesh = Node.Mesh;

                mesh m = new mesh();
                m.name  = Mesh.Name;
                m.geoms = new List <geom>();
                foreach (MeshPrimitive Primitive in Mesh.Primitives)
                {
                    geom g = new geom();
                    g.strips = new List <strip>();

                    g.diffuse = Primitive.Material?.Channels?.First(channel => channel.Key == "BaseColor").Parameter ?? Vector4.One;

                    var texture = Primitive.Material?.Channels?.FirstOrDefault(channel => channel.Key == "BaseColor").Texture;
                    if (texture != null)
                    {
                        g.texture = texture.PrimaryImage.Name + ".png";
                    }

                    GetVertexBuffer(Primitive, out List <Vector3> Vertices);
                    GetNormalBuffer(Primitive, out List <Vector3> Normals);
                    GetTexCoordBuffer(Primitive, out List <Vector2> Uvs);
                    Vector3[] vs  = Vertices.ToArray();
                    Vector3[] ns  = Normals.ToArray();
                    Vector2[] uvs = Uvs.ToArray();
                    GetIndexBuffer(Primitive, out List <(int A, int B, int C)> Indices);

                    //TODO stripify triangles


                    foreach (var tri in Indices)
                    {
                        g.strips.Add(new strip {
                            triangleCount = 1, vertexOffset = verts.Count
                        });
                        Vector4 PosC = new Vector4(vs[tri.C].X, vs[tri.C].Y, vs[tri.C].Z, 1);
                        Vector4 PosB = new Vector4(vs[tri.B].X, vs[tri.B].Y, vs[tri.B].Z, 1);
                        Vector4 PosA = new Vector4(vs[tri.A].X, vs[tri.A].Y, vs[tri.A].Z, 1);
                        PosC = Vector4.Transform(PosC, Node.WorldMatrix);
                        PosB = Vector4.Transform(PosB, Node.WorldMatrix);
                        PosA = Vector4.Transform(PosA, Node.WorldMatrix);
                        verts.Add(new Vertex {
                            X = PosC.X, Y = PosC.Y, Z = PosC.Z, NX = ns[tri.C].X, NY = ns[tri.C].Y, NZ = ns[tri.C].Z, U = uvs[tri.C].X, V = uvs[tri.C].Y
                        }.Converted());
                        verts.Add(new Vertex {
                            X = PosB.X, Y = PosB.Y, Z = PosB.Z, NX = ns[tri.B].X, NY = ns[tri.B].Y, NZ = ns[tri.B].Z, U = uvs[tri.B].X, V = uvs[tri.B].Y
                        }.Converted());
                        verts.Add(new Vertex {
                            X = PosA.X, Y = PosA.Y, Z = PosA.Z, NX = ns[tri.A].X, NY = ns[tri.A].Y, NZ = ns[tri.A].Z, U = uvs[tri.A].X, V = uvs[tri.A].Y
                        }.Converted());
                    }
                    m.geoms.Add(g);
                }
                meshes.Add(m);
            }

            return(verts);
        }
Example #11
0
        protected override void CreateGeometry()
        {
            base.CreateGeometry();

            if (!GenerateGeometry)
            {
                return;
            }

            //create buffer descriptions
            var vDesc = new BufferDesc()
            {
                Width = (uint)Positions.Count, Format = Format.Float3, Type = BufferType.Input
            };
            var nDesc = new BufferDesc()
            {
                Width = (uint)Normals.Count, Format = Format.Float3, Type = BufferType.Input
            };
            var tcDesc = new BufferDesc()
            {
                Width = (uint)Texcoords.Count, Format = Format.Float2, Type = BufferType.Input
            };

            // Create the buffers to hold our geometry data
            var vBuffer  = new OptixBuffer(Context, vDesc);
            var nBuffer  = new OptixBuffer(Context, nDesc);
            var tcBuffer = new OptixBuffer(Context, tcDesc);

            vBuffer.SetData <Vector3>(Positions.ToArray());
            nBuffer.SetData <Vector3>(Normals.ToArray());
            tcBuffer.SetData <Vector2>(Texcoords.ToArray());

            List <GeometryInstance> instances = new List <GeometryInstance>();

            foreach (ObjGroup group in Groups)
            {
                //empty group
                if (group.VIndices.Count == 0 && group.NIndices.Count == 0 && group.TIndices.Count == 0)
                {
                    continue;
                }

                //ValidateGroup( group );

                var normalsUseVIndices = GenerateNormals && group.NIndices.Count == 0 && Normals.Count > 0;

                if (normalsUseVIndices)
                {
                    Debug.Assert(Normals.Count == Positions.Count);
                }

                var numNormIndices = normalsUseVIndices ? group.VIndices.Count : group.NIndices.Count;

                var viDesc = new BufferDesc {
                    Width = (uint)group.VIndices.Count, Format = Format.Int3, Type = BufferType.Input
                };
                var niDesc = new BufferDesc {
                    Width = (uint)numNormIndices, Format = Format.Int3, Type = BufferType.Input
                };
                var tiDesc = new BufferDesc {
                    Width = (uint)group.TIndices.Count, Format = Format.Int3, Type = BufferType.Input
                };

                var viBuffer = new OptixBuffer(Context, viDesc);
                var niBuffer = new OptixBuffer(Context, niDesc);
                var tiBuffer = new OptixBuffer(Context, tiDesc);

                viBuffer.SetData(group.VIndices.ToArray());
                //if normals weren't in the obj and we genereated them, use the vertex indices
                niBuffer.SetData(normalsUseVIndices ? group.VIndices.ToArray() : group.NIndices.ToArray());
                tiBuffer.SetData(group.TIndices.ToArray());

                //create a geometry node and set the buffers
                var geometry = new Geometry(Context);
                geometry.IntersectionProgram = new OptixProgram(Context, IntersecitonProgPath, IntersecitonProgName);
                geometry.BoundingBoxProgram  = new OptixProgram(Context, BoundingBoxProgPath, BoundingBoxProgName);
                geometry.PrimitiveCount      = (uint)group.VIndices.Count;

                geometry["vertex_buffer"].Set(vBuffer);
                geometry["normal_buffer"].Set(nBuffer);
                geometry["texcoord_buffer"].Set(tcBuffer);
                geometry["vindex_buffer"].Set(viBuffer);
                geometry["nindex_buffer"].Set(niBuffer);
                geometry["tindex_buffer"].Set(tiBuffer);

                //create a geometry instance
                GeometryInstance instance = new GeometryInstance(Context);
                instance.Geometry = geometry;
                instance.AddMaterial(_materialResolveFunc(group.mtrl) ?? DefaultMaterial);

                if (group.mtrl != null)
                {
                    ObjMaterial mtrl = mMtrls[group.mtrl];
                    instance["diffuse_color"].SetFloat3(ref mtrl.Kd);
                    instance["emission_color"].SetFloat3(ref mtrl.Ke);
                }
                else
                {
                    instance["diffuse_color"].Set(1.0f, 1.0f, 1.0f);
                }

                instances.Add(instance);
            }

            //create an acceleration structure for the geometry
            var accel = new Acceleration(Context, Builder, Traverser);

            if (Builder == AccelBuilder.Sbvh || Builder == AccelBuilder.TriangleKdTree)
            {
                accel.VertexBufferName = "vertex_buffer";
                accel.IndexBufferName  = "vindex_buffer";
            }

            //now attach the instance and accel to the geometry group
            GeoGroup.Acceleration = accel;
            GeoGroup.AddChildren(instances);
        }
        /// <summary>
        /// Converts the record/s into a Unity GameObject structure with meshes,
        /// materials etc and imports into the scene.
        /// </summary>
        public override void ImportIntoScene()
        {
            // Create an empty gameobject
            UnityGameObject = new GameObject(ID);
            // DuckbearLab: FIX!
            //UnityGameObject.transform.localScale = Vector3.one;

            // Apply transformations
            UnityGameObject.transform.localPosition = Position;
            UnityGameObject.transform.localRotation = Rotation;
            if (Scale != Vector3.one)
            {
                UnityGameObject.transform.localScale = Scale;
            }

            // Assign parent
            if (Parent != null && Parent is InterRecord)
            {
                // DuckbearLab: FIX!
                UnityGameObject.transform.SetParent((Parent as InterRecord).UnityGameObject.transform, false);
                //UnityGameObject.transform.parent = (Parent as InterRecord).UnityGameObject.transform;
            }

            // Add Comment
            if (!string.IsNullOrEmpty(Comment))
            {
                UnityGameObject.AddComponent <UFLT.MonoBehaviours.Comment>().Value = Comment;
            }

            // Processes children
            base.ImportIntoScene();

            // Create mesh
            if (Vertices != null && Vertices.Count > 0)
            {
                Mesh m = new Mesh();
                m.name     = ID;
                m.vertices = VertexPositions.ToArray();
                m.normals  = Normals.ToArray();
                m.uv       = UVS.ToArray();

                // DuckbearLab: Fix for very large meshes
                if (VertexPositions.Count >= ushort.MaxValue)
                {
                    m.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                }

                MeshRenderer mr   = UnityGameObject.AddComponent <MeshRenderer>();
                Material[]   mats = new Material[SubMeshes.Count];
                MeshFilter   mf   = UnityGameObject.AddComponent <MeshFilter>();

                // Set submeshes
                m.subMeshCount = SubMeshes.Count;
                for (int i = 0; i < SubMeshes.Count; i++)
                {
                    mats[i] = SubMeshes[i].Key.UnityMaterial;
                    m.SetTriangles(SubMeshes[i].Value.ToArray(), i);
                }


                // DuckbearLab: OPTIMIZE!
                //mr.materials = mats;
                ;
                {
                    bool equal = true;
                    foreach (var material in mats)
                    {
                        if (material != mats[0])
                        {
                            equal = false;
                        }
                    }

                    if (equal && SubMeshes.Count > 1)
                    {
                        CombineInstance[] combine = new CombineInstance[SubMeshes.Count];
                        for (int i = 0; i < combine.Length; i++)
                        {
                            combine[i].mesh         = m;
                            combine[i].subMeshIndex = i;
                        }
                        var newMesh = new Mesh();
                        newMesh.name = ID;
                        newMesh.CombineMeshes(combine, true, false);
                        m = newMesh;

                        Material[] newMats = new Material[1];
                        newMats[0]   = mats[0];
                        mr.materials = newMats;
                    }
                    else
                    {
                        mr.materials = mats;
                    }
                }

#if UNITY_EDITOR
                UnityEditor.MeshUtility.Optimize(m);
#endif
                mf.mesh = m;
            }
        }
        internal void Write(BinaryDataWriter writer, FileVersion version)
        {
            long modelPosition = writer.Position;

            Offset positionArrayOffset = writer.ReserveOffset();
            Offset normalArrayOffset   = writer.ReserveOffset();
            Offset triangleArrayOffset = writer.ReserveOffset();
            Offset octreeOffset        = writer.ReserveOffset();

            if (version == FileVersion.VersionDS)
            {
                writer.WriteFx32(PrismThickness);
                writer.WriteVector3Fx32(MinCoordinate);
                writer.Write(CoordinateMask);
                writer.Write(CoordinateShift);
                writer.WriteFx32(SphereRadius);

                // Write the positions.
                positionArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.WriteVector3Fx32s(Positions.ToArray());

                // Write the normals.
                normalArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.WriteVector3Fx16s(Normals.ToArray());

                // Write the triangles.
                triangleArrayOffset.Satisfy((int)(writer.Position - modelPosition - 0x10));
                writer.Write(Prisms, version);
            }
            else
            {
                writer.Write(PrismThickness);
                writer.Write(MinCoordinate);
                writer.Write(CoordinateMask);
                writer.Write(CoordinateShift);
                if (version > FileVersion.VersionGC)
                {
                    writer.Write(SphereRadius);
                }

                // Write the positions.
                positionArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.Write(Positions.ToArray());

                // Write the normals.
                normalArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.Write(Normals.ToArray());

                // Write the triangles.
                if (version < FileVersion.Version2)
                {
                    triangleArrayOffset.Satisfy((int)(writer.Position - modelPosition - 0x10));
                }
                else
                {
                    triangleArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                }
                writer.Write(Prisms, version);
            }

            // Write the octree.
            int octreeOffsetValue = (int)(writer.Position - modelPosition);

            octreeOffset.Satisfy(octreeOffsetValue);

            // Write the node keys, and compute the correct offsets into the triangle lists or to child nodes.
            // Nintendo writes child nodes behind the current node, so the children need to be queued.
            // In this implementation, empty triangle lists point to the same terminator behind the last node.
            int triangleListPos = GetNodeCount(PolygonOctreeRoots) * sizeof(uint);
            Queue <PolygonOctree[]>    queuedNodes = new Queue <PolygonOctree[]>();
            Dictionary <ushort[], int> indexPool   = CreateIndexBuffer(queuedNodes);

            queuedNodes.Enqueue(PolygonOctreeRoots);
            while (queuedNodes.Count > 0)
            {
                PolygonOctree[] nodes  = queuedNodes.Dequeue();
                long            offset = writer.Position - modelPosition - octreeOffsetValue;
                foreach (PolygonOctree node in nodes)
                {
                    if (node.Children == null)
                    {
                        // Node is a leaf and points to triangle index list.
                        ushort[] indices = node.TriangleIndices.ToArray();
                        int      listPos = triangleListPos + indexPool[indices];
                        node.Key = (uint)ModelOctreeNode.Flags.Values | (uint)(listPos - offset - sizeof(ushort));
                    }
                    else
                    {
                        // Node is a branch and points to 8 children.
                        node.Key = (uint)(nodes.Length + queuedNodes.Count * 8) * sizeof(uint);
                        queuedNodes.Enqueue(node.Children);
                    }
                    writer.Write(node.Key);
                }
            }

            foreach (var ind in indexPool)
            {
                //Last value skip. Uses terminator of previous index list
                if (ind.Key.Length == 0)
                {
                    break;
                }

                //Save the index lists and terminator
                if (version < FileVersion.Version2)
                {
                    for (int i = 0; i < ind.Key.Length; i++)
                    {
                        writer.Write((ushort)(ind.Key[i] + 1)); //-1 indexed
                    }
                    writer.Write((ushort)0);                    // Terminator
                }
                else
                {
                    writer.Write(ind.Key);
                    writer.Write((ushort)0xFFFF); // Terminator
                }
            }
        }
Example #14
0
        /// <summary>
        /// Saves the data into the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
        /// <param name="leaveOpen"><c>true</c> to leave <paramref name="stream"/> open after saving the instance.
        /// </param>
        public void Save(Stream stream, bool leaveOpen = false, ByteOrder Endianness = ByteOrder.LittleEndian)
        {
            using (BinaryDataWriter writer = new BinaryDataWriter(stream, leaveOpen))
            {
                writer.ByteOrder = Endianness;

                long modelPosition = writer.Position;

                // Write the header.
                Offset positionArrayOffset = writer.ReserveOffset();
                Offset normalArrayOffset   = writer.ReserveOffset();
                Offset triangleArrayOffset = writer.ReserveOffset();
                Offset octreeOffset        = writer.ReserveOffset();
                writer.Write(30f);
                writer.Write(MinCoordinate);
                writer.Write(CoordinateMask);
                writer.Write(CoordinateShift);
                writer.Write(25f);

                // Write the positions.
                positionArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.Write(Positions.ToArray());

                // Write the normals.
                normalArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.Write(Normals.ToArray());

                // Write the triangles.
                triangleArrayOffset.Satisfy((int)(writer.Position - modelPosition));
                writer.Write(Faces);

                // Write the octree.
                int octreeOffsetValue = (int)(writer.Position - modelPosition);
                octreeOffset.Satisfy(octreeOffsetValue);
                // Write the node keys, and compute the correct offsets into the triangle lists or to child nodes.
                // Nintendo writes child nodes behind the current node, so the children need to be queued.
                // In this implementation, empty triangle lists point to the same terminator behind the last node.
                // This could be further optimized by reusing equal parts of lists as Nintendo apparently did it.
                int emptyListPos    = GetNodeCount(ModelOctreeRoots) * sizeof(uint);
                int triangleListPos = emptyListPos + sizeof(ushort);
                Queue <ModelOctreeNode[]> queuedNodes = new Queue <ModelOctreeNode[]>();
                queuedNodes.Enqueue(ModelOctreeRoots);
                while (queuedNodes.Count > 0)
                {
                    ModelOctreeNode[] nodes = queuedNodes.Dequeue();
                    long offset             = writer.Position - modelPosition - octreeOffsetValue;
                    foreach (ModelOctreeNode node in nodes)
                    {
                        if (node.Children == null)
                        {
                            // Node is a leaf and points to triangle index list.
                            int listPos;
                            if (node.TriangleIndices.Count == 0)
                            {
                                listPos = emptyListPos;
                            }
                            else
                            {
                                listPos          = triangleListPos;
                                triangleListPos += (node.TriangleIndices.Count + 1) * sizeof(ushort);
                            }
                            node.Key = (uint)ModelOctreeNode.Flags.Values | (uint)(listPos - offset - sizeof(ushort));
                        }
                        else
                        {
                            // Node is a branch and points to 8 children.
                            node.Key = (uint)(nodes.Length + queuedNodes.Count * 8) * sizeof(uint);
                            queuedNodes.Enqueue(node.Children);
                        }
                        writer.Write(node.Key);
                    }
                }
                // Iterate through the nodes again and write their triangle lists now.
                writer.Write((ushort)0xFFFF); // Terminator for all empty lists.
                queuedNodes.Enqueue(ModelOctreeRoots);
                while (queuedNodes.Count > 0)
                {
                    ModelOctreeNode[] nodes = queuedNodes.Dequeue();
                    foreach (ModelOctreeNode node in nodes)
                    {
                        if (node.Children == null)
                        {
                            if (node.TriangleIndices.Count > 0)
                            {
                                // Node is a leaf and points to triangle index list.
                                writer.Write(node.TriangleIndices);
                                writer.Write((ushort)0xFFFF);
                            }
                        }
                        else
                        {
                            // Node is a branch and points to 8 children.
                            queuedNodes.Enqueue(node.Children);
                        }
                    }
                }
            }
        }