Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  How to query Obi geometry with raycasts?
#9
This is mostly done, I'm now writing unit tests and performing benchmark scenes.

Here's an example:



The API looks like this:

Code:
// Multiple general queries: pass a list of heterogeneous query shapes, return list of results.
// All other query methods are built on top of this one.
public void SpatialQuery(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms, ObiNativeQueryResultList results);

Code:
// Single general query: pass a single query shape, returns the result.
public QueryResult[] SpatialQuery(QueryShape shape, AffineTransform transform)

Code:
// Multiple raycasts: pass a list of rays, returns list of hits.
public QueryResult[] Raycast(List<Ray> rays, int filter, float maxDistance = 100, float rayThickness = 0)

Code:
// Single raycast: pass a single ray, returns whether it hit, and the hit result.
public bool Raycast(Ray ray, out QueryResult hitInfo, int filter, float maxDistance = 100, float rayThickness = 0)

QueryResult struct:
Code:
public Vector4 simplexBary;        /**< Barycentric coords of nearest point in simplex  */
public Vector4 queryPoint;        /**< Nearest point in query shape*/
public Vector4 normal;       /**< Closest direction between simplex and query shape. */
public float distance;         /** < distance between simplex and query shape.*/
public int simplexIndex;            /** < simplex index*/
public int queryIndex;               /** < query index*/


The code used in the above sample scene, to perform multiple raycasts in parallel:

Code:
using System.Collections.Generic;
using UnityEngine;
using Obi;

public class RaycastLasers : MonoBehaviour
{
    public ObiSolver solver;
    public LineRenderer[] lasers;

    List<Ray> rays = new List<Ray>();

    void Update()
    {
        rays.Clear();

        for (int i = 0; i < lasers.Length; ++i)
        {
            lasers[i].useWorldSpace = true;
            lasers[i].positionCount = 2;
            lasers[i].SetPosition(0, lasers[i].transform.position);
            rays.Add(new Ray(lasers[i].transform.position, lasers[i].transform.up));
        }

        int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);

        var results = solver.Raycast(rays, filter);

        if (results != null)
        {
            for (int i = 0; i < results.Length; ++i)
            {
                lasers[i].SetPosition(1, rays[i].GetPoint(results[i].distance));

                if (results[i].bodyA >= 0)
                {
                    lasers[i].startColor = Color.red;
                    lasers[i].endColor = Color.red;
                }
                else
                {
                    lasers[i].startColor = Color.blue;
                    lasers[i].endColor = Color.blue;
                }
            }
        }
    }
}

Using this API requires particle collisions to be enabled in the solver, otherwise the spatial structure used to perform collision queries (multilevel grid) isn't built. You can set particle collision iterations to zero if you only want queries, but no collisions.

Also, queries work both for regular particle-based collisions and surface-based collisions.

You can specify a ray thickness for raycasts. If the ray hits a simplex, it will return a point on the simplex. If it merely passes near the simplex (within its thickness distance, but no actual hit), it will return the point on the ray closest to the simplex surface.

Internally, this works by iterating trough all occupied cells in the simplex grid. For each cell, tests if the cell intersects the query shape's bounds. If it does, it iterates trough all simplices in the cell, calculating their distance to the query shape (using the same method used for collision detection). Pseudocode:

Code:
parallel foreach (query in queries)
{
   var bounds = calculateQueryBounds(query);
   foreach (occupiedCell in grid)
   {
        if (Intersects(bounds,cell))
        {
            foreach(simplex in occupiedCell)
                 AppendQueryResult(Result(query,simplex));
        }
   }
}

For raycasts I'm not using Bresenham/DDA algorithm as long rays would visit a lot of empty cells (tried it, not good). It's much cheaper to treat them like any other shape: iterate trough all cells we know to have any contents, and reject/accept them based on aabb.
Reply


Messages In This Thread
RE: How to query Obi geometry with raycasts? - by josemendez - 28-05-2021, 11:35 AM