public override void Mouse_Move(MouseEventArgs e) { _endMouseRay = Editor.Camera.GetWorldRay(new Vector2(e.X, e.Y)); if (!_startSet) { _startPositions.Clear(); foreach (var control in Editor.Selection) { _startPositions.Add(control, control.Position); } _startMouseRay = _endMouseRay; _cameraPlane = new Plane(Editor.Camera.LookAt, Editor.Camera.LookAt + Editor.Camera.Right, Editor.Camera.LookAt + Editor.Camera.UpEffective); _startSet = true; } float? distanceStart, distance; _startMouseRay.Intersects(ref _cameraPlane, out distanceStart); _endMouseRay.Intersects(ref _cameraPlane, out distance); if (distanceStart != null && distance != null) { var deltaMouse = (_endMouseRay.Position + Vector3.Multiply(_endMouseRay.Direction, distance.Value)) - (_startMouseRay.Position + Vector3.Multiply(_startMouseRay.Direction, distanceStart.Value)); foreach (var control in Editor.Selection) { control.Position = _startPositions[control] + deltaMouse; } } }
public static bool RayVsPlane(Ray ray, Plane plane, out float outDistance, out Vector3 intersectPoint) { bool bIntersect = SegmentVsPlane(ray.Origin, ray.Origin + ray.Direction * float.MaxValue, plane, out outDistance, out intersectPoint); outDistance *= float.MaxValue; return bIntersect; }
public CSGMesh(Plane[] planes, List<Polygon> polygons, List<HalfEdge> edges, List<Vector3> vertices, AABBi bounds) { this.Planes = planes; this.Polygons = polygons; this.Edges = edges; this.Vertices = vertices; this.Bounds.Set(bounds); }
public override void Mouse_Down(MouseEventArgs e) { if (e.Button == MouseButtons.Middle) { //freeze camera _camera = Editor.Camera.Clone(); _startMouseRay = _camera.GetWorldRay(new Vector2(e.X, e.Y)); _cameraPlane = new Plane(_camera.LookAt, _camera.LookAt + _camera.Right, _camera.LookAt + _camera.UpEffective); } }
public static Frustum ExtractFrustum(Matrix4 viewProjMatrix) { Frustum frustum = new Frustum(); Plane left = new Plane(); left.A = viewProjMatrix.M14 + viewProjMatrix.M11; left.B = viewProjMatrix.M24 + viewProjMatrix.M21; left.C = viewProjMatrix.M34 + viewProjMatrix.M31; left.D = viewProjMatrix.M44 + viewProjMatrix.M41; left.Normalize(); frustum.Left = left; Plane right = new Plane(); right.A = viewProjMatrix.M14 - viewProjMatrix.M11; right.B = viewProjMatrix.M24 - viewProjMatrix.M21; right.C = viewProjMatrix.M34 - viewProjMatrix.M31; right.D = viewProjMatrix.M44 - viewProjMatrix.M41; right.Normalize(); frustum.Right = right; Plane top = new Plane(); top.A = viewProjMatrix.M14 - viewProjMatrix.M12; top.B = viewProjMatrix.M24 - viewProjMatrix.M22; top.C = viewProjMatrix.M34 - viewProjMatrix.M32; top.D = viewProjMatrix.M44 - viewProjMatrix.M42; top.Normalize(); frustum.Top = top; Plane bottom = new Plane(); bottom.A = viewProjMatrix.M14 + viewProjMatrix.M12; bottom.B = viewProjMatrix.M24 + viewProjMatrix.M22; bottom.C = viewProjMatrix.M34 + viewProjMatrix.M32; bottom.D = viewProjMatrix.M44 + viewProjMatrix.M42; bottom.Normalize(); frustum.Bottom = bottom; Plane near = new Plane(); near.A = viewProjMatrix.M13; near.B = viewProjMatrix.M23; near.C = viewProjMatrix.M33; near.D = viewProjMatrix.M43; near.Normalize(); frustum.Near = near; Plane far = new Plane(); far.A = viewProjMatrix.M14 - viewProjMatrix.M13; far.B = viewProjMatrix.M24 - viewProjMatrix.M23; far.C = viewProjMatrix.M34 - viewProjMatrix.M33; far.D = viewProjMatrix.M44 - viewProjMatrix.M43; far.Normalize(); frustum.Far = far; return frustum; }
public override void DrawGui(LineBatch lineBatch, SpriteBatch spriteBatch) { var ray = Camera.GetWorldRay(MousePos); var plane = new Plane(new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 0)); float? distance; ray.Intersects(ref plane, out distance); if (distance != null) { var pos = ray.Position + Vector3.Multiply(ray.Direction, distance.Value); spriteBatch.AddText(GuiFont, 250, 0, string.Format("X={0:n2} Y={1:n2}", pos.X, pos.Y), Color.White, HorizontalAlignment.Left, VerticalAlignment.Top); } }
public static bool SegmentVsPlane(Vector3 pointA, Vector3 pointB, Plane plane, out float outFraction, out Vector3 intersectPoint) { //Compute the t value for the directed line ab intersecting the plane. Vector3 ab = pointB - pointA; //Plane ABC is the plane's normal. outFraction = (plane.D - Vector3.Dot(new Vector3(plane.A, plane.B, plane.C), pointA)) / Vector3.Dot(new Vector3(plane.A, plane.B, plane.C), ab); //If t in [0..1] compute and return intersection point if (outFraction >= 0f && outFraction <= 1f) { intersectPoint = pointA + outFraction * ab; return true; } //Else, no intersection. intersectPoint = Vector3.Zero; return false; }
public bool Intersection(Plane plane, out Vector3 intersection) { float normDot = Vector3.Dot(Normal, plane.Normal); if (normDot == 0) { intersection = Vector3.Zero; return false; } float t = -(Vector3.Dot(Origin, plane.Normal) + plane.D) / normDot; if (t <= 0) { intersection = Vector3.Zero; return false; } intersection = Origin + t * Normal; return true; }
// Note: This method is not optimized! Code is simplified for clarity! // for example: Plane.Distance / Plane.OnSide should be inlined manually and shouldn't use enums, but floating point values directly! public PolygonSplitResult PolygonSplit(Plane cuttingPlane, Vector3 translation, ref Polygon inputPolygon, out Polygon outsidePolygon) { HalfEdge prev = Edges[inputPolygon.FirstIndex]; HalfEdge current = Edges[prev.NextIndex]; HalfEdge next = Edges[current.NextIndex]; HalfEdge last = next; HalfEdge enterEdge = null; HalfEdge exitEdge = null; var prevVertex = Vertices[prev.VertexIndex]; var prevDistance = cuttingPlane.SignedDistance(prevVertex); // distance to previous vertex var prevSide = Plane.OnSide(prevDistance); // side of plane of previous vertex var currentVertex = Vertices[current.VertexIndex]; var currentDistance = cuttingPlane.SignedDistance(currentVertex); // distance to current vertex var currentSide = Plane.OnSide(currentDistance); // side of plane of current vertex do { var nextVertex = Vertices[next.VertexIndex]; var nextDistance = cuttingPlane.SignedDistance(nextVertex); // distance to next vertex var nextSide = Plane.OnSide(nextDistance); // side of plane of next vertex if (prevSide != currentSide) // check if edge crossed the plane ... { if (currentSide != PlaneSideResult.Intersects) // prev:inside/outside - current:inside/outside - next:?? { if (prevSide != PlaneSideResult.Intersects) // prev:inside/outside - current:outside - next:?? { // Calculate intersection of edge with plane split the edge into two, inserting the new vertex var newVertex = Plane.Intersection(prevVertex, currentVertex, prevDistance, currentDistance); var newEdge = EdgeSplit(current, newVertex); if (prevSide == PlaneSideResult.Inside) // prev:inside - current:outside - next:?? { //edge01 exits: // // outside // 1 // * // ......./........ intersect // / // 0 // inside exitEdge = current; } else if (prevSide == PlaneSideResult.Outside) // prev:outside - current:inside - next:?? { //edge01 enters: // // outside // 0 // \ // .......\........ intersect // * // 1 // inside enterEdge = current; } prevDistance = 0; prev = Edges[prev.NextIndex]; prevSide = PlaneSideResult.Intersects; if (exitEdge != null && enterEdge != null) break; current = Edges[prev.NextIndex]; currentVertex = Vertices[current.VertexIndex]; next = Edges[current.NextIndex]; nextVertex = Vertices[next.VertexIndex]; } } else // prev:?? - current:intersects - next:?? { if (prevSide == PlaneSideResult.Intersects || // prev:intersects - current:intersects - next:?? nextSide == PlaneSideResult.Intersects || // prev:?? - current:intersects - next:intersects prevSide == nextSide) // prev:inside/outde - current:intersects - next:inside/outde { if (prevSide == PlaneSideResult.Inside || // prev:inside - current:intersects - next:intersects/inside nextSide == PlaneSideResult.Inside) // prev:intersects/inside - current:intersects - next:inside { // outside // 0 1 // --------*....... intersect // \ // 2 // inside // // outside // 1 2 // ........*------- intersect // / // 0 // inside // // outside // 1 //........*....... intersect // / \ // 0 2 // inside // prevSide = PlaneSideResult.Inside; enterEdge = exitEdge = null; break; } else if (prevSide == PlaneSideResult.Outside || // prev:outside - current:intersects - next:intersects/outside nextSide == PlaneSideResult.Outside) // prev:intersects/outside - current:intersects - next:outside { // outside // 2 // / //..------*....... intersect // 0 1 // inside // // outside // 0 // \ //........*------- intersect // 1 2 // inside // // outside // 0 2 // \ / //........*....... intersect // 1 // inside // prevSide = PlaneSideResult.Outside; enterEdge = exitEdge = null; break; } } else // prev:inside/outside - current:intersects - next:inside/outside { if (prevSide == PlaneSideResult.Inside) // prev:inside - current:intersects - next:outside { //find exit edge: // // outside // 2 // 1 / // ........*....... intersect // / // 0 // inside exitEdge = current; if (enterEdge != null) break; } else // prev:outside - current:intersects - next:inside { //find enter edge: // // outside // 0 // \ 1 // ........*....... intersect // \ // 2 // inside enterEdge = current; if (exitEdge != null) break; } } } } prev = current; current = next; next = Edges[next.NextIndex]; prevDistance = currentDistance; currentDistance = nextDistance; prevSide = currentSide; currentSide = nextSide; prevVertex = currentVertex; currentVertex = nextVertex; } while (next != last); // We should never have only one edge crossing the plane .. Debug.Assert((enterEdge == null) == (exitEdge == null)); // Check if we have an edge that exits and an edge that enters the plane and split the polygon into two if we do if (enterEdge != null && exitEdge != null) { //enter . // . // =====>*-----> // . // //outside . inside // . // <-----*<===== // . // . exit outsidePolygon = new Polygon(); var outsidePolygonIndex = (short)this.Polygons.Count; this.Polygons.Add(outsidePolygon); var outsideEdge = new HalfEdge(); var outsideEdgeIndex = (short)Edges.Count; var insideEdge = new HalfEdge(); var insideEdgeIndex = (short)(outsideEdgeIndex + 1); outsideEdge.TwinIndex = insideEdgeIndex; insideEdge.TwinIndex = outsideEdgeIndex; //insideEdge.PolygonIndex = inputPolygonIndex;// index does not change outsideEdge.PolygonIndex = outsidePolygonIndex; outsideEdge.VertexIndex = exitEdge.VertexIndex; insideEdge.VertexIndex = enterEdge.VertexIndex; outsideEdge.NextIndex = exitEdge.NextIndex; insideEdge.NextIndex = enterEdge.NextIndex; exitEdge.NextIndex = insideEdgeIndex; enterEdge.NextIndex = outsideEdgeIndex; outsidePolygon.FirstIndex = outsideEdgeIndex; inputPolygon.FirstIndex = insideEdgeIndex; outsidePolygon.Visible = inputPolygon.Visible; outsidePolygon.Category = inputPolygon.Category; outsidePolygon.PlaneIndex = inputPolygon.PlaneIndex; Edges.Add(outsideEdge); Edges.Add(insideEdge); // calculate the bounds of the polygons outsidePolygon.Bounds.Clear(); var first = Edges[outsidePolygon.FirstIndex]; var iterator = first; do { outsidePolygon.Bounds.Add(Vertices[iterator.VertexIndex]); iterator.PolygonIndex = outsidePolygonIndex; iterator = Edges[iterator.NextIndex]; } while (iterator != first); inputPolygon.Bounds.Clear(); first = Edges[inputPolygon.FirstIndex]; iterator = first; do { inputPolygon.Bounds.Add(Vertices[iterator.VertexIndex]); iterator = Edges[iterator.NextIndex]; } while (iterator != first); return PolygonSplitResult.Split; } else { outsidePolygon = null; switch (prevSide) { case PlaneSideResult.Inside: return PolygonSplitResult.CompletelyInside; case PlaneSideResult.Outside: return PolygonSplitResult.CompletelyOutside; default: case PlaneSideResult.Intersects: { var polygonPlane = Planes[inputPolygon.PlaneIndex]; var result = Vector3.Dot(polygonPlane.Normal, cuttingPlane.Normal); if (result > 0) return PolygonSplitResult.PlaneAligned; else return PolygonSplitResult.PlaneOppositeAligned; } } } }
public void Intersect(AABBi cuttingNodeBounds, Plane[] cuttingNodePlanes, Vector3 cuttingNodeTranslation, Vector3 inputPolygonTranslation, List<Polygon> inputPolygons, List<Polygon> inside, List<Polygon> aligned, List<Polygon> revAligned, List<Polygon> outside) { var categories = new PolygonSplitResult[cuttingNodePlanes.Length]; var translatedPlanes = new Plane[cuttingNodePlanes.Length]; var translation = cuttingNodeTranslation - inputPolygonTranslation; // translate the planes we cut our polygons with so that they're located at the same // relative distance from the polygons as the brushes are from each other. for (int i = 0; i < cuttingNodePlanes.Length; i++) translatedPlanes[i] = Plane.Translated(cuttingNodePlanes[i], translation); var vertices = this.Vertices; var edges = this.Edges; var planes = this.Planes; for (int i = inputPolygons.Count - 1; i >= 0; i--) { var inputPolygon = inputPolygons[i]; if (inputPolygon.FirstIndex == -1) continue; var bounds = inputPolygon.Bounds; var finalResult = PolygonSplitResult.CompletelyInside; // A quick check if the polygon lies outside the planes we're cutting our polygons with. if (!AABBi.IsOutside(cuttingNodeBounds, translation, bounds)) { PolygonSplitResult intermediateResult; Polygon outsidePolygon = null; for (int otherIndex = 0; otherIndex < translatedPlanes.Length; otherIndex++) { var translatedCuttingPlane = translatedPlanes[otherIndex]; var side = cuttingNodePlanes[otherIndex].OnSide(bounds, -translation); if (side == PlaneSideResult.Outside) { finalResult = PolygonSplitResult.CompletelyOutside; break; // nothing left to process, so we exit } else if (side == PlaneSideResult.Inside) continue; var polygon = inputPolygon; intermediateResult = PolygonSplit(translatedCuttingPlane, inputPolygonTranslation, ref polygon, out outsidePolygon); inputPolygon = polygon; if (intermediateResult == PolygonSplitResult.CompletelyOutside) { finalResult = PolygonSplitResult.CompletelyOutside; break; // nothing left to process, so we exit } else if (intermediateResult == PolygonSplitResult.Split) { if (outside != null) outside.Add(outsidePolygon); // Note: left over is still completely inside, // or plane (opposite) aligned } else if (intermediateResult != PolygonSplitResult.CompletelyInside) finalResult = intermediateResult; } } else finalResult = PolygonSplitResult.CompletelyOutside; switch (finalResult) { case PolygonSplitResult.CompletelyInside: inside.Add(inputPolygon); break; case PolygonSplitResult.CompletelyOutside: outside.Add(inputPolygon); break; // The polygon can only be visible if it's part of the last brush that shares it's surface area, // otherwise we'd get overlapping polygons if two brushes overlap. // When the (final) polygon is aligned with one of the cutting planes, we know it lies on the surface of // the CSG node we're cutting the polygons with. We also know that this node is not the node this polygon belongs to // because we've done that check earlier on. So we flag this polygon as being invisible. case PolygonSplitResult.PlaneAligned: inputPolygon.Visible = false; aligned.Add(inputPolygon); break; case PolygonSplitResult.PlaneOppositeAligned: inputPolygon.Visible = false; revAligned.Add(inputPolygon); break; } } }
public CSGMesh Clone() { var newPlanes = new Plane[Planes.Length]; for (int i = 0; i < Planes.Length; i++) { var plane = Planes[i]; newPlanes[i] = new Plane(plane.A, plane.B, plane.C, plane.D); } var newPolygons = new List<Polygon>(Polygons.Count); foreach (var polygon in Polygons) { var newPolygon = new Polygon(); newPolygon.FirstIndex = polygon.FirstIndex; newPolygon.Visible = polygon.Visible; newPolygon.Category = polygon.Category; newPolygon.PlaneIndex = polygon.PlaneIndex; newPolygon.Bounds.Set(polygon.Bounds); newPolygons.Add(newPolygon); } var newEdges = new List<HalfEdge>(Edges.Count); foreach (var edge in Edges) { var newEdge = new HalfEdge(); newEdge.NextIndex = edge.NextIndex; newEdge.PolygonIndex = edge.PolygonIndex; newEdge.TwinIndex = edge.TwinIndex; newEdge.VertexIndex = edge.VertexIndex; newEdges.Add(newEdge); } var newVertices = new List<Vector3>(Vertices.Count); foreach (var vertex in Vertices) { newVertices.Add(vertex); } var newBounds = new AABBi(Bounds); var newMesh = new CSGMesh( newPlanes, newPolygons, newEdges, newVertices, newBounds); return newMesh; }
public Intersect CalculateIntersect(Vector3 location, Vector3 direction) { Intersect i = new Intersect(); i.intersectPoint = new Vector3(0, 0, 0); i.intersects = false; Plane plane = new Plane(this); float distance = plane.Distance(location, direction); //Console.WriteLine("Distance = {0}", distance); i.distance = distance; i.intersectPoint = distance * direction + location; if (distance < 0) { i.intersects = false; } else { i.intersects = ObjLoader.IsPointInPolygon(this, i.intersectPoint); } return i; }
public static CSGMesh CreateFromPlanes(Plane[] brushPlanes) { var planes = new Plane[brushPlanes.Length]; for (int i = 0; i < brushPlanes.Length; i++) { var plane = brushPlanes[i]; planes[i] = new Plane(plane.A, plane.B, plane.C, plane.D); } var pointIntersections = new List<PointIntersection>(planes.Length * planes.Length); var intersectingPlanes = new List<short>(); var vertices = new List<Vector3>(); var edges = new List<HalfEdge>(); // Find all point intersections where 3 (or more planes) intersect for (short planeIndex1 = 0; planeIndex1 < planes.Length - 2; planeIndex1++) { var plane1 = planes[planeIndex1]; for (short planeIndex2 = (short)(planeIndex1 + 1); planeIndex2 < planes.Length - 1; planeIndex2++) { var plane2 = planes[planeIndex2]; for (short planeIndex3 = (short)(planeIndex2 + 1); planeIndex3 < planes.Length; planeIndex3++) { var plane3 = planes[planeIndex3]; // Calculate the intersection var vertex = Plane.Intersection(plane1, plane2, plane3); // Check if the intersection is valid if (float.IsNaN(vertex.X) || float.IsNaN(vertex.Y) || float.IsNaN(vertex.Z) || float.IsInfinity(vertex.X) || float.IsInfinity(vertex.Y) || float.IsInfinity(vertex.Z)) continue; intersectingPlanes.Clear(); intersectingPlanes.Add(planeIndex1); intersectingPlanes.Add(planeIndex2); intersectingPlanes.Add(planeIndex3); for (short planeIndex4 = 0; planeIndex4 < planes.Length; planeIndex4++) { if (planeIndex4 == planeIndex1 || planeIndex4 == planeIndex2 || planeIndex4 == planeIndex3) continue; var plane4 = planes[planeIndex4]; var side = plane4.OnSide(vertex); if (side == PlaneSideResult.Intersects) { if (planeIndex4 < planeIndex3) // Already found this vertex goto SkipIntersection; // We've found another plane which goes trough our found intersection point intersectingPlanes.Add(planeIndex4); } else if (side == PlaneSideResult.Outside) // Intersection is outside of brush goto SkipIntersection; } var vertexIndex = (short)vertices.Count; vertices.Add(vertex); // Add intersection point to our list pointIntersections.Add(new PointIntersection(vertexIndex, intersectingPlanes)); SkipIntersection: ; } } } var foundPlanes = new short[2]; // Find all our intersection edges which are formed by a pair of planes // (this could probably be done inside the previous loop) for (int i = 0; i < pointIntersections.Count; i++) { var pointIntersectionA = pointIntersections[i]; for (int j = i + 1; j < pointIntersections.Count; j++) { var pointIntersectionB = pointIntersections[j]; var planesIndicesA = pointIntersectionA.PlaneIndices; var planesIndicesB = pointIntersectionB.PlaneIndices; short foundPlaneIndex = 0; foreach (var currentPlaneIndex in planesIndicesA) { if (!planesIndicesB.Contains(currentPlaneIndex)) continue; foundPlanes[foundPlaneIndex] = currentPlaneIndex; foundPlaneIndex++; if (foundPlaneIndex == 2) break; } // If foundPlaneIndex is 0 or 1 then either this combination does not exist, // or only goes trough one point if (foundPlaneIndex < 2) continue; // Create our found intersection edge var halfEdgeA = new HalfEdge(); var halfEdgeAIndex = (short)edges.Count; edges.Add(halfEdgeA); var halfEdgeB = new HalfEdge(); var halfEdgeBIndex = (short)edges.Count; edges.Add(halfEdgeB); halfEdgeA.TwinIndex = halfEdgeBIndex; halfEdgeB.TwinIndex = halfEdgeAIndex; halfEdgeA.VertexIndex = pointIntersectionA.VertexIndex; halfEdgeB.VertexIndex = pointIntersectionB.VertexIndex; // Add it to our points pointIntersectionA.Edges.Add(new EdgeIntersection( halfEdgeA, foundPlanes[0], foundPlanes[1])); pointIntersectionB.Edges.Add(new EdgeIntersection( halfEdgeB, foundPlanes[0], foundPlanes[1])); } } var polygons = new List<Polygon>(); for (short i = 0; i < (short)planes.Length; i++) { var polygon = new Polygon(); polygon.PlaneIndex = i; polygons.Add(polygon); } var bounds = new AABBi(); var direction = new Vector3(); for (int i = pointIntersections.Count - 1; i >= 0; i--) { var pointIntersection = pointIntersections[i]; var pointEdges = pointIntersection.Edges; // Make sure that we have at least 2 edges ... // This may happen when a plane only intersects at a single edge. if (pointEdges.Count <= 2) { pointIntersections.RemoveAt(i); continue; } var vertexIndex = pointIntersection.VertexIndex; var vertex = vertices[vertexIndex]; for (int j = 0; j < pointEdges.Count - 1; j++) { var edge1 = pointEdges[j]; for (int k = j + 1; k < pointEdges.Count; k++) { var edge2 = pointEdges[k]; int planeIndex1 = -1; int planeIndex2 = -1; // Determine if and which of our 2 planes are identical if (edge1.PlaneIndices[0] == edge2.PlaneIndices[0]) { planeIndex1 = 0; planeIndex2 = 0; } else if (edge1.PlaneIndices[0] == edge2.PlaneIndices[1]) { planeIndex1 = 0; planeIndex2 = 1; } else if (edge1.PlaneIndices[1] == edge2.PlaneIndices[0]) { planeIndex1 = 1; planeIndex2 = 0; } else if (edge1.PlaneIndices[1] == edge2.PlaneIndices[1]) { planeIndex1 = 1; planeIndex2 = 1; } else continue; HalfEdge ingoing; HalfEdge outgoing; short outgoingIndex; var shared_plane = planes[edge1.PlaneIndices[planeIndex1]]; var edge1_plane = planes[edge1.PlaneIndices[1 - planeIndex1]]; var edge2_plane = planes[edge2.PlaneIndices[1 - planeIndex2]]; direction = Vector3.Cross(shared_plane.Normal, edge1_plane.Normal); // Determine the orientation of our two edges to determine // which edge is in-going, and which one is out-going if (Vector3.Dot(direction, edge2_plane.Normal) < 0) { ingoing = edge2.Edge; outgoingIndex = edge1.Edge.TwinIndex; outgoing = edges[outgoingIndex]; } else { ingoing = edge1.Edge; outgoingIndex = edge2.Edge.TwinIndex; outgoing = edges[outgoingIndex]; } // Link the out-going half-edge to the in-going half-edge ingoing.NextIndex = outgoingIndex; // Add reference to polygon to half-edge, and make sure our // polygon has a reference to a half-edge // Since a half-edge, in this case, serves as a circular // linked list this just works. var polygonIndex = edge1.PlaneIndices[planeIndex1]; ingoing.PolygonIndex = polygonIndex; outgoing.PolygonIndex = polygonIndex; var polygon = polygons[polygonIndex]; polygon.FirstIndex = outgoingIndex; polygon.Bounds.Add(vertex); } } // Add the intersection point to the area of our bounding box bounds.Add(vertex); } return new CSGMesh(planes, polygons, edges, vertices, bounds); }
public static bool ComputeBestFitPlane(Vector3[] points, out Plane plane) { Vector3 origin = new Vector3(0, 0, 0); for (int i = 0; i < points.Length; i++) { origin = origin + points[i]; } float recip = 1.0f / points.Length; // reciprocol of total weighting origin.X *= recip; origin.Y *= recip; origin.Z *= recip; float fSumXX = 0; float fSumXY = 0; float fSumXZ = 0; float fSumYY = 0; float fSumYZ = 0; float fSumZZ = 0; Vector3 kDiff; for (int i = 0; i < points.Length; i++) { Vector3 p = points[i]; kDiff.X = (p.X - origin.X); // apply vertex weighting! kDiff.Y = (p.Y - origin.Y); kDiff.Z = (p.Z - origin.Z); fSumXX += kDiff.X * kDiff.X; // sume of the squares of the differences. fSumXY += kDiff.X * kDiff.Y; // sume of the squares of the differences. fSumXZ += kDiff.X * kDiff.Z; // sume of the squares of the differences. fSumYY += kDiff.Y * kDiff.Y; fSumYZ += kDiff.Y * kDiff.Z; fSumZZ += kDiff.Z * kDiff.Z; } fSumXX *= recip; fSumXY *= recip; fSumXZ *= recip; fSumYY *= recip; fSumYZ *= recip; fSumZZ *= recip; // setup the eigensolver Eigen kES = new Eigen(); kES.mElement[0][0] = fSumXX; kES.mElement[0][1] = fSumXY; kES.mElement[0][2] = fSumXZ; kES.mElement[1][0] = fSumXY; kES.mElement[1][1] = fSumYY; kES.mElement[1][2] = fSumYZ; kES.mElement[2][0] = fSumXZ; kES.mElement[2][1] = fSumYZ; kES.mElement[2][2] = fSumZZ; // compute eigenstuff, smallest eigenvalue is in last position kES.DecrSortEigenStuff(); Vector3 kNormal = new Vector3(); kNormal.X = kES.mElement[0][2]; kNormal.Y = kES.mElement[1][2]; kNormal.Z = kES.mElement[2][2]; // the minimum energy plane = new Plane(kNormal, 0 - Vector3.Dot(kNormal, origin)); return true; }
private void Slice(Plane p) { float epsilon = 0.01f; // TODO: compute proper epsilon value List<PolyLine> linePile = new List<PolyLine>(); // Pile of disconnected lines on the slice plane List<Vector3> all_points = new List<Vector3>(); foreach (Face f in this.faces) { PolyLine newLine = TrianglePlaneIntersect(f, p); // Only add lines with exactly 2 points - others are a no match or error if (newLine.points.Count() == 2 && (newLine.points[0] - newLine.points[1]).Length> epsilon) { linePile.Add(newLine); // Add the vertices to the all_points list - only need to add the first one, the tail will be the head of another point bool matched = false; foreach (Vector3 point in all_points) { if ((point - newLine.points[0]).Length < epsilon) { matched = true; break; } } if (!matched) { all_points.Add(newLine.points[0]); } } } // linePile is a unordered list of line segments. // If a line segment is oriented with point[0] on (0, 0, 0) and point[1] // somewhere on the positive Y axis, the solid object is in the direction of the positive x axis. // // p[1]xxxxxxxxxxxxxxxxxxxxxxxx // xx xx // xx <object over here> xx // xx xx // p[0]xxxxxxxxxxxxxxxxxxxxxxxx // List<PolyLine> newPolyLines = new List<PolyLine>(); for (int i = 0; i < linePile.Count(); i++) { int points = linePile[i].points.Count(); Vector3 v1 = linePile[i].points[0]; Vector3 v2 = linePile[i].points[1]; //DrawCone1(v1, v2); List<Vector3> points_on_line = new List<Vector3>(); foreach (Vector3 v in all_points) { if ((v1 - v).Length >= epsilon && (v2 - v).Length >= epsilon && DistanceToCylinder(v1, v2, v) < epsilon) { points_on_line.Add(v); } } points_on_line.Insert(0, v1); points_on_line.Add(v2); // Order from v1 to v2 var sorted = points_on_line.OrderBy(order_vec => (order_vec - v1).Length); PolyLine newPolyLine = new PolyLine(); foreach (Vector3 v in sorted) { if (newPolyLine.points.Count() == 0 || (newPolyLine.points[newPolyLine.points.Count() - 1] - v).Length > epsilon) { newPolyLine.points.Add(v); } } if (newPolyLine.points.Count() >= 2) { newPolyLines.Add(newPolyLine); } if (newPolyLine.points.Count() >= 3) { // Shouldn't get here! } } List<LinePointIndices> lpis = new List<LinePointIndices>(); List<Vector3> vertices = new List<Vector3>(); List<List<int>> v_lookup = new List<List<int>>(); foreach (PolyLine l in newPolyLines) { int lastIndex = -1; foreach (Vector3 pointVec in l.points) { int currentIndex = -1; for (int i = 0; i < vertices.Count(); i++) { float length = (vertices[i] - pointVec).Length; if (length < epsilon) { currentIndex = i; continue; } } if (currentIndex == -1) { vertices.Add(pointVec); v_lookup.Add(new List<int>()); currentIndex = vertices.Count() - 1; } if (lastIndex != -1 && lastIndex != currentIndex) { LinePointIndices line = new LinePointIndices(); bool already_matched = false; foreach (int line_index in v_lookup[lastIndex]) { LinePointIndices l2 = lpis[line_index]; if (l2.indices[1] == currentIndex) { already_matched = true; } } if (!already_matched) { line.indices.Add(lastIndex); line.indices.Add(currentIndex); lpis.Add(line); v_lookup[lastIndex].Add(lpis.Count() - 1); v_lookup[currentIndex].Add(lpis.Count() - 1); } } lastIndex = currentIndex; } } //List<Vector3> scaled = new List<Vector3>(); List<int> vector_indices_to_see = new List<int>(); foreach (Vector3 v in vertices) { //scaled.Add(v / 125); vector_indices_to_see.Add(vector_indices_to_see.Count()); } List<LinePointIndices> slices = new List<LinePointIndices>(); GL.PushMatrix(); GL.PointSize(10); List<int> seenVertices = new List<int>(); while(vector_indices_to_see.Count() > 0) { List<int> line_indices = v_lookup [vector_indices_to_see[0]]; vector_indices_to_see.RemoveAt(0); if (line_indices.Count() == 0) { continue; } LinePointIndices line = lpis[line_indices[0]]; // Only need to look at one line with this vertex LinePointIndices start_line = new LinePointIndices(); start_line.indices.Add(line.indices[0]); start_line.indices.Add(line.indices[1]); GL.Color3(Color.Green); DrawCone1(vertices[start_line.indices[0]], vertices[start_line.indices[1]]); LinePointIndices loop = FindLoop(seenVertices, p.normal, vertices, v_lookup, lpis, start_line); if (loop != null) { slices.Add(loop); GL.Color3(Color.LightBlue); GL.Begin(BeginMode.LineLoop); Vector3 add = new Vector3(0, 0, 0); foreach (int i in loop.indices) { vector_indices_to_see.RemoveAll(value => value == i); GL.Vertex3(vertices[i] + add); seenVertices.Add(i); //add += new Vector3(0, 0, 25); } GL.End(); //GL.Translate(new Vector3(0, 0, +100)); } //break; } GL.PointSize(1); GL.PopMatrix(); Vector3 normal = new Vector3(0, 0, 1); float toolRadius = 100; GL.LineWidth(1); List<IntPoint> boundingBox = new List<IntPoint>(); boundingBox.Add(new IntPoint(-1000, -1000)); boundingBox.Add(new IntPoint(3000, -1000)); boundingBox.Add(new IntPoint(3000, 3000)); boundingBox.Add(new IntPoint(-1000, 3000)); List<LineLoop> loops = new List<LineLoop>(); foreach (LinePointIndices l in slices) { LineStrip line = new LineStrip(); for (int i = 0; i < l.indices.Count (); i++) { line.Append(vertices[l.indices[i]]); } line.Append(vertices[l.indices[0]]); loops.Add(new LineLoop (line)); } if (loops.Count() > 0) { Vector3 up = new Vector3(0, 0, 1); if (Math.Abs (normal.Z) > 0.8) { up = new Vector3(1, 0, 0); } float distance = Vector3.Dot(loops[0].GetVertex(0), normal); Matrix4 transform = Matrix4.LookAt(normal * distance, normal * (distance - 1), up); Matrix4 inverseTransform = Matrix4.Invert(transform); Clipper c = new Clipper(); c.Clear(); try { // These loops go clockwise foreach (LineLoop loop in loops) { List<IntPoint> polygon = new List<IntPoint>(); foreach (Vector3 vertex in loop.Vertices) { Vector3 result = Vector3.Transform(vertex, transform); polygon.Add(new IntPoint((long)result.X, (long)result.Y)); } polygon.RemoveAt(0); c.AddPolygon(polygon, PolyType.ptClip); GL.PushMatrix(); GL.Translate(new Vector3(0, 0, 100)); //loop.Draw(); GL.PopMatrix(); } List<List<IntPoint>> union = new List<List<IntPoint>>(); bool r = c.Execute(ClipType.ctUnion, union, PolyFillType.pftNonZero, PolyFillType.pftNonZero); List<List<IntPoint>> with_offset = Clipper.OffsetPolygons(union, toolRadius, JoinType.jtSquare); List<List<IntPoint>> whatsLeft = Clipper.OffsetPolygons(with_offset, -toolRadius, JoinType.jtRound); List<LineStrip> strips = new List<LineStrip>(); foreach (List<IntPoint> polygon in with_offset) { LineStrip strip = new LineStrip(); foreach (IntPoint point in polygon) { strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); } strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); strips.Add(strip); //new LineLoop(strip).Draw(); } List<List<IntPoint>> removeArea = new List<List<IntPoint>>(); c.Clear(); c.AddPolygons(with_offset, PolyType.ptClip); c.AddPolygon(boundingBox, PolyType.ptSubject); List<List<IntPoint>> resultingPolygon = new List<List<IntPoint>>(); c.Execute(ClipType.ctDifference, removeArea, PolyFillType.pftNonZero, PolyFillType.pftNonZero); removeArea = Clipper.CleanPolygons(removeArea, toolRadius / 100); c.Clear(); c.AddPolygons(removeArea, PolyType.ptClip); PolyTree test = new PolyTree(); c.Execute(ClipType.ctUnion, test, PolyFillType.pftNonZero, PolyFillType.pftNonZero); //PolyNode pn = test.GetFirst(); //while (pn != null) //{ // if (pn.IsHole) // { // LineLoop l = new LineLoop(pn.Contour, inverseTransform); // l.Draw(); // } // pn = pn.GetNext(); //} List<Polygons> polys = FlattenPolyTree(test); //GL.PushMatrix(); foreach (Polygons polygons in polys) { //GL.Translate(new Vector3 (0, 0, 100)); //foreach (Polygon polygon in polygons) //{ // LineLoop l = new LineLoop(polygon, inverseTransform); // l.Draw(); //} List<Polygons> paths = ReducePolygon(polygons, toolRadius, inverseTransform); //IOrderedEnumerable<List<IntPoint>> ordered = paths.OrderBy(poly => Clipper.Area(poly)); GL.PushMatrix(); List<Polygons> paths2 = new List<Polygons>(); List<Polygons> paths3 = new List<Polygons>(); foreach (Polygons polygons2 in paths) { var newPolys = new Polygons(); foreach (Polygon poly in polygons2) { if (Clipper.Area(poly) > 0) { newPolys.Add(poly); } } paths2.Add(newPolys); //GL.Translate(new Vector3(0, 0, 100)); var newInnerPolys = new Polygons(); foreach (Polygon poly in polygons2) { if (paths3.Count() == 0) { //newInnerPoly } if (Clipper.Area(poly) < 0) { LineLoop l = new LineLoop(poly, inverseTransform); l.Draw(); } } } foreach (Polygons polygons2 in paths2) { GL.Translate(new Vector3(0, 0, 100)); foreach (Polygon poly in polygons2) { LineLoop l = new LineLoop(poly, inverseTransform); l.Draw(); } } GL.PopMatrix(); } //GL.PopMatrix(); double boundingBoxArea = Clipper.Area(boundingBox); // Outer Polygon // Inner Polygons //ReducePolygon(boundingBox, with_offset, toolRadius, inverseTransform); //strips = new List<LineStrip>(); //double area = 1; //int loopTimes = 0; //List<List<IntPoint>> cutPolygons = new List<List<IntPoint>>(); //List<Vector3> parentPoints = new List<Vector3>(); //GL.PushMatrix(); //while (removeArea.Count() > 0) //{ // List<Vector3> points = new List<Vector3>(); // foreach (List<IntPoint> polygon in removeArea) // { // double area = Clipper.Area(polygon); // // if (area > 0) // Bigger to Smaller // { // } // IntPoint[] newP = new IntPoint[polygon.Count()]; // polygon.CopyTo(newP); // cutPolygons.Add(new List<IntPoint>(newP)); // // // LineLoop l = new LineLoop(polygon, inverseTransform); // //l.Draw(); // points.AddRange(l.Vertices); // // //ReducePolygon(null, polygon, toolRadius, inverseTransform); // //area += Clipper.Area(polygon); // //LineStrip strip = new LineStrip(); // //foreach (IntPoint point in polygon) // //{ // // strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); // //} // //strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); // // //strips.Add(strip); // //new LineLoop(strip).Draw(); // } // // //GL.Color3(Color.Black); // //GL.Begin(BeginMode.Lines); // //foreach (Vector3 v in points) // //{ // // foreach (Vector3 v2 in parentPoints) // // { // // if ((v - v2).Length < toolRadius * 2) // // { // // GL.Vertex3(v); // // GL.Vertex3(v2); // // } // // } // //} // //GL.End(); // // parentPoints = points; // removeArea = Clipper.OffsetPolygons(removeArea, -toolRadius, JoinType.jtRound); // removeArea = Clipper.CleanPolygons(removeArea, toolRadius / 100); //} //GL.PopMatrix(); //IOrderedEnumerable<List<IntPoint>> ordered = cutPolygons.OrderBy(poly => Clipper.Area(poly)); // //GL.PushMatrix(); //foreach (List<IntPoint> poly in ordered) //{ // GL.Translate(new Vector3(0, 0, 100)); // LineLoop l = new LineLoop(poly, inverseTransform); // l.Draw(); //} //GL.PopMatrix(); ////strips = new List<LineStrip>(); //GL.Color3(Color.Red); //GL.LineWidth(2); //foreach (List<IntPoint> polygon in whatsLeft) //{ // LineStrip strip = new LineStrip(); // foreach (IntPoint point in polygon) // { // strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); // } // strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); // // strips.Add(strip); // new LineLoop(strip).Draw(); //} //GL.LineWidth(1); } catch (Exception e) { } } }
private PolyLine TrianglePlaneIntersect(Face f, Plane p) { PolyLine polyLine = new PolyLine(); float epsilon = 0.01f; // TODO: Auto compute based on scale float epsilon_unit = 0.00001f; // Unit size epsilon value Vector3 f_normal = f.Normal; f_normal.Normalize(); p.normal.Normalize(); if ((f_normal - p.normal).Length < epsilon_unit || (f_normal + p.normal).Length < epsilon_unit) { // No intersection } else { Vector3 expected_direction = Vector3.Cross(f.Normal, p.normal); // Assume we're dealing with triangles only int verts = f.vertices.Count(); if (verts != 3) { throw new Exception("The number of vertices is not 3!"); } float[] d = new float[3]; for (int i = 0; i < 3; i++) { d[i] = p.Distance(f.vertices[i]); } for (int i = 0; i < 3; i++) { // Is the line on the plane? if (Math.Abs(d[i]) < epsilon && Math.Abs(d[(i + 1) % 3]) < epsilon) { polyLine.points.Add(f.vertices[i]); polyLine.points.Add(f.vertices[(i + 1) % 3]); break; } } if (polyLine.points.Count() == 0) { // Line not on a plain: might have an intersection with a point and the opposite line for (int i = 0; i < 3; i++) { float d1 = d[i]; float d2 = d[(i + 1) % 3]; float d3 = d[(i + 2) % 3]; if (Math.Abs(d[i]) < epsilon && Math.Sign(d2) != Math.Sign(d3)) { d2 = Math.Abs(d2); d3 = Math.Abs(d3); // One negative, one positive float total = d2 + d3; Vector3 result = (f.vertices[(i + 1) % 3] * d3 + f.vertices[(i + 2) % 3] * d2) / total; polyLine.points.Add(f.vertices[i]); polyLine.points.Add(result); break; } } if (polyLine.points.Count() == 0) { // No edge in plane and no point + line intersect: maybe two lines intersect? for (int i = 0; i < 3; i++) { // Intersection with an edge if (Math.Sign(d[i]) != Math.Sign(d[(i + 1) % 3])) { float d1 = Math.Abs(d[i]); float d2 = Math.Abs(d[(i + 1) % 3]); float total = d1 + d2; Vector3 result = (f.vertices[i] * d2 + f.vertices[(i + 1) % 3] * d1) / total; polyLine.points.Add(result); if (polyLine.points.Count() == 2) { break; } } } } } if (polyLine.points.Count() >= 2) { //DrawCone1(polyLine.points[0], polyLine.points[1]); Vector3 direction = polyLine.points[1] - polyLine.points[0]; if (Vector3.Dot(direction, expected_direction) < 0) { PolyLine reversed = new PolyLine(); reversed.points.Add(polyLine.points[1]); reversed.points.Add(polyLine.points[0]); polyLine = reversed; } // // // Color[] colors = new Color[] { Color.DarkRed, Color.LightGreen, Color.DarkBlue }; // int i = 0; // GL.Begin(BeginMode.LineLoop); // foreach (Vector3 v in polyLine.points) // { // GL.Color3(colors[i++]); // GL.Vertex3(v); // // } // GL.End(); // // GL.PointSize(10); // GL.Color3(Color.Orange); // GL.Begin(BeginMode.Points); // foreach (Vector3 v in polyLine.points) // { // GL.Vertex3(v); // } // GL.End(); // GL.PointSize(1); } } return polyLine; }
/// <summary> /// Test scene for BezierSurface. /// </summary> public static void Bezier( IRayScene sc ) { Debug.Assert( sc != null ); // CSG scene: CSGInnerNode root = new CSGInnerNode( SetOperation.Union ); root.SetAttribute( PropertyName.REFLECTANCE_MODEL, new PhongModel() ); root.SetAttribute( PropertyName.MATERIAL, new PhongMaterial( new double[] { 1.0, 0.8, 0.1 }, 0.1, 0.5, 0.5, 64 ) ); sc.Intersectable = root; // Background color: sc.BackgroundColor = new double[] { 0.0, 0.05, 0.07 }; // Camera: sc.Camera = new StaticCamera( new Vector3d( 0.7, 0.5, -5.0 ), new Vector3d( 0.0, -0.18, 1.0 ), 50.0 ); // Light sources: sc.Sources = new LinkedList<ILightSource>(); sc.Sources.Add( new AmbientLightSource( 0.8 ) ); sc.Sources.Add( new PointLightSource( new Vector3d( -5.0, 3.0, -3.0 ), 1.0 ) ); // --- NODE DEFINITIONS ---------------------------------------------------- // Bezier patch (not yet): BezierSurface b = new BezierSurface( 1, 2, new double[] { 0.0, 0.0, 3.0, // row 0 1.0, 0.0, 3.0, 2.0, 0.0, 3.0, 3.0, 0.0, 3.0, 4.0, 0.0, 3.0, 5.0, 0.0, 3.0, 6.0, 0.0, 3.0, 0.0, 0.0, 2.0, // row 1 1.0, 0.0, 2.0, 2.0, 3.0, 2.0, 3.0, 3.0, 2.0, 4.0, 3.0, 2.0, 5.0, 0.0, 2.0, 6.0, 0.0, 2.0, 0.0, 0.0, 1.0, // row 2 1.0, 0.0, 1.0, 2.0, 0.0, 1.0, 3.0, 1.5, 1.0, 4.0, 3.0, 1.0, 5.0, 0.0, 1.0, 6.0, 0.0, 1.0, 0.0, 0.0, 0.0, // row 3 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 0.0, 4.0, 0.0, 0.0, 5.0, 0.0, 0.0, 6.0, 0.0, 0.0, } ); b.SetAttribute( PropertyName.TEXTURE, new CheckerTexture( 10.5, 12.0, new double[] { 0.0, 0.0, 0.1 } ) ); root.InsertChild( b, Matrix4d.RotateY( -0.4 ) * Matrix4d.CreateTranslation( -1.1, -0.9, 0.0 ) ); // Cylinders for reflections.. Cylinder c = new Cylinder(); c.SetAttribute( PropertyName.MATERIAL, new PhongMaterial( new double[] { 0.0, 0.6, 0.0 }, 0.2, 0.6, 0.3, 8 ) ); root.InsertChild( c, Matrix4d.Scale( 0.15 ) * Matrix4d.RotateX( MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( -0.4, 0.0, 0.0 ) ); c = new Cylinder(); c.SetAttribute( PropertyName.MATERIAL, new PhongMaterial( new double[] { 0.8, 0.2, 0.0 }, 0.2, 0.6, 0.3, 8 ) ); root.InsertChild( c, Matrix4d.Scale( 0.2 ) * Matrix4d.RotateX( MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( -1.9, 0.0, 3.0 ) ); // Infinite plane with checker: Plane pl = new Plane(); pl.SetAttribute( PropertyName.COLOR, new double[] { 0.3, 0.0, 0.0 } ); pl.SetAttribute( PropertyName.TEXTURE, new CheckerTexture( 0.6, 0.6, new double[] { 1.0, 1.0, 1.0 } ) ); root.InsertChild( pl, Matrix4d.RotateX( -MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( 0.0, -1.0, 0.0 ) ); }
/// <summary> /// Test scene for cylinders /// </summary> public static void Cylinders( IRayScene sc ) { Debug.Assert( sc != null ); // CSG scene: CSGInnerNode root = new CSGInnerNode( SetOperation.Union ); root.SetAttribute( PropertyName.REFLECTANCE_MODEL, new PhongModel() ); root.SetAttribute( PropertyName.MATERIAL, new PhongMaterial( new double[] { 0.6, 0.0, 0.0 }, 0.15, 0.8, 0.15, 16 ) ); sc.Intersectable = root; // Background color: sc.BackgroundColor = new double[] { 0.0, 0.05, 0.07 }; // Camera: sc.Camera = new StaticCamera( new Vector3d( 0.7, 3.0, -10.0 ), new Vector3d( 0.0, -0.2, 1.0 ), 50.0 ); // Light sources: sc.Sources = new LinkedList<ILightSource>(); sc.Sources.Add( new AmbientLightSource( 1.0 ) ); sc.Sources.Add( new PointLightSource( new Vector3d( -5.0, 3.0, -3.0 ), 1.6 ) ); // --- NODE DEFINITIONS ---------------------------------------------------- // Base plane Plane pl = new Plane(); pl.SetAttribute( PropertyName.COLOR, new double[] { 0.0, 0.2, 0.0 } ); pl.SetAttribute( PropertyName.TEXTURE, new CheckerTexture( 0.5, 0.5, new double[] { 1.0, 1.0, 1.0 } ) ); root.InsertChild( pl, Matrix4d.RotateX( -MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( 0.0, -1.0, 0.0 ) ); // Cylinders Cylinder c = new Cylinder(); root.InsertChild( c, Matrix4d.RotateX( MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( -2.1, 0.0, 1.0 ) ); c = new Cylinder(); c.SetAttribute( PropertyName.COLOR, new double[] { 0.2, 0.0, 0.7 } ); c.SetAttribute( PropertyName.TEXTURE, new CheckerTexture( 12.0, 1.0, new double[] { 0.0, 0.0, 0.3 } ) ); root.InsertChild( c, Matrix4d.RotateY( -0.4 ) * Matrix4d.CreateTranslation( 1.0, 0.0, 1.0 ) ); c = new Cylinder( 0.0, 100.0 ); c.SetAttribute( PropertyName.COLOR, new double[] { 0.1, 0.7, 0.0 } ); root.InsertChild( c, Matrix4d.RotateY( 0.2 ) * Matrix4d.CreateTranslation( 5.0, 0.3, 4.0 ) ); c = new Cylinder( -0.5, 0.5 ); c.SetAttribute( PropertyName.COLOR, new double[] { 0.8, 0.6, 0.0 } ); root.InsertChild( c, Matrix4d.Scale( 2.0 ) * Matrix4d.RotateX( 1.2 ) * Matrix4d.CreateTranslation( 2.0, 1.8, 16.0 ) ); }
/// <summary> /// Trim interior loops from the line /// </summary> public void Clean(Vector3 normal) { List<int> remove = new List<int>(); for (int i = 0; i < indices.Count(); i++) { Vector3 v1 = GetVertex(i); Vector3 v2 = GetVertex((i + 1) % indices.Count()); for (int j = 2; j < indices.Count() - 1; j++) { int j1 = (j + i) % indices.Count(); int j2 = (j + i + 1) % indices.Count(); Vector3 v3 = GetVertex(j1); Vector3 v4 = GetVertex(j2); Vector3 n1 = v2 - v1; Vector3 n2 = v4 - v3; n1.Normalize(); n2.Normalize(); if ((n1 - n2).Length < 0.0001f) { // No crossing, same direction continue; } Vector3 perp = Vector3.Cross(normal, n2); Plane p = new Plane (); //GL.Begin(BeginMode.Lines); //GL.Vertex3(v4); //GL.Vertex3(v4 + perp * 100); //GL.End(); p.normal = perp; p.point = v3; float distance = p.Distance(v1, n1); if (distance > 0 && distance < (v2 - v1).Length && (Vector3.Dot(perp, n1) < 0))//!float.IsInfinity(distance)) { Vector3 point = n1 * distance + v1; if (DistanceToCylinder (v3, v4, point) < 0.01f) { for (int k = 0; k < j; k++) { int removeIndex = (i + 1) % indices.Count(); indices.RemoveAt(removeIndex); } vertices.Add(point); indices.Insert(i + 1, vertices.Count() - 1); //GL.PointSize(15); //GL.Color3(Color.Turquoise); //GL.Begin(BeginMode.Points); //GL.Vertex3(point); // //GL.End(); //GL.PointSize(1); i = i - 1; continue; } } } } LineLoop n = new LineLoop(this); this.indices = n.indices; this.vertices = n.vertices; }
/// <summary> /// Returns the perpendicular distance from a point to a plane /// </summary> /// <param name="point">The point to check</param> /// <param name="plane">The place to check</param> /// <returns>The perpendicular distance from the point to the plane</returns> public static float PerpendicularDistance(ref Vector3 point, ref Plane plane) { // dist = (ax + by + cz + d) / sqrt(a*a + b*b + c*c) return (float)Math.Abs((plane.Normal.X * point.X + plane.Normal.Y * point.Y + plane.Normal.Z * point.Z) / Math.Sqrt(plane.Normal.X * plane.Normal.X + plane.Normal.Y * plane.Normal.Y + plane.Normal.Z * plane.Normal.Z)); }
/// <summary> /// Returns a value indicating what side (positive/negative) of a plane a point is /// </summary> /// <param name="point">The point to check with</param> /// <param name="plane">The plane to check against</param> /// <returns>Greater than zero if on the positive side, less than zero if on the negative size, 0 otherwise</returns> public static float ClassifyPoint(ref Vector3 point, ref Plane plane) { return point.X * plane.Normal.X + point.Y * plane.Normal.Y + point.Z * plane.Normal.Z + plane.D; }
public void MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { if(!glcontrol.Focused) glcontrol.Focus(); mouseDownInsideImageCloud = false; if(ContextMenu.MouseDown(sender, e, backbuffersize)) return; if(colorTableMgr.Visible && colorTableMgr.MouseDown(e)) return; #if USE_ARG_IDX if(argIndex.Visible && argIndex.MouseDown(Size, e)) { mouseDownInsideArgIndex = true; return; } #endif #if USE_PARAM_IDX if(paramIndex.Visible && paramIndex.MouseDown(Size, e)) { mouseDownInsideParamIndex = true; return; } #endif #if USE_CUSTOM_CONTROLS if(ccContainer.Visible && ccContainer.MouseDown(Size, e)) { mouseDownInsideCcContainer = true; return; } #endif Viewer.RequestInputProcessing(); if(images == null) return; mouseDownPos = new Vector2(2.0f * e.X / backbuffersize.Width - 1.0f, 1.0f - 2.0f * e.Y / backbuffersize.Height); mouseDownLocation = e.Location; mouseDownInsideImageCloud = true; Matrix4 invvieworient = freeview.viewmatrix; invvieworient.M41 = invvieworient.M42 = invvieworient.M43 = 0.0f; invvieworient.Transpose(); Vector3 vnear = new Vector3(mouseDownPos.X, mouseDownPos.Y, 0.0f); Vector3 vfar = new Vector3(vnear.X, vnear.Y, 1.0f); Matrix4 invviewprojmatrix = freeview.viewprojmatrix.Inverted(); vnear = Vector3.TransformPerspective(vnear, invviewprojmatrix); vfar = Vector3.TransformPerspective(vfar, invviewprojmatrix); Vector3 vdir = (vfar - vnear).Normalized(); float dist, closest_dist = float.MaxValue; Vector2 uv, closest_uv = Vector2.Zero; TransformedImage closest_image = default(TransformedImage); foreach(TransformedImage image in images.Values) if(image != null && (dist = image.CastRay(vnear, vdir, invvieworient, out uv)) < closest_dist) { closest_dist = dist; closest_uv = uv; //Global.cle.PrintOutput(closest_uv.ToString()); closest_image = image; } if(closest_dist < float.MaxValue) { mouseDownImage = closest_image; bool enableDrag; Viewer.browser.OnImageMouseDown(e.Button, closest_image, closest_uv, out enableDrag); foreach(ImageTransform transform in closest_image.transforms) { bool transformAllowsDrag; transform.OnImageMouseDown((ImageTransform.MouseButtons)e.Button, closest_image, closest_uv, out transformAllowsDrag); enableDrag &= transformAllowsDrag; } if(enableDrag) { dragImage = closest_image; dragImageOffset = closest_image.Position - (vnear + vdir * closest_dist); // dragImagePlane = plane parallel to screen, going through point of intersection Vector3 vsnear = Vector3.TransformPerspective(new Vector3(0.0f, 0.0f, 0.0f), invviewprojmatrix); Vector3 vsfar = Vector3.TransformPerspective(new Vector3(0.0f, 0.0f, 1.0f), invviewprojmatrix); Vector3 vsdir = (vsfar - vsnear).Normalized(); dragImagePlane = new Plane(vnear + vdir * closest_dist, vsdir); } } else if(e.Button == MouseButtons.Left) { dragImage = mouseDownImage = null; Viewer.browser.OnNonImageMouseDown(); } }
public void Draw() { GL.Color3(Color.DarkGreen); GL.PointSize(5); GL.Begin(BeginMode.Points); GL.Vertex3(mousePoint); GL.End(); GL.PointSize(1); foreach (Face bad in this.badfaces) { GL.Color3(Color.Red); GL.Begin(BeginMode.TriangleFan); foreach (Vector3 v in bad.vertices) { GL.Normal3(bad.Normal); GL.Vertex3(v); } GL.End(); } for (int i = 0; i < faces.Count(); i++) { Face f = faces[i]; Vector3 normal = f.Normal; if (withinFace) { GL.Color3(Color.Orange); } else { GL.Color3(Color.White); } GL.Begin(BeginMode.TriangleFan); foreach (Vector3 v in f.vertices) { GL.Normal3(normal); GL.Vertex3(v); } GL.End(); //GL.Color3(Color.LightGray); //GL.Begin(BeginMode.LineLoop); //foreach (Vector3 v in f.vertices) //{ // GL.Vertex3(v); //} //GL.End(); if (i == closestFace) { GL.PointSize(10); GL.Begin(BeginMode.Points); GL.Color3(Color.Red); GL.Vertex3(f.vertices[0]); GL.Color3(Color.Green); GL.Vertex3(f.vertices[1]); GL.Color3(Color.Blue); GL.Vertex3(f.vertices[2]); GL.End(); GL.PointSize(1); } //for (int i = 0; i < 1000; i += 500) //{ //int i = (int)((DateTime.Now.Ticks & Int32.MaxValue) / 50000) % 1000; //int i = (int)mousePoint.Z; //GL.Color3(Color.Green); //p.normal = new Vector3(0, 1, 0); //p.point = new Vector3(0, mousePoint.Y, 0); //Slice(f, p); //GL.Color3(Color.Red); //p.normal = new Vector3(1, 0, 0); //p.point = new Vector3(mousePoint.X, 0, 0); //Slice(f, p); //} } GL.LineWidth(4); GL.Color3(Color.Blue); Plane p = new Plane(); p.point = new Vector3(0, 0, mousePoint.Z); p.normal = new Vector3(0, 0, 1); Slice(p); GL.LineWidth(1); }
public void DoubleClick(object sender, Point mousepos) { Viewer.RequestInputProcessing(); Matrix4 invvieworient = freeview.viewmatrix; invvieworient.M41 = invvieworient.M42 = invvieworient.M43 = 0.0f; invvieworient.Transpose(); Vector3 vnear = new Vector3(mouseDownPos.X, mouseDownPos.Y, 0.0f); Vector3 vfar = new Vector3(vnear.X, vnear.Y, 1.0f); Matrix4 invviewprojmatrix = freeview.viewprojmatrix.Inverted(); vnear = Vector3.TransformPerspective(vnear, invviewprojmatrix); vfar = Vector3.TransformPerspective(vfar, invviewprojmatrix); Vector3 vdir = (vfar - vnear).Normalized(); float dist, closest_dist = float.MaxValue; Vector2 uv, closest_uv; TransformedImage closest_image = default(TransformedImage); foreach(TransformedImage image in images.Values) if(image != null && (dist = image.CastRay(vnear, vdir, invvieworient, out uv)) < closest_dist) { closest_dist = dist; closest_uv = uv; closest_image = image; } if(closest_dist < float.MaxValue) { dragImage = mouseDownImage = closest_image; dragImageOffset = closest_image.Position - (vnear + vdir * closest_dist); // dragImagePlane = plane parallel to screen, going through point of intersection Vector3 vsnear = Vector3.TransformPerspective(new Vector3(0.0f, 0.0f, 0.0f), invviewprojmatrix); Vector3 vsfar = Vector3.TransformPerspective(new Vector3(0.0f, 0.0f, 1.0f), invviewprojmatrix); Vector3 vsdir = (vsfar - vsnear).Normalized(); dragImagePlane = new Plane(vnear + vdir * closest_dist, vsdir); Viewer.browser.OnImageDoubleClick(closest_image); } }
static float DistanceToCylinder(Vector3 cy1, Vector3 cy2, Vector3 point) { float distance = 0; if ((cy1 - cy2).Length < 0.0001) { return (cy1 - point).Length; } Plane p = new Plane(); p.normal = cy1 - cy2; p.normal.Normalize(); p.point = cy2; float d1 = p.Distance(point); p.normal = -p.normal; p.point = cy1; float d2 = p.Distance(point); if (float.IsNaN(d2) || float.IsNaN(d1)) { } if (Math.Sign(d1) == Math.Sign(d2)) { // Inside Endpoints Vector3 pt = point - p.normal * d2; distance = (pt - p.point).Length; } else { distance = (float)Math.Min((point - cy1).Length, (point - cy2).Length); } return distance; }
/// <summary> /// Infinite plane with two spheres on it (one of them is transparent) /// </summary> public static void TwoSpheres( IRayScene sc ) { Debug.Assert( sc != null ); // CSG scene: CSGInnerNode root = new CSGInnerNode( SetOperation.Union ); root.SetAttribute( PropertyName.REFLECTANCE_MODEL, new PhongModel() ); root.SetAttribute( PropertyName.MATERIAL, new PhongMaterial( new double[] { 1.0, 0.8, 0.1 }, 0.1, 0.6, 0.4, 128 ) ); sc.Intersectable = root; // Background color: sc.BackgroundColor = new double[] { 0.0, 0.05, 0.07 }; // Camera: sc.Camera = new StaticCamera( new Vector3d( 0.7, 0.5, -5.0 ), new Vector3d( 0.0, -0.18, 1.0 ), 50.0 ); // Light sources: sc.Sources = new LinkedList<ILightSource>(); sc.Sources.Add( new AmbientLightSource( 0.8 ) ); sc.Sources.Add( new PointLightSource( new Vector3d( -5.0, 4.0, -3.0 ), 1.2 ) ); // --- NODE DEFINITIONS ---------------------------------------------------- // Transparent sphere: Sphere s; s = new Sphere(); PhongMaterial pm = new PhongMaterial( new double[] { 0.0, 0.2, 0.1 }, 0.05, 0.05, 0.1, 128 ); pm.n = 1.6; pm.Kt = 0.9; s.SetAttribute( PropertyName.MATERIAL, pm ); root.InsertChild( s, Matrix4d.Identity ); // Opaque sphere: s = new Sphere(); root.InsertChild( s, Matrix4d.Scale( 1.2 ) * Matrix4d.CreateTranslation( 1.5, 0.2, 2.4 ) ); // Infinite plane with checker: Plane pl = new Plane(); pl.SetAttribute( PropertyName.COLOR, new double[] { 0.3, 0.0, 0.0 } ); pl.SetAttribute( PropertyName.TEXTURE, new CheckerTexture( 0.6, 0.6, new double[] { 1.0, 1.0, 1.0 } ) ); root.InsertChild( pl, Matrix4d.RotateX( -MathHelper.PiOver2 ) * Matrix4d.CreateTranslation( 0.0, -1.0, 0.0 ) ); }
public static float DotNormal(Plane plane, Vector3 normal) { return Vector3.Dot(plane.Normal, normal); }
public static float DotCoordinate(Plane plane, Vector3 point) { return Vector3.Dot(plane.Normal, point) + plane.D; }
public Vector3? GetIntersectionPoint(Plane plane) { float t = 0.0f; float numerator = Vector3.Dot(plane.Normal, plane.Point - Origin); float denominator = Vector3.Dot(plane.Normal, Direction); if (denominator != 0) t = numerator / denominator; return origin + direction * t; }