public void AddNewPolygonList(List<TransparentPolygonReference> p, Transform transform, Frustum frustum, Camera cam) { foreach (var pp in p) { var transformed = new Polygon(); transformed.Vertices.Resize(pp.Polygon.Vertices.Count, () => new Vertex()); transformed.Transform(pp.Polygon, transform); transformed.DoubleSide = pp.Polygon.DoubleSide; if(frustum.IsPolyVisible(transformed, cam)) { addPolygon(ref _root, new BSPFaceRef(transform, pp), transformed); } } }
/// <summary> /// Check polygon visibility through the portal. /// </summary> public bool IsPolyVisible(Polygon p, Camera cam) { if (!p.DoubleSide && p.Plane.Distance(cam.Position) < 0) return false; // Direction from the camera position to an arbitrary vertex frustum StaticFuncs.Assert(Vertices.Any()); var dir = Vertices[0] - cam.Position; var lambda = 0.0f; // Polygon fits whole frustum (shouldn't happen, but we check anyway) if(p.RayIntersect(dir, cam.Position, ref lambda)) { return true; } // Generate queue order var nextPlaneIdx = 0; // 3 neighboring clipping planes var currentPlane = Planes.Last(); var prevPlane = Planes[Planes.Count - 2]; // in case no intersection var ins = true; // iterate through all the planes of this frustum for (var i = 0; i < Vertices.Count; i++) { var nextPlane = Planes[nextPlaneIdx]; // Queue vertices for testing var prevVertex = p.Vertices.Last(); // signed distance from the current point to the previous plane var dist0 = currentPlane.Distance(prevVertex.Position); var outs = true; // iterate through all the vertices of the polygon foreach (var currentVertex in p.Vertices) { var dist1 = currentPlane.Distance(currentVertex.Position); // the split point in the plane if(Math.Abs(dist0) < SPLIT_EPSILON) { if(prevPlane.Distance(prevVertex.Position) > -SPLIT_EPSILON && nextPlane.Distance(prevVertex.Position) > -SPLIT_EPSILON && Normal.Distance(prevVertex.Position) > -SPLIT_EPSILON) { // Frustum-vertex intersection test is passed return true; } } // vertices from different sides of the plane (or on it) if(dist0 * dist1 < 0 && Math.Abs(dist1) >= SPLIT_EPSILON) { // vector connecting vertices dir = currentVertex.Position - prevVertex.Position; // We are looking for the point of intersection var T = currentPlane.RayIntersect(prevVertex.Position, dir); if(prevPlane.Distance(T) > -SPLIT_EPSILON && nextPlane.Distance(T) > -SPLIT_EPSILON) { // Frustum-ray intersection test is passed return true; } } // point is outside if(dist1 < -SPLIT_EPSILON) { ins = false; } else { outs = false; } // We moved all the vertices of the polygon prevVertex = currentVertex; // We moved all distances dist0 = dist1; // finished with all polygon vertices } if(outs) { // all points are outside of the current plane - definitely exit return false; } // We moved all the clipping planes prevPlane = currentPlane; currentPlane = nextPlane; nextPlaneIdx++; // finished with all planes of this frustum } if(ins) { // all the vertices are inside - test is passed return true; } return false; }
public void GenClipPlanes(Camera cam) { if (Vertices.Count == 0) return; Planes.Resize(Vertices.Count, () => new Plane()); var curr_v = Vertices.Last(); var prev_v = Vertices[Vertices.Count - 2]; for (var i = 0; i < Vertices.Count; i++) { var V1 = prev_v - cam.Position; // POV-vertx vector var V2 = prev_v - curr_v; // vector connecting neighbor vertices V1.Normalize(); V2.Normalize(); Planes[i].Assign(V1, V2, curr_v); prev_v = curr_v; curr_v = Vertices[i]; } }
public bool IsOBBVisible(OBB obb, Camera cam) { var ins = true; foreach (var p in obb.Polygons) { var t = p.Plane.Distance(cam.Position); if(t > 0.0 && IsPolyVisible(p, cam)) { return true; } if(ins && t > 0) { ins = false; } } return ins; }
public bool IsAABBVisible(Vector3 bbMin, Vector3 bbMax, Camera cam) { var poly = new Polygon(); poly.Vertices = new List<Vertex>(4); var ins = true; // X AXIS if(cam.Position.X < bbMin.X) { poly.Plane.Normal.X = -1.0f; poly.Plane.Dot = -bbMin.X; poly.Vertices[0].Position[0] = bbMin[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMin[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if(IsPolyVisible(poly, cam)) { return true; } ins = false; } else if(cam.Position.X > bbMax.X) { poly.Plane.Normal.X = 1.0f; poly.Plane.Dot = bbMax.X; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMax[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMax[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } // Y AXIS poly.Plane.Normal.X = 0; poly.Plane.Normal.Z = 0; if (cam.Position.Y < bbMin.Y) { poly.Plane.Normal.Y = -1.0f; poly.Plane.Dot = -bbMin.Y; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMin[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } else if (cam.Position.Y > bbMax.Y) { poly.Plane.Normal.Y = 1.0f; poly.Plane.Dot = -bbMax.Y; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMax[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMax[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } // Z AXIS poly.Plane.Normal.X = 0; poly.Plane.Normal.Y = 0; if (cam.Position.Z < bbMin.Z) { poly.Plane.Normal.Z = -1.0f; poly.Plane.Dot = -bbMin.Z; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMin[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMax[1]; poly.Vertices[1].Position[2] = bbMin[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } else if (cam.Position.Z > bbMax.Z) { poly.Plane.Normal.Z = 1.0f; poly.Plane.Dot = -bbMax.Z; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMax[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMax[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMax[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } return ins; }
public static void Cam_FollowEntity(Camera cam, Entity ent, float dx, float dz) { var cameraFrom = new Transform(); var cameraTo = new Transform(); // Reset to initial cameraFrom.SetIdentity(); cameraTo.SetIdentity(); var cb = ent.CallbackForCamera(); var camPos = cam.Position; // Basic camera override, completely placeholder until a system classic-like is created if (!ControlStates.MouseLook) // If mouse look is off { var currentAngle = CamAngles.X * RadPerDeg; // Current is the current cam angle var targetAngle = ent.Angles.X * RadPerDeg; // Target is the target angle which is the entity's angle itself var rotSpeed = 2.0f; // Speed of rotation //@FIXME // If Lara is in a specific state we want to rotate -75 deg or +75 deg depending on camera collision if(ent.Bf.Animations.LastState == TR_STATE.LaraReach) { if(cam.TargetDir == TR_CAM_TARG.Back) { var camPos2 = camPos; cameraFrom.Origin = camPos2; camPos2.X += (float) (Math.Sin((ent.Angles.X - 90.0f) * RadPerDeg) * ControlStates.CamDistance); camPos2.Y -= (float) (Math.Cos((ent.Angles.X - 90.0f) * RadPerDeg) * ControlStates.CamDistance); cameraTo.Origin = camPos2; // If collided we want to go right otherwise stay left if(Cam_HasHit(cb, cameraFrom, cameraTo)) { camPos2 = camPos; cameraFrom.Origin = camPos2; camPos2.X += (float)(Math.Sin((ent.Angles.X + 90.0f) * RadPerDeg) * ControlStates.CamDistance); camPos2.Y -= (float)(Math.Cos((ent.Angles.X + 90.0f) * RadPerDeg) * ControlStates.CamDistance); cameraTo.Origin = camPos2; // If collided we want to go to back else right cam.TargetDir = Cam_HasHit(cb, cameraFrom, cameraTo) ? TR_CAM_TARG.Back : TR_CAM_TARG.Right; } else { cam.TargetDir = TR_CAM_TARG.Left; } } } else if(ent.Bf.Animations.LastState == TR_STATE.LaraJumpBack) { cam.TargetDir = TR_CAM_TARG.Front; } // ReSharper disable once RedundantCheckBeforeAssignment else if(cam.TargetDir != TR_CAM_TARG.Back) { cam.TargetDir = TR_CAM_TARG.Back; // Reset to back } // If target mis-matches current we need to update the camera's angle to reach target! if (currentAngle != targetAngle) { switch (cam.TargetDir) { case TR_CAM_TARG.Back: targetAngle = ent.Angles.X * RadPerDeg; break; case TR_CAM_TARG.Front: targetAngle = (ent.Angles.X - 180.0f) * RadPerDeg; break; case TR_CAM_TARG.Left: targetAngle = (ent.Angles.X - 75.0f) * RadPerDeg; break; case TR_CAM_TARG.Right: targetAngle = (ent.Angles.X + 75.0f) * RadPerDeg; break; default: targetAngle = ent.Angles.X * RadPerDeg; // Same as TR_CAM_TARG_BACK (default pos) break; } var dAngle = CamAngles.X - targetAngle; if (dAngle > Rad90) { dAngle -= 1 * RadPerDeg; } else { dAngle += 1 * RadPerDeg; } CamAngles.X = (CamAngles.X + Helper.Atan2((float) Math.Sin(currentAngle - dAngle), (float) Math.Cos(currentAngle + dAngle)) * EngineFrameTime * rotSpeed) % Rad360; // Update camera's angle } } camPos = ent.CamPosForFollowing(dz); // Code to manage screen shaking effects if(Renderer.Camera.ShakeTime > 0.0f && Renderer.Camera.ShakeValue > 0.0f) { camPos = camPos.AddF((Helper.CPPRand() % Math.Abs(Renderer.Camera.ShakeValue) - Renderer.Camera.ShakeValue / 2.0f) * Renderer.Camera.ShakeTime); Renderer.Camera.ShakeTime = Renderer.Camera.ShakeTime < 0.0f ? 0.0f : Renderer.Camera.ShakeTime - EngineFrameTime; } cameraFrom.Origin = camPos; camPos.Z += dz; cameraTo.Origin = camPos; if(Cam_HasHit(cb, cameraFrom, cameraTo)) { Helper.SetInterpolate3(out camPos, cameraFrom.Origin, cameraTo.Origin, cb.ClosestHitFraction); camPos += (cb.HitNormalWorld * 2.0f).ToOpenTK(); } if(dx != 0.0f) { cameraFrom.Origin = camPos; camPos += dx * cam.RightDirection; cameraTo.Origin = camPos; if (Cam_HasHit(cb, cameraFrom, cameraTo)) { Helper.SetInterpolate3(out camPos, cameraFrom.Origin, cameraTo.Origin, cb.ClosestHitFraction); camPos += (cb.HitNormalWorld * 2.0f).ToOpenTK(); } cameraFrom.Origin = camPos; var cosAy = Math.Cos(CamAngles.Y); var camDx = Math.Sin(CamAngles.X) * cosAy; var camDy = -Math.Cos(CamAngles.X) * cosAy; var camDz = -Math.Sin(CamAngles.Y); camPos.X += (float) (camDx * ControlStates.CamDistance); camPos.Y += (float) (camDy * ControlStates.CamDistance); camPos.Z += (float) (camDz * ControlStates.CamDistance); cameraTo.Origin = camPos; if (Cam_HasHit(cb, cameraFrom, cameraTo)) { Helper.SetInterpolate3(out camPos, cameraFrom.Origin, cameraTo.Origin, cb.ClosestHitFraction); camPos += (cb.HitNormalWorld * 2.0f).ToOpenTK(); } } // Update cam pos cam.Position = camPos; // Modify cam pos for quicksand rooms cam.CurrentRoom = Room.FindPosCogerrence(cam.Position - new Vector3(0, 0, 128), cam.CurrentRoom); if(cam.CurrentRoom != null && cam.CurrentRoom.Flags.HasFlagUns(RoomFlag.Quicksand)) { var pos = cam.Position; pos.Z = cam.CurrentRoom.BBMax.Z + 2.0f * 64.0f; cam.Position = pos; } cam.SetRotation(CamAngles); cam.CurrentRoom = Room.FindPosCogerrence(cam.Position, cam.CurrentRoom); }
public bool IsVisibleInRoom(Room room, Camera cam) { var polys = Polygons; if (room.Frustum.Count == 0) { var ins = true; foreach (var polygon in polys) { var t = polygon.Plane.Distance(EngineCamera.Position); if (t > 0 && EngineCamera.Frustum.IsPolyVisible(polygon, cam)) { return true; } if (ins && t > 0) { ins = false; } } return ins; } return room.Frustum.Any(frustum => (from polygon in polys let t = polygon.Plane.Distance(cam.Position) where t > 0 && frustum.IsPolyVisible(polygon, cam) select polygon).Any()); }