/// <summary> /// Creates an <see cref="OrientedBoundingBox">OrientedBoundingBox</see> from a <see cref="BoundingBox">BoundingBox</see>. /// </summary> /// <param name="box">A <see cref="BoundingBox">BoundingBox</see> to create the <see cref="OrientedBoundingBox">OrientedBoundingBox</see> from</param> /// <returns>The generated <see cref="OrientedBoundingBox">OrientedBoundingBox</see></returns> public static OrientedBoundingBox FromAABB(BoundingBox box) { var center = BoundingVolumesExtensions.GetCenter(box); var extents = BoundingVolumesExtensions.GetExtents(box); return(new OrientedBoundingBox(center, extents)); }
/// <inheritdoc /> public override void Draw(GameTime gameTime) { // Set the DiffuseColor to white to draw this Robot RobotEffect.DiffuseColor = Color.White.ToVector3(); Robot.Draw(RobotWorld, Camera.View, Camera.Projection); // Set the DiffuseColor to red to draw this Robot RobotEffect.DiffuseColor = Color.Red.ToVector3(); Robot.Draw(RobotTwoWorld, Camera.View, Camera.Projection); // Draw the Chair Chair.Draw(ChairWorld, Camera.View, Camera.Projection); // Draw the Tank Tank.Draw(TankWorld, Camera.View, Camera.Projection); // Draw bounding volumes Game.Gizmos.DrawCube(BoundingVolumesExtensions.GetCenter(RobotBox), BoundingVolumesExtensions.GetExtents(RobotBox) * 2f, Color.Yellow); Game.Gizmos.DrawCube(ChairOBBWorld, TouchingChair ? Color.Orange : Color.Green); Game.Gizmos.DrawSphere(_tankSphere.Center, _tankSphere.Radius * Vector3.One, TouchingTank ? Color.Orange : Color.Purple); Game.Gizmos.DrawCylinder(RobotTwoCylinder.Transform, TouchingOtherRobot ? Color.Orange : Color.White); base.Draw(gameTime); }
/// <summary> /// Apply horizontal movement, detecting and solving collisions with sliding /// </summary> /// <param name="scaledVelocity">The current velocity scaled by deltaTime</param> private void SolveHorizontalMovementSliding(Vector3 scaledVelocity) { // Has horizontal movement? if (Vector3.Dot(scaledVelocity, new Vector3(1f, 0f, 1f)) == 0f) { return; } // Start by moving the Cylinder horizontally RobotCylinder.Center += new Vector3(scaledVelocity.X, 0f, scaledVelocity.Z); // Check intersection for every collider for (var index = 0; index < Colliders.Length; index++) { if (!RobotCylinder.Intersects(Colliders[index]).Equals(BoxCylinderIntersection.Intersecting)) { continue; } // Get the intersected collider and its center var collider = Colliders[index]; var colliderCenter = BoundingVolumesExtensions.GetCenter(collider); // The Robot collided with this thing // Is it a step? Can the Robot climb it? bool stepClimbed = SolveStepCollision(collider, index); // If the Robot collided with a step and climbed it, stop here // Else go on if (stepClimbed) { return; } // Get the cylinder center at the same Y-level as the box var sameLevelCenter = RobotCylinder.Center; sameLevelCenter.Y = colliderCenter.Y; // Find the closest horizontal point from the box var closestPoint = BoundingVolumesExtensions.ClosestPoint(collider, sameLevelCenter); // Calculate our normal vector from the "Same Level Center" of the cylinder to the closest point // This happens in a 2D fashion as we are on the same Y-Plane var normalVector = sameLevelCenter - closestPoint; var normalVectorLength = normalVector.Length(); // Our penetration is the difference between the radius of the Cylinder and the Normal Vector // For precission problems, we push the cylinder with a small increment to prevent re-colliding into the geometry var penetration = RobotCylinder.Radius - normalVector.Length() + EPSILON; // Push the center out of the box // Normalize our Normal Vector using its length first RobotCylinder.Center += (normalVector / normalVectorLength * penetration); } }
/// <inheritdoc /> public override void Draw(GameTime gameTime) { // Calculate the ViewProjection matrix var viewProjection = Camera.View * Camera.Projection; // Draw the Robot Robot.Draw(RobotWorld, Camera.View, Camera.Projection); // Set the WorldViewProjection and Texture for the Floor and draw it TilingEffect.Parameters["WorldViewProjection"].SetValue(FloorWorld * viewProjection); TilingEffect.Parameters["Texture"].SetValue(FloorTexture); Quad.Draw(TilingEffect); // Draw each Wall // First, set the Wall Texture TilingEffect.Parameters["Texture"].SetValue(WallTexture); for (int index = 0; index < WallWorldMatrices.Length - 1; index++) { // Set the WorldViewProjection matrix for each Wall TilingEffect.Parameters["WorldViewProjection"].SetValue(WallWorldMatrices[index] * viewProjection); // Draw the Wall Quad.Draw(TilingEffect); } // Draw the traversing Wall // For this, disable Back-Face Culling as we want to draw both sides of the Quad // Save the past RasterizerState var rasterizerState = GraphicsDevice.RasterizerState; // Use a RasterizerState which has Back-Face Culling disabled GraphicsDevice.RasterizerState = RasterizerState.CullNone; // Set the WorldViewProjection matrix and draw the Wall TilingEffect.Parameters["WorldViewProjection"].SetValue(WallWorldMatrices[WallWorldMatrices.Length - 1] * viewProjection); Quad.Draw(TilingEffect); // Restore the old RasterizerState GraphicsDevice.RasterizerState = rasterizerState; // Draw Gizmos for Bounding Boxes and Robot Cylinder for (int index = 0; index < WallBoxes.Length; index++) { var box = WallBoxes[index]; var center = BoundingVolumesExtensions.GetCenter(box); var extents = BoundingVolumesExtensions.GetExtents(box); Game.Gizmos.DrawCube(center, extents * 2f, Color.Red); } Game.Gizmos.DrawCylinder(RobotCylinder.Transform, Color.Yellow); base.Draw(gameTime); }
/// <inheritdoc /> protected override void LoadContent() { // Load the models Robot = Game.Content.Load <Model>(ContentFolder3D + "tgcito-classic/tgcito-classic"); // Enable default lighting for the Robot foreach (var mesh in Robot.Meshes) { ((BasicEffect)mesh.Effects.FirstOrDefault())?.EnableDefaultLighting(); } // Create a BasicEffect to draw the Box BoxesEffect = new BasicEffect(GraphicsDevice); BoxesEffect.TextureEnabled = true; // Load our Tiling Effect TilingEffect = Game.Content.Load <Effect>(ContentFolderEffects + "TextureTiling"); TilingEffect.Parameters["Tiling"].SetValue(new Vector2(10f, 10f)); // Load Textures StonesTexture = Game.Content.Load <Texture2D>(ContentFolderTextures + "stones"); WoodenTexture = Game.Content.Load <Texture2D>(ContentFolderTextures + "wood/caja-madera-1"); CobbleTexture = Game.Content.Load <Texture2D>(ContentFolderTextures + "floor/adoquin"); // Create our Quad (to draw the Floor) //Quad = new QuadPrimitive(GraphicsDevice); // Create our Box Model BoxPrimitive = new BoxPrimitive(GraphicsDevice, Vector3.One, WoodenTexture); // Calculate the height of the Model of the Robot // Create a Bounding Box from it, then subtract the max and min Y to get the height // Use the height to set the Position of the robot // (it is half the height, multiplied by its scale in Y -RobotScale.M22-) var extents = BoundingVolumesExtensions.CreateAABBFrom(Robot); var height = extents.Max.Y - extents.Min.Y; RobotPosition += height * 0.5f * Vector3.Up * RobotScale.M22; // Assign the center of the Cylinder as the Robot Position RobotCylinder.Center = RobotPosition; // Update our World Matrix to draw the Robot RobotWorld = RobotScale * Matrix.CreateTranslation(RobotPosition); // Update our camera to set its initial values UpdateCamera(); base.LoadContent(); }
/// <inheritdoc /> protected override void LoadContent() { // Load the models Robot = Game.Content.Load <Model>(ContentFolder3D + "tgcito-classic/tgcito-classic"); Chair = Game.Content.Load <Model>(ContentFolder3D + "chair/chair"); Tank = Game.Content.Load <Model>(ContentFolder3D + "tank/tank"); // Enable the default lighting for the models EnableDefaultLighting(Robot); EnableDefaultLighting(Tank); EnableDefaultLighting(Chair); // Save our RobotEffect RobotEffect = ((BasicEffect)Robot.Meshes.FirstOrDefault()?.Effects.FirstOrDefault()); // Create an AABB // This gets an AABB with the bounds of the robot model RobotBox = BoundingVolumesExtensions.CreateAABBFrom(Robot); // Scale it down half-size as the model is scaled down as well RobotBox = BoundingVolumesExtensions.Scale(RobotBox, 0.3f); // Create an OBB for a model // First, get an AABB from the model var temporaryCubeAABB = BoundingVolumesExtensions.CreateAABBFrom(Chair); // Scale it to match the model's transform temporaryCubeAABB = BoundingVolumesExtensions.Scale(temporaryCubeAABB, 0.5f); // Create an Oriented Bounding Box from the AABB ChairBox = OrientedBoundingBox.FromAABB(temporaryCubeAABB); // Move the center ChairBox.Center = Vector3.UnitX * 50f; // Then set its orientation! ChairBox.Orientation = Matrix.CreateRotationY(ChairAngle); // Create a Bounding Sphere for a model _tankSphere = BoundingVolumesExtensions.CreateSphereFrom(Tank); _tankSphere.Center = TankPosition; _tankSphere.Radius *= 3f; // Set depth to default GraphicsDevice.DepthStencilState = DepthStencilState.Default; base.LoadContent(); }
public Laser(Vector3 pos, Matrix rotation, Matrix srt, Vector3 fd, Vector3 c) { Position = pos; SRT = srt; FrontDirection = fd; Color = c; Game = TGCGame.Instance; //BoundingCylinder = new BoundingCylinder(Position, 10f, 20f); //BoundingCylinder.Rotation = rotation; var tempAABB = BoundingVolumesExtensions.CreateAABBFrom(Game.Drawer.LaserModel); tempAABB = BoundingVolumesExtensions.Scale(tempAABB, 0.3f); BoundingBox = OrientedBoundingBox.FromAABB(tempAABB); BoundingBox.Center = pos; BoundingBox.Orientation = rotation; }
/// <summary> /// Solves the intersection between the Robot and a collider. /// </summary> /// <param name="collider">The collider the Robot intersected with</param> /// <param name="colliderIndex">The index of the collider in the collider array the Robot intersected with</param> /// <returns>True if the collider was a step and it was climbed, False otherwise</returns> private bool SolveStepCollision(BoundingBox collider, int colliderIndex) { // Get the collider properties to check if it's a step // Also, to calculate penetration var extents = BoundingVolumesExtensions.GetExtents(collider); var colliderCenter = BoundingVolumesExtensions.GetCenter(collider); // Is this collider a step? // If not, exit if (extents.Y >= 6f) { return(false); } // Is the base of the cylinder close to the step top? // If not, exit var distanceToTop = MathF.Abs((RobotCylinder.Center.Y - RobotCylinder.HalfHeight) - (colliderCenter.Y + extents.Y)); if (distanceToTop >= 12f) { return(false); } // We want to climb the step // It is climbable if we can reposition our cylinder in a way that // it doesn't collide with anything else var pastPosition = RobotCylinder.Center; RobotCylinder.Center += Vector3.Up * distanceToTop; for (int index = 0; index < Colliders.Length; index++) { if (index != colliderIndex && RobotCylinder.Intersects(Colliders[index]).Equals(BoxCylinderIntersection.Intersecting)) { // We found a case in which the cylinder // intersects with other colliders, so the climb is not possible RobotCylinder.Center = pastPosition; return(false); } } // If we got here the climb was possible // (And the Robot position was already updated) return(true); }
/// <inheritdoc /> protected override void LoadContent() { // Load the Robot Model and enable default lighting Robot = Game.Content.Load <Model>(ContentFolder3D + "tgcito-classic/tgcito-classic"); ((BasicEffect)Robot.Meshes.FirstOrDefault()?.Effects.FirstOrDefault())?.EnableDefaultLighting(); // Create AABBs // This gets an AABB with the bounds of the robot model RobotOneBox = BoundingVolumesExtensions.CreateAABBFrom(Robot); // This moves the min and max points to the world position of each robot (one and two) RobotTwoBox = new BoundingBox(RobotOneBox.Min + RobotTwoPosition, RobotOneBox.Max + RobotTwoPosition); RobotOneBox = new BoundingBox(RobotOneBox.Min + RobotOnePosition, RobotOneBox.Max + RobotOnePosition); // Set depth to default GraphicsDevice.DepthStencilState = DepthStencilState.Default; base.LoadContent(); }
public void Update(float elapsedTime, MyCamera camera) { Time = elapsedTime; // cuanto tengo que rotar (roll), dependiendo de que tanto giro la camara //TurnDelta = camera.delta; //updateRoll(); //actualizo todos los parametros importantes del xwing updateSRT(camera); updateFireRate(); updateSlideDamage(); updateEnergyRegen(elapsedTime); if (boundingSphere == null) { boundingSphere = new BoundingSphere(Position, 15f); } else { boundingSphere.Center = Position; } var min = Position - new Vector3(10, 5, 10); var max = Position + new Vector3(10, 5, 10); if (BB == null) { //BB = BoundingVolumesExtensions.CreateAABBFrom(Model); //BB = BoundingVolumesExtensions.Scale(BB, 0.026f); BB = new BoundingBox(min, max); } BB.Min = min; BB.Max = max; if (OBB == null) { var temporaryCubeAABB = BoundingVolumesExtensions.CreateAABBFrom(Game.Drawer.XwingModel); // Scale it to match the model's transform temporaryCubeAABB = BoundingVolumesExtensions.Scale(temporaryCubeAABB, 0.026f); // Create an Oriented Bounding Box from the AABB OBB = OrientedBoundingBox.FromAABB(temporaryCubeAABB); var halfW = BoundingVolumesExtensions.Scale(temporaryCubeAABB, new Vector3(0.5f, 0.3f, 1f)); var halfH = BoundingVolumesExtensions.Scale(temporaryCubeAABB, new Vector3(0.8f, 0.5f, 1f)); OBBL = OrientedBoundingBox.FromAABB(halfW); OBBR = OrientedBoundingBox.FromAABB(halfW); OBBU = OrientedBoundingBox.FromAABB(halfH); OBBD = OrientedBoundingBox.FromAABB(halfH); } OBB.Center = Position; OBBL.Center = Position - RightDirection * 7; OBBR.Center = Position + RightDirection * 7; OBBU.Center = Position + UpDirection * 2; OBBD.Center = Position - UpDirection * 2; OBB.Orientation = YPR; OBBL.Orientation = YPR; OBBR.Orientation = YPR; OBBU.Orientation = YPR; OBBD.Orientation = YPR; float blockSize = MapLimit / MapSize; CurrentBlock = new Vector2( (int)((Position.X / blockSize) + 0.5), (int)((Position.Z / blockSize) + 0.5)); CurrentBlock.X = MathHelper.Clamp(CurrentBlock.X, 0, MapSize - 1); CurrentBlock.Y = MathHelper.Clamp(CurrentBlock.Y, 0, MapSize - 1); }
/// <inheritdoc /> public override void Draw(GameTime gameTime) { // Calculate the ViewProjection matrix var viewProjection = Camera.View * Camera.Projection; // Robot drawing Robot.Draw(RobotWorld, Camera.View, Camera.Projection); // Floor drawing // Set the Technique inside the TilingEffect to "BaseTiling", we want to control the tiling on the floor // Using its original Texture Coordinates TilingEffect.CurrentTechnique = TilingEffect.Techniques["BaseTiling"]; // Set the Tiling value TilingEffect.Parameters["Tiling"].SetValue(new Vector2(10f, 10f)); // Set the WorldViewProjection matrix TilingEffect.Parameters["WorldViewProjection"].SetValue(FloorWorld * viewProjection); // Set the Texture that the Floor will use TilingEffect.Parameters["Texture"].SetValue(StonesTexture); Quad.Draw(TilingEffect); // Steps drawing // Set the Technique inside the TilingEffect to "WorldTiling" // We want to use the world position of the steps to define how to sample the Texture TilingEffect.CurrentTechnique = TilingEffect.Techniques["WorldTiling"]; // Set the Texture that the Steps will use TilingEffect.Parameters["Texture"].SetValue(CobbleTexture); // Set the Tiling value TilingEffect.Parameters["Tiling"].SetValue(Vector2.One * 0.05f); // Draw every Step for (int index = 0; index < StairsWorld.Length; index++) { // Get the World Matrix var matrix = StairsWorld[index]; // Set the World Matrix TilingEffect.Parameters["World"].SetValue(matrix); // Set the WorldViewProjection Matrix TilingEffect.Parameters["WorldViewProjection"].SetValue(matrix * viewProjection); BoxPrimitive.Draw(TilingEffect); } // Draw the Box, setting every matrix and its Texture BoxesEffect.World = BoxWorld; BoxesEffect.View = Camera.View; BoxesEffect.Projection = Camera.Projection; BoxesEffect.Texture = WoodenTexture; BoxPrimitive.Draw(BoxesEffect); // Gizmos Drawing for (int index = 0; index < Colliders.Length; index++) { var box = Colliders[index]; var center = BoundingVolumesExtensions.GetCenter(box); var extents = BoundingVolumesExtensions.GetExtents(box); Game.Gizmos.DrawCube(center, extents * 2f, Color.Red); } Game.Gizmos.DrawCylinder(RobotCylinder.Transform, Color.Yellow); base.Draw(gameTime); }
/// <summary> /// Apply horizontal movement, detecting and solving collisions /// </summary> /// <param name="scaledVelocity">The current velocity scaled by deltaTime</param> private void SolveVerticalMovement(Vector3 scaledVelocity) { // If the Robot has vertical velocity if (scaledVelocity.Y == 0f) { return; } // Start by moving the Cylinder RobotCylinder.Center += Vector3.Up * scaledVelocity.Y; // Set the OnGround flag on false, update it later if we find a collision OnGround = false; // Collision detection var collided = false; var foundIndex = -1; for (var index = 0; index < Colliders.Length; index++) { if (!RobotCylinder.Intersects(Colliders[index]).Equals(BoxCylinderIntersection.Intersecting)) { continue; } // If we collided with something, set our velocity in Y to zero to reset acceleration RobotVelocity = new Vector3(RobotVelocity.X, 0f, RobotVelocity.Z); // Set our index and collision flag to true // The index is to tell which collider the Robot intersects with collided = true; foundIndex = index; break; } // We correct based on differences in Y until we don't collide anymore // Not usual to iterate here more than once, but could happen while (collided) { var collider = Colliders[foundIndex]; var colliderY = BoundingVolumesExtensions.GetCenter(collider).Y; var cylinderY = RobotCylinder.Center.Y; var extents = BoundingVolumesExtensions.GetExtents(collider); float penetration; // If we are on top of the collider, push up // Also, set the OnGround flag to true if (cylinderY > colliderY) { penetration = colliderY + extents.Y - cylinderY + RobotCylinder.HalfHeight; OnGround = true; } // If we are on bottom of the collider, push down else { penetration = -cylinderY - RobotCylinder.HalfHeight + colliderY - extents.Y; } // Move our Cylinder so we are not colliding anymore RobotCylinder.Center += Vector3.Up * penetration; collided = false; // Check for collisions again for (var index = 0; index < Colliders.Length; index++) { if (!RobotCylinder.Intersects(Colliders[index]).Equals(BoxCylinderIntersection.Intersecting)) { continue; } // Iterate until we don't collide with anything anymore collided = true; foundIndex = index; break; } } }
/// <inheritdoc /> public override void Initialize() { Game.Background = Color.Black; // Set the ground flag to false, as the Robot starts in the air OnGround = false; // Robot position and matrix initialization RobotPosition = Vector3.UnitX * 30f; RobotScale = Matrix.CreateScale(0.3f); RobotCylinder = new BoundingCylinder(RobotPosition, 10f, 15f); RobotRotation = Matrix.Identity; RobotFrontDirection = Vector3.Backward; // Create the Camera Camera = new TargetCamera(GraphicsDevice.Viewport.AspectRatio, Vector3.One * 100f, Vector3.Zero); // Create World matrices for our stairs StairsWorld = new Matrix[] { Matrix.CreateScale(70f, 6f, 15f) * Matrix.CreateTranslation(0f, 3f, 125f), Matrix.CreateScale(70f, 6f, 15f) * Matrix.CreateTranslation(0f, 9f, 140f), Matrix.CreateScale(70f, 6f, 15f) * Matrix.CreateTranslation(0f, 15f, 155f), Matrix.CreateScale(70f, 6f, 40f) * Matrix.CreateTranslation(0f, 21f, 182.5f), Matrix.CreateScale(15f, 6f, 40f) * Matrix.CreateTranslation(-42.5f, 27f, 182.5f), Matrix.CreateScale(15f, 6f, 40f) * Matrix.CreateTranslation(-57.5f, 33f, 182.5f), Matrix.CreateScale(15f, 6f, 40f) * Matrix.CreateTranslation(-72.5f, 39f, 182.5f), Matrix.CreateScale(100f, 6f, 100f) * Matrix.CreateTranslation(-130f, 45f, 152.5f), }; // Create World matrices for the Floor and Box FloorWorld = Matrix.CreateScale(200f, 0.001f, 200f); BoxWorld = Matrix.CreateScale(30f) * Matrix.CreateTranslation(85f, 15f, -15f); // Create Bounding Boxes for the static geometries // Stairs + Floor + Box Colliders = new BoundingBox[StairsWorld.Length + 2]; // Instantiate Bounding Boxes for the stairs int index = 0; for (; index < StairsWorld.Length; index++) { Colliders[index] = BoundingVolumesExtensions.FromMatrix(StairsWorld[index]); } // Instantiate a BoundingBox for the Box Colliders[index] = BoundingVolumesExtensions.FromMatrix(BoxWorld); index++; // Instantiate a BoundingBox for the Floor. Note that the height is almost zero Colliders[index] = new BoundingBox(new Vector3(-200f, -0.001f, -200f), new Vector3(200f, 0f, 200f)); // Set the Acceleration (which in this case won't change) to the Gravity pointing down RobotAcceleration = Vector3.Down * Gravity; // Initialize the Velocity as zero RobotVelocity = Vector3.Zero; base.Initialize(); }
/// <summary> /// Calculates an OBB with a given set of points. /// Tests every orientations between initValues and endValues, stepping through angle intervals with a given step size. /// Goes on until it reaches a step less than 0.01 /// </summary> /// <returns>A generated Oriented Bounding Box that contains the set of points</returns> private static OrientedBoundingBox ComputeFromPointsRecursive(Vector3[] points, Vector3 initValues, Vector3 endValues, float step) { var minObb = new OrientedBoundingBox(); var minimumVolume = float.MaxValue; var minInitValues = Vector3.Zero; var minEndValues = Vector3.Zero; var transformedPoints = new Vector3[points.Length]; float y, z; var x = initValues.X; while (x <= endValues.X) { y = initValues.Y; var rotationX = MathHelper.ToRadians(x); while (y <= endValues.Y) { z = initValues.Z; var rotationY = MathHelper.ToRadians(y); while (z <= endValues.Z) { // Rotation matrix var rotationZ = MathHelper.ToRadians(z); var rotationMatrix = Matrix.CreateFromYawPitchRoll(rotationY, rotationX, rotationZ); // Transform every point to OBB-Space for (var index = 0; index < transformedPoints.Length; index++) { transformedPoints[index] = Vector3.Transform(points[index], rotationMatrix); } // Obtain an AABB enclosing every transformed point var aabb = BoundingBox.CreateFromPoints(transformedPoints); // Calculate the volume of the AABB var volume = BoundingVolumesExtensions.GetVolume(aabb); // Find lesser volume if (volume < minimumVolume) { minimumVolume = volume; minInitValues = new Vector3(x, y, z); minEndValues = new Vector3(x + step, y + step, z + step); // Restore the AABB center in World-Space var center = BoundingVolumesExtensions.GetCenter(aabb); center = Vector3.Transform(center, rotationMatrix); // Create OBB minObb = new OrientedBoundingBox(center, BoundingVolumesExtensions.GetExtents(aabb)); minObb.Orientation = rotationMatrix; } z += step; } y += step; } x += step; } // Loop again if the step is higher than a given acceptance threshold if (step > 0.01f) { minObb = ComputeFromPointsRecursive(points, minInitValues, minEndValues, step / 10f); } return(minObb); }