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);
        }
Beispiel #2
0
    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);
        }
    }
Beispiel #3
0
        /// <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);
        }
Beispiel #4
0
        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);
        }
Beispiel #6
0
        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)));
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        /// <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);
        }
Beispiel #9
0
        /// <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);
        }
Beispiel #10
0
        // [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);
        }
Beispiel #11
0
    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);
    }
Beispiel #12
0
        /// <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);
            }
        }
Beispiel #13
0
        /// <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);
        }
Beispiel #15
0
        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();
        }
Beispiel #16
0
        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;
        }