// ---- METHODS (PUBLIC) -------------------------------------------------------------------------------------

        /// <summary>
        /// Creates a triangle with 3 positions from the given collision prism.
        /// </summary>
        /// <returns></returns>
        public Triangle GetTriangle(KclPrism prism)
        {
            Vector3 A      = Positions[prism.PositionIndex];
            Vector3 CrossA = Vector3.Cross(Normals[prism.Normal1Index], Normals[prism.DirectionIndex]);
            Vector3 CrossB = Vector3.Cross(Normals[prism.Normal2Index], Normals[prism.DirectionIndex]);
            Vector3 B      = A + CrossB * (prism.Length / Vector3.Dot(CrossB, Normals[prism.Normal3Index]));
            Vector3 C      = A + CrossA * (prism.Length / Vector3.Dot(CrossA, Normals[prism.Normal3Index]));

            return(new Triangle(A, B, C)
            {
                Attribute = prism.CollisionFlags
            });
        }
Beispiel #2
0
        // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------

        /// <summary>
        /// Reads <see cref="KclFace"/> instances from the current stream and returns them.
        /// </summary>
        /// <param name="self">The extended <see cref="BinaryDataReader"/>.</param>
        /// <param name="count">The number of instances to read.</param>
        /// <returns>The <see cref="KclFace"/> instances.</returns>
        internal static KclPrism[] ReadPrisms(this BinaryDataReader self, int count, FileVersion version)
        {
            KclPrism[] values = new KclPrism[count];
            for (int i = 0; i < count; i++)
            {
                values[i] = new KclPrism();
                values[i].Read(self, version);
                if (version != FileVersion.Version2) //Manually set the global index for older versions
                {
                    values[i].GlobalIndex = (ushort)i;
                }
            }
            return(values);
        }
        public KCLModel(List <Triangle> triangleList, uint baseTriCount,
                        FileVersion version, CollisionImportSettings settings)
        {
            // Transfer the faces to collision faces and find the smallest and biggest coordinates.
            Vector3 minCoordinate = new Vector3(Single.MaxValue, Single.MaxValue, Single.MaxValue);
            Vector3 maxCoordinate = new Vector3(Single.MinValue, Single.MinValue, Single.MinValue);

            List <KclPrism> prismList = new List <KclPrism>();
            Dictionary <ushort, Triangle> triangles = new Dictionary <ushort, Triangle>();

            Positions = new List <Vector3>();
            Normals   = new List <Vector3>();
            Prisms    = new KclPrism[0];
            Version   = version;

            PrismThickness = settings.PrismThickness;
            SphereRadius   = settings.SphereRadius;

            Dictionary <string, int> positionTable = new Dictionary <string, int>();
            Dictionary <string, int> normalTable   = new Dictionary <string, int>();

            ushort triindex = 0;

            for (int i = 0; i < triangleList.Count; i++)
            {
                var triangle = triangleList[i];

                Vector3 direction = Vector3.Cross(
                    triangle.Vertices[1] - triangle.Vertices[0],
                    triangle.Vertices[2] - triangle.Vertices[0]);

                if ((direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z) < 0.01)
                {
                    continue;
                }
                direction = Vector3.Normalize(direction);

                // Get the position vectors and find the smallest and biggest coordinates.
                for (int j = 0; j < 3; j++)
                {
                    Vector3 position = triangle.Vertices[j];
                    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, maxCoordinate.X);
                    maxCoordinate.Y = Math.Max(position.Y, maxCoordinate.Y);
                    maxCoordinate.Z = Math.Max(position.Z, maxCoordinate.Z);
                }

                //Calculate the ABC normal values.
                Vector3 normalA = Vector3.Cross(direction,
                                                triangle.Vertices[2] - triangle.Vertices[0]);

                Vector3 normalB = (-(Vector3.Cross(direction,
                                                   triangle.Vertices[1] - triangle.Vertices[0])));

                Vector3 normalC = Vector3.Cross(direction,
                                                triangle.Vertices[1] - triangle.Vertices[2]);

                //Normalize the ABC normal values.
                normalA = Vector3.Normalize(normalA);
                normalB = Vector3.Normalize(normalB);
                normalC = Vector3.Normalize(normalC);

                //Create a KCL Prism
                KclPrism face = new KclPrism()
                {
                    PositionIndex  = (ushort)IndexOfVertex(triangle.Vertices[0], Positions, positionTable),
                    DirectionIndex = (ushort)IndexOfVertex(direction, Normals, normalTable),
                    Normal1Index   = (ushort)IndexOfVertex(normalA, Normals, normalTable),
                    Normal2Index   = (ushort)IndexOfVertex(normalB, Normals, normalTable),
                    Normal3Index   = (ushort)IndexOfVertex(normalC, Normals, normalTable),
                    GlobalIndex    = baseTriCount + (uint)prismList.Count,
                    CollisionFlags = triangle.Attribute,
                };

                // Compute the face direction (normal) and add it to the normal list.
                triangles.Add((ushort)triindex++, triangle);

                //Compute the length
                float length = Vector3.Dot(triangle.Vertices[1] - triangle.Vertices[0], normalC);
                face.Length = length;

                prismList.Add(face);
            }

            positionTable.Clear();
            normalTable.Clear();

            //No triangles found to intersect the current box, return.
            if (prismList.Count == 0)
            {
                return;
            }

            //Padd the coordinates
            minCoordinate += settings.PaddingMin;
            maxCoordinate += settings.PaddingMax;

            MinCoordinate = minCoordinate;
            Prisms        = prismList.ToArray();

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

            if (cubeSizePower > Maths.GetNext2Exponent(settings.MaxRootSize))
            {
                cubeSizePower = Maths.GetNext2Exponent(settings.MaxRootSize);
            }

            int cubeSize = 1 << cubeSizePower;

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

            // Generate the root nodes, which are square cubes required to cover all of the model.
            PolygonOctreeRoots = new PolygonOctree[cubeCounts.X * cubeCounts.Y * cubeCounts.Z];


            int cubeBlow = SphereRadius > 0 ? (int)(SphereRadius * 2) : 50;

            DebugLogger.WriteLine($"Octree Distance Bias {cubeBlow}");
            DebugLogger.WriteLine($"Creating Octrees {cubeCounts}");

            int index = 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++)
                    {
                        Vector3 cubePosition = minCoordinate + ((float)cubeSize) * new Vector3(x, y, z);
                        PolygonOctreeRoots[index++] = new PolygonOctree(triangles, cubePosition, cubeSize,
                                                                        settings.MaxTrianglesInCube, settings.MaxCubeSize,
                                                                        settings.MinCubeSize, cubeBlow, settings.MaxOctreeDepth);
                    }
                }
            }
            DebugLogger.WriteLine($"Finished Octree");
        }