float GetSubmergedHeight(Collidable info, float maxLength, float boundingBoxHeight, ref Vector3 rayOrigin, ref Vector3 xSpacing, ref Vector3 zSpacing, int i, int j, out Vector3 volumeCenter) { Ray ray; Vector3.Multiply(ref xSpacing, i, out ray.Position); Vector3.Multiply(ref zSpacing, j, out ray.Direction); Vector3.Add(ref ray.Position, ref ray.Direction, out ray.Position); Vector3.Add(ref ray.Position, ref rayOrigin, out ray.Position); ray.Direction = upVector; //do a bottom-up raycast. RayHit rayHit; //Only go up to maxLength. If it's further away than maxLength, then it's above the water and it doesn't contribute anything. if (info.RayCast(ray, maxLength, out rayHit)) { //Position the ray to point from the other side. Vector3.Multiply(ref ray.Direction, boundingBoxHeight, out ray.Direction); Vector3.Add(ref ray.Position, ref ray.Direction, out ray.Position); Vector3.Negate(ref upVector, out ray.Direction); float bottomY = rayHit.Location.Y; float bottom = rayHit.T; Vector3 bottomPosition = rayHit.Location; if (info.RayCast(ray, boundingBoxHeight - rayHit.T, out rayHit)) { Vector3.Add(ref rayHit.Location, ref bottomPosition, out volumeCenter); Vector3.Multiply(ref volumeCenter, .5f, out volumeCenter); return(Math.Min(surfacePlaneHeight - bottomY, boundingBoxHeight - rayHit.T - bottom)); } //This inner raycast should always hit, but just in case it doesn't due to some numerical problem, give it a graceful way out. volumeCenter = Vector3.Zero; return(0); } volumeCenter = Vector3.Zero; return(0); }
/// <summary> /// Locates the closest support entity by performing a raycast at collected candidates. /// </summary> /// <param name="supportEntity">The closest supporting entity.</param> /// <param name="supportLocation">The support location where the ray hit the entity.</param> /// <param name="supportNormal">The normal at the surface where the ray hit the entity.</param> /// <param name="supportDistance">Distance from the character to the support location.</param> /// <returns>Whether or not a support was located.</returns> private bool FindSupport(out BEPUphysics.Entities.Entity supportEntity, out Vector3 supportLocation, out Vector3 supportNormal, out float supportDistance) { supportEntity = null; supportLocation = Toolbox.NoVector; supportNormal = Toolbox.NoVector; supportDistance = float.MaxValue; Vector3 rayOrigin = Body.Position + rayOriginOffset; for (int i = 0; i < collisionPairCollector.CollisionInformation.Pairs.Count; i++) { var pair = collisionPairCollector.CollisionInformation.Pairs[i]; //Determine which member of the collision pair is the possible support. Collidable candidate = (pair.BroadPhaseOverlap.EntryA == collisionPairCollector.CollisionInformation ? pair.BroadPhaseOverlap.EntryB : pair.BroadPhaseOverlap.EntryA) as Collidable; //Ensure that the candidate is a valid supporting entity. if (candidate.CollisionRules.Personal >= CollisionRule.NoSolver) { continue; //It is invalid! } //The maximum length is supportHeight * 2 instead of supportHeight alone because the character should be able to step downwards. //This acts like a sort of 'glue' to help the character stick on the ground in general. float maximumDistance; //The 'glue' effect should only occur if the character has a solid hold on the ground though. //Otherwise, the character is falling or sliding around uncontrollably. if (HasTraction) { maximumDistance = supportHeight * 2; } else { maximumDistance = supportHeight; } RayHit rayHit; //Fire a ray at the candidate and determine some details! if (candidate.RayCast(new Ray(rayOrigin, Vector3.Down), maximumDistance, out rayHit)) { //We want to find the closest support, so compare it against the last closest support. if (rayHit.T < supportDistance) { supportDistance = rayHit.T; supportLocation = rayHit.Location; supportNormal = rayHit.T > 0 ? rayHit.Normal : Vector3.Up; var entityInfo = candidate as EntityCollidable; if (entityInfo != null) { supportEntity = entityInfo.Entity; } else { supportEntity = null; } } } } supportNormal.Normalize(); return(supportDistance < float.MaxValue); }
/// <summary> /// Locates the closest support entity by performing a raycast at collected candidates. /// </summary> /// <param name="supportEntity">The closest supporting entity.</param> /// <param name="supportLocation">The support location where the ray hit the entity.</param> /// <param name="supportNormal">The normal at the surface where the ray hit the entity.</param> /// <param name="supportDistance">Distance from the character to the support location.</param> /// <returns>Whether or not a support was located.</returns> private bool findSupport(out object supportEntityTag, out BEPUphysics.Entities.Entity supportEntity, out Vector3 supportLocation, out Vector3 supportNormal, out float supportDistance) { supportEntity = null; supportEntityTag = null; supportLocation = BEPUutilities.Toolbox.NoVector; supportNormal = BEPUutilities.Toolbox.NoVector; supportDistance = float.MaxValue; const float fudgeFactor = 0.1f; Vector3 rayOrigin = this.Body.Position; rayOrigin.Y += fudgeFactor + this.Height * -0.5f; for (int i = 0; i < this.collisionPairCollector.CollisionInformation.Pairs.Count; i++) { var pair = this.collisionPairCollector.CollisionInformation.Pairs[i]; //Determine which member of the collision pair is the possible support. Collidable candidate = (pair.BroadPhaseOverlap.EntryA == collisionPairCollector.CollisionInformation ? pair.BroadPhaseOverlap.EntryB : pair.BroadPhaseOverlap.EntryA) as Collidable; //Ensure that the candidate is a valid supporting entity. if (candidate.CollisionRules.Personal >= CollisionRule.NoSolver) { continue; //It is invalid! } if (candidate.CollisionRules.Group == Character.NoCollideGroup) { continue; } //The maximum length is supportHeight * 2 instead of supportHeight alone because the character should be able to step downwards. //This acts like a sort of 'glue' to help the character stick on the ground in general. float maximumDistance; //The 'glue' effect should only occur if the character has a solid hold on the ground though. //Otherwise, the character is falling or sliding around uncontrollably. if (this.HasTraction && !this.IsSwimming) { maximumDistance = fudgeFactor + (this.SupportHeight * 2.0f); } else { maximumDistance = fudgeFactor + this.SupportHeight; } foreach (Vector3 rayStart in this.rayOffsets.Select(x => x + rayOrigin)) { BEPUutilities.RayHit rayHit; // Fire a ray at the candidate and determine some details! if (candidate.RayCast(new Ray(rayStart, Vector3.Down), maximumDistance, out rayHit)) { Vector3 n = Vector3.Normalize(rayHit.Normal); if (n.Y > supportNormal.Y) { supportNormal = n; } // We want to find the closest support, so compare it against the last closest support. if (rayHit.T < supportDistance && n.Y > 0.25f) { supportDistance = rayHit.T - fudgeFactor; supportLocation = rayHit.Location; if (rayHit.T < 0.0f) { supportNormal = Vector3.Up; } var entityInfo = candidate as EntityCollidable; if (entityInfo != null) { supportEntity = entityInfo.Entity; supportEntityTag = supportEntity != null ? supportEntity.Tag : candidate.Tag; } else { supportEntityTag = candidate.Tag; } } } } } bool isSupported = supportDistance < float.MaxValue; return(isSupported); }