/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> /// state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>False on error, otherwise true.</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) { return(true); } PolyMesh mesh = context.PolyMesh; PolyMeshData data = mesh.GetData(false); if (data.polyCount == 0) { return(true); } bool applied = false; for (int i = 0; i < mAreas.Length; i++) { byte area = mAreas[i]; ushort flags = mFlags[i]; int marked = 0; for (int iPoly = 0; iPoly < data.polyCount; iPoly++) { if (data.areas[iPoly] == area) { data.flags[iPoly] |= flags; marked++; } } if (marked > 0) { string msg = string.Format( "{0} : Added '0x{1:X}' flag(s) to {2} poylgons assigned to area {3}." , Name, flags, marked, area); context.Log(msg, this); applied = true; } } if (applied) { mesh.Load(data); } else { context.Log(Name + ": No flags applied.", this); } return(true); }
private TileBuildTask(int tx, int tz , PolyMeshData polyData , PolyMeshDetailData detailData , ConnectionSet connections , bool bvTreeEnabled , bool isThreadSafe , int priority) : base(priority) { mTileX = tx; mTileZ = tz; mPolyData = polyData; mDetailData = detailData; mConnections = connections; mBVTreeEnabled = bvTreeEnabled; mIsThreadSafe = isThreadSafe; }
/// <summary> /// Creates a new task. /// </summary> /// <remarks> /// <para> /// The task should only be marked as thread-safe if the data parameters are treated /// as immutable while the task is running. /// </para> /// <para> /// Creation will fail on null parameters, invalid tile indices, and an empty /// polygon mesh. /// </para> /// </remarks> /// <param name="tx">The x-index of the tile within the tile grid. (x, z)</param> /// <param name="tz">The z-index of the tile within the tile grid. (x, z)</param> /// <param name="polyData">The polygon mesh data.</param> /// <param name="detailData">The detail mesh data. (Optional)</param> /// <param name="conns">The off-mesh connection set.</param> /// <param name="bvTreeEnabled">True if bounding volumes should be generated.</param> /// <param name="isThreadSafe">True if the task is safe to run on its own thread.</param> /// <param name="priority">The task priority.</param> /// <returns>A new task, or null on error.</returns> public static TileBuildTask Create(int tx, int tz , PolyMeshData polyData , PolyMeshDetailData detailData , ConnectionSet conns , bool bvTreeEnabled , bool isThreadSafe , int priority) { if (tx < 0 || tz < 0 || polyData == null || polyData.polyCount == 0 || conns == null) { return(null); } return(new TileBuildTask(tx, tz , polyData, detailData, conns, bvTreeEnabled, isThreadSafe, priority)); }
/// <summary> /// Process the build context. /// </summary> /// <remarks> /// <para> /// The flags will be applied during the <see cref="NMGenState.PolyMeshBuild"/> state. /// </para> /// </remarks> /// <param name="state">The current build state.</param> /// <param name="context">The context to process.</param> /// <returns>True</returns> public override bool ProcessBuild(NMGenContext context, NMGenState state) { if (state != NMGenState.PolyMeshBuild) { return(true); } PolyMeshData data = context.PolyMesh.GetData(false); for (int i = 0; i < data.flags.Length; i++) { data.flags[i] |= mFlags; } context.PolyMesh.Load(data); context.Log(string.Format("{0}: Applied flag(s) to all polys. Flag(s): 0x{1:X}" , Name, mFlags) , this); return(true); }
private void SetBleedingObjects(PolyMeshData meshData, Renderer renderer) { var meshRenderer = renderer as MeshRenderer; var skinnedRenderer = renderer as SkinnedMeshRenderer; GameObject blood = null; if (meshRenderer != null) { // add blood object Vector3 position = AVG(meshData.vertices); Vector3 direction = AVG(meshData.normals).normalized; var rotation = Quaternion.FromToRotation(_prefubDirection, direction); blood = Instantiate(_bloodPrefub, renderer.gameObject.transform); blood.transform.localPosition = position; blood.transform.localRotation = rotation; } else if (skinnedRenderer != null) { var bones = skinnedRenderer.bones; float[] weightSums = new float[bones.Length]; for (int i = 0; i < meshData.boneWeights.Length; i++) { var w = meshData.boneWeights[i]; weightSums[w.boneIndex0] += w.weight0; weightSums[w.boneIndex1] += w.weight1; weightSums[w.boneIndex2] += w.weight2; weightSums[w.boneIndex3] += w.weight3; } // detect most weightful bone for this PolyMeshData int maxIndex = 0; for (int i = 0; i < weightSums.Length; i++) { float maxValue = weightSums[maxIndex]; float current = weightSums[i]; if (current > maxValue) { maxIndex = i; } } Transform bone = bones[maxIndex]; // add blood object to the bone Vector3 position = AVG(meshData.vertices); Vector3 normal = AVG(meshData.normals).normalized; var rotation = Quaternion.FromToRotation(_prefubDirection, normal); var m = skinnedRenderer.sharedMesh.bindposes[maxIndex]; position = m.MultiplyPoint3x4(position); blood = Instantiate(_bloodPrefub, bone); blood.transform.localPosition = position; blood.transform.localRotation = rotation; } if (_alignPrefSize) { var parentScale = blood.transform.parent.lossyScale; var newScale = new Vector3( 1f / parentScale.x, 1f / parentScale.y, 1f / parentScale.z); blood.transform.localScale = Vector3.Scale(newScale, blood.transform.localScale); } }
/// <summary> /// Draws a debug view of a <see cref="PolyMeshData"/> object. /// </summary> /// <remarks> /// <para> /// Meant to be called during the MonoBehavior.OnRenderObject() method. /// </para> /// </remarks> /// <param name="polyData">The polygon mesh to draw.</param> public static void Draw(PolyMeshData polyData) { DebugDraw.SimpleMaterial.SetPass(0); Color walkableColor = new Color(0, 0.75f, 1.0f, 0.25f); Color nullRegionColor = new Color(0, 0, 0, 0.25f); int[] pTargetVert = new int[3]; GL.Begin(GL.TRIANGLES); for (int iPoly = 0; iPoly < polyData.polyCount; iPoly++) { int pPoly = iPoly * polyData.maxVertsPerPoly * 2; if (polyData.areas[iPoly] == NMGen.MaxArea) { GL.Color(walkableColor); } else if (polyData.areas[iPoly] == NMGen.NullRegion) { GL.Color(nullRegionColor); } else { GL.Color(ColorUtil.IntToColor(polyData.areas[iPoly], 1.0f)); } pTargetVert[0] = polyData.polys[pPoly + 0] * 3; for (int iPolyVert = 2 ; iPolyVert < polyData.maxVertsPerPoly ; iPolyVert++) { if (polyData.polys[pPoly + iPolyVert] == PolyMesh.NullIndex) { break; } pTargetVert[1] = polyData.polys[pPoly + iPolyVert] * 3; pTargetVert[2] = polyData.polys[pPoly + iPolyVert - 1] * 3; for (int i = 0; i < 3; i++) { int p = pTargetVert[i]; int x = polyData.verts[p + 0]; // Offset y a little to ensure it clears the // source geometry. int y = polyData.verts[p + 1] + 1; int z = polyData.verts[p + 2]; GL.Vertex3(polyData.boundsMin[0] + x * polyData.xzCellSize , polyData.boundsMin[1] + y * polyData.yCellSize , polyData.boundsMin[2] + z * polyData.xzCellSize); } } } GL.End(); Color internalEdgeColor = new Color(0, 0.2f, 0.25f, 0.25f); Color boundaryEdgeColor = new Color(0.65f, 0.2f, 0, 0.9f); GL.Begin(GL.LINES); for (int iPoly = 0; iPoly < polyData.polyCount; iPoly++) { int pPoly = iPoly * polyData.maxVertsPerPoly * 2; for (int iPolyVert = 0; iPolyVert < polyData.maxVertsPerPoly; iPolyVert++) { int iv = polyData.polys[pPoly + iPolyVert]; if (iv == PolyMesh.NullIndex) { break; } if (polyData.polys[pPoly + polyData.maxVertsPerPoly + iPolyVert] == PolyMesh.NullIndex) { GL.Color(boundaryEdgeColor); } else { GL.Color(internalEdgeColor); } // Note: Using only first two indexes. pTargetVert[0] = iv * 3; if (iPolyVert + 1 >= polyData.maxVertsPerPoly) { // Reached hard end of polygon. Loop back. iv = polyData.polys[pPoly + 0]; } else { iv = polyData.polys[pPoly + iPolyVert + 1]; if (iv == PolyMesh.NullIndex) { // Reached soft end of polygon. Loop back. iv = polyData.polys[pPoly + 0]; } } pTargetVert[1] = iv * 3; for (int i = 0; i < 2; i++) { int p = pTargetVert[i]; int x = polyData.verts[p + 0]; // Offset y a little to ensure it clears the // source geometry. int y = polyData.verts[p + 1] + 1; int z = polyData.verts[p + 2]; GL.Vertex3(polyData.boundsMin[0] + x * polyData.xzCellSize , polyData.boundsMin[1] + y * polyData.yCellSize , polyData.boundsMin[2] + z * polyData.xzCellSize); } } } GL.End(); }
/// <summary> /// Finalize the task. /// </summary> protected override void FinalizeTask() { mPolyData = null; mDetailData = null; mConnections = null; }
/// <summary> /// Creates a standard <see cref="NavmeshTileBuildData"/> object from the provided /// parameters. /// </summary> /// <remarks> /// <para> /// Errors will be logged to the build context. /// </para> /// </remarks> /// <param name="tx">The x-index of the tile.</param> /// <param name="tz">The z-index of the tile.</param> /// <param name="polyMesh">The polygon mesh data.</param> /// <param name="detailMesh">The detail mesh data. (Optional)</param> /// <param name="connections">The off-mesh connections. (Null allowed.)</param> /// <param name="bvTreeEnabled">True if bounding volumes should be generated.</param> /// <param name="context">The build context.</param> /// <returns>The tile build data, or null on error.</returns> public static NavmeshTileBuildData GetBuildData(BuildContext context , int tx, int tz , PolyMeshData polyMesh, PolyMeshDetailData detailMesh , ConnectionSet connections , bool bvTreeEnabled) { if (context == null) { // Silent. return(null); } Vector3[] verts = null; float[] radii = null; byte[] dirs = null; byte[] areas = null; ushort[] flags = null; uint[] userIds = null; Vector3 bmin = polyMesh.boundsMin; Vector3 bmax = polyMesh.boundsMax; int connCount = (connections == null) ? 0 : connections.GetConnections(bmin.x, bmin.z, bmax.x, bmax.z , out verts, out radii, out dirs, out areas, out flags, out userIds); NavmeshTileBuildData result = new NavmeshTileBuildData( polyMesh.vertCount , polyMesh.polyCount , polyMesh.maxVertsPerPoly , (detailMesh == null ? 0 : detailMesh.vertCount) , (detailMesh == null ? 0 : detailMesh.triCount) , connCount); if (!result.LoadBase(tx, tz, 0, 0 , polyMesh.boundsMin , polyMesh.boundsMax , polyMesh.xzCellSize , polyMesh.yCellSize , polyMesh.walkableHeight , polyMesh.walkableRadius , polyMesh.walkableStep , bvTreeEnabled)) { context.LogError("Base data load failed. Bad configuration data or internal error." , null); return(null); } if (!result.LoadPolys(polyMesh.verts , polyMesh.vertCount , polyMesh.polys , polyMesh.flags , polyMesh.areas , polyMesh.polyCount)) { context.LogError("Polygon load failed. Bad mesh data or internal error.", null); return(null); } if (detailMesh != null) { if (!result.LoadDetail(detailMesh.verts , detailMesh.vertCount , detailMesh.tris , detailMesh.triCount , detailMesh.meshes , detailMesh.meshCount)) { context.LogError("Detail load failed. Bad mesh data or internal error.", null); return(null); } } if (connCount > 0) { if (!result.LoadConns(verts, radii, dirs, areas, flags, userIds, connCount)) { context.LogError("Off-mesh connection load failed. Bad data or internal error." , null); return(null); } } return(result); }