示例#1
0
        private void CreateModelOctree(List <ModelGroup> modelRoots, ModelOctreeNode[] nodes,
                                       CollisionImportSettings settings, uint baseTriCount, int level = 0)
        {
            for (int i = 0; i < modelRoots.Count; i++)
            {
                int nodeIndex = modelRoots[i].BlockIndex;

                if (modelRoots[i].Children.Count > 0)
                {
                    nodes[nodeIndex].Children = new ModelOctreeNode[ModelOctreeNode.ChildCount];
                    for (int j = 0; j < ModelOctreeNode.ChildCount; j++)
                    {
                        nodes[nodeIndex].Children[j] = new ModelOctreeNode();
                    }
                    //Load addtional subidivison models
                    CreateModelOctree(modelRoots[i].Children, nodes[nodeIndex].Children, settings, baseTriCount, level + 1);
                }//If the model has triangle data, add it to our global model list
                else if (modelRoots[i].Triangles.Count > 0)
                {
                    var model = new KCLModel(modelRoots[i].Triangles, baseTriCount, Version, settings);
                    baseTriCount += (uint)modelRoots[i].Triangles.Count;

                    nodes[nodeIndex].ModelIndex = (uint)Models.Count;
                    foreach (var index in modelRoots[i].MergedBlockIndices)
                    {
                        nodes[index].ModelIndex = (uint)Models.Count;
                    }

                    Models.Add(model);
                }
            }
        }
示例#2
0
        // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="KclFile"/> class, created from the given
        /// <paramref name="objModel"/>.
        /// </summary>
        public KCLFile(List <Triangle> triangles, FileVersion version,
                       bool isBigEndian, CollisionImportSettings settings = null)
        {
            if (settings == null)
            {
                settings = new CollisionImportSettings();
            }

            Version   = version;
            ByteOrder = isBigEndian ? ByteOrder.BigEndian : ByteOrder.LittleEndian;
            Replace(triangles, settings);
        }
示例#3
0
        /// <summary>
        /// Replaces the current collision model from the given
        /// <paramref name="objModel"/>.
        /// </summary>
        /// <param name="objModel">The <see cref="ObjModel"/> to create the collision data from.</param>
        public void Replace(List <Triangle> triangles, CollisionImportSettings settings)
        {
            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();

            // Find the smallest and biggest coordinate (and add padding).
            Vector3 minCoordinate = new Vector3(Single.MaxValue, Single.MaxValue, Single.MaxValue);
            Vector3 maxCoordinate = new Vector3(Single.MinValue, Single.MinValue, Single.MinValue);

            DebugLogger.WriteLine($"Replacing Collision...");

            DebugLogger.WriteLine($"Settings:");
            DebugLogger.WriteLine($"-MaxRootSize {settings.MaxRootSize}");
            DebugLogger.WriteLine($"-MaxTrianglesInCube {settings.MaxTrianglesInCube}");
            DebugLogger.WriteLine($"-MinCubeSize {settings.MinCubeSize}");
            DebugLogger.WriteLine($"-PaddingMax {settings.PaddingMax}");
            DebugLogger.WriteLine($"-PaddingMin {settings.PaddingMin}");
            DebugLogger.WriteLine($"-PrismThickness {settings.PrismThickness}");
            DebugLogger.WriteLine($"-SphereRadius {settings.SphereRadius}");

            DebugLogger.WriteLine($"Calculating bounding sizes...");

            for (int i = 0; i < triangles.Count; i++)
            {
                for (int v = 0; v < triangles[i].Vertices.Length; v++)
                {
                    Vector3 position = triangles[i].Vertices[v];
                    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);
                }
            }

            MinCoordinate = minCoordinate + settings.PaddingMin;
            MaxCoordinate = maxCoordinate + settings.PaddingMax;

            DebugLogger.WriteLine($"MinCoordinate: {MinCoordinate}");
            DebugLogger.WriteLine($"MaxCoordinate: {MaxCoordinate}");

            // Compute square cube size of the world, and with it the coordinate shift for use with the model octree.
            Vector3 size           = MaxCoordinate - MinCoordinate;
            int     worldLengthExp = Maths.GetNext2Exponent(Math.Min(Math.Min(size.X, size.Y), size.Z));
            int     cubeSize       = 1 << worldLengthExp;
            Vector3 exponents      = new Vector3(
                Maths.GetNext2Exponent(size.X),
                Maths.GetNext2Exponent(size.Y),
                Maths.GetNext2Exponent(size.Z));

            CoordinateShift = new Vector3U(
                (uint)(exponents.X),
                (uint)(exponents.Y),
                (uint)(exponents.Z));

            Models = new List <KCLModel>();
            Vector3 boxSize = new Vector3(
                1 << (int)CoordinateShift.X,
                    1 << (int)CoordinateShift.Y,
                    1 << (int)CoordinateShift.Z);

            DebugLogger.WriteLine($"Model Octree Bounds: {boxSize}");

            //Create subdivied triangle models
            var modelRoots = CreateModelDivision(MinCoordinate, triangles, boxSize / 2f);

            //For a model octree, we need 8 octrees per division
            ModelOctreeRoot          = new ModelOctreeNode();
            ModelOctreeRoot.Children = new ModelOctreeNode[ModelOctreeNode.ChildCount];
            for (int i = 0; i < ModelOctreeNode.ChildCount; i++)
            {
                ModelOctreeRoot.Children[i] = new ModelOctreeNode();
            }
            Models.Clear();

            //Load all the model data
            CreateModelOctree(modelRoots, ModelOctreeRoot.Children, settings, 0);
            PrismCount = Models.Sum(x => x.Prisms.Length);

            stopWatch.Stop();

            DebugLogger.WriteLine($"Model Octree:");
            PrintModelOctree(ModelOctreeRoot.Children);

            DebugLogger.WriteLine($"Finished Collsion Generation {stopWatch.Elapsed}");
        }
        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");
        }