/// <summary> /// Plane/Box intersection test. /// </summary> /// <param name="plane"></param> /// <param name="box"></param> /// <returns>True if there was an intersection, false otherwise.</returns> public static bool Intersects(Plane plane, AxisAlignedBox box) { if (box.IsNull) { return(false); } // Get corners of the box Vector3[] corners = box.Corners; // Test which side of the plane the corners are // Intersection occurs when at least one corner is on the // opposite side to another PlaneSide lastSide = plane.GetSide(corners[0]); for (int corner = 1; corner < 8; corner++) { if (plane.GetSide(corners[corner]) != lastSide) { return(true); } } return(false); }
/// <summary> /// Intersection test with an <see cref="AxisAlignedBox2"/>. /// </summary> /// <remarks> /// May return false positives but will never miss an intersection. /// </remarks> /// <param name="box">Box to test.</param> /// <returns>True if interesecting, false otherwise.</returns> public bool Intersects(AxisAlignedBox3 box) { if (box.IsNull) { return(false); } if (box.IsInfinite) { return(true); } // Get centre of the box Vector3 center = box.Center; // Get the half-size of the box Vector3 halfSize = box.HalfSize; // If all points are on outside of any plane, we fail Vector3[] points = box.Corners; for (int i = 0; i < planes.Count; i++) { Plane plane = (Plane)planes[i]; PlaneSide side = plane.GetSide(center, halfSize); if (side == outside) { // Found a splitting plane therefore return not intersecting return(false); } } // couldn't find a splitting plane, assume intersecting return(true); }
public bool IsObjectVisible(Sphere bound) { // Check originplane if told to if (this.mUseOriginPlane) { PlaneSide side = this.mOriginPlane.GetSide(bound.Center); if (side == PlaneSide.Negative) { Real dist = this.mOriginPlane.GetDistance(bound.Center); if (dist > bound.Radius) { return(false); } } } // For each extra active culling plane, see if the entire sphere is on the negative side // If so, object is not visible foreach (PCPlane plane in this.mActiveCullingPlanes) { PlaneSide xside = plane.GetSide(bound.Center); if (xside == PlaneSide.Negative) { float dist = this.mOriginPlane.GetDistance(bound.Center); if (dist > bound.Radius) { return(false); } } } return(true); }
/// <summary> /// A 'more detailed' check for visibility of an AAB. /// </summary> /// <remarks> /// This is useful for stuff like Octree leaf culling. /// </remarks> /// <param name="bound">the <see cref="AxisAlignedBox"/> to check visibility aginst.</param> /// <returns> /// None, Partial, or Full for visibility of the box. /// </returns> public PCZFrustum.Visibility GetVisibility(AxisAlignedBox bound) { // Null boxes always invisible if (bound.IsNull) { return(PCZFrustum.Visibility.None); } // Get centre of the box Vector3 centre = bound.Center; // Get the half-size of the box Vector3 halfSize = bound.HalfSize; bool all_inside = true; for (int plane = 0; plane < 6; ++plane) { // Skip far plane if infinite view frustum if (plane == (int)FrustumPlane.Far && Far == 0) { continue; } // This updates frustum planes and deals with cull frustum PlaneSide side = FrustumPlanes[plane].GetSide(centre, halfSize); if (side == PlaneSide.Negative) { return(PCZFrustum.Visibility.None); } // We can't return now as the box could be later on the negative side of a plane. if (side == PlaneSide.Both) { all_inside = false; } } switch (this.extraCullingFrustum.GetVisibility(bound)) { case PCZFrustum.Visibility.None: return(PCZFrustum.Visibility.None); case PCZFrustum.Visibility.Partial: return(PCZFrustum.Visibility.Partial); case PCZFrustum.Visibility.Full: break; } if (all_inside) { return(PCZFrustum.Visibility.Full); } else { return(PCZFrustum.Visibility.Partial); } }
/// <summary> /// A 'more detailed' check for visibility of an AAB. /// </summary> /// <remarks> /// This is useful for stuff like Octree leaf culling. /// </remarks> /// <param name="bound">the <see cref="AxisAlignedBox"/> to check visibility aginst.</param> /// <returns> /// None, Partial, or Full for visibility of the box. /// </returns> public Visibility GetVisibility(AxisAlignedBox bound) { // Null boxes always invisible if (bound.IsNull) { return(Visibility.None); } // Get centre of the box Vector3 centre = bound.Center; // Get the half-size of the box Vector3 halfSize = bound.HalfSize; bool all_inside = true; // Check originplane if told to if (this.mUseOriginPlane) { PlaneSide side = this.mOriginPlane.GetSide(centre, halfSize); if (side == PlaneSide.Negative) { return(Visibility.None); } // We can't return now as the box could be later on the negative side of another plane. if (side == PlaneSide.Both) { all_inside = false; } } // For each active culling plane, see if the entire aabb is on the negative side // If so, object is not visible foreach (PCPlane plane in this.mActiveCullingPlanes) { PlaneSide xside = plane.GetSide(centre, halfSize); if (xside == PlaneSide.Negative) { return(Visibility.None); } // We can't return now as the box could be later on the negative side of a plane. if (xside == PlaneSide.Both) { all_inside = false; } } if (all_inside) { return(Visibility.Full); } else { return(Visibility.Partial); } }
static bool areAllVerteciesOnTheSameSide(PlaneSide aSide, PlaneSide bSide, PlaneSide cSide, PlaneSide side) { bool result = false; if (aSide == side && bSide == side && cSide == side) { result = true; } else if (aSide == PlaneSide.ON_PLANE && bSide == side && cSide == side) { result = true; } else if (bSide == PlaneSide.ON_PLANE && cSide == side && aSide == side) { result = true; } else if (cSide == PlaneSide.ON_PLANE && aSide == side && bSide == side) { result = true; } else if (aSide == PlaneSide.ON_PLANE && bSide == PlaneSide.ON_PLANE && cSide == side) { result = true; } else if (bSide == PlaneSide.ON_PLANE && cSide == PlaneSide.ON_PLANE && aSide == side) { result = true; } else if (cSide == PlaneSide.ON_PLANE && aSide == PlaneSide.ON_PLANE && bSide == side) { result = true; } return(result); }
/** Checks how the axis aligned box intersects with the plane bounded volume */ private static Intersection intersect(PlaneBoundedVolume one, AxisAlignedBox two) { // Null box? if (two.IsNull) { return(Intersection.OUTSIDE); } // Infinite box? if (two.IsInfinite) { return(Intersection.INTERSECT); } // Get centre of the box Vector3 centre = two.Center; // Get the half-size of the box Vector3 halfSize = two.HalfSize; // For each plane, see if all points are on the negative side // If so, object is not visible. // If one or more are, it's partial. // If all aren't, full bool all_inside = true; foreach (Plane plane in one.planes) { PlaneSide side = plane.GetSide(centre, halfSize); if (side == one.outside) { return(Intersection.OUTSIDE); } if (side == PlaneSide.Both) { all_inside = false; } } if (all_inside) { return(Intersection.INSIDE); } else { return(Intersection.INTERSECT); } }
/// <summary> /// Gets the next node down in the tree, with the intention of locating the leaf containing the given point. /// </summary> /// <remarks> /// This method should only be called on a splitting node, i.e. where <see cref="Plugin_BSPSceneManager.BspSceneNode"/> returns false. /// Calling this method on a leaf node will throw an exception. /// </remarks> public BspNode GetNextNode(Vector3 point) { if (IsLeaf) { throw new Exception("This property is not valid on a leaf node."); } PlaneSide sd = GetSide(point); if (sd == PlaneSide.Negative) { return(this.BackNode); } else { return(this.FrontNode); } }
/// <summary> /// Return positive number if given point is on the same side of plane that /// plane normal is facing. Negative number otherwise. /// </summary> /// <param name="point"></param> /// <returns></returns> public PlaneSide GetSide(Vector3 point) { PlaneSide result = PlaneSide.INVALID; float dot = Vector3.Dot(Normal, Origin - point); if (dot == 0) { result = PlaneSide.ON_PLANE; } else if (dot > 0) { result = PlaneSide.POSITIVE; } else { result = PlaneSide.NEGATIVE; } return(result); }
/// <summary> /// Returns the visiblity of the box. /// </summary> /// <param name="bound"></param> /// <returns></returns> public Visibility GetVisibility(AxisAlignedBox bound) { if (bound.IsNull) { return(Visibility.None); } var center = bound.Center; var halfSize = bound.HalfSize; var allInside = true; for (int plane = 0; plane < 6; plane++) { // Skip far plane if infinite view frustum if (plane == (int)FrustumPlane.Far && Far == 0) { continue; } // This updates frustum planes and deals with cull frustum PlaneSide side = this.FrustumPlanes[plane].GetSide(center, halfSize); if (side == PlaneSide.Negative) { return(Visibility.None); } // We can't return now as the box could be later on the negative side of a plane. if (side == PlaneSide.Both) { allInside = false; } } if (allInside) { return(Visibility.Full); } else { return(Visibility.Partial); } }
public bool IsObjectVisible(AxisAlignedBox bound) { // Null boxes are always invisible if (bound.IsNull) { return(false); } // Infinite boxes are always visible if (bound.IsInfinite) { return(true); } // Get centre of the box Vector3 centre = bound.Center; // Get the half-size of the box Vector3 halfSize = bound.HalfSize; // Check originplane if told to if (this.mUseOriginPlane) { PlaneSide side = this.mOriginPlane.GetSide(centre, halfSize); if (side == PlaneSide.Negative) { return(false); } } // For each extra active culling plane, see if the entire aabb is on the negative side // If so, object is not visible foreach (PCPlane plane in this.mActiveCullingPlanes) { PlaneSide xside = plane.GetSide(centre, halfSize); if (xside == PlaneSide.Negative) { return(false); } } return(true); }
/** * Compute the two new triangles when only one point is on the plane. * It avoids to duplicate code. */ private void TriangleSliceWithOnePointOnPlane(Vector3 pA, Vector3 pB, Vector3 pC, Vector3 nA, Vector3 nB, Vector3 nC, Vector2 uvA, Vector2 uvB, Vector2 uvC, PlaneSide planeSideB) { float distance; if (_plane.Intersects(pB, pC, out distance)) { // If the plane cuts at B-C we have to compute two new triangles : A-B-BC and A-BC-C Vector3 pBC = Vector3.Lerp(pB, pC, distance); Vector3 nBC = Vector3.Lerp(nB, nC, distance); Vector3 uvBC = Vector3.Lerp(uvB, uvC, distance); Triangle newTriangleB = new Triangle(pA, pB, pBC); newTriangleB.SetNormals(nA, nB, nBC); newTriangleB.SetUVs(uvA, uvB, uvBC); Triangle newTriangleC = new Triangle(pA, pBC, pC); newTriangleC.SetNormals(nA, nBC, nC); newTriangleC.SetUVs(uvA, uvBC, uvC); if (planeSideB == PlaneSide.UP) { _upperMesh.Add(newTriangleB); _lowerMesh.Add(newTriangleC); } else { _upperMesh.Add(newTriangleC); _lowerMesh.Add(newTriangleB); } _cut.Add(pBC); } else { // If there isn't any intersection then we can push the whole triangle into whether the upper or the lower mesh Triangle newTriangle = new Triangle(pA, pB, pC); newTriangle.SetNormals(nA, nB, nC); newTriangle.SetUVs(uvA, uvB, uvC); if (planeSideB == PlaneSide.UP) { _upperMesh.Add(newTriangle); } else { _lowerMesh.Add(newTriangle); } } }
/// <summary> /// Constructor. /// </summary> /// <param name="outside">Side of the plane to be considered 'outside'.</param> public PlaneBoundedVolume(PlaneSide outside) { this.outside = outside; }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <param name="culledBy"> /// The <see cref="FrustumPlane"/> that the Portal is in. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible(Portal portal, out FrustumPlane culledBy) { culledBy = FrustumPlane.None; // if portal isn't open, it's not visible if (!portal.IsOpen) { return(false); } // check the extra frustum first if (!this.extraCullingFrustum.IsObjectVisible(portal)) { return(false); } // if portal is of type AABB or Sphere, then use simple bound check against planes if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB) { var aabb = new AxisAlignedBox(portal.getDerivedCorner(0), portal.getDerivedCorner(1)); return(base.IsObjectVisible(aabb, out culledBy)); } else if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE) { return(base.IsObjectVisible(portal.getDerivedSphere(), out culledBy)); } // check if the portal norm is facing the camera Vector3 cameraToPortal = portal.getDerivedCP() - DerivedPosition; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = cameraToPortal.Dot(portalDirection); if (dotProduct > 0) { // portal is faced away from camera return(false); } // check against regular frustum planes bool visible_flag; if (null != CullFrustum) { // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. for (int plane = 1; plane < 6; ++plane) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ((FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0) { continue; } // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = CullFrustum.FrustumPlanes[plane].GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view if (culledBy != FrustumPlane.None) { culledBy = (FrustumPlane)plane; } return(false); } } } else { // Make any pending updates to the calculated frustum planes UpdateFrustumPlanes(); // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. // BUGBUG: This can cause a false positive situation when a portal is // behind the camera but close. This could be fixed by having another // culling plane at the camera location with normal same as camera direction. for (int plane = 1; plane < 6; ++plane) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ((FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0) { continue; } // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = _planes[plane].GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view if (culledBy != FrustumPlane.None) { culledBy = (FrustumPlane)plane; } return(false); } } } // no plane culled all the portal points and the norm // was facing the camera, so this portal is visible return(true); }
/// <summary> /// Split faces so that no face is intercepted by a face of other object /// </summary> /// <param name="compareObject">the other object 3d used to make the split</param> public void SplitFaces(CsgObject3D compareObject, CancellationToken cancellationToken, Action <CsgFace, CsgFace> splitFaces = null, Action <List <CsgFace> > results = null) { Stack <CsgFace> newFacesFromSplitting = new Stack <CsgFace>(); int numFacesStart = this.Faces.Count; //if the objects bounds overlap... //for each object1 face... var bounds = compareObject.GetBound(); Faces.SearchBounds(bounds); foreach (var thisFaceIn in Faces.QueryResults) // put it in an array as we will be adding new faces to it { newFacesFromSplitting.Push(thisFaceIn); // make sure we process every face that we have added during splitting before moving on to the next face while (newFacesFromSplitting.Count > 0) { var faceToSplit = newFacesFromSplitting.Pop(); // stop processing if operation has been canceled cancellationToken.ThrowIfCancellationRequested(); //if object1 face bound and object2 bound overlap ... //for each object2 face... compareObject.Faces.SearchBounds(faceToSplit.GetBound()); foreach (var cuttingFace in compareObject.Faces.QueryResults) { //if object1 face bound and object2 face bound overlap... //PART I - DO TWO POLIGONS INTERSECT? //POSSIBLE RESULTS: INTERSECT, NOT_INTERSECT, COPLANAR //distance from the face1 vertices to the face2 plane double v1DistToCuttingFace = cuttingFace.DistanceFromPlane(faceToSplit.v1); double v2DistToCuttingFace = cuttingFace.DistanceFromPlane(faceToSplit.v2); double v3DistToCuttingFace = cuttingFace.DistanceFromPlane(faceToSplit.v3); //distances signs from the face1 vertices to the face2 plane PlaneSide sideFace1Vert1 = (v1DistToCuttingFace > EqualityTolerance ? PlaneSide.Front : (v1DistToCuttingFace < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); PlaneSide sideFace1Vert2 = (v2DistToCuttingFace > EqualityTolerance ? PlaneSide.Front : (v2DistToCuttingFace < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); PlaneSide sideFace1Vert3 = (v3DistToCuttingFace > EqualityTolerance ? PlaneSide.Front : (v3DistToCuttingFace < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); //if all the signs are zero, the planes are coplanar //if all the signs are positive or negative, the planes do not intersect //if the signs are not equal... if (!(sideFace1Vert1 == sideFace1Vert2 && sideFace1Vert2 == sideFace1Vert3)) { //distance from the face2 vertices to the face1 plane double faceToSplitTo1 = faceToSplit.DistanceFromPlane(cuttingFace.v1); double faceToSplitTo2 = faceToSplit.DistanceFromPlane(cuttingFace.v2); double faceToSplitTo3 = faceToSplit.DistanceFromPlane(cuttingFace.v3); //distances signs from the face2 vertices to the face1 plane PlaneSide signFace2Vert1 = (faceToSplitTo1 > EqualityTolerance ? PlaneSide.Front : (faceToSplitTo1 < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); PlaneSide signFace2Vert2 = (faceToSplitTo2 > EqualityTolerance ? PlaneSide.Front : (faceToSplitTo2 < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); PlaneSide signFace2Vert3 = (faceToSplitTo3 > EqualityTolerance ? PlaneSide.Front : (faceToSplitTo3 < -EqualityTolerance ? PlaneSide.Back : PlaneSide.On)); //if the signs are not equal... if (!(signFace2Vert1 == signFace2Vert2 && signFace2Vert2 == signFace2Vert3)) { var line = new Line(faceToSplit, cuttingFace); //intersection of the face1 and the plane of face2 var segment1 = new Segment(line, faceToSplit, sideFace1Vert1, sideFace1Vert2, sideFace1Vert3); //intersection of the face2 and the plane of face1 var segment2 = new Segment(line, cuttingFace, signFace2Vert1, signFace2Vert2, signFace2Vert3); //if the two segments intersect... if (segment1.Intersect(segment2)) { //PART II - SUBDIVIDING NON-COPLANAR POLYGONS Stack <CsgFace> facesFromSplit = new Stack <CsgFace>(); if (this.SplitFace(faceToSplit, segment1, segment2, facesFromSplit) && facesFromSplit.Count > 0 && !(facesFromSplit.Count == 1 && facesFromSplit.Peek().Equals(faceToSplit))) { foreach (var face in facesFromSplit) { newFacesFromSplitting.Push(face); } // send debugging information if registered splitFaces?.Invoke(faceToSplit, cuttingFace); results?.Invoke(facesFromSplit.ToList()); break; } } } } } } } }
/// <summary> /// Constructor. /// </summary> /// <param name="outside">Side of the plane to be considered 'outside'.</param> public PlaneBoundedVolume(PlaneSide outside) { this.outside = outside; }
/// <summary> /// Constructs a Segment based on elements obtained from the two planes relations /// </summary> /// <param name="line"></param> /// <param name="face"></param> /// <param name="side1"></param> /// <param name="side2"></param> /// <param name="side3"></param> public Segment(Line line, CsgFace face, PlaneSide side1, PlaneSide side2, PlaneSide side3) { this.line = line; index = 0; //VERTEX is an end if (side1 == PlaneSide.On) { SetVertex(face.v1); //other vertices on the same side - VERTEX-VERTEX VERTEX if (side2 == side3) { SetVertex(face.v1); } } //VERTEX is an end if (side2 == PlaneSide.On) { SetVertex(face.v2); //other vertices on the same side - VERTEX-VERTEX VERTEX if (side1 == side3) { SetVertex(face.v2); } } //VERTEX is an end if (side3 == PlaneSide.On) { SetVertex(face.v3); //other vertices on the same side - VERTEX-VERTEX VERTEX if (side1 == side2) { SetVertex(face.v3); } } //There are undefined ends - one or more edges cut the planes intersection line if (GetNumEndsSet() != 2) { //EDGE is an end if ((side1 == PlaneSide.Front && side2 == PlaneSide.Back) || (side1 == PlaneSide.Back && side2 == PlaneSide.Front)) { SetEdge(face.v1, face.v2); } //EDGE is an end if ((side2 == PlaneSide.Front && side3 == PlaneSide.Back) || (side2 == PlaneSide.Back && side3 == PlaneSide.Front)) { SetEdge(face.v2, face.v3); } //EDGE is an end if ((side3 == PlaneSide.Front && side1 == PlaneSide.Back) || (side3 == PlaneSide.Back && side1 == PlaneSide.Front)) { SetEdge(face.v3, face.v1); } } }
/** * Compute the three new triangles when the plane intersects with the triangle on two points. * Here the intersection points are AB and CA. * It avoids to duplicate code. */ private void TriangleSliceWithTwoIntersections(Vector3 pA, Vector3 pB, Vector3 pC, Vector3 nA, Vector3 nB, Vector3 nC, Vector2 uvA, Vector2 uvB, Vector2 uvC, float coeffAB, float coeffCA, PlaneSide planeSideA) { Vector3 pAB = Vector3.Lerp(pA, pB, coeffAB); Vector3 pCA = Vector3.Lerp(pC, pA, coeffCA); Vector3 nAB = Vector3.Lerp(nA, nB, coeffAB); Vector3 nCA = Vector3.Lerp(nC, nA, coeffCA); Vector2 uvAB = Vector2.Lerp(uvA, uvB, coeffAB); Vector2 uvCA = Vector2.Lerp(uvC, uvA, coeffCA); Triangle triangle_A_AB_CA = new Triangle(pA, pAB, pCA); triangle_A_AB_CA.SetNormals(nA, nAB, nCA); triangle_A_AB_CA.SetUVs(uvA, uvAB, uvCA); Triangle triangle_B_CA_AB = new Triangle(pB, pCA, pAB); triangle_B_CA_AB.SetNormals(nB, nCA, nAB); triangle_B_CA_AB.SetUVs(uvB, uvCA, uvAB); Triangle triangle_C_CA_B = new Triangle(pC, pCA, pB); triangle_C_CA_B.SetNormals(nC, nCA, nB); triangle_C_CA_B.SetUVs(uvC, uvCA, uvB); if (planeSideA == PlaneSide.UP) { _upperMesh.Add(triangle_A_AB_CA); _lowerMesh.Add(triangle_B_CA_AB); _lowerMesh.Add(triangle_C_CA_B); } else { _upperMesh.Add(triangle_B_CA_AB); _upperMesh.Add(triangle_C_CA_B); _lowerMesh.Add(triangle_A_AB_CA); } }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible(Portal portal) { // if portal isn't open, it's not visible if (!portal.IsOpen) { return(false); } // if the frustum has no planes, just return true if (this.mActiveCullingPlanes.Count == 0) { return(true); } // check if this portal is already in the list of active culling planes (avoid // infinite recursion case) foreach (PCPlane plane in this.mActiveCullingPlanes) { if (plane.Portal == portal) { return(false); } } // if portal is of type AABB or Sphere, then use simple bound check against planes if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB) { var aabb = new AxisAlignedBox(); aabb.SetExtents(portal.getDerivedCorner(0), portal.getDerivedCorner(1)); return(IsObjectVisible(aabb)); } else if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE) { return(IsObjectVisible(portal.getDerivedSphere())); } // check if the portal norm is facing the frustum Vector3 frustumToPortal = portal.getDerivedCP() - this.mOrigin; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = frustumToPortal.Dot(portalDirection); if (dotProduct > 0) { // portal is faced away from Frustum return(false); } // check against frustum culling planes bool visible_flag; // Check originPlane if told to if (this.mUseOriginPlane) { // set the visible flag to false visible_flag = false; // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = this.mOriginPlane.GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then the origin plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view return(false); } } // For each active culling plane, see if all portal points are on the negative // side. If so, the portal is not visible foreach (PCPlane plane in this.mActiveCullingPlanes) { visible_flag = false; // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = plane.GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view return(false); } } // no plane culled all the portal points and the norm // was facing the frustum, so this portal is visible return(true); }
public static CutResult Cut(int [] meshTriangles, Vector3 [] meshVertives, Vector3 [] meshNormals, Vector2 [] meshUVs, Plane plane) { MeshData aMeshData = new MeshData(); MeshData bMeshData = new MeshData(); List <Vector3> sliceEdgeVertecies = new List <Vector3> (); for (int i = 0; i < meshTriangles.Length; i += 3) { int aVertexIndex = meshTriangles [i]; int bVertexIndex = meshTriangles [i + 1]; int cVertexIndex = meshTriangles [i + 2]; PlaneSide aSide = plane.GetSide(meshVertives [aVertexIndex]); PlaneSide bSide = plane.GetSide(meshVertives [bVertexIndex]); PlaneSide cSide = plane.GetSide(meshVertives [cVertexIndex]); if (areAllVerteciesOnTheSameSide(aSide, bSide, cSide, PlaneSide.POSITIVE)) { bool addUV = meshUVs.Length > 0; aMeshData.Add(meshVertives [aVertexIndex], meshNormals [aVertexIndex], addUV ? meshUVs [aVertexIndex] : Vector2.zero); aMeshData.Add(meshVertives [bVertexIndex], meshNormals [bVertexIndex], addUV ? meshUVs [bVertexIndex] : Vector2.zero); aMeshData.Add(meshVertives [cVertexIndex], meshNormals [cVertexIndex], addUV ? meshUVs [cVertexIndex] : Vector2.zero); if (aSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [aVertexIndex]); } if (bSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [bVertexIndex]); } if (cSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [cVertexIndex]); } } else if (areAllVerteciesOnTheSameSide(aSide, bSide, cSide, PlaneSide.NEGATIVE)) { bool addUV = meshUVs.Length > 0; bMeshData.Add(meshVertives [aVertexIndex], meshNormals [aVertexIndex], addUV ? meshUVs [aVertexIndex] : Vector2.zero); bMeshData.Add(meshVertives [bVertexIndex], meshNormals [bVertexIndex], addUV ? meshUVs [bVertexIndex] : Vector2.zero); bMeshData.Add(meshVertives [cVertexIndex], meshNormals [cVertexIndex], addUV ? meshUVs [cVertexIndex] : Vector2.zero); if (aSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [aVertexIndex]); } if (bSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [bVertexIndex]); } if (cSide == PlaneSide.ON_PLANE) { sliceEdgeVertecies.Add(meshVertives [cVertexIndex]); } } else if (aSide == PlaneSide.POSITIVE && bSide == PlaneSide.NEGATIVE && cSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, aVertexIndex, bVertexIndex, cVertexIndex, sliceEdgeVertecies); } else if (bSide == PlaneSide.POSITIVE && cSide == PlaneSide.NEGATIVE && aSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, bVertexIndex, cVertexIndex, aVertexIndex, sliceEdgeVertecies); } else if (cSide == PlaneSide.POSITIVE && aSide == PlaneSide.NEGATIVE && bSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, cVertexIndex, aVertexIndex, bVertexIndex, sliceEdgeVertecies); } else if (aSide == PlaneSide.NEGATIVE && bSide == PlaneSide.POSITIVE && cSide == PlaneSide.POSITIVE) { sliceTriangleWhenTwoVerticesAreOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, bVertexIndex, cVertexIndex, aVertexIndex, sliceEdgeVertecies); } else if (bSide == PlaneSide.NEGATIVE && cSide == PlaneSide.POSITIVE && aSide == PlaneSide.POSITIVE) { sliceTriangleWhenTwoVerticesAreOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, cVertexIndex, aVertexIndex, bVertexIndex, sliceEdgeVertecies); } else if (cSide == PlaneSide.NEGATIVE && aSide == PlaneSide.POSITIVE && bSide == PlaneSide.POSITIVE) { sliceTriangleWhenTwoVerticesAreOnAPositiveHalfSpace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, aVertexIndex, bVertexIndex, cVertexIndex, sliceEdgeVertecies); } else if (aSide == PlaneSide.ON_PLANE && bSide == PlaneSide.POSITIVE && cSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnAPositiveHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, aVertexIndex, bVertexIndex, cVertexIndex, sliceEdgeVertecies); } else if (aSide == PlaneSide.ON_PLANE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnANegativeHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, aVertexIndex, bVertexIndex, cVertexIndex, sliceEdgeVertecies); } else if (bSide == PlaneSide.ON_PLANE && cSide == PlaneSide.POSITIVE && aSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnAPositiveHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, bVertexIndex, cVertexIndex, aVertexIndex, sliceEdgeVertecies); } else if (bSide == PlaneSide.ON_PLANE && cSide == PlaneSide.NEGATIVE && aSide == PlaneSide.POSITIVE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnANegativeHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, bVertexIndex, cVertexIndex, aVertexIndex, sliceEdgeVertecies); } else if (cSide == PlaneSide.ON_PLANE && aSide == PlaneSide.POSITIVE && bSide == PlaneSide.NEGATIVE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnAPositiveHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, cVertexIndex, aVertexIndex, bVertexIndex, sliceEdgeVertecies); } else if (cSide == PlaneSide.ON_PLANE && aSide == PlaneSide.NEGATIVE && bSide == PlaneSide.POSITIVE) { sliceTriangleWhenOneVertexIsOnAPlaneAndNextIsOnANegativeHalfspace(aMeshData, bMeshData, meshVertives, meshNormals, meshUVs, plane, cVertexIndex, aVertexIndex, bVertexIndex, sliceEdgeVertecies); } } return(new CutResult(aMeshData, bMeshData, sliceEdgeVertecies, plane)); }
//----------------------------------------------------------------------- public static bool Intersects(Ray ray, List <Plane> planeList, bool normalIsOutside, out float distance) { distance = float.NegativeInfinity; bool allInside = true; bool ret = false; float retDist = 0.0f; bool end = false; float endDist = 0.0f; // derive side // NB we don't pass directly since that would require Plane::Side in // interface, which results in recursive includes since Math is so fundamental PlaneSide outside = normalIsOutside ? PlaneSide.POSITIVE_SIDE : PlaneSide.NEGATIVE_SIDE; foreach (Plane plane in planeList) { // is origin outside? if (GetSide(plane, ray.origin) == outside) { allInside = false; // Test single plane if (Intersects(ray, plane, out distance)) { // Ok, we intersected ret = true; // Use the most distant result since convex volume retDist = Mathf.Max(retDist, distance); } else { distance = 0.0f; return(false); } } else { if (Intersects(ray, plane, out distance)) { if (!end) { end = true; endDist = distance; } else { endDist = Mathf.Min(distance, endDist); } } } } if (allInside) { // Intersecting at 0 distance since inside the volume! ret = true; retDist = 0.0f; distance = retDist; return(ret); } if (end) { if (endDist < retDist) { ret = false; distance = float.NegativeInfinity; return(ret); } } return(ret); }