public void Initialize_AutoFitBox() { DMeshSO TargetMeshSO = TargetSO as DMeshSO; // initialize w/ auto-fit box DMesh3 mesh = TargetMeshSO.Mesh; DMeshAABBTree3 spatial = TargetMeshSO.Spatial; meshBounds = mesh.CachedBounds; create_preview_so(); ContOrientedBox3 boxFitter = new ContOrientedBox3( new RemapItr <Vector3d, int>(mesh.TriangleIndices(), (tid) => { return(mesh.GetTriCentroid(tid)); }), new RemapItr <double, int>(mesh.TriangleIndices(), (tid) => { return(mesh.GetTriArea(tid)); })); //DebugUtil.EmitDebugBox("fitbox", boxFitter.Box, Colorf.Red, TargetSO.RootGameObject, false); Box3d fitBox = boxFitter.Box; int longest = 0; if (fitBox.Extent.y > fitBox.Extent.x) { longest = 1; } if (fitBox.Extent.z > fitBox.Extent[longest]) { longest = 2; } Vector3d vTop = fitBox.Center + fitBox.Extent[longest] * fitBox.Axis(longest); Vector3d vBottom = fitBox.Center - fitBox.Extent[longest] * fitBox.Axis(longest); int base_tid = spatial.FindNearestTriangle(vBottom); int top_tid = spatial.FindNearestTriangle(vTop); if (vTop.y < vBottom.y) { int tmp = base_tid; base_tid = top_tid; top_tid = tmp; } Vector3d vBasePt = mesh.GetTriCentroid(base_tid); Vector3d vTopPt = mesh.GetTriCentroid(top_tid); int other1 = (longest + 1) % 3, other2 = (longest + 2) % 3; int front_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, fitBox.Axis(other1))); Vector3d vFrontPt = mesh.GetTriCentroid(front_tid); int back_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, -fitBox.Axis(other1))); Vector3d vBackPt = mesh.GetTriCentroid(back_tid); int right_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, fitBox.Axis(other2))); Vector3d vRightPt = mesh.GetTriCentroid(right_tid); int left_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, -fitBox.Axis(other2))); Vector3d vLeftPt = mesh.GetTriCentroid(left_tid); initialFrontPt = (Vector3f)vFrontPt; SetPointPosition_Internal(BasePointID, MeshQueries.SurfaceFrame(mesh, base_tid, vBasePt), CoordSpace.ObjectCoords); SetPointPosition_Internal(FrontPointID, MeshQueries.SurfaceFrame(mesh, front_tid, vFrontPt), CoordSpace.ObjectCoords); SetPointPosition(TopPointID, MeshQueries.SurfaceFrame(mesh, top_tid, vTopPt), CoordSpace.ObjectCoords); }
internal void MoveAllPointsDepthDependant(CuttingInfo info, DMesh3 newMesh, Dictionary <int, BacksideAlgorithm.PeprStatusVert> stati) { var tree = new DMeshAABBTree3(info.oldMesh, true); tree.TriangleFilterF = i => tree.Mesh.GetTriangleGroup(i) != info.data.ColorNum; foreach (var status in stati) { var shellPoint = newMesh.GetVertex(status.Value.idNewMeshOuter.Value); var normal = info.oldMesh.CalcVertexNormal(status.Value.idOldMeshOuter); var position = shellPoint + info.data.minDepth * normal; Ray3d ray = new Ray3d(shellPoint - normal * info.data.minDepth, -normal); //tiny shift to make sure it's not hitting itself int hit_tid = tree.FindNearestHitTriangle(ray); Debug.Log("Hit " + hit_tid); if (hit_tid != DMesh3.InvalidID) { IntrRay3Triangle3 intr = MeshQueries.TriangleIntersection(info.oldMesh, hit_tid, ray); double hit_dist = shellPoint.Distance(ray.PointAt(intr.RayParameter)); position = shellPoint - normal * hit_dist * (info.data.depth / 100); Debug.Log($"Hit Dist: {hit_dist}"); } else { StaticFunctions.ErrorMessage("Depth Dependant Calculation has encountered an error"); } info.mesh.SetVertex(status.Value.idOldMeshInner.Value, position); newMesh.SetVertex(status.Value.idNewMeshInner.Value, position); } }
/// <summary> /// Returns the height of the ground at the given 2D position via /// out. Returns false if there is no ground at the position. /// </summary> /// <remarks> /// Only X and Z are used by this function. /// </remarks> /// <param name="pos"></param> /// <returns></returns> public bool TryGetHeightAt(Position pos, out float height) { if (_spatial == null) { height = -1; return(false); } var origin = new Vector3f(pos.X, 1000, pos.Z); var ray = new Ray3f(origin, new Vector3f(0, -1, 0)); var hitId = _spatial.FindNearestHitTriangle(ray); if (hitId == DMesh3.InvalidID) { height = float.NaN; return(false); } var intersection = MeshQueries.TriangleIntersection(_mesh, hitId, ray); var hitDistance = origin.Distance(ray.PointAt((float)intersection.RayParameter)); height = (1000 - hitDistance); return(true); }
public int FindNearestHitTriangle(Ray3d ray, double fMaxDist = double.MaxValue) { var save_filter = SourceSpatial.TriangleFilterF; SourceSpatial.TriangleFilterF = source_filter; int hit_source_tid = SourceSpatial.FindNearestHitTriangle(ray); SourceSpatial.TriangleFilterF = save_filter; int hit_edit_tid; IntrRay3Triangle3 edit_hit = find_added_hit(ref ray, out hit_edit_tid); if (hit_source_tid == DMesh3.InvalidID && hit_edit_tid == DMesh3.InvalidID) { return(DMesh3.InvalidID); } else if (hit_source_tid == DMesh3.InvalidID) { return(hit_edit_tid); } else if (hit_edit_tid == DMesh3.InvalidID) { return(hit_source_tid); } IntrRay3Triangle3 source_hit = (hit_source_tid != -1) ? MeshQueries.TriangleIntersection(SourceMesh, hit_source_tid, ray) : null; return((edit_hit.RayParameter < source_hit.RayParameter) ? hit_edit_tid : hit_source_tid); }
void compute_trimmed_mesh() { // curve is on base leg, map to deformed leg // [TODO] really should be doing this via deformation, rather than nearest-point DCurve3 curve = new DCurve3(CurveSource.GetICurve()); for (int i = 0; i < curve.VertexCount; ++i) { curve[i] = MeshQueries.NearestPointFrame(cachedInputMesh, cachedInputMeshSpatial, curve[i]).Origin; } TrimmedMesh = new DMesh3(cachedInputMesh); TrimmedMesh.EnableTriangleGroups(0); AxisAlignedBox3d bounds = TrimmedMesh.CachedBounds; // try to find seed based on raycast, which doesn't always work. // Note that seed is the seed for the *eroded* region, not the kept region Vector3d basePt = bounds.Center + 10 * bounds.Extents.y * Vector3d.AxisY; int hit_tid = cachedInputMeshSpatial.FindNearestHitTriangle(new Ray3d(basePt, -Vector3d.AxisY)); Vector3d seed = cachedInputMesh.GetTriCentroid(hit_tid); if (flip_trim_side) { basePt = bounds.Center - 10 * bounds.Extents.y * Vector3d.AxisY; hit_tid = cachedInputMeshSpatial.FindNearestHitTriangle(new Ray3d(basePt, Vector3d.AxisY)); seed = cachedInputMesh.GetTriCentroid(hit_tid); } MeshTrimLoop trim = new MeshTrimLoop(TrimmedMesh, curve, seed, cachedInputMeshSpatial); trim.Trim(); if (TrimmedMesh.HasVertexColors == false) { TrimmedMesh.EnableVertexColors(SocketVertexColor); } else { foreach (int vid in TrimmedMesh.VertexIndices()) { TrimmedMesh.SetVertexColor(vid, SocketVertexColor); } } MeshTransforms.FromFrame(TrimmedMesh, cachedInputsTransform); }
public static double?DistanceToTree(this DMeshAABBTree3 tree, Ray3d ray) { var hit_tid = tree.FindNearestHitTriangle(ray); if (hit_tid == DMesh3.InvalidID) { return(null); } var intr = MeshQueries.TriangleIntersection(tree.Mesh, hit_tid, ray); return(ray.Origin.Distance(ray.PointAt(intr.RayParameter))); }
public HitResultLocal HitLocalBy(Ray rayLocal) { var res = new HitResultLocal(); try { int hit_tid = TreeLocal.FindNearestHitTriangle(rayLocal.g3Rayf); if (hit_tid == DMesh3.InvalidID) { return(res); } var intr = MeshQueries.TriangleIntersection(DMeshLocal, hit_tid, rayLocal.g3Rayf); res.Distance = (float)rayLocal.g3Rayd.Origin.Distance(rayLocal.g3Rayd.PointAt(intr.RayParameter)); res.Point = intr.Triangle.V1.ToVector3(); res.IsHitted = true; }catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } return(res); }
/// <summary> /// Find intersection of *WORLD* ray with Mesh /// </summary> override public bool FindRayIntersection(Ray3f rayW, out SORayHit hit) { hit = null; if (enable_spatial == false) { return(false); } validate_spatial(); // convert ray to local FScene scene = this.GetScene(); Ray3f rayS = scene.ToSceneRay(rayW); Ray3d local_ray = SceneTransforms.SceneToObject(this, rayS); int hit_tid = spatial.FindNearestHitTriangle(local_ray); if (hit_tid != DMesh3.InvalidID) { IntrRay3Triangle3 intr = MeshQueries.TriangleIntersection(mesh, hit_tid, local_ray); Vector3f hitPos = (Vector3f)local_ray.PointAt(intr.RayParameter); hitPos = SceneTransforms.ObjectToSceneP(this, hitPos); hitPos = scene.ToWorldP(hitPos); Vector3f hitNormal = (Vector3f)mesh.GetTriNormal(hit_tid); hitNormal = SceneTransforms.ObjectToSceneN(this, hitNormal); hitNormal = scene.ToWorldN(hitNormal); hit = new SORayHit(); hit.hitPos = hitPos; hit.hitNormal = hitNormal; hit.hitIndex = hit_tid; hit.fHitDist = hit.hitPos.Distance(rayW.Origin); // simpler than transforming! hit.hitGO = RootGameObject; hit.hitSO = this; return(true); } return(false); }
/// <summary> /// Find intersection of *WORLD* ray with Mesh /// </summary> override public bool FindRayIntersection(Ray3f rayW, out SORayHit hit) { hit = null; if (enable_spatial == false) { return(false); } if (spatial == null) { spatial = new DMeshAABBTree3(mesh); spatial.Build(); } // convert ray to local Frame3f f = new Frame3f(rayW.Origin, rayW.Direction); f = SceneTransforms.TransformTo(f, this, CoordSpace.WorldCoords, CoordSpace.ObjectCoords); Ray3d local_ray = new Ray3d(f.Origin, f.Z); int hit_tid = spatial.FindNearestHitTriangle(local_ray); if (hit_tid != DMesh3.InvalidID) { IntrRay3Triangle3 intr = MeshQueries.TriangleIntersection(mesh, hit_tid, local_ray); Frame3f hitF = new Frame3f(local_ray.PointAt(intr.RayParameter), mesh.GetTriNormal(hit_tid)); hitF = SceneTransforms.TransformTo(hitF, this, CoordSpace.ObjectCoords, CoordSpace.WorldCoords); hit = new SORayHit(); hit.hitPos = hitF.Origin; hit.hitNormal = hitF.Z; hit.hitIndex = hit_tid; hit.fHitDist = hit.hitPos.Distance(rayW.Origin); // simpler than transforming! hit.hitGO = RootGameObject; hit.hitSO = this; return(true); } return(false); }
// [RMS] this is not working right now... override public bool FindRayIntersection(Ray3f ray, out SORayHit hit) { hit = null; if (enable_spatial == false) { return(false); } if (spatial == null) { spatial = new DMeshAABBTree3(mesh); spatial.Build(); } Transform xform = ((GameObject)RootGameObject).transform; // convert ray to local Ray3d local_ray = new Ray3d(); local_ray.Origin = xform.InverseTransformPoint(ray.Origin); local_ray.Direction = xform.InverseTransformDirection(ray.Direction); local_ray.Direction.Normalize(); int hit_tid = spatial.FindNearestHitTriangle(local_ray); if (hit_tid != DMesh3.InvalidID) { IntrRay3Triangle3 intr = MeshQueries.TriangleIntersection(mesh, hit_tid, local_ray); hit = new SORayHit(); hit.fHitDist = (float)intr.RayParameter; hit.hitPos = xform.TransformPoint((Vector3f)local_ray.PointAt(intr.RayParameter)); hit.hitNormal = xform.TransformDirection((Vector3f)mesh.GetTriNormal(hit_tid)); hit.hitGO = RootGameObject; hit.hitSO = this; return(true); } return(false); }
internal Vector3d MovePointDepthDependant(CuttingInfo info, Vector3d shellPoint, Vector3d normal) { normal = normal.Normalized; var position = shellPoint + info.data.minDepth * normal;; var tree = new DMeshAABBTree3(info.oldMesh, true); Ray3d ray = new Ray3d(shellPoint, -normal); int hit_tid = tree.FindNearestHitTriangle(ray); Debug.Log("Hit " + hit_tid); if (hit_tid != DMesh3.InvalidID) { IntrRay3Triangle3 intr = MeshQueries.TriangleIntersection(info.oldMesh, hit_tid, ray); double hit_dist = shellPoint.Distance(ray.PointAt(intr.RayParameter)); position = shellPoint - normal * hit_dist * (info.data.depth / 100); Debug.Log($"Hit Dist: {hit_dist}"); } else { StaticFunctions.ErrorMessage("Depth Dependant Calculation failed"); } return(position); }
/// <summary> /// Cut a "partial" hole, ie we cut the mesh with the polygon once, and then /// extrude downwards to a planar version of the cut boundary. /// /// Currently only supports extruding downwards from topmost intersection. /// /// </summary> protected bool CutPartialHole(DMesh3 mesh, HoleInfo hi, Vector3d translate, bool bUpwards) { if (hi.IsVertical == false) { throw new Exception("unsupported!"); } Vector3d basePoint = CombinedBounds.Center - CombinedBounds.Extents.y * Vector3d.AxisY + translate; // do we need to compute spatial DS for each hole? not super efficient... DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Vector3d direction = (bUpwards) ? Vector3d.AxisY : -Vector3d.AxisY; Vector3d center = basePoint + new Vector3d(hi.XZOffset.x, 0, hi.XZOffset.y) - 10000 * direction; Ray3d ray = new Ray3d(center, direction); int hit_tid = spatial.FindNearestHitTriangle(ray); if (hit_tid == DMesh3.InvalidID) { return(false); } IntrRay3Triangle3 intersection = MeshQueries.TriangleIntersection(mesh, hit_tid, ray); Vector3d inter_pos = ray.PointAt(intersection.RayParameter); Frame3f projectFrame = new Frame3f(ray.Origin, ray.Direction); int nVerts = 32; if (hi.Vertices != 0) { nVerts = hi.Vertices; } double angleShiftRad = hi.AxisAngleD * MathUtil.Deg2Rad; Polygon2d circle = Polygon2d.MakeCircle(hi.Radius, nVerts, angleShiftRad); try { EdgeLoop loop = null; MeshInsertProjectedPolygon insert = new MeshInsertProjectedPolygon(mesh, circle, projectFrame, hit_tid) { SimplifyInsertion = false }; if (insert.Insert()) { loop = insert.InsertedLoop; // [RMS] do we need to simplify for this one? //if (loop.VertexCount > circle.VertexCount) // loop = simplify_loop(mesh, loop, circle.VertexCount); MeshEditor editor = new MeshEditor(mesh); Vector3d base_pos = inter_pos; base_pos.y = basePoint.y + hi.PartialHoleBaseHeight; int N = loop.VertexCount; int[] newLoop = new int[N]; for (int k = 0; k < N; ++k) { newLoop[k] = mesh.AppendVertex(mesh, loop.Vertices[k]); Vector3d cur_v = mesh.GetVertex(newLoop[k]); cur_v.y = base_pos.y; mesh.SetVertex(newLoop[k], cur_v); } int base_vid = mesh.AppendVertex(base_pos); int[] fan_tris = editor.AddTriangleFan_OrderedVertexLoop(base_vid, newLoop); FaceGroupUtil.SetGroupID(mesh, fan_tris, hi.PartialHoleGroupID); int[] stitch_tris = editor.StitchLoop(loop.Vertices, newLoop); // need to remesh fan region because otherwise we get pathological cases RegionRemesher remesh = new RegionRemesher(mesh, fan_tris); remesh.SetTargetEdgeLength(2.0); remesh.SmoothSpeedT = 1.0; remesh.PreventNormalFlips = true; for (int k = 0; k < 25; ++k) { remesh.BasicRemeshPass(); } //remesh.EnableCollapses = remesh.EnableFlips = remesh.EnableSplits = false; //for (int k = 0; k < 20; ++k) // remesh.BasicRemeshPass(); remesh.BackPropropagate(); return(true); } else { return(false); } } catch (Exception e) { f3.DebugUtil.Log("partial hole {0} failed!! {1}", hi.nHole, e.Message); return(false); } }
/// <summary> /// Cut through-hole either vertically or horizontally. /// /// One current failure mode is if we get more than two ray-hits, which /// can happen due pathological cases or unexpected mesh shape. Currently /// trying to handle the pathological cases (ie ray hits adjacent triangles cases) /// via sorting, not sure if this works spectacularly well. /// /// </summary> protected bool CutThroughHole(DMesh3 mesh, HoleInfo hi, Vector3d translate) { Vector3d basePoint = CombinedBounds.Center - CombinedBounds.Extents.y * Vector3d.AxisY + translate; // do we need to compute spatial DS for each hole? not super efficient... DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Vector3d origin = Vector3d.Zero; Vector3d direction = Vector3d.One; if (hi.IsVertical) { direction = Vector3d.AxisY; origin = basePoint + new Vector3d(hi.XZOffset.x, 0, hi.XZOffset.y) - 100 * direction; } else { origin = basePoint + hi.Height * Vector3d.AxisY; direction = Quaterniond.AxisAngleD(Vector3d.AxisY, hi.AroundAngle) * Vector3d.AxisX; } // Find upper and lower triangles that contain center-points of // holes we want to cut. This is the most error-prone part // because we depend on ray-hits, which is not very reliable... Ray3d ray1 = new Ray3d(origin, direction); Ray3d ray2 = new Ray3d(origin + 10000 * direction, -direction); if (hi.GroupIDFilters.a > 0) { spatial.TriangleFilterF = (tid) => { return(mesh.GetTriangleGroup(tid) == hi.GroupIDFilters.a); }; } int hit_1 = spatial.FindNearestHitTriangle(ray1); spatial.TriangleFilterF = null; if (hi.GroupIDFilters.b > 0) { spatial.TriangleFilterF = (tid) => { return(mesh.GetTriangleGroup(tid) == hi.GroupIDFilters.b); }; } int hit_2 = spatial.FindNearestHitTriangle(ray2); spatial.TriangleFilterF = null; if (hit_1 == DMesh3.InvalidID || hit_2 == DMesh3.InvalidID) { return(false); } if (hit_1 == hit_2) { return(false); } List <int> hitTris = new List <int>() { hit_1, hit_2 }; Frame3f projectFrame = new Frame3f(ray1.Origin, ray1.Direction); int nVerts = 32; if (hi.Vertices != 0) { nVerts = hi.Vertices; } double angleShiftRad = hi.AxisAngleD * MathUtil.Deg2Rad; Polygon2d circle = Polygon2d.MakeCircle(hi.Radius, nVerts, angleShiftRad); List <EdgeLoop> edgeLoops = new List <EdgeLoop>(); foreach (int hit_tid in hitTris) { try { MeshInsertProjectedPolygon insert = new MeshInsertProjectedPolygon(mesh, circle, projectFrame, hit_tid) { SimplifyInsertion = true }; if (insert.Insert()) { // if we have extra edges just randomly collapse EdgeLoop loop = insert.InsertedLoop; if (loop.VertexCount > circle.VertexCount) { loop = simplify_loop(mesh, loop, circle.VertexCount); } edgeLoops.Add(loop); } else { f3.DebugUtil.Log("insert.Insert() failed!!"); return(false); } } catch (Exception e) { // ignore this loop but we might already be in trouble... f3.DebugUtil.Log("insert.Insert() threw exception for hole {0}!!", hi.nHole); f3.DebugUtil.Log(e.Message); } } if (edgeLoops.Count != 2) { return(false); } try { MeshEditor editor = new MeshEditor(mesh); EdgeLoop l0 = edgeLoops[0]; EdgeLoop l1 = edgeLoops[1]; l1.Reverse(); editor.StitchVertexLoops_NearestV(l0.Vertices, l1.Vertices); // split edges around the holes we cut. This is helpful // if we are going to do additional operations in these areas, // as it gives us extra rings to work with //MeshEdgeSelection edges = new MeshEdgeSelection(mesh); //edges.SelectVertexEdges(l0.Vertices); //edges.SelectVertexEdges(l1.Vertices); //DMesh3.EdgeSplitInfo splitInfo; //foreach ( int eid in edges ) // mesh.SplitEdge(eid, out splitInfo); return(true); } catch { f3.DebugUtil.Log("stitch threw exception!"); return(false); } }
public virtual bool Apply() { DMesh3 testAgainstMesh = Mesh; if (InsideMode == CalculationMode.RayParity) { MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh); if (loops.Count > 0) { testAgainstMesh = new DMesh3(Mesh); foreach (var loop in loops) { if (Cancelled()) { return(false); } SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop); filler.Fill(); } } } DMeshAABBTree3 spatial = (Spatial != null && testAgainstMesh == Mesh) ? Spatial : new DMeshAABBTree3(testAgainstMesh, true); if (InsideMode == CalculationMode.AnalyticWindingNumber) { spatial.WindingNumber(Vector3d.Zero); } else if (InsideMode == CalculationMode.FastWindingNumber) { spatial.FastWindingNumber(Vector3d.Zero); } if (Cancelled()) { return(false); } // ray directions List <Vector3d> ray_dirs = null; int NR = 0; if (InsideMode == CalculationMode.SimpleOcclusionTest) { ray_dirs = new List <Vector3d>(); ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX); ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY); ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ); NR = ray_dirs.Count; } Func <Vector3d, bool> isOccludedF = (pt) => { if (InsideMode == CalculationMode.RayParity) { return(spatial.IsInside(pt)); } else if (InsideMode == CalculationMode.AnalyticWindingNumber) { return(spatial.WindingNumber(pt) > WindingIsoValue); } else if (InsideMode == CalculationMode.FastWindingNumber) { return(spatial.FastWindingNumber(pt) > WindingIsoValue); } else { for (int k = 0; k < NR; ++k) { int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k])); if (hit_tid == DMesh3.InvalidID) { return(false); } } return(true); } }; bool cancel = false; BitArray vertices = null; if (PerVertex) { vertices = new BitArray(Mesh.MaxVertexID); MeshNormals normals = null; if (Mesh.HasVertexNormals == false) { normals = new MeshNormals(Mesh); normals.Compute(); } gParallel.ForEach(Mesh.VertexIndices(), (vid) => { if (cancel) { return; } if (vid % 10 == 0) { cancel = Cancelled(); } Vector3d c = Mesh.GetVertex(vid); Vector3d n = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid]; c += n * NormalOffset; vertices[vid] = isOccludedF(c); }); } if (Cancelled()) { return(false); } RemovedT = new List <int>(); SpinLock removeLock = new SpinLock(); gParallel.ForEach(Mesh.TriangleIndices(), (tid) => { if (cancel) { return; } if (tid % 10 == 0) { cancel = Cancelled(); } bool inside = false; if (PerVertex) { Index3i tri = Mesh.GetTriangle(tid); inside = vertices[tri.a] || vertices[tri.b] || vertices[tri.c]; } else { Vector3d c = Mesh.GetTriCentroid(tid); Vector3d n = Mesh.GetTriNormal(tid); c += n * NormalOffset; inside = isOccludedF(c); } if (inside) { bool taken = false; removeLock.Enter(ref taken); RemovedT.Add(tid); removeLock.Exit(); } }); if (Cancelled()) { return(false); } if (RemovedT.Count > 0) { MeshEditor editor = new MeshEditor(Mesh); bool bOK = editor.RemoveTriangles(RemovedT, true); RemoveFailed = (bOK == false); } return(true); }
protected void do_flatten(DMesh3 mesh) { double BAND_HEIGHT = flatten_band_height; Vector3d down_axis = -Vector3d.AxisY; double dot_thresh = 0.2; AxisAlignedBox3d bounds = mesh.CachedBounds; DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Ray3d ray = new Ray3d(bounds.Center - 2 * bounds.Height * Vector3d.AxisY, Vector3d.AxisY); int hit_tid = spatial.FindNearestHitTriangle(ray); Frame3f hitF; MeshQueries.RayHitPointFrame(mesh, spatial, ray, out hitF); Vector3d basePt = hitF.Origin; Frame3f basePlane = new Frame3f(basePt, Vector3f.AxisY); MeshConnectedComponents components = new MeshConnectedComponents(mesh) { FilterF = (tid) => { if (mesh.GetTriangleGroup(tid) != LastExtrudeOuterGroupID) { return(false); } Vector3d n, c; double a; mesh.GetTriInfo(tid, out n, out a, out c); double h = Math.Abs(c.y - basePt.y); if (h > BAND_HEIGHT) { return(false); } if (n.Dot(down_axis) < dot_thresh) { return(false); } return(true); }, SeedFilterF = (tid) => { return(tid == hit_tid); } }; components.FindConnectedT(); MeshFaceSelection all_faces = new MeshFaceSelection(mesh); foreach (var comp in components) { MeshVertexSelection vertices = new MeshVertexSelection(mesh); vertices.SelectTriangleVertices(comp.Indices); foreach (int vid in vertices) { Vector3d v = mesh.GetVertex(vid); v = basePlane.ProjectToPlane((Vector3f)v, 2); mesh.SetVertex(vid, v); } all_faces.SelectVertexOneRings(vertices); } all_faces.ExpandToOneRingNeighbours(3, (tid) => { return(mesh.GetTriangleGroup(tid) == LastExtrudeOuterGroupID); }); RegionRemesher r = new RegionRemesher(mesh, all_faces); r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); r.SetTargetEdgeLength(2.0f); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.BackPropropagate(); }
public static void test_AABBTree_RayHit(int meshCase = 8) { DMesh3 mesh = MakeSpatialTestMesh(meshCase); DMeshAABBTree3 tree = new DMeshAABBTree3(mesh); tree.Build(); tree.TestCoverage(); AxisAlignedBox3d bounds = mesh.CachedBounds; Vector3d ext = bounds.Extents; Vector3d c = bounds.Center; double r = bounds.DiagonalLength / 4; Random rand = new Random(316136327); tree.FindNearestHitTriangle( new Ray3f(100 * Vector3f.One, Vector3f.One)); // test rays out from center of box, and rays in towards it // (should all hit for standard test cases) int hits = 0; int N = (meshCase > 7) ? 1000 : 10000; #if true for (int ii = 0; ii < N; ++ii) { if (ii % 100 == 0) { System.Console.WriteLine("{0} / {1}", ii, N); } Vector3d p = (ii < N / 2) ? c : c + 2 * r * rand.Direction(); Vector3d d = (ii < N / 2) ? rand.Direction() : (c - p).Normalized; Ray3d ray = new Ray3d(p, d); int tNearBrute = MeshQueries.FindHitTriangle_LinearSearch(mesh, ray); int tNearTree = tree.FindNearestHitTriangle(ray); //System.Console.WriteLine("{0} - {1}", tNearBrute, tree.TRI_TEST_COUNT); if (tNearBrute == DMesh3.InvalidID) { Debug.Assert(tNearBrute == tNearTree); continue; } ++hits; IntrRay3Triangle3 qBrute = MeshQueries.TriangleIntersection(mesh, tNearBrute, ray); IntrRay3Triangle3 qTree = MeshQueries.TriangleIntersection(mesh, tNearTree, ray); double dotBrute = mesh.GetTriNormal(tNearBrute).Dot(ray.Direction); double dotTree = mesh.GetTriNormal(tNearTree).Dot(ray.Direction); Debug.Assert(Math.Abs(qBrute.RayParameter - qTree.RayParameter) < MathUtil.ZeroTolerance); } Debug.Assert(hits == N); System.Console.WriteLine("in/out rays: {0} hits out of {1} rays", hits, N); #endif // random rays hits = 0; for (int ii = 0; ii < N; ++ii) { if (ii % 100 == 0) { System.Console.WriteLine("{0} / {1}", ii, N); } Vector3d target = c + rand.PointInRange(r); Vector3d o = c + rand.PointInRange(10 * r); Ray3d ray = new Ray3d(o, (target - o).Normalized); int tNearBrute = MeshQueries.FindHitTriangle_LinearSearch(mesh, ray); int tNearTree = tree.FindNearestHitTriangle(ray); //System.Console.WriteLine("{0} - {1}", tNearBrute, tree.TRI_TEST_COUNT); if (tNearBrute == DMesh3.InvalidID) { Debug.Assert(tNearBrute == tNearTree); continue; } ++hits; IntrRay3Triangle3 qBrute = MeshQueries.TriangleIntersection(mesh, tNearBrute, ray); IntrRay3Triangle3 qTree = MeshQueries.TriangleIntersection(mesh, tNearTree, ray); double dotBrute = mesh.GetTriNormal(tNearBrute).Dot(ray.Direction); double dotTree = mesh.GetTriNormal(tNearTree).Dot(ray.Direction); Debug.Assert(Math.Abs(qBrute.RayParameter - qTree.RayParameter) < MathUtil.ZeroTolerance); } System.Console.WriteLine("random rays: hit {0} of {1} rays", hits, N); }
void compute_statistics(Component c) { int NC = c.triangles.Count; c.inFacing = c.outFacing = 0; double dist = 2 * Mesh.CachedBounds.DiagonalLength; // only want to raycast triangles in this HashSet <int> tris = new HashSet <int>(c.triangles); spatial.TriangleFilterF = tris.Contains; // We want to try to figure out what is 'outside' relative to the world. // Assumption is that faces we can hit from far away should be oriented outwards. // So, for each triangle we construct far-away points in positive and negative normal // direction, then raycast back towards the triangle. If we hit the triangle from // one side and not the other, that is evidence we should keep/reverse that triangle. // If it is not hit, or hit from both, that does not provide any evidence. // We collect up this keep/reverse evidence and use the larger to decide on the global orientation. SpinLock count_lock = new SpinLock(); gParallel.BlockStartEnd(0, NC - 1, (a, b) => { for (int i = a; i <= b; ++i) { int ti = c.triangles[i]; Vector3d normal, centroid; double area; Mesh.GetTriInfo(ti, out normal, out area, out centroid); if (area < MathUtil.ZeroTolerancef) { continue; } // construct far away points Vector3d pos_pt = centroid + dist * normal; Vector3d neg_pt = centroid - dist * normal; // raycast towards triangle from far-away point int hit_pos = spatial.FindNearestHitTriangle(new Ray3d(pos_pt, -normal)); int hit_neg = spatial.FindNearestHitTriangle(new Ray3d(neg_pt, normal)); if (hit_pos != ti && hit_neg != ti) { continue; // no evidence } if (hit_pos == ti && hit_neg == ti) { continue; // no evidence (?) } bool taken = false; count_lock.Enter(ref taken); if (hit_neg == ti) { c.inFacing += area; } else if (hit_pos == ti) { c.outFacing += area; } count_lock.Exit(); } }); spatial.TriangleFilterF = null; }