/// <summary>
        /// Loads the data from the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
        /// <param name="loadOctree"><c>true</c> to also load the octree referencing triangles.</param>
        public void Load(Stream stream, bool loadOctree)
        {
            using (BinaryDataReader reader = new BinaryDataReader(stream, true)
            {
                ByteOrder = ByteOrder.BigEndian
            })
            {
                long modelPosition = reader.Position;

                // Read the header.
                int positionArrayOffset = reader.ReadInt32();
                int normalArrayOffset   = reader.ReadInt32();
                int triangleArrayOffset = reader.ReadInt32();
                int octreeOffset        = reader.ReadInt32();
                Unknown1        = reader.ReadSingle();
                MinCoordinate   = reader.ReadVector3F();
                CoordinateMask  = reader.ReadVector3();
                CoordinateShift = reader.ReadVector3();
                Unknown2        = reader.ReadSingle();

                // Read the positions.
                reader.Position = modelPosition + positionArrayOffset; // Mostly unrequired, data is successive.
                int positionCount = (normalArrayOffset - positionArrayOffset) / Vector3F.SizeInBytes;
                Positions = reader.ReadVector3Fs(positionCount);

                // Read the normals.
                reader.Position = modelPosition + normalArrayOffset; // Mostly unrequired, data is successive.
                int normalCount = (triangleArrayOffset - normalArrayOffset) / Vector3F.SizeInBytes;
                Normals = reader.ReadVector3Fs(normalCount);

                // Read the triangles.
                reader.Position = modelPosition + triangleArrayOffset; // Mostly unrequired, data is successive.
                int triangleCount = (octreeOffset - triangleArrayOffset) / Marshal.SizeOf <Triangle>();
                Triangles = reader.ReadTriangles(triangleCount);

                // Read the octree.
                if (loadOctree)
                {
                    reader.Position = modelPosition + octreeOffset; // Mostly unrequired, data is successive.
                    int nodeCount
                        = ((~CoordinateMask.X >> CoordinateShift.X) + 1)
                          * ((~CoordinateMask.Y >> CoordinateShift.X) + 1)
                          * ((~CoordinateMask.Z >> CoordinateShift.X) + 1);
                    ModelOctreeRoots = new ModelOctreeNode[nodeCount];
                    for (int i = 0; i < nodeCount; i++)
                    {
                        ModelOctreeRoots[i] = new ModelOctreeNode(null, reader, modelPosition + octreeOffset);
                    }
                    // Reader is now behind the last octree key, not at end of the model / behind the last separator.
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Loads the data from the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
        /// <param name="loadOctree"><c>true</c> to also load the octree referencing triangles.</param>
        /// <param name="leaveOpen"><c>true</c> to leave <paramref name="stream"/> open after loading the instance.
        /// </param>
        public void Load(Stream stream, bool loadOctree = true, bool leaveOpen = false, ByteOrder Endianness = ByteOrder.LittleEndian)
        {
            using (BinaryDataReader reader = new BinaryDataReader(stream, leaveOpen))
            {
                reader.ByteOrder = Endianness;

                long modelPosition = reader.Position;

                // Read the header.
                int positionArrayOffset = reader.ReadInt32();
                int normalArrayOffset   = reader.ReadInt32();
                int triangleArrayOffset = reader.ReadInt32();
                int octreeOffset        = reader.ReadInt32();
                reader.Seek(sizeof(float)); // Unknown value always being 30.0.
                MinCoordinate   = reader.ReadVector3F();
                CoordinateMask  = reader.ReadVector3();
                CoordinateShift = reader.ReadVector3();
                reader.Seek(sizeof(float)); // Unknown value always being 25.0.

                // Read the positions.
                reader.Position = modelPosition + positionArrayOffset; // Mostly unrequired, data is successive.
                int positionCount = (normalArrayOffset - positionArrayOffset) / Vector3F.SizeInBytes;
                Positions = new List <Vector3F>(reader.ReadVector3Fs(positionCount));

                // Read the normals.
                reader.Position = modelPosition + normalArrayOffset; // Mostly unrequired, data is successive.
                int normalCount = (triangleArrayOffset - normalArrayOffset) / Vector3F.SizeInBytes;
                Normals = new List <Vector3F>(reader.ReadVector3Fs(normalCount));

                // Read the triangles.
                reader.Position = modelPosition + triangleArrayOffset; // Mostly unrequired, data is successive.
                int triangleCount = (octreeOffset - triangleArrayOffset) / Marshal.SizeOf <KclFace>();
                Faces = reader.ReadTriangles(triangleCount);

                // Read the octree.
                if (loadOctree)
                {
                    reader.Position = modelPosition + octreeOffset; // Mostly unrequired, data is successive.
                    int nodeCount
                        = ((~CoordinateMask.X >> CoordinateShift.X) + 1)
                          * ((~CoordinateMask.Y >> CoordinateShift.X) + 1)
                          * ((~CoordinateMask.Z >> CoordinateShift.X) + 1);
                    ModelOctreeRoots = new ModelOctreeNode[nodeCount];
                    for (int i = 0; i < nodeCount; i++)
                    {
                        ModelOctreeRoots[i] = new ModelOctreeNode(reader, modelPosition + octreeOffset);
                    }
                    // Reader is now behind the last octree key, not at end of the model / behind the last separator.
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="KclModel"/> class, created from the given
        /// <paramref name="objModel"/>.
        /// </summary>
        /// <param name="objModel">The <see cref="ObjModel"/> to create the collision data from.</param>
        internal KclModel(ObjModel objModel)
        {
            if (objModel.Faces.Count > UInt16.MaxValue)
            {
                throw new InvalidOperationException("KCL models must not have more than 65535 triangles.");
            }

            // Transfer the faces to collision faces and find the smallest and biggest coordinates.
            Positions = new List <Vector3F>(objModel.Positions);
            Normals   = new List <Vector3F>(objModel.Normals);

            Vector3F minCoordinate = new Vector3F(Single.MaxValue, Single.MaxValue, Single.MaxValue);
            Vector3F maxCoordinate = new Vector3F(Single.MinValue, Single.MinValue, Single.MinValue);

            KclFace[] faces    = new KclFace[objModel.Faces.Count];
            Triangle  triangle = new Triangle();
            Dictionary <ushort, Triangle> triangles = new Dictionary <ushort, Triangle>(objModel.Faces.Count);
            ushort i = 0;

            foreach (ObjFace objFace in objModel.Faces)
            {
                KclFace face = new KclFace()
                {
                    PositionIndex = (ushort)objFace.Vertices[0].PositionIndex,
                    Normal1Index  = (ushort)objFace.Vertices[0].NormalIndex,
                    Normal2Index  = (ushort)objFace.Vertices[1].NormalIndex,
                    Normal3Index  = (ushort)objFace.Vertices[2].NormalIndex
                };

                // Get the position vectors and find the smallest and biggest coordinates.
                for (int j = 0; j < 3; j++)
                {
                    Vector3F position = Positions[objFace.Vertices[j].PositionIndex];
                    minCoordinate.X      = Math.Min(position.X, minCoordinate.X);
                    minCoordinate.Y      = Math.Min(position.Y, minCoordinate.Y);
                    minCoordinate.Z      = Math.Min(position.Z, minCoordinate.Z);
                    maxCoordinate.X      = Math.Max(position.X, minCoordinate.X);
                    maxCoordinate.Y      = Math.Max(position.X, minCoordinate.Y);
                    maxCoordinate.Z      = Math.Max(position.X, minCoordinate.Z);
                    triangle.Vertices[j] = position;
                }

                // Compute the face direction (normal) and add it to the normal list.
                face.DirectionIndex = (ushort)(Normals.Count);
                Normals.Add(triangle.Normal);

                triangles.Add(i, triangle);
                faces[i++] = face;
            }
            minCoordinate += _minCoordinatePadding;
            maxCoordinate += _maxCoordinatePadding;
            MinCoordinate  = minCoordinate;
            Faces          = faces;

            // Compute the octree.
            Vector3F size      = maxCoordinate - minCoordinate;
            Vector3  exponents = new Vector3(
                Maths.GetNext2Exponent(size.X),
                Maths.GetNext2Exponent(size.Y),
                Maths.GetNext2Exponent(size.Z));
            int cubeSizePower = Maths.GetNext2Exponent(Math.Min(Math.Min(size.X, size.Y), size.Z));
            int cubeSize      = 1 << cubeSizePower;

            CoordinateShift = new Vector3(
                cubeSizePower,
                exponents.X - cubeSizePower,
                exponents.X - cubeSizePower + exponents.Y - cubeSizePower);
            CoordinateMask = new Vector3(
                (int)(0xFFFFFFFF << exponents.X),
                (int)(0xFFFFFFFF << exponents.Y),
                (int)(0xFFFFFFFF << exponents.Z));
            Vector3 cubeCounts = new Vector3(
                Math.Max(1, (1 << exponents.X) / cubeSize),
                Math.Max(1, (1 << exponents.Y) / cubeSize),
                Math.Max(1, (1 << exponents.Z) / cubeSize));

            // Generate the root nodes, which are square cubes required to cover all of the model.
            ModelOctreeRoots = new ModelOctreeNode[cubeCounts.X * cubeCounts.Y * cubeCounts.Z];
            i = 0;
            for (int z = 0; z < cubeCounts.Z; z++)
            {
                for (int y = 0; y < cubeCounts.Y; y++)
                {
                    for (int x = 0; x < cubeCounts.X; x++)
                    {
                        Vector3F cubePosition = new Vector3F(x, y, z) * cubeSize + minCoordinate;
                        ModelOctreeRoots[i++] = new ModelOctreeNode(triangles, cubePosition, cubeSize,
                                                                    _maxTrianglesInCube, _minCubeSize);
                    }
                }
            }
        }