/// <summary> /// Constructs the query manager for a character. /// </summary> /// <param name="character">Character to manage queries for.</param> public QueryManager(CharacterController character) { this.character = character; //We can share the real shape with the 'current' query object. currentQueryObject = new ConvexCollidable <CylinderShape>(character.Body.CollisionInformation.Shape); standingQueryObject = new ConvexCollidable <CylinderShape>(new CylinderShape(character.StanceManager.StandingHeight, character.Body.Radius)); crouchingQueryObject = new ConvexCollidable <CylinderShape>(new CylinderShape(character.StanceManager.CrouchingHeight, character.Body.Radius)); //Share the collision rules between the main body and its query objects. That way, the character's queries return valid results. currentQueryObject.CollisionRules = character.Body.CollisionInformation.CollisionRules; standingQueryObject.CollisionRules = character.Body.CollisionInformation.CollisionRules; crouchingQueryObject.CollisionRules = character.Body.CollisionInformation.CollisionRules; SupportRayFilter = SupportRayFilterFunction; }
///<summary> /// Initializes the manifold. ///</summary> ///<param name="newCollidableA">First collidable.</param> ///<param name="newCollidableB">Second collidable.</param> ///<exception cref="Exception">Thrown when the collidables being used are not of the proper type.</exception> public override void Initialize(Collidable newCollidableA, Collidable newCollidableB) { box = newCollidableA as ConvexCollidable <BoxShape>; sphere = newCollidableB as ConvexCollidable <SphereShape>; if (box == null || sphere == null) { box = newCollidableB as ConvexCollidable <BoxShape>; sphere = newCollidableA as ConvexCollidable <SphereShape>; if (box == null || sphere == null) { throw new Exception("Inappropriate types used to initialize pair."); } } }
.1f; //There's a little extra space above the maximum step height to start the obstruction and downcast test rays. Helps when a step is very close to the max step height. /// <summary> /// Constructs a new step manager for a character. /// </summary> /// <param name="characterBody">The character's body.</param> /// <param name="contactCategorizer">Contact categorizer used by the character.</param> /// <param name="supportFinder">Support finder used by the character.</param> /// <param name="queryManager">Query provider to use in checking for obstructions.</param> /// <param name="horizontalMotionConstraint">Horizontal motion constraint used by the character. Source of 3d movement direction.</param> public StepManager(Cylinder characterBody, CharacterContactCategorizer contactCategorizer, SupportFinder supportFinder, QueryManager queryManager, HorizontalMotionConstraint horizontalMotionConstraint) { this.characterBody = characterBody; currentQueryObject = new ConvexCollidable <CylinderShape>(characterBody.CollisionInformation.Shape); ContactCategorizer = contactCategorizer; SupportFinder = supportFinder; QueryManager = queryManager; HorizontalMotionConstraint = horizontalMotionConstraint; //The minimum step height is just barely above where the character would generally find the ground. //This helps avoid excess tests. minimumUpStepHeight = CollisionDetectionSettings.AllowedPenetration * 1.1f; // Math.Max(0, -.01f + character.Body.CollisionInformation.Shape.CollisionMargin * (1 - character.SupportFinder.sinMaximumSlope)); }
public override void Initialize(Collidable newCollidableA, Collidable newCollidableB) { convex = newCollidableA as ConvexCollidable; mesh = newCollidableB as FullChunkObject; if (convex == null || mesh == null) { convex = newCollidableB as ConvexCollidable; mesh = newCollidableA as FullChunkObject; if (convex == null || mesh == null) { throw new ArgumentException("Inappropriate types used to initialize contact manifold."); } } ActivePairs = new QuickDictionary <Vector3i, GeneralConvexPairTester>(BufferPools <Vector3i> .Locking, BufferPools <GeneralConvexPairTester> .Locking, BufferPools <int> .Locking, 3); activePairsBackBuffer = new QuickDictionary <Vector3i, GeneralConvexPairTester>(BufferPools <Vector3i> .Locking, BufferPools <GeneralConvexPairTester> .Locking, BufferPools <int> .Locking, 3); }
/// <summary> /// Constructs a stance manager for a character. /// </summary> /// <param name="characterBody">The character's body entity.</param> /// <param name="crouchingHeight">Crouching height of the character.</param> /// <param name="proneHeight">Prone height of the character.</param> /// <param name="queryManager">Provider of queries used by the stance manager to test if it is okay to change stances.</param> /// <param name="supportFinder">Support finder used by the character.</param> public StanceManager(Cylinder characterBody, float crouchingHeight, float proneHeight, QueryManager queryManager, SupportFinder supportFinder) { QueryManager = queryManager; SupportFinder = supportFinder; this.characterBody = characterBody; standingHeight = characterBody.Height; if (crouchingHeight < standingHeight) { this.crouchingHeight = crouchingHeight; } else { throw new ArgumentException("Crouching height must be less than standing height."); } if (proneHeight < crouchingHeight) { this.proneHeight = proneHeight; } else { throw new ArgumentException("Prone height must be less than crouching height."); } //We can share the real shape with the query objects. currentQueryObject = new ConvexCollidable <CylinderShape>(characterBody.CollisionInformation.Shape); standingQueryObject = new ConvexCollidable <CylinderShape>( new CylinderShape(StandingHeight, characterBody.Radius) { CollisionMargin = currentQueryObject.Shape.CollisionMargin }); crouchingQueryObject = new ConvexCollidable <CylinderShape>( new CylinderShape(CrouchingHeight, characterBody.Radius) { CollisionMargin = currentQueryObject.Shape.CollisionMargin }); proneQueryObject = new ConvexCollidable <CylinderShape>(new CylinderShape(proneHeight, characterBody.Radius) { CollisionMargin = currentQueryObject.Shape.CollisionMargin }); //Share the collision rules between the main body and its query objects. That way, the character's queries return valid results. currentQueryObject.CollisionRules = characterBody.CollisionInformation.CollisionRules; standingQueryObject.CollisionRules = characterBody.CollisionInformation.CollisionRules; crouchingQueryObject.CollisionRules = characterBody.CollisionInformation.CollisionRules; proneQueryObject.CollisionRules = characterBody.CollisionInformation.CollisionRules; }
CharacterContactPositionState TrySupportLocation(ConvexCollidable <CylinderShape> queryObject, ref Vector3 position, out float hintOffset, ref QuickList <CharacterContact> tractionContacts, ref QuickList <CharacterContact> supportContacts, ref QuickList <CharacterContact> sideContacts, ref QuickList <CharacterContact> headContacts) { hintOffset = 0; PrepareQueryObject(queryObject, ref position); QueryManager.QueryContacts(queryObject, ref tractionContacts, ref supportContacts, ref sideContacts, ref headContacts, true); bool obstructed = IsObstructed(ref sideContacts, ref headContacts); if (obstructed) { return(CharacterContactPositionState.Obstructed); } if (supportContacts.Count > 0) { CharacterContactPositionState supportState; CharacterContact supportContact; QueryManager.AnalyzeSupportState(ref tractionContacts, ref supportContacts, out supportState, out supportContact); var down = characterBody.orientationMatrix.Down; //Note that traction is not tested for; it isn't important for the stance manager. if (supportState == CharacterContactPositionState.Accepted) { //We're done! The guess found a good spot to stand on. //We need to have fairly good contacts after this process, so only push it up a bit. hintOffset = Math.Min(0, Vector3.Dot(supportContact.Contact.Normal, down) * (CollisionDetectionSettings.AllowedPenetration * .5f - supportContact.Contact.PenetrationDepth)); return(CharacterContactPositionState.Accepted); } else if (supportState == CharacterContactPositionState.TooDeep) { //Looks like we have to keep trying, but at least we found a good hint. hintOffset = Math.Min(0, Vector3.Dot(supportContact.Contact.Normal, down) * (CollisionDetectionSettings.AllowedPenetration * .5f - supportContact.Contact.PenetrationDepth)); return(CharacterContactPositionState.TooDeep); } else //if (supportState == SupportState.Separated) { //It's not obstructed, but the support isn't quite right. //It's got a negative penetration depth. //We can use that as a hint. hintOffset = -.001f - Vector3.Dot(supportContact.Contact.Normal, down) * supportContact.Contact.PenetrationDepth; return(CharacterContactPositionState.NoHit); } } else //Not obstructed, but no support. { return(CharacterContactPositionState.NoHit); } }
///<summary> /// Cleans up the manifold. ///</summary> public override void CleanUp() { supplementData.Clear(); convex = null; for (int i = activePairTesters.Count - 1; i >= 0; --i) { activePairTesters.Values[i].CleanUp(); GiveBackTester(activePairTesters.Values[i]); } activePairTesters.Dispose(); activePairTesters = new QuickDictionary <TriangleIndices, TrianglePairTester>( BufferPools <TriangleIndices> .Locking, BufferPools <TrianglePairTester> .Locking, BufferPools <int> .Locking, 3); CleanUpOverlappingTriangles(); base.CleanUp(); }
public override void CleanUp() { convex = null; for (int i = ActivePairs.Count - 1; i >= 0; --i) { ReturnPair(ActivePairs.Values[i]); ActivePairs.Values[i].CleanUp(); } //Clear->dispose is technically unnecessary now, but it may avoid some pain later on when this behavior changes in v2. //This will be a very sneaky breaking change... ActivePairs.Clear(); ActivePairs.Dispose(); Debug.Assert(activePairsBackBuffer.Count == 0); activePairsBackBuffer.Dispose(); base.CleanUp(); }
public override void Initialize(Collidable newCollidableA, Collidable newCollidableB) { convex = newCollidableA as ConvexCollidable; triangle = newCollidableB as ConvexCollidable <TriangleShape>; if (convex == null || triangle == null) { convex = newCollidableB as ConvexCollidable; triangle = newCollidableA as ConvexCollidable <TriangleShape>; if (convex == null || triangle == null) { throw new ArgumentException("Inappropriate types used to initialize contact manifold."); } } pairTester.Initialize(convex.Shape); }
public override void Initialize(Collidable newCollidableA, Collidable newCollidableB) { convex = newCollidableA as ConvexCollidable; voxelGrid = newCollidableB as VoxelGrid; if (convex == null || voxelGrid == null) { convex = newCollidableB as ConvexCollidable; voxelGrid = newCollidableA as VoxelGrid; if (convex == null || voxelGrid == null) { throw new ArgumentException("Inappropriate types used to initialize contact manifold."); } } ActivePairs = new QuickDictionary <Int3, GeneralConvexPairTester>(BufferPools <Int3> .Locking, BufferPools <GeneralConvexPairTester> .Locking, BufferPools <int> .Locking, 3); activePairsBackBuffer = new QuickDictionary <Int3, GeneralConvexPairTester>(BufferPools <Int3> .Locking, BufferPools <GeneralConvexPairTester> .Locking, BufferPools <int> .Locking, 3); }
///<summary> /// Initializes the pair handler. ///</summary> ///<param name="entryA">First entry in the pair.</param> ///<param name="entryB">Second entry in the pair.</param> public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { box = entryA as ConvexCollidable <BoxShape>; sphere = entryB as ConvexCollidable <SphereShape>; if (box == null || sphere == null) { box = entryB as ConvexCollidable <BoxShape>; sphere = entryA as ConvexCollidable <SphereShape>; if (box == null || sphere == null) { throw new ArgumentException("Inappropriate types used to initialize pair."); } } //Reorder the entries so that the guarantee that the normal points from A to B is satisfied. broadPhaseOverlap.entryA = box; broadPhaseOverlap.entryB = sphere; base.Initialize(entryA, entryB); }
///<summary> /// Initializes the pair handler. ///</summary> ///<param name="entryA">First entry in the pair.</param> ///<param name="entryB">Second entry in the pair.</param> public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { triangle = entryA as ConvexCollidable <TriangleShape>; convex = entryB as ConvexCollidable; if (triangle == null || convex == null) { triangle = entryB as ConvexCollidable <TriangleShape>; convex = entryA as ConvexCollidable; if (triangle == null || convex == null) { throw new ArgumentException("Inappropriate types used to initialize pair."); } } //Contact normal goes from A to B. broadPhaseOverlap.entryA = convex; broadPhaseOverlap.entryB = triangle; base.Initialize(entryA, entryB); }
/// <summary> /// /// </summary> /// <param name="from"></param> /// <param name="to"></param> /// <param name="normal"></param> /// <param name="pos"></param> /// <returns></returns> public List <Vector3> RayCastAgainstStatic(Vector3 from, Vector3 to) { var list = new List <Vector3>(); var dir = to - from; var dist = dir.Length(); var ndir = dir.Normalized(); Ray ray = new Ray(from, ndir); Func <BroadPhaseEntry, bool> filterFunc = delegate(BroadPhaseEntry bpe) { ConvexCollidable cc = bpe as ConvexCollidable; if (cc != null) { return(false); } return(true); }; IList <RayCastResult> rcrList = new List <RayCastResult>(); bool result = Space.RayCast(ray, dist, filterFunc, rcrList); if (!result) { return(list); } foreach (var rcr in rcrList) { list.Add(rcr.HitData.Location); list.Add(rcr.HitData.Normal); } return(list); }
public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { if (noRecurse) { return; } noRecurse = true; mesh = entryA as MobileChunkCollidable; convex = entryB as ConvexCollidable; if (mesh == null || convex == null) { mesh = entryB as MobileChunkCollidable; convex = entryA as ConvexCollidable; if (mesh == null || convex == null) { throw new ArgumentException("Inappropriate types used to initialize pair."); } } broadPhaseOverlap = new BroadPhaseOverlap(convex, mesh, broadPhaseOverlap.CollisionRule); UpdateMaterialProperties(convex.Entity?.Material, mesh.Entity?.Material); base.Initialize(entryA, entryB); noRecurse = false; }
public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { voxelGrid = entryA as VoxelGrid; convex = entryB as ConvexCollidable; if (voxelGrid == null || convex == null) { voxelGrid = entryB as VoxelGrid; convex = entryA as ConvexCollidable; if (voxelGrid == null || convex == null) { throw new ArgumentException("Inappropriate types used to initialize pair."); } } //Contact normal goes from A to B. broadPhaseOverlap = new BroadPhaseOverlap(convex, voxelGrid, broadPhaseOverlap.CollisionRule); UpdateMaterialProperties(convex.Entity != null ? convex.Entity.Material : null, voxelGrid.Material); base.Initialize(entryA, entryB); }
///<summary> /// Initializes the pair handler. ///</summary> ///<param name="entryA">First entry in the pair.</param> ///<param name="entryB">Second entry in the pair.</param> public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { mobileMesh = entryA as MobileMeshCollidable; convex = entryB as ConvexCollidable; if (mobileMesh == null || convex == null) { mobileMesh = entryB as MobileMeshCollidable; convex = entryA as ConvexCollidable; if (mobileMesh == null || convex == null) throw new Exception("Inappropriate types used to initialize pair."); } //Contact normal goes from A to B. broadPhaseOverlap.entryA = convex; broadPhaseOverlap.entryB = mobileMesh; //It's possible that the convex does not have an entity if it is a proxy for a non-entity collidable. //Similarly, the mesh could be a query object. UpdateMaterialProperties(convex.entity != null ? convex.entity.material : null, mobileMesh.entity != null ? mobileMesh.entity.material : null); base.Initialize(entryA, entryB); }
///<summary> /// Initializes the pair handler. ///</summary> ///<param name="entryA">First entry in the pair.</param> ///<param name="entryB">Second entry in the pair.</param> public override void Initialize(BroadPhaseEntry entryA, BroadPhaseEntry entryB) { terrain = entryA as Terrain; convex = entryB as ConvexCollidable; if (terrain == null || convex == null) { terrain = entryB as Terrain; convex = entryA as ConvexCollidable; if (terrain == null || convex == null) { throw new Exception("Inappropriate types used to initialize pair."); } } //Contact normal goes from A to B. broadPhaseOverlap.entryA = convex; broadPhaseOverlap.entryB = terrain; UpdateMaterialProperties(convex.entity != null ? convex.entity.material : null, terrain.material); base.Initialize(entryA, entryB); }
///<summary> /// Updates the time of impact for the pair. ///</summary> ///<param name="requester">Collidable requesting the update.</param> ///<param name="dt">Timestep duration.</param> public override void UpdateTimeOfImpact(Collidable requester, float dt) { ConvexCollidable collidableA = CollidableA as ConvexCollidable; ConvexCollidable collidableB = CollidableB as ConvexCollidable; PositionUpdateMode modeA = collidableA.entity == null ? PositionUpdateMode.Discrete : collidableA.entity.PositionUpdateMode; PositionUpdateMode modeB = collidableB.entity == null ? PositionUpdateMode.Discrete : collidableB.entity.PositionUpdateMode; BroadPhaseOverlap overlap = BroadPhaseOverlap; if ( (overlap.entryA.IsActive || overlap.entryB.IsActive) && //At least one has to be active. ( modeA == PositionUpdateMode.Continuous && //If both are continuous, only do the process for A. modeB == PositionUpdateMode.Continuous && overlap.entryA == requester || (modeA == PositionUpdateMode.Continuous) ^ //If only one is continuous, then we must do it. (modeB == PositionUpdateMode.Continuous) ) ) { //Only perform the test if the minimum radii are small enough relative to the size of the velocity. //Discrete objects have already had their linear motion integrated, so don't use their velocity. Vector3 velocity; if (modeA == PositionUpdateMode.Discrete) //CollidableA is static for the purposes of this continuous test. { velocity = collidableB.entity.linearVelocity; } else if (modeB == PositionUpdateMode.Discrete) //CollidableB is static for the purposes of this continuous test. { Vector3.Negate(ref collidableA.entity.linearVelocity, out velocity); } else //Both objects are moving. { Vector3.Subtract(ref collidableB.entity.linearVelocity, ref collidableA.entity.linearVelocity, out velocity); } Vector3.Multiply(ref velocity, dt, out velocity); float velocitySquared = velocity.LengthSquared(); float minimumRadiusA = collidableA.Shape.MinimumRadius * MotionSettings.CoreShapeScaling; timeOfImpact = 1; if (minimumRadiusA * minimumRadiusA < velocitySquared) { //Spherecast A against B. RayHit rayHit; if (GJKToolbox.CCDSphereCast(new Ray(collidableA.worldTransform.Position, -velocity), minimumRadiusA, collidableB.Shape, ref collidableB.worldTransform, timeOfImpact, out rayHit)) { timeOfImpact = rayHit.T; } } float minimumRadiusB = collidableB.Shape.MinimumRadius * MotionSettings.CoreShapeScaling; if (minimumRadiusB * minimumRadiusB < velocitySquared) { //Spherecast B against A. RayHit rayHit; if (GJKToolbox.CCDSphereCast(new Ray(collidableB.worldTransform.Position, velocity), minimumRadiusB, collidableA.Shape, ref collidableA.worldTransform, timeOfImpact, out rayHit)) { timeOfImpact = rayHit.T; } } //If it's intersecting, throw our hands into the air and give up. //This is generally a perfectly acceptable thing to do, since it's either sitting //inside another object (no ccd makes sense) or we're still in an intersecting case //from a previous frame where CCD took place and a contact should have been created //to deal with interpenetrating velocity. Sometimes that contact isn't sufficient, //but it's good enough. if (timeOfImpact == 0) { timeOfImpact = 1; } } }
///<summary> /// Cleans up the pair handler. ///</summary> public override void CleanUp() { base.CleanUp(); voxelGrid = null; convex = null; }
public override void CleanUp() { base.CleanUp(); mesh = null; convex = null; }
///<summary> /// Cleans up the pair handler. ///</summary> public override void CleanUp() { base.CleanUp(); instancedMesh = null; convex = null; }
/// <summary> /// Constructs a new demo. /// </summary> /// <param name="game">Game owning this demo.</param> public StaticGroupDemo(DemosGame game) : base(game) { //Creating a bunch of separate StaticMeshes or kinematic Entity objects for an environment can pollute the broad phase. //This is because the broad phase implementation doesn't have guarantees about what elements can collide, so it has to //traverse the acceleration structure all the way down to pairs to figure it out. That can get expensive! //Individual objects, like StaticMeshes, can have very complicated geometry without hurting the broad phase because the broad phase //has no knowledge of the thousands of triangles in the mesh. The StaticMesh itself knows that the triangles within the mesh //never need to collide, so it never needs to test them against each other. //Similarly, the StaticGroup can be given a bunch of separate collidables. The broad phase doesn't directly know about these child collidables- //it only sees the StaticGroup. The StaticGroup knows that the things inside it can't ever collide with each other, so no tests are needed. //This avoids the performance problem! //To demonstrate, we'll be creating a set of static objects and giving them to a group to manage. var collidables = new List <Collidable>(); //Start with a whole bunch of boxes. These are entity collidables, but without entities! float xSpacing = 6; float ySpacing = 6; float zSpacing = 6; //NOTE: You might notice this demo takes a while to start, especially on the Xbox360. Do not fear! That's due to the creation of the graphics data, not the physics. //The physics can handle over 100,000 static objects pretty easily. The graphics, not so much :) //Try disabling the game.ModelDrawer.Add() lines and increasing the number of static objects. int xCount = 15; int yCount = 7; int zCount = 15; var random = new Random(); for (int i = 0; i < xCount; i++) { for (int j = 0; j < yCount; j++) { for (int k = 0; k < zCount; k++) { //Create a transform and the instance of the mesh. var collidable = new ConvexCollidable <BoxShape>(new BoxShape((float)random.NextDouble() * 6 + .5f, (float)random.NextDouble() * 6 + .5f, (float)random.NextDouble() * 6 + .5f)); //This EntityCollidable isn't associated with an entity, so we must manually tell it where to sit by setting the WorldTransform. //This also updates its bounding box. collidable.WorldTransform = new RigidTransform( new Vector3(i * xSpacing - xCount * xSpacing * .5f, j * ySpacing + 3, k * zSpacing - zCount * zSpacing * .5f), Quaternion.CreateFromAxisAngle(Vector3.Normalize(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), (float)random.NextDouble() * 100)); collidables.Add(collidable); game.ModelDrawer.Add(collidable); } } } //Now create a bunch of instanced meshes too. xSpacing = 6; ySpacing = 6; zSpacing = 6; xCount = 10; yCount = 2; zCount = 10; Vector3[] vertices; int[] indices; ModelDataExtractor.GetVerticesAndIndicesFromModel(game.Content.Load <Model>("fish"), out vertices, out indices); var meshShape = new InstancedMeshShape(vertices, indices); for (int i = 0; i < xCount; i++) { for (int j = 0; j < yCount; j++) { for (int k = 0; k < zCount; k++) { //Create a transform and the instance of the mesh. var transform = new AffineTransform( new Vector3((float)random.NextDouble() * 6 + .5f, (float)random.NextDouble() * 6 + .5f, (float)random.NextDouble() * 6 + .5f), Quaternion.CreateFromAxisAngle(Vector3.Normalize(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), (float)random.NextDouble() * 100), new Vector3(i * xSpacing - xCount * xSpacing * .5f, j * ySpacing + 50, k * zSpacing - zCount * zSpacing * .5f)); var mesh = new InstancedMesh(meshShape, transform); //Making the triangles one-sided makes collision detection a bit more robust, since the backsides of triangles won't try to collide with things //and 'pull' them back into the mesh. mesh.Sidedness = TriangleSidedness.Counterclockwise; collidables.Add(mesh); game.ModelDrawer.Add(mesh); } } } var ground = new ConvexCollidable <BoxShape>(new BoxShape(200, 1, 200)); ground.WorldTransform = new RigidTransform(new Vector3(0, -3, 0), Quaternion.Identity); collidables.Add(ground); game.ModelDrawer.Add(ground); var group = new StaticGroup(collidables); Space.Add(group); //Create a bunch of dynamic boxes to drop on the staticswarm. xCount = 8; yCount = 3; zCount = 8; xSpacing = 3f; ySpacing = 5f; zSpacing = 3f; for (int i = 0; i < xCount; i++) { for (int j = 0; j < zCount; j++) { for (int k = 0; k < yCount; k++) { Space.Add(new Box(new Vector3( xSpacing * i - (xCount - 1) * xSpacing / 2f, 100 + k * (ySpacing), 2 + zSpacing * j - (zCount - 1) * zSpacing / 2f), 1, 1, 1, 10)); } } } game.Camera.Position = new Vector3(0, 60, 90); }
///<summary> /// Cleans up the manifold. ///</summary> public override void CleanUp() { boxA = null; boxB = null; base.CleanUp(); }
public override void CleanUp() { base.CleanUp(); convex = null; checkContainment = true; }
///<summary> /// Cleans up the pair handler. ///</summary> public override void CleanUp() { base.CleanUp(); convexInfo = null; }
/// <summary> /// Constructs a new demo. /// </summary> /// <param name="game">Game owning this demo.</param> public CharacterStressierTestDemo(DemosGame game) : base(game) { //Load in mesh data and create the group. Vector3[] staticTriangleVertices; int[] staticTriangleIndices; var playgroundModel = game.Content.Load <Model>("playground"); //This is a little convenience method used to extract vertices and indices from a model. //It doesn't do anything special; any approach that gets valid vertices and indices will work. ModelDataExtractor.GetVerticesAndIndicesFromModel(playgroundModel, out staticTriangleVertices, out staticTriangleIndices); var meshShape = new InstancedMeshShape(staticTriangleVertices, staticTriangleIndices); var meshes = new List <Collidable>(); var xSpacing = 400; var ySpacing = 400; var xCount = 11; var yCount = 11; for (int i = 0; i < xCount; i++) { for (int j = 0; j < yCount; j++) { var staticMesh = new InstancedMesh(meshShape, new AffineTransform(Matrix3x3.Identity, new Vector3(-xSpacing * (xCount - 1) / 2 + i * xSpacing, 0, -ySpacing * (yCount - 1) / 2 + j * ySpacing))); staticMesh.Sidedness = TriangleSidedness.Counterclockwise; Space.Add(staticMesh); //meshes.Add(staticMesh); game.ModelDrawer.Add(staticMesh); } } //var group = new StaticGroup(meshes); //Space.Add(group); //To demonstrate, we'll be creating a set of static objects and giving them to a group to manage. var collidables = new List <Collidable>(); //Start with a whole bunch of boxes. These are entity collidables, but without entities! xSpacing = 25; ySpacing = 16; float zSpacing = 25; xCount = 25; yCount = 7; int zCount = 25; var random = new Random(); for (int i = 0; i < xCount; i++) { for (int j = 0; j < yCount; j++) { for (int k = 0; k < zCount; k++) { //Create a transform and the instance of the mesh. var collidable = new ConvexCollidable <BoxShape>(new BoxShape((float)random.NextDouble() * 25 + 5.5f, (float)random.NextDouble() * 25 + 5.5f, (float)random.NextDouble() * 25 + 5.5f)); //This EntityCollidable isn't associated with an entity, so we must manually tell it where to sit by setting the WorldTransform. //This also updates its bounding box. collidable.WorldTransform = new RigidTransform( new Vector3(i * xSpacing - xCount * xSpacing * .5f, j * ySpacing + -50, k * zSpacing - zCount * zSpacing * .5f), Quaternion.CreateFromAxisAngle(Vector3.Normalize(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), (float)random.NextDouble() * 100)); collidables.Add(collidable); game.ModelDrawer.Add(collidable); } } } var group = new StaticGroup(collidables); Space.Add(group); //Now drop the characters on it! var numColumns = 16; var numRows = 16; var numHigh = 16; float separation = 24; for (int i = 0; i < numRows; i++) { for (int j = 0; j < numColumns; j++) { for (int k = 0; k < numHigh; k++) { var character = new CharacterController(); character.Body.Position = new Vector3( separation * i - numRows * separation / 2, 50f + k * separation, separation * j - numColumns * separation / 2); characters.Add(character); Space.Add(character); } } } game.Camera.Position = new Vector3(0, 10, 40); //Dump some boxes on top of the characters for fun. numColumns = 16; numRows = 16; numHigh = 8; separation = 24; for (int i = 0; i < numRows; i++) { for (int j = 0; j < numColumns; j++) { for (int k = 0; k < numHigh; k++) { var toAdd = new Box( new Vector3( separation * i - numRows * separation / 2, 52f + k * separation, separation * j - numColumns * separation / 2), 0.8f, 0.8f, 0.8f, 15); toAdd.PositionUpdateMode = BEPUphysics.PositionUpdating.PositionUpdateMode.Continuous; Space.Add(toAdd); } } } }