public hkRootLevelContainer BuildNavmesh(BuildParams p, List <Vector3> verts, List <int> indices) { var root = new hkRootLevelContainer(); NavMeshNative.SetNavmeshBuildParams(p.Cellsize, p.Cellheight, p.SlopeAngle, p.AgentHeight, p.AgentClimb, p.AgentRadius, p.MinRegionArea); var buildSuccess = NavMeshNative.BuildNavmeshForMesh(verts.ToArray(), verts.Count, indices.ToArray(), indices.Count); if (!buildSuccess) { return(null); } var vcount = NavMeshNative.GetMeshVertCount(); var icount = NavMeshNative.GetMeshTriCount(); if (vcount == 0 || icount == 0) { return(null); } ushort[] bverts = new ushort[vcount * 3]; ushort[] bindices = new ushort[icount * 3 * 2]; Vector3[] vbverts = new Vector3[vcount]; NavMeshNative.GetMeshVerts(bverts); NavMeshNative.GetMeshTris(bindices); Vector3[] bounds = new Vector3[2]; NavMeshNative.GetBoundingBox(bounds); var nmesh = new hkaiNavMesh(); nmesh.m_aabb = new hkAabb(); nmesh.m_aabb.m_min = new Vector4(bounds[0].X, bounds[0].Y, bounds[0].Z, 1.0f); nmesh.m_aabb.m_max = new Vector4(bounds[1].X, bounds[1].Y, bounds[1].Z, 1.0f); nmesh.m_edgeData = new List <int>(); nmesh.m_edgeDataStriding = 1; nmesh.m_edges = new List <hkaiNavMeshEdge>(); nmesh.m_erosionRadius = 0.0f; nmesh.m_faceData = new List <int>(); nmesh.m_faceDataStriding = 1; nmesh.m_faces = new List <hkaiNavMeshFace>(); nmesh.m_flags = 0; nmesh.m_vertices = new List <Vector4>(); for (int i = 0; i < bverts.Length / 3; i++) { var vx = bverts[i * 3]; var vy = bverts[i * 3 + 1]; var vz = bverts[i * 3 + 2]; var vert = new Vector3(bounds[0].X + (float)vx * p.Cellsize, bounds[0].Y + (float)vy * p.Cellheight, bounds[0].Z + (float)vz * p.Cellsize); nmesh.m_vertices.Add(new Vector4(vert.X, vert.Y, vert.Z, 1.0f)); vbverts[i] = vert; } for (int t = 0; t < bindices.Length / 2; t += 3) { var f = new hkaiNavMeshFace(); f.m_clusterIndex = 0; f.m_numEdges = 3; f.m_startEdgeIndex = nmesh.m_edges.Count; f.m_startUserEdgeIndex = -1; f.m_padding = 0xCDCD; nmesh.m_faces.Add(f); nmesh.m_faceData.Add(0); for (int i = 0; i < 3; i++) { var e = new hkaiNavMeshEdge(); e.m_a = bindices[t * 2 + i]; e.m_b = bindices[t * 2 + ((i + 1) % 3)]; e.m_flags = 4; // Record adjacency if (bindices[t * 2 + 3 + i] == 0xFFFF) { // No adjacency e.m_oppositeEdge = 0xFFFFFFFF; e.m_oppositeFace = 0xFFFFFFFF; } else { e.m_oppositeFace = bindices[t * 2 + 3 + i]; // Find the edge that has this face as an adjancency for (int j = 0; j < 3; j++) { var edge = bindices[t * 2 + 3 + i] * 6 + 3 + j; if (bindices[edge] == (t / 3)) { e.m_oppositeEdge = (uint)bindices[t * 2 + 3 + i] * 3 + (uint)j; } } } nmesh.m_edges.Add(e); nmesh.m_edgeData.Add(0); } } root.m_namedVariants = new List <hkRootLevelContainerNamedVariant>(); var variant = new hkRootLevelContainerNamedVariant(); variant.m_className = "hkaiNavMesh"; variant.m_name = "hkaiNavMesh"; variant.m_variant = nmesh; root.m_namedVariants.Add(variant); // Next step: build a bvh var shortIndices = new ushort[bindices.Length / 2]; for (int i = 0; i < bindices.Length / 2; i += 3) { shortIndices[i] = bindices[i * 2]; shortIndices[i + 1] = bindices[i * 2 + 1]; shortIndices[i + 2] = bindices[i * 2 + 2]; } bool didbuild = BVHNative.BuildBVHForMesh(vbverts, vbverts.Length, shortIndices, shortIndices.Length); if (!didbuild) { return(null); } var nodecount = BVHNative.GetBVHSize(); var nsize = BVHNative.GetNodeSize(); var nodes = new NativeBVHNode[nodecount]; BVHNative.GetBVHNodes(nodes); // Rebuild in friendlier tree form List <BVNode> bnodes = new List <BVNode>((int)nodecount); foreach (var n in nodes) { var bnode = new BVNode(); bnode.Min = new Vector3(n.minX, n.minY, n.minZ); bnode.Max = new Vector3(n.maxX, n.maxY, n.maxZ); bnode.IsLeaf = n.isLeaf == 1; bnode.PrimitiveCount = n.primitiveCount; bnode.Primitive = n.firstChildOrPrimitive; bnodes.Add(bnode); } for (int i = 0; i < nodes.Length; i++) { if (nodes[i].isLeaf == 0) { bnodes[i].Left = bnodes[(int)nodes[i].firstChildOrPrimitive]; bnodes[i].Right = bnodes[(int)nodes[i].firstChildOrPrimitive + 1]; } } var bvhvariant = new hkRootLevelContainerNamedVariant(); bvhvariant.m_className = "hkcdStaticAabbTree"; bvhvariant.m_name = "hkcdStaticAabbTree"; var tree = new hkcdStaticAabbTree(); bvhvariant.m_variant = tree; root.m_namedVariants.Add(bvhvariant); tree.m_treePtr = new hkcdStaticTreeDefaultTreeStorage6(); tree.m_treePtr.m_nodes = bnodes[0].BuildAxis6Tree(); var min = bnodes[0].Min; var max = bnodes[0].Max; tree.m_treePtr.m_domain = new hkAabb(); tree.m_treePtr.m_domain.m_min = new Vector4(min.X, min.Y, min.Z, 1.0f); tree.m_treePtr.m_domain.m_max = new Vector4(max.X, max.Y, max.Z, 1.0f); // Build a dummy directed graph var gvariant = new hkRootLevelContainerNamedVariant(); gvariant.m_className = "hkaiDirectedGraphExplicitCost"; gvariant.m_name = "hkaiDirectedGraphExplicitCost"; var graph = new hkaiDirectedGraphExplicitCost(); gvariant.m_variant = graph; root.m_namedVariants.Add(gvariant); graph.m_nodes = new List <hkaiDirectedGraphExplicitCostNode>(); var node = new hkaiDirectedGraphExplicitCostNode(); node.m_numEdges = 0; node.m_startEdgeIndex = 0; graph.m_nodes.Add(node); graph.m_positions = new List <Vector4>(); var c = (max - min) / 2; graph.m_positions.Add(new Vector4(c.X, c.Y, c.Z, 1.0f)); return(root); }
public void AddMesh(List <Vector3> verts, List <ushort> indices) { // Try and build the BVH for the mesh first var bv = verts.ToArray(); var bi = indices.ToArray(); bool didbuild = BVHNative.BuildBVHForMesh(bv, bv.Count(), bi, bi.Count()); if (didbuild) { var nodecount = BVHNative.GetBVHSize(); var nsize = BVHNative.GetNodeSize(); var nodes = new NativeBVHNode[nodecount]; BVHNative.GetBVHNodes(nodes); // Rebuild in friendlier tree form List <BVNode> bnodes = new List <BVNode>((int)nodecount); foreach (var n in nodes) { var bnode = new BVNode(); bnode.Min = new Vector3(n.minX, n.minY, n.minZ); bnode.Max = new Vector3(n.maxX, n.maxY, n.maxZ); bnode.IsLeaf = n.isLeaf == 1; bnode.PrimitiveCount = n.primitiveCount; bnode.Primitive = n.firstChildOrPrimitive; bnodes.Add(bnode); } for (int i = 0; i < nodes.Length; i++) { if (nodes[i].isLeaf == 0) { bnodes[i].Left = bnodes[(int)nodes[i].firstChildOrPrimitive]; bnodes[i].Right = bnodes[(int)nodes[i].firstChildOrPrimitive + 1]; } } // Split the mesh into sections using the BVH and primitive counts as // guidence bnodes[0].ComputePrimitiveCounts(); bnodes[0].ComputeUniqueIndicesCounts(indices); bnodes[0].IsSectionHead = true; bnodes[0].AttemptSectionSplit(); // Take out the section heads and replace them with new leafs that reference // the new section List <BVNode> sectionBVHs = new List <BVNode>(); foreach (var node in bnodes) { if (node.IsSectionHead) { var secnode = new BVNode(); secnode.Min = node.Min; secnode.Max = node.Max; secnode.PrimitiveCount = node.PrimitiveCount; secnode.Left = node.Left; secnode.Right = node.Right; node.Left = null; node.Right = null; node.IsLeaf = true; node.Primitive = (uint)sectionBVHs.Count(); sectionBVHs.Add(secnode); } } List <CollisionSection> sections = new List <CollisionSection>(); foreach (var b in sectionBVHs) { var s = new CollisionSection(); s.SectionHead = b; s.GatherSectionIndices(indices); sections.Add(s); } // Count all the indices across sections to figure out what vertices need to be shared byte[] indicescount = new byte[indices.Count]; foreach (var s in sections) { foreach (var v in s.UsedIndices) { indicescount[v]++; } } var shared = new HashSet <ushort>(); for (ushort i = 0; i < indices.Count(); i++) { if (indicescount[i] > 1) { shared.Add(i); } } // Build shared indices mapping table and compress the shared verts List <ulong> sharedVerts = new List <ulong>(); Dictionary <ushort, ushort> sharedIndexRemapTable = new Dictionary <ushort, ushort>(); foreach (var i in shared.OrderBy(x => x)) { sharedIndexRemapTable.Add(i, (ushort)sharedVerts.Count()); sharedVerts.Add(CompressSharedVertex(verts[i], bnodes[0].Min, bnodes[0].Max)); } // build the havok mesh fsnpCustomParamCompressedMeshShape mesh = new fsnpCustomParamCompressedMeshShape(); mesh.m_convexRadius = 0.01f; mesh.m_dispatchType = Enum.COMPOSITE; mesh.m_edgeWeldingMap = new hknpSparseCompactMapunsignedshort(); mesh.m_edgeWeldingMap.m_primaryKeyToIndex = null; mesh.m_edgeWeldingMap.m_secondaryKeyMask = 0; mesh.m_edgeWeldingMap.m_sencondaryKeyBits = 0; mesh.m_edgeWeldingMap.m_valueAndSecondaryKeys = null; mesh.m_quadIsFlat = new hkBitField(); mesh.m_quadIsFlat.m_storage = new hkBitFieldStoragehkArrayunsignedinthkContainerHeapAllocator(); mesh.m_quadIsFlat.m_storage.m_numBits = 0; mesh.m_quadIsFlat.m_storage.m_words = null; mesh.m_triangleIsInterior = new hkBitField(); mesh.m_triangleIsInterior.m_storage = new hkBitFieldStoragehkArrayunsignedinthkContainerHeapAllocator(); mesh.m_triangleIsInterior.m_storage.m_numBits = 0; mesh.m_triangleIsInterior.m_storage.m_words = null; mesh.m_triangleIndexToShapeKey = null; mesh.m_pParam = null; mesh.m_shapeTagCodecInfo = 4294967295; mesh.m_flags = 516; mesh.m_numShapeKeyBits = 5; // ? var meshdata = new hknpCompressedMeshShapeData(); mesh.m_data = meshdata; var meshtree = new hknpCompressedMeshShapeTree(); meshdata.m_meshTree = meshtree; meshtree.m_bitsPerKey = 5; // ? meshtree.m_domain = new hkAabb(); meshtree.m_domain.m_min = new Vector4(bnodes[0].Min.X, bnodes[0].Min.Y, bnodes[0].Min.Z, 1.0f); meshtree.m_domain.m_max = new Vector4(bnodes[0].Max.X, bnodes[0].Max.Y, bnodes[0].Max.Z, 1.0f); meshtree.m_maxKeyValue = 30; // ? meshtree.m_nodes = bnodes[0].BuildAxis5Tree(); meshtree.m_sharedVertices = sharedVerts; var bvh = meshdata.getMeshBVH(); // Now let's process all the sections meshtree.m_packedVertices = new List <uint>(); meshtree.m_primitiveDataRuns = new List <hknpCompressedMeshShapeTreeDataRun>(); meshtree.m_sharedVerticesIndex = new List <ushort>(); meshtree.m_primitives = new List <hkcdStaticMeshTreeBasePrimitive>(); meshtree.m_sections = new List <hkcdStaticMeshTreeBaseSection>(); foreach (var s in sections) { var sharedindexbase = meshtree.m_sharedVerticesIndex.Count; var packedvertbase = meshtree.m_packedVertices.Count; var primitivesbase = meshtree.m_primitives.Count; var sec = new hkcdStaticMeshTreeBaseSection(); var offset = s.SectionHead.Min; var scale = (s.SectionHead.Max - s.SectionHead.Min) / new Vector3((float)0x7FF, (float)0x7FF, (float)0x3FF); sec.m_codecParms_0 = offset.X; sec.m_codecParms_1 = offset.Y; sec.m_codecParms_2 = offset.Z; sec.m_codecParms_3 = scale.X; sec.m_codecParms_4 = scale.Y; sec.m_codecParms_5 = scale.Z; sec.m_domain = new hkAabb(); sec.m_domain.m_min = new Vector4(s.SectionHead.Min.X, s.SectionHead.Min.Y, s.SectionHead.Min.Z, 1.0f); sec.m_domain.m_max = new Vector4(s.SectionHead.Max.X, s.SectionHead.Max.Y, s.SectionHead.Max.Z, 1.0f); // Map the indices to either shared/packed verts and pack verts that need packing var packedIndicesRemap = new Dictionary <ushort, byte>(); byte idxcounter = 0; foreach (var idx in s.UsedIndices.OrderBy(x => x)) { if (!shared.Contains(idx)) { packedIndicesRemap.Add(idx, idxcounter); var vert = verts[idx]; meshtree.m_packedVertices.Add(CompressPackedVertex(verts[idx], scale, offset)); var decomp = meshdata.DecompressPackedVertex(meshtree.m_packedVertices.Last(), scale, offset); idxcounter++; } } var sharedstart = idxcounter; idxcounter = 0; foreach (var idx in s.UsedIndices.OrderBy(x => x)) { if (shared.Contains(idx)) { packedIndicesRemap.Add(idx, (byte)(idxcounter + sharedstart)); meshtree.m_sharedVerticesIndex.Add(sharedIndexRemapTable[idx]); idxcounter++; } } sec.m_firstPackedVertex = (uint)packedvertbase; sec.m_numSharedIndices = idxcounter; sec.m_numPackedVertices = sharedstart; sec.m_sharedVertices = new hkcdStaticMeshTreeBaseSectionSharedVertices(); sec.m_sharedVertices.m_data = (uint)(((uint)sharedstart & 0xFF) | ((uint)sharedindexbase << 8)); // Now pack the primitives for (int i = 0; i < s.Indices.Count / 3; i++) { var p = new hkcdStaticMeshTreeBasePrimitive(); p.m_indices_0 = packedIndicesRemap[s.Indices[i * 3]]; p.m_indices_1 = packedIndicesRemap[s.Indices[i * 3 + 1]]; p.m_indices_2 = packedIndicesRemap[s.Indices[i * 3 + 2]]; p.m_indices_3 = p.m_indices_2; meshtree.m_primitives.Add(p); } sec.m_primitives = new hkcdStaticMeshTreeBaseSectionPrimitives(); sec.m_primitives.m_data = (((uint)s.Indices.Count / 3) & 0xFF) | ((uint)primitivesbase << 8); // Create a data run sec.m_dataRuns = new hkcdStaticMeshTreeBaseSectionDataRuns(); sec.m_dataRuns.m_data = ((uint)meshtree.m_primitiveDataRuns.Count() << 8) | 1; var run = new hknpCompressedMeshShapeTreeDataRun(); run.m_count = (byte)(s.Indices.Count / 3); run.m_index = 0; run.m_value = new hknpCompressedMeshShapeTreeDataRunData(); run.m_value.m_data = 65535; meshtree.m_primitiveDataRuns.Add(run); sec.m_nodes = s.SectionHead.BuildAxis4Tree(); meshtree.m_sections.Add(sec); } meshtree.m_numPrimitiveKeys = meshtree.m_primitives.Count; var simdtree = new hkcdSimdTree(); meshdata.m_simdTree = simdtree; simdtree.m_nodes = new List <hkcdSimdTreeNode>(); for (int i = 0; i < 2; i++) { hkcdSimdTreeNode n = new hkcdSimdTreeNode(); n.m_data_0 = 0; n.m_data_1 = 0; n.m_data_2 = 0; n.m_data_3 = 0; float mi = -3.40282E+38F; float ma = 3.40282E+38F; n.m_hx = new Vector4(mi, mi, mi, mi); n.m_hy = new Vector4(mi, mi, mi, mi); n.m_hz = new Vector4(mi, mi, mi, mi); n.m_lx = new Vector4(ma, ma, ma, ma); n.m_ly = new Vector4(ma, ma, ma, ma); n.m_lz = new Vector4(ma, ma, ma, ma); simdtree.m_nodes.Add(n); } _meshes.Add(mesh); } }