Search Unity

How is the world space radius of a sphere collider / capsule collider calculated?

Discussion in 'Physics' started by pk1234dva, Oct 14, 2021.

  1. pk1234dva

    pk1234dva

    Joined:
    May 27, 2019
    Posts:
    84
    I can't find a reference on this - how is the "world space" sphere collider radius determined based on the scaling aspect of the object (and its parents)?

    I'd guess the "correct" approach would be to calculate the SVD of the local to world matrix, and take the maximum from the diagonal matrix. But that doesn't seem to be happening, as the attached example shows - you can replicate it by (all in x,y,z order as in the editor, using a standard sphere primitive):

    Parent game object:
    position (0, 0 ,0 ), rotation (0, 0, 0), scale (1, 1, 3),
    Child sphere object:
    position (0, 5 ,5 ), rotation (45, 0, 0), scale (1, 2, 1).

    After some testing it seems like maybe the radius is just determined by taking the maximum of the lengths of the first 3 columns of the local to world matrix, but I'd appreciate a confirmation on this.
     

    Attached Files:

    • tst.jpg
      tst.jpg
      File size:
      20.9 KB
      Views:
      276
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,443
    Without checking in detail, you're probably right. It would be nice if the colliders also got distorted to match the uneven scale or skew that you have applied to the object, but I completely understand why they didn't do that. It's a LOT heavier computation to compute each possible intersection of primitives (ray vs sphere, sphere vs capsule, capsule vs box, etc.) if those primitives can be arbitrarily scaled with independent axes.
     
  3. pk1234dva

    pk1234dva

    Joined:
    May 27, 2019
    Posts:
    84
    That's not exactly what I meant - I don't have an issue with the collider not being flattened, what I "mind" is that the mesh goes outside the sphere collider. I would expect the sphere's radius would be adjusted in such a way, so that the mesh fits inside it, i.e. the sphere acting as a "bounding sphere". But that's not what happens.

    I don't really care what happens, this is fine too, but I'm working on an asset where I need to replicate the shape of the collider, so I would like to know how the radius is decided.

    The "reasonable" method, of adjusting the sphere radius, so that the mesh fits inside it would be done using an SVD decomposition, and I'd know how to do that, but I don't know how the radius is determined in Unity. Might just be maximum of length of columns of the local to world matrix like I mentioned, but I don't know.
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    I'm not a 3D physics dev but here's a pseudo-cope snippet from the SphereCollider native code. This is passed directly into the PhysX geometry (physx: PxSphereGeometry sSphere) radius:
    Code (CSharp):
    1. float SphereCollider::GetScaledRadius() const
    2. {
    3.     Vector3f scale = GetComponent<Transform>().GetWorldScaleLossy();
    4.     float absoluteRadius = Abs(std::max(std::max(Abs(scale.x), Abs(scale.y)), Abs(scale.z)) * m_Radius);
    5.     return std::max(absoluteRadius, 0.00001F);
    6. }
    I'm sure you can do the translation from above into C#. The scale though is Transform.lossyScale.
     
  5. pk1234dva

    pk1234dva

    Joined:
    May 27, 2019
    Posts:
    84
    Thanks a ton, yes it seems it uses lossyScale. Could I please ask for similar pseudo-code for capsule and box?
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Box is the same where the box size is scaled by the lossy scale and Abs used to give a positive size.

    Capsule is more complex. Rough code is:
    Code (CSharp):
    1.  
    2. {
    3.     const float kMinSize = 0.00001F;
    4.  
    5.     const Vector3f scale = Vector3f scale = GetComponent<Transform>().GetWorldScaleLossy();
    6.  
    7.     // m_Direction is enum { X-Axis = 0, Y-Axis = 1, Z-Axis = 2 }
    8.     const float absoluteHeight = std::max(Abs(m_Height * scale[m_Direction]), kMinSize);
    9.     const float scale1 = scale[(m_Direction + 1) % 3];
    10.     const float scale2 = scale[(m_Direction + 2) % 3];
    11.     const float absoluteRadius = std::max(Abs(scale1), Abs(scale2)) * m_Radius;
    12.  
    13.     float height = absoluteHeight - absoluteRadius * 2.0F;
    14.     height = std::max(height, kMinSize);
    15.     float radius = std::max(absoluteRadius, kMinSize);
    16.  
    17.     Vector2f extents = Vector2f(radius, height);
    18.     PxCapsuleGeometry sCapsule(extents.x, extents.y * 0.5f);
    19. }
    20.  
    NOTE: The code above isn't a copy of the code, it's an approximation of it but it's essentially correct.
     
  7. pk1234dva

    pk1234dva

    Joined:
    May 27, 2019
    Posts:
    84
    Thanks a ton, you've saved me loads of time, I've tested it and everything works fine.
     
    MelvMay likes this.