namespace Fusion.Addons.KCC
{
using System;
using System.Collections.Generic;
using UnityEngine;
// This file contains implementation related to physics.
public partial class KCC
{
// PUBLIC METHODS
///
/// Sphere overlap using same filtering as for KCC physics query.
///
/// Contains results of the overlap.
/// Center position of the sphere.
/// Radius of the sphere.
/// Use to enable/disable trigger hits.
public bool SphereOverlap(KCCOverlapInfo overlapInfo, Vector3 position, float radius, QueryTriggerInteraction triggerInteraction)
{
return SphereOverlap(overlapInfo, Data, position, radius, default, _settings.CollisionLayerMask, triggerInteraction);
}
///
/// Capsule overlap using same filtering as for KCC physics query.
///
/// Contains results of the overlap.
/// Bottom position of the capsule.
/// Radius of the capsule.
/// Height of the capsule.
/// Use to enable/disable trigger hits.
public bool CapsuleOverlap(KCCOverlapInfo overlapInfo, Vector3 position, float radius, float height, QueryTriggerInteraction triggerInteraction)
{
return CapsuleOverlap(overlapInfo, Data, position, radius, height, default, _settings.CollisionLayerMask, triggerInteraction);
}
///
/// Ray cast using same filtering as for KCC physics query.
///
/// Contains results of the cast sorted by distance.
/// Origin position of the cast.
/// Direction of the cast.
/// Distance of the cast.
/// Use to enable/disable trigger hits.
public bool RayCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction)
{
return RayCast(shapeCastInfo, Data, position, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction);
}
///
/// Sphere cast using same filtering as for KCC physics query.
///
/// Contains results of the cast sorted by distance.
/// Center position of the sphere.
/// Radius of the sphere.
/// Direction of the cast.
/// Distance of the cast.
/// Use to enable/disable trigger hits.
/// Set to true for the result to contain initially overlapping colliders.
public bool SphereCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, float radius, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps = true)
{
return SphereCast(shapeCastInfo, Data, position, radius, default, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction, trackInitialOverlaps);
}
///
/// Capsule cast using same filtering as for KCC physics query.
///
/// Contains results of the cast sorted by distance.
/// Bottom position of the capsule.
/// Radius of the capsule.
/// Height of the capsule.
/// Direction of the cast.
/// Distance of the cast.
/// Use to enable/disable trigger hits.
/// Set to true for the result to contain initially overlapping colliders.
public bool CapsuleCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, float radius, float height, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps = true)
{
return CapsuleCast(shapeCastInfo, Data, position, radius, height, default, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction, trackInitialOverlaps);
}
///
/// Force refresh KCCData.Hits based on current position. If an existing overlap info is provided, the method takes as much information as possible from it.
/// Metadata (collision type, penetration, ...) for other (new) hits will be missing. Use with caution.
///
/// Base overlap query results. Only matching colliders metadata is taken from this info if new overlap query is executed.
/// Controls reuse of base query results / execution of a new overlap query.
///
/// - Default - Hits from base overlap query will be reused only if all colliders are within extent, otherwise new overlap query will be executed.
/// - Reuse - Force reuse hits from base overlap query, even if colliders are not within extent.
/// - New - Force execute new overlap query.
///
///
public void UpdateHits(KCCOverlapInfo baseOverlapInfo, EKCCHitsOverlapQuery overlapQuery)
{
UpdateHits(Data, baseOverlapInfo, overlapQuery);
}
///
/// Check if the KCC potentially collides with a collider, using same filtering as physics query.
/// Returning true doesn't mean the collider overlaps. Can be used as a filter after custom overlap/shapecast query.
///
/// Collider instance.
public bool IsValidHitCollider(Collider hitCollider)
{
if (hitCollider == null)
return false;
return IsValidHitCollider(Data, hitCollider);
}
// PRIVATE METHODS
private bool SphereOverlap(KCCOverlapInfo overlapInfo, KCCData data, Vector3 position, float radius, float extent, LayerMask layerMask, QueryTriggerInteraction triggerInteraction)
{
overlapInfo.Reset(false);
overlapInfo.Position = position;
overlapInfo.Radius = radius;
overlapInfo.Height = 0.0f;
overlapInfo.Extent = extent;
overlapInfo.LayerMask = layerMask;
overlapInfo.TriggerInteraction = triggerInteraction;
Collider hitCollider;
Collider[] hitColliders = _hitColliders;
int hitColliderCount = Runner.GetPhysicsScene().OverlapSphere(position, radius + extent, hitColliders, layerMask, triggerInteraction);
for (int i = 0; i < hitColliderCount; ++i)
{
hitCollider = hitColliders[i];
if (IsValidHitColliderUnsafe(data, hitCollider) == true)
{
overlapInfo.AddHit(hitCollider);
}
}
return overlapInfo.AllHitCount > 0;
}
private bool CapsuleOverlap(KCCOverlapInfo overlapInfo, KCCData data, Vector3 position, float radius, float height, float extent, LayerMask layerMask, QueryTriggerInteraction triggerInteraction)
{
overlapInfo.Reset(false);
overlapInfo.Position = position;
overlapInfo.Radius = radius;
overlapInfo.Height = height;
overlapInfo.Extent = extent;
overlapInfo.LayerMask = layerMask;
overlapInfo.TriggerInteraction = triggerInteraction;
Vector3 positionUp = position + new Vector3(0.0f, height - radius, 0.0f);
Vector3 positionDown = position + new Vector3(0.0f, radius, 0.0f);
Collider hitCollider;
Collider[] hitColliders = _hitColliders;
int hitColliderCount = Runner.GetPhysicsScene().OverlapCapsule(positionDown, positionUp, radius + extent, hitColliders, layerMask, triggerInteraction);
for (int i = 0; i < hitColliderCount; ++i)
{
hitCollider = hitColliders[i];
if (IsValidHitColliderUnsafe(data, hitCollider) == true)
{
overlapInfo.AddHit(hitCollider);
}
}
return overlapInfo.AllHitCount > 0;
}
private bool RayCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction)
{
shapeCastInfo.Reset(false);
shapeCastInfo.Position = position;
shapeCastInfo.Direction = direction;
shapeCastInfo.MaxDistance = maxDistance;
shapeCastInfo.LayerMask = layerMask;
shapeCastInfo.TriggerInteraction = triggerInteraction;
RaycastHit raycastHit;
RaycastHit[] raycastHits = _raycastHits;
int raycastHitCount = Runner.GetPhysicsScene().Raycast(position, direction, raycastHits, maxDistance, layerMask, triggerInteraction);
for (int i = 0; i < raycastHitCount; ++i)
{
raycastHit = raycastHits[i];
if (IsValidHitColliderUnsafe(data, raycastHit.collider) == true)
{
shapeCastInfo.AddHit(raycastHit);
}
}
shapeCastInfo.Sort();
return shapeCastInfo.AllHitCount > 0;
}
private bool SphereCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, float radius, float extent, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps)
{
shapeCastInfo.Reset(false);
shapeCastInfo.Position = position;
shapeCastInfo.Radius = radius;
shapeCastInfo.Extent = extent;
shapeCastInfo.Direction = direction;
shapeCastInfo.MaxDistance = maxDistance;
shapeCastInfo.LayerMask = layerMask;
shapeCastInfo.TriggerInteraction = triggerInteraction;
RaycastHit raycastHit;
RaycastHit[] raycastHits = _raycastHits;
int raycastHitCount = Runner.GetPhysicsScene().SphereCast(position, radius + extent, direction, raycastHits, maxDistance, layerMask, triggerInteraction);
for (int i = 0; i < raycastHitCount; ++i)
{
raycastHit = raycastHits[i];
if (trackInitialOverlaps == false && raycastHit.distance <= 0.0f && raycastHit.point.Equals(default) == true)
continue;
if (IsValidHitColliderUnsafe(data, raycastHit.collider) == true)
{
shapeCastInfo.AddHit(raycastHit);
}
}
shapeCastInfo.Sort();
return shapeCastInfo.AllHitCount > 0;
}
private bool CapsuleCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, float radius, float height, float extent, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps)
{
shapeCastInfo.Reset(false);
shapeCastInfo.Position = position;
shapeCastInfo.Radius = radius;
shapeCastInfo.Height = height;
shapeCastInfo.Extent = extent;
shapeCastInfo.Position = position;
shapeCastInfo.Direction = direction;
shapeCastInfo.MaxDistance = maxDistance;
shapeCastInfo.LayerMask = layerMask;
shapeCastInfo.TriggerInteraction = triggerInteraction;
Vector3 positionUp = position + new Vector3(0.0f, height - radius, 0.0f);
Vector3 positionDown = position + new Vector3(0.0f, radius, 0.0f);
RaycastHit raycastHit;
RaycastHit[] raycastHits = _raycastHits;
int raycastHitCount = Runner.GetPhysicsScene().CapsuleCast(positionDown, positionUp, radius + extent, direction, raycastHits, maxDistance, layerMask, triggerInteraction);
for (int i = 0; i < raycastHitCount; ++i)
{
raycastHit = raycastHits[i];
if (trackInitialOverlaps == false && raycastHit.distance <= 0.0f && raycastHit.point.Equals(default) == true)
continue;
if (IsValidHitColliderUnsafe(data, raycastHit.collider) == true)
{
shapeCastInfo.AddHit(raycastHit);
}
}
shapeCastInfo.Sort();
return shapeCastInfo.AllHitCount > 0;
}
private void UpdateHits(KCCData data, KCCOverlapInfo baseOverlapInfo, EKCCHitsOverlapQuery overlapQuery)
{
bool reuseOverlapInfo;
switch (overlapQuery)
{
case EKCCHitsOverlapQuery.Default: { reuseOverlapInfo = baseOverlapInfo != null && baseOverlapInfo.AllHitsWithinExtent() == true; break; }
case EKCCHitsOverlapQuery.Reuse: { reuseOverlapInfo = baseOverlapInfo != null; break; }
case EKCCHitsOverlapQuery.New: { reuseOverlapInfo = false; break; }
default:
throw new NotImplementedException(nameof(overlapQuery));
}
if (reuseOverlapInfo == true)
{
_trackOverlapInfo.CopyFromOther(baseOverlapInfo);
}
else
{
CapsuleOverlap(_trackOverlapInfo, data, data.TargetPosition, _settings.Radius, _settings.Height, _settings.Extent, _settings.CollisionLayerMask, QueryTriggerInteraction.Collide);
if (baseOverlapInfo != null)
{
for (int i = 0; i < _trackOverlapInfo.AllHitCount; ++i)
{
KCCOverlapHit trackedHit = _trackOverlapInfo.AllHits[i];
for (int j = 0; j < baseOverlapInfo.AllHitCount; ++j)
{
KCCOverlapHit extendedHit = baseOverlapInfo.AllHits[j];
if (object.ReferenceEquals(trackedHit.Collider, extendedHit.Collider) == true)
{
trackedHit.CopyFromOther(extendedHit);
}
}
}
}
}
data.Hits.Clear();
for (int i = 0, count = _trackOverlapInfo.AllHitCount; i < count; ++i)
{
data.Hits.Add(_trackOverlapInfo.AllHits[i]);
}
}
private void ForceRemoveAllHits(KCCData data)
{
_trackOverlapInfo.Reset(false);
_extendedOverlapInfo.Reset(false);
data.Hits.Clear();
}
private bool IsValidHitCollider(KCCData data, Collider hitCollider)
{
if (hitCollider == _collider.Collider)
return false;
for (int i = 0, count = _childColliders.Count; i < count; ++i)
{
if (hitCollider == _childColliders[i])
return false;
}
int colliderLayerMask = 1 << hitCollider.gameObject.layer;
if ((_settings.CollisionLayerMask & colliderLayerMask) != colliderLayerMask)
return false;
List ignores = data.Ignores.All;
for (int i = 0, count = ignores.Count; i < count; ++i)
{
if (hitCollider == ignores[i].Collider)
return false;
}
if (ResolveCollision != null)
{
try
{
return ResolveCollision(this, hitCollider);
}
catch (Exception exception)
{
UnityEngine.Debug.LogException(exception);
}
}
return true;
}
private bool IsValidHitColliderUnsafe(KCCData data, Collider overlapCollider)
{
if (object.ReferenceEquals(overlapCollider, _collider.Collider) == true)
return false;
for (int i = 0, count = _childColliders.Count; i < count; ++i)
{
if (object.ReferenceEquals(overlapCollider, _childColliders[i]) == true)
return false;
}
List ignores = data.Ignores.All;
for (int i = 0, count = ignores.Count; i < count; ++i)
{
if (object.ReferenceEquals(overlapCollider, ignores[i].Collider) == true)
return false;
}
if (ResolveCollision != null)
{
try
{
return ResolveCollision(this, overlapCollider);
}
catch (Exception exception)
{
UnityEngine.Debug.LogException(exception);
}
}
return true;
}
}
}