DotRecastNetSim/src/DotRecast.Recast.Demo/Tools/DynamicUpdateSampleTool.cs

772 lines
30 KiB
C#

/*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org
DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using DotRecast.Core;
using DotRecast.Detour.Dynamic;
using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Detour.Dynamic.Io;
using DotRecast.Recast.Toolset.Builder;
using DotRecast.Recast.Demo.Draw;
using DotRecast.Recast.Toolset.Geom;
using DotRecast.Recast.Demo.Tools.Gizmos;
using DotRecast.Recast.Demo.UI;
using DotRecast.Recast.Toolset;
using DotRecast.Recast.Toolset.Tools;
using ImGuiNET;
using Serilog;
using static DotRecast.Recast.Demo.Draw.DebugDraw;
using static DotRecast.Recast.Demo.Draw.DebugDrawPrimitives;
namespace DotRecast.Recast.Demo.Tools;
public class DynamicUpdateSampleTool : ISampleTool
{
private static readonly ILogger Logger = Log.ForContext<DynamicUpdateSampleTool>();
private DemoSample _sample;
private readonly DynamicUpdateToolImpl _impl;
private int toolModeIdx = DynamicUpdateToolMode.BUILD.Idx;
private DynamicUpdateToolMode mode = DynamicUpdateToolMode.BUILD;
private float cellSize = 0.3f;
private int partitioning = RcPartitionType.WATERSHED.Value;
private bool filterLowHangingObstacles = true;
private bool filterLedgeSpans = true;
private bool filterWalkableLowHeightSpans = true;
private float walkableHeight = 2f;
private float walkableRadius = 0.6f;
private float walkableClimb = 0.9f;
private float walkableSlopeAngle = 45f;
private float minRegionArea = 6f;
private float regionMergeSize = 36f;
private float maxEdgeLen = 12f;
private float maxSimplificationError = 1.3f;
private int vertsPerPoly = 6;
private bool buildDetailMesh = true;
private bool compression = true;
private float detailSampleDist = 6f;
private float detailSampleMaxError = 1f;
private bool showColliders = false;
private long buildTime;
private long raycastTime;
private int colliderShapeIdx = (int)DynamicColliderShape.SPHERE;
private DynamicColliderShape colliderShape = DynamicColliderShape.SPHERE;
private DynamicNavMesh dynaMesh;
private readonly TaskFactory executor;
private readonly Dictionary<long, ICollider> colliders = new();
private readonly Dictionary<long, IColliderGizmo> colliderGizmos = new();
private readonly Random random = Random.Shared;
private readonly DemoInputGeomProvider bridgeGeom;
private readonly DemoInputGeomProvider houseGeom;
private readonly DemoInputGeomProvider convexGeom;
private bool sposSet;
private bool eposSet;
private RcVec3f spos;
private RcVec3f epos;
private bool raycastHit;
private RcVec3f raycastHitPos;
public DynamicUpdateSampleTool()
{
_impl = new();
executor = Task.Factory;
bridgeGeom = DemoObjImporter.Load(Loader.ToBytes("bridge.obj"));
houseGeom = DemoObjImporter.Load(Loader.ToBytes("house.obj"));
convexGeom = DemoObjImporter.Load(Loader.ToBytes("convex.obj"));
}
public IRcToolable GetTool()
{
return _impl;
}
public void SetSample(DemoSample sample)
{
_sample = sample;
}
public void OnSampleChanged()
{
// ..
}
public void HandleClick(RcVec3f s, RcVec3f p, bool shift)
{
if (mode == DynamicUpdateToolMode.COLLIDERS)
{
if (!shift)
{
ColliderWithGizmo colliderWithGizmo = null;
if (dynaMesh != null)
{
if (colliderShape == DynamicColliderShape.SPHERE)
{
colliderWithGizmo = SphereCollider(p);
}
else if (colliderShape == DynamicColliderShape.CAPSULE)
{
colliderWithGizmo = CapsuleCollider(p);
}
else if (colliderShape == DynamicColliderShape.BOX)
{
colliderWithGizmo = BoxCollider(p);
}
else if (colliderShape == DynamicColliderShape.CYLINDER)
{
colliderWithGizmo = CylinderCollider(p);
}
else if (colliderShape == DynamicColliderShape.COMPOSITE)
{
colliderWithGizmo = CompositeCollider(p);
}
else if (colliderShape == DynamicColliderShape.TRIMESH_BRIDGE)
{
colliderWithGizmo = TrimeshBridge(p);
}
else if (colliderShape == DynamicColliderShape.TRIMESH_HOUSE)
{
colliderWithGizmo = TrimeshHouse(p);
}
else if (colliderShape == DynamicColliderShape.CONVEX)
{
colliderWithGizmo = ConvexTrimesh(p);
}
}
if (colliderWithGizmo != null)
{
long id = dynaMesh.AddCollider(colliderWithGizmo.Collider);
colliders.Add(id, colliderWithGizmo.Collider);
colliderGizmos.Add(id, colliderWithGizmo.Gizmo);
}
}
}
if (mode == DynamicUpdateToolMode.RAYCAST)
{
if (shift)
{
sposSet = true;
spos = p;
}
else
{
eposSet = true;
epos = p;
}
if (sposSet && eposSet && dynaMesh != null)
{
RcVec3f sp = RcVec3f.Of(spos.x, spos.y + 1.3f, spos.z);
RcVec3f ep = RcVec3f.Of(epos.x, epos.y + 1.3f, epos.z);
long t1 = RcFrequency.Ticks;
bool hasHit = dynaMesh.VoxelQuery().Raycast(sp, ep, out var hitPos);
long t2 = RcFrequency.Ticks;
raycastTime = (t2 - t1) / TimeSpan.TicksPerMillisecond;
raycastHit = hasHit;
raycastHitPos = hasHit
? RcVec3f.Of(sp.x + hitPos * (ep.x - sp.x), sp.y + hitPos * (ep.y - sp.y), sp.z + hitPos * (ep.z - sp.z))
: ep;
}
}
}
private ColliderWithGizmo SphereCollider(RcVec3f p)
{
float radius = 1 + (float)random.NextDouble() * 10;
var collider = new SphereCollider(p, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, dynaMesh.config.walkableClimb);
var gizmo = GizmoFactory.Sphere(p, radius);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo CapsuleCollider(RcVec3f p)
{
float radius = 0.4f + (float)random.NextDouble() * 4f;
RcVec3f a = RcVec3f.Of(
(1f - 2 * (float)random.NextDouble()),
0.01f + (float)random.NextDouble(),
(1f - 2 * (float)random.NextDouble())
);
a.Normalize();
float len = 1f + (float)random.NextDouble() * 20f;
a.x *= len;
a.y *= len;
a.z *= len;
RcVec3f start = RcVec3f.Of(p.x, p.y, p.z);
RcVec3f end = RcVec3f.Of(p.x + a.x, p.y + a.y, p.z + a.z);
var collider = new CapsuleCollider(start, end, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, dynaMesh.config.walkableClimb);
var gizmo = GizmoFactory.Capsule(start, end, radius);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo BoxCollider(RcVec3f p)
{
RcVec3f extent = RcVec3f.Of(
0.5f + (float)random.NextDouble() * 6f,
0.5f + (float)random.NextDouble() * 6f,
0.5f + (float)random.NextDouble() * 6f
);
RcVec3f forward = RcVec3f.Of((1f - 2 * (float)random.NextDouble()), 0, (1f - 2 * (float)random.NextDouble()));
RcVec3f up = RcVec3f.Of((1f - 2 * (float)random.NextDouble()), 0.01f + (float)random.NextDouble(), (1f - 2 * (float)random.NextDouble()));
RcVec3f[] halfEdges = Detour.Dynamic.Colliders.BoxCollider.GetHalfEdges(up, forward, extent);
var collider = new BoxCollider(p, halfEdges, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, dynaMesh.config.walkableClimb);
var gizmo = GizmoFactory.Box(p, halfEdges);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo CylinderCollider(RcVec3f p)
{
float radius = 0.7f + (float)random.NextDouble() * 4f;
RcVec3f a = RcVec3f.Of(1f - 2 * (float)random.NextDouble(), 0.01f + (float)random.NextDouble(), 1f - 2 * (float)random.NextDouble());
a.Normalize();
float len = 2f + (float)random.NextDouble() * 20f;
a[0] *= len;
a[1] *= len;
a[2] *= len;
RcVec3f start = RcVec3f.Of(p.x, p.y, p.z);
RcVec3f end = RcVec3f.Of(p.x + a.x, p.y + a.y, p.z + a.z);
var collider = new CylinderCollider(start, end, radius, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_WATER, dynaMesh.config.walkableClimb);
var gizmo = GizmoFactory.Cylinder(start, end, radius);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo CompositeCollider(RcVec3f p)
{
RcVec3f baseExtent = RcVec3f.Of(5, 3, 8);
RcVec3f baseCenter = RcVec3f.Of(p.x, p.y + 3, p.z);
RcVec3f baseUp = RcVec3f.Of(0, 1, 0);
RcVec3f forward = RcVec3f.Of((1f - 2 * (float)random.NextDouble()), 0, (1f - 2 * (float)random.NextDouble()));
forward.Normalize();
RcVec3f side = RcVec3f.Cross(forward, baseUp);
BoxCollider @base = new BoxCollider(baseCenter, Detour.Dynamic.Colliders.BoxCollider.GetHalfEdges(baseUp, forward, baseExtent),
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, dynaMesh.config.walkableClimb);
var roofUp = RcVec3f.Zero;
RcVec3f roofExtent = RcVec3f.Of(4.5f, 4.5f, 8f);
float[] rx = GLU.Build_4x4_rotation_matrix(45, forward.x, forward.y, forward.z);
roofUp = MulMatrixVector(ref roofUp, rx, baseUp);
RcVec3f roofCenter = RcVec3f.Of(p.x, p.y + 6, p.z);
BoxCollider roof = new BoxCollider(roofCenter, Detour.Dynamic.Colliders.BoxCollider.GetHalfEdges(roofUp, forward, roofExtent),
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, dynaMesh.config.walkableClimb);
RcVec3f trunkStart = RcVec3f.Of(
baseCenter.x - forward.x * 15 + side.x * 6,
p.y,
baseCenter.z - forward.z * 15 + side.z * 6
);
RcVec3f trunkEnd = RcVec3f.Of(trunkStart.x, trunkStart.y + 10, trunkStart.z);
CapsuleCollider trunk = new CapsuleCollider(trunkStart, trunkEnd, 0.5f, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD,
dynaMesh.config.walkableClimb);
RcVec3f crownCenter = RcVec3f.Of(
baseCenter.x - forward.x * 15 + side.x * 6, p.y + 10,
baseCenter.z - forward.z * 15 + side.z * 6
);
SphereCollider crown = new SphereCollider(crownCenter, 4f, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GRASS,
dynaMesh.config.walkableClimb);
CompositeCollider collider = new CompositeCollider(@base, roof, trunk, crown);
IColliderGizmo baseGizmo = GizmoFactory.Box(baseCenter, Detour.Dynamic.Colliders.BoxCollider.GetHalfEdges(baseUp, forward, baseExtent));
IColliderGizmo roofGizmo = GizmoFactory.Box(roofCenter, Detour.Dynamic.Colliders.BoxCollider.GetHalfEdges(roofUp, forward, roofExtent));
IColliderGizmo trunkGizmo = GizmoFactory.Capsule(trunkStart, trunkEnd, 0.5f);
IColliderGizmo crownGizmo = GizmoFactory.Sphere(crownCenter, 4f);
IColliderGizmo gizmo = GizmoFactory.Composite(baseGizmo, roofGizmo, trunkGizmo, crownGizmo);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo TrimeshBridge(RcVec3f p)
{
return TrimeshCollider(p, bridgeGeom);
}
private ColliderWithGizmo TrimeshHouse(RcVec3f p)
{
return TrimeshCollider(p, houseGeom);
}
private ColliderWithGizmo ConvexTrimesh(RcVec3f p)
{
float[] verts = TransformVertices(p, convexGeom, 360);
var collider = new ConvexTrimeshCollider(verts, convexGeom.faces,
SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD, dynaMesh.config.walkableClimb * 10);
var gizmo = GizmoFactory.Trimesh(verts, convexGeom.faces);
return new ColliderWithGizmo(collider, gizmo);
}
private ColliderWithGizmo TrimeshCollider(RcVec3f p, DemoInputGeomProvider geom)
{
float[] verts = TransformVertices(p, geom, 0);
var collider = new TrimeshCollider(verts, geom.faces, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_ROAD,
dynaMesh.config.walkableClimb * 10);
var gizmo = GizmoFactory.Trimesh(verts, geom.faces);
return new ColliderWithGizmo(collider, gizmo);
}
private float[] TransformVertices(RcVec3f p, DemoInputGeomProvider geom, float ax)
{
float[] rx = GLU.Build_4x4_rotation_matrix((float)random.NextDouble() * ax, 1, 0, 0);
float[] ry = GLU.Build_4x4_rotation_matrix((float)random.NextDouble() * 360, 0, 1, 0);
float[] m = GLU.Mul(rx, ry);
float[] verts = new float[geom.vertices.Length];
RcVec3f v = new RcVec3f();
RcVec3f vr = new RcVec3f();
for (int i = 0; i < geom.vertices.Length; i += 3)
{
v.x = geom.vertices[i];
v.y = geom.vertices[i + 1];
v.z = geom.vertices[i + 2];
MulMatrixVector(ref vr, m, v);
vr.x += p.x;
vr.y += p.y - 0.1f;
vr.z += p.z;
verts[i] = vr.x;
verts[i + 1] = vr.y;
verts[i + 2] = vr.z;
}
return verts;
}
private float[] MulMatrixVector(float[] resultvector, float[] matrix, float[] pvector)
{
resultvector[0] = matrix[0] * pvector[0] + matrix[4] * pvector[1] + matrix[8] * pvector[2];
resultvector[1] = matrix[1] * pvector[0] + matrix[5] * pvector[1] + matrix[9] * pvector[2];
resultvector[2] = matrix[2] * pvector[0] + matrix[6] * pvector[1] + matrix[10] * pvector[2];
return resultvector;
}
private RcVec3f MulMatrixVector(ref RcVec3f resultvector, float[] matrix, RcVec3f pvector)
{
resultvector.x = matrix[0] * pvector.x + matrix[4] * pvector.y + matrix[8] * pvector.z;
resultvector.y = matrix[1] * pvector.x + matrix[5] * pvector.y + matrix[9] * pvector.z;
resultvector.z = matrix[2] * pvector.x + matrix[6] * pvector.y + matrix[10] * pvector.z;
return resultvector;
}
public void HandleClickRay(RcVec3f start, RcVec3f dir, bool shift)
{
if (mode == DynamicUpdateToolMode.COLLIDERS)
{
if (shift)
{
foreach (var e in colliders)
{
if (Hit(start, dir, e.Value.Bounds()))
{
dynaMesh.RemoveCollider(e.Key);
colliders.Remove(e.Key);
colliderGizmos.Remove(e.Key);
break;
}
}
}
}
}
private bool Hit(RcVec3f point, RcVec3f dir, float[] bounds)
{
float cx = 0.5f * (bounds[0] + bounds[3]);
float cy = 0.5f * (bounds[1] + bounds[4]);
float cz = 0.5f * (bounds[2] + bounds[5]);
float dx = 0.5f * (bounds[3] - bounds[0]);
float dy = 0.5f * (bounds[4] - bounds[1]);
float dz = 0.5f * (bounds[5] - bounds[2]);
float rSqr = dx * dx + dy * dy + dz * dz;
float mx = point.x - cx;
float my = point.y - cy;
float mz = point.z - cz;
float c = mx * mx + my * my + mz * mz - rSqr;
if (c <= 0.0f)
{
return true;
}
float b = mx * dir.x + my * dir.y + mz * dir.z;
if (b > 0.0f)
{
return false;
}
float disc = b * b - c;
return disc >= 0.0f;
}
public void HandleRender(NavMeshRenderer renderer)
{
if (mode == DynamicUpdateToolMode.COLLIDERS)
{
if (showColliders)
{
colliderGizmos.Values.ForEach(g => g.Render(renderer.GetDebugDraw()));
}
}
if (mode == DynamicUpdateToolMode.RAYCAST)
{
RecastDebugDraw dd = renderer.GetDebugDraw();
int startCol = DuRGBA(128, 25, 0, 192);
int endCol = DuRGBA(51, 102, 0, 129);
if (sposSet)
{
DrawAgent(dd, spos, startCol);
}
if (eposSet)
{
DrawAgent(dd, epos, endCol);
}
dd.DepthMask(false);
if (raycastHitPos != RcVec3f.Zero)
{
int spathCol = raycastHit ? DuRGBA(128, 32, 16, 220) : DuRGBA(64, 128, 240, 220);
dd.Begin(LINES, 2.0f);
dd.Vertex(spos.x, spos.y + 1.3f, spos.z, spathCol);
dd.Vertex(raycastHitPos.x, raycastHitPos.y, raycastHitPos.z, spathCol);
dd.End();
}
dd.DepthMask(true);
}
}
private void DrawAgent(RecastDebugDraw dd, RcVec3f pos, int col)
{
var settings = _sample.GetSettings();
float r = settings.agentRadius;
float h = settings.agentHeight;
float c = settings.agentMaxClimb;
dd.DepthMask(false);
// Agent dimensions.
dd.DebugDrawCylinderWire(pos.x - r, pos.y + 0.02f, pos.z - r, pos.x + r, pos.y + h, pos.z + r, col, 2.0f);
dd.DebugDrawCircle(pos.x, pos.y + c, pos.z, r, DuRGBA(0, 0, 0, 64), 1.0f);
int colb = DuRGBA(0, 0, 0, 196);
dd.Begin(LINES);
dd.Vertex(pos.x, pos.y - c, pos.z, colb);
dd.Vertex(pos.x, pos.y + c, pos.z, colb);
dd.Vertex(pos.x - r / 2, pos.y + 0.02f, pos.z, colb);
dd.Vertex(pos.x + r / 2, pos.y + 0.02f, pos.z, colb);
dd.Vertex(pos.x, pos.y + 0.02f, pos.z - r / 2, colb);
dd.Vertex(pos.x, pos.y + 0.02f, pos.z + r / 2, colb);
dd.End();
dd.DepthMask(true);
}
public void HandleUpdate(float dt)
{
if (dynaMesh != null)
{
UpdateDynaMesh();
}
}
private void UpdateDynaMesh()
{
long t = RcFrequency.Ticks;
try
{
bool updated = dynaMesh.Update(executor).Result;
if (updated)
{
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
_sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
_sample.SetChanged(false);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void Layout()
{
ImGui.Text($"Dynamic Update Tool Modes");
ImGui.Separator();
var prevMode = mode;
ImGui.RadioButton(DynamicUpdateToolMode.BUILD.Label, ref toolModeIdx, DynamicUpdateToolMode.BUILD.Idx);
ImGui.RadioButton(DynamicUpdateToolMode.COLLIDERS.Label, ref toolModeIdx, DynamicUpdateToolMode.COLLIDERS.Idx);
ImGui.RadioButton(DynamicUpdateToolMode.RAYCAST.Label, ref toolModeIdx, DynamicUpdateToolMode.RAYCAST.Idx);
ImGui.NewLine();
if (prevMode.Idx != toolModeIdx)
{
mode = DynamicUpdateToolMode.Values[toolModeIdx];
}
ImGui.Text($"Selected mode - {mode.Label}");
ImGui.Separator();
if (mode == DynamicUpdateToolMode.BUILD)
{
var loadVoxelPopupStrId = "Load Voxels Popup";
bool isLoadVoxelPopup = true;
if (ImGui.Button("Load Voxels..."))
{
ImGui.OpenPopup(loadVoxelPopupStrId);
}
if (ImGui.BeginPopupModal(loadVoxelPopupStrId, ref isLoadVoxelPopup, ImGuiWindowFlags.NoTitleBar))
{
var picker = ImFilePicker.GetFilePicker(loadVoxelPopupStrId, Path.Combine(Environment.CurrentDirectory), ".voxels");
if (picker.Draw())
{
Load(picker.SelectedFile);
ImFilePicker.RemoveFilePicker(loadVoxelPopupStrId);
}
ImGui.EndPopup();
}
var saveVoxelPopupStrId = "Save Voxels Popup";
bool isSaveVoxelPopup = true;
if (dynaMesh != null)
{
ImGui.Checkbox("Compression", ref compression);
if (ImGui.Button("Save Voxels..."))
{
ImGui.BeginPopup(saveVoxelPopupStrId);
}
if (ImGui.BeginPopupModal(saveVoxelPopupStrId, ref isSaveVoxelPopup, ImGuiWindowFlags.NoTitleBar))
{
var picker = ImFilePicker.GetFilePicker(saveVoxelPopupStrId, Path.Combine(Environment.CurrentDirectory), ".voxels");
if (picker.Draw())
{
if (string.IsNullOrEmpty(picker.SelectedFile))
Save(picker.SelectedFile);
ImFilePicker.RemoveFilePicker(saveVoxelPopupStrId);
}
ImGui.EndPopup();
}
}
ImGui.NewLine();
ImGui.Text("Rasterization");
ImGui.Separator();
ImGui.Text($"Cell Size - {cellSize}");
ImGui.NewLine();
ImGui.Text("Agent");
ImGui.Separator();
ImGui.SliderFloat("Height", ref walkableHeight, 0f, 5f, "%.2f");
ImGui.SliderFloat("Radius", ref walkableRadius, 0f, 10f, "%.2f");
ImGui.SliderFloat("Max Climb", ref walkableClimb, 0f, 10f, "%.2f");
ImGui.Text($"Max Slope : {walkableSlopeAngle}");
ImGui.NewLine();
ImGui.Text("Partitioning");
ImGui.Separator();
RcPartitionType.Values.ForEach(partition =>
{
var label = partition.Name.Substring(0, 1).ToUpper()
+ partition.Name.Substring(1).ToLower();
ImGui.RadioButton(label, ref partitioning, partition.Value);
});
ImGui.NewLine();
ImGui.Text("Filtering");
ImGui.Separator();
ImGui.Checkbox("Low Hanging Obstacles", ref filterLowHangingObstacles);
ImGui.Checkbox("Ledge Spans", ref filterLedgeSpans);
ImGui.Checkbox("Walkable Low Height Spans", ref filterWalkableLowHeightSpans);
ImGui.NewLine();
ImGui.Text("Region");
ImGui.Separator();
ImGui.SliderFloat("Min Region Size", ref minRegionArea, 0, 150, "%.1f");
ImGui.SliderFloat("Merged Region Size", ref regionMergeSize, 0, 400, "%.1f");
ImGui.NewLine();
ImGui.Text("Polygonization");
ImGui.Separator();
ImGui.SliderFloat("Max Edge Length", ref maxEdgeLen, 0f, 50f, "%.1f");
ImGui.SliderFloat("Max Edge Error", ref maxSimplificationError, 0.1f, 10f, "%.1f");
ImGui.SliderInt("Verts Per Poly", ref vertsPerPoly, 3, 12);
ImGui.NewLine();
ImGui.Text("Detail Mesh");
ImGui.Separator();
ImGui.Checkbox("Enable", ref buildDetailMesh);
ImGui.SliderFloat("Sample Distance", ref detailSampleDist, 0f, 16f, "%.1f");
ImGui.SliderFloat("Max Sample Error", ref detailSampleMaxError, 0f, 16f, "%.1f");
ImGui.NewLine();
if (ImGui.Button("Build Dynamic mesh"))
{
if (dynaMesh != null)
{
BuildDynaMesh();
_sample.SetChanged(false);
}
}
}
if (mode == DynamicUpdateToolMode.COLLIDERS)
{
ImGui.Text("Colliders");
ImGui.Separator();
var prev = colliderShape;
ImGui.Checkbox("Show", ref showColliders);
ImGui.RadioButton("Sphere", ref colliderShapeIdx, (int)DynamicColliderShape.SPHERE);
ImGui.RadioButton("Capsule", ref colliderShapeIdx, (int)DynamicColliderShape.CAPSULE);
ImGui.RadioButton("Box", ref colliderShapeIdx, (int)DynamicColliderShape.BOX);
ImGui.RadioButton("Cylinder", ref colliderShapeIdx, (int)DynamicColliderShape.CYLINDER);
ImGui.RadioButton("Composite", ref colliderShapeIdx, (int)DynamicColliderShape.COMPOSITE);
ImGui.RadioButton("Convex Trimesh", ref colliderShapeIdx, (int)DynamicColliderShape.CONVEX);
ImGui.RadioButton("Trimesh Bridge", ref colliderShapeIdx, (int)DynamicColliderShape.TRIMESH_BRIDGE);
ImGui.RadioButton("Trimesh House", ref colliderShapeIdx, (int)DynamicColliderShape.TRIMESH_HOUSE);
ImGui.NewLine();
if ((int)prev != colliderShapeIdx)
{
colliderShape = (DynamicColliderShape)colliderShapeIdx;
}
}
if (mode == DynamicUpdateToolMode.RAYCAST)
{
ImGui.Text($"Raycast Time: {raycastTime} ms");
ImGui.Separator();
if (sposSet)
{
ImGui.Text($"Start: {spos.x}, {spos.y + 1.3f}, {spos.z}");
}
if (eposSet)
{
ImGui.Text($"End: {epos.x}, {epos.y + 1.3f}, {epos.z}");
}
if (raycastHit)
{
ImGui.Text($"Hit: {raycastHitPos.x}, {raycastHitPos.y}, {raycastHitPos.z}");
}
ImGui.NewLine();
}
else
{
ImGui.Text($"Build Time: {buildTime} ms");
}
}
private void Load(string filename)
{
try
{
using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
using var br = new BinaryReader(fs);
VoxelFileReader reader = new VoxelFileReader(DtVoxelTileLZ4DemoCompressor.Shared);
VoxelFile voxelFile = reader.Read(br);
dynaMesh = new DynamicNavMesh(voxelFile);
dynaMesh.config.keepIntermediateResults = true;
UpdateUI();
BuildDynaMesh();
colliders.Clear();
}
catch (Exception e)
{
Console.WriteLine(e);
dynaMesh = null;
}
}
private void Save(string filename)
{
using var fs = new FileStream(filename, FileMode.CreateNew, FileAccess.Write);
using var bw = new BinaryWriter(fs);
VoxelFile voxelFile = VoxelFile.From(dynaMesh);
VoxelFileWriter writer = new VoxelFileWriter(DtVoxelTileLZ4DemoCompressor.Shared);
writer.Write(bw, voxelFile, compression);
}
private void BuildDynaMesh()
{
ConfigDynaMesh();
long t = RcFrequency.Ticks;
try
{
var _ = dynaMesh.Build(executor).Result;
}
catch (Exception e)
{
Console.WriteLine(e);
}
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
_sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
}
private void ConfigDynaMesh()
{
dynaMesh.config.partition = partitioning;
dynaMesh.config.walkableHeight = walkableHeight;
dynaMesh.config.walkableSlopeAngle = walkableSlopeAngle;
dynaMesh.config.walkableRadius = walkableRadius;
dynaMesh.config.walkableClimb = walkableClimb;
dynaMesh.config.filterLowHangingObstacles = filterLowHangingObstacles;
dynaMesh.config.filterLedgeSpans = filterLedgeSpans;
dynaMesh.config.filterWalkableLowHeightSpans = filterWalkableLowHeightSpans;
dynaMesh.config.minRegionArea = minRegionArea;
dynaMesh.config.regionMergeArea = regionMergeSize;
dynaMesh.config.maxEdgeLen = maxEdgeLen;
dynaMesh.config.maxSimplificationError = maxSimplificationError;
dynaMesh.config.vertsPerPoly = vertsPerPoly;
dynaMesh.config.buildDetailMesh = buildDetailMesh;
dynaMesh.config.detailSampleDistance = detailSampleDist;
dynaMesh.config.detailSampleMaxError = detailSampleMaxError;
}
private void UpdateUI()
{
cellSize = dynaMesh.config.cellSize;
partitioning = (int)dynaMesh.config.partition;
walkableHeight = dynaMesh.config.walkableHeight;
walkableSlopeAngle = dynaMesh.config.walkableSlopeAngle;
walkableRadius = dynaMesh.config.walkableRadius;
walkableClimb = dynaMesh.config.walkableClimb;
minRegionArea = dynaMesh.config.minRegionArea;
regionMergeSize = dynaMesh.config.regionMergeArea;
maxEdgeLen = dynaMesh.config.maxEdgeLen;
maxSimplificationError = dynaMesh.config.maxSimplificationError;
vertsPerPoly = dynaMesh.config.vertsPerPoly;
buildDetailMesh = dynaMesh.config.buildDetailMesh;
detailSampleDist = dynaMesh.config.detailSampleDistance;
detailSampleMaxError = dynaMesh.config.detailSampleMaxError;
filterLowHangingObstacles = dynaMesh.config.filterLowHangingObstacles;
filterLedgeSpans = dynaMesh.config.filterLedgeSpans;
filterWalkableLowHeightSpans = dynaMesh.config.filterWalkableLowHeightSpans;
}
}