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); } } }
// ---- 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); }
/// <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"); }