In addition to collision callbacks w/ triggers, Obi supports several kinds of spatial queries. These are useful to know which simplices are nearby a given position, which simplices are inside a given shape, or if a ray hits any simplex.

Please note that for queries to work, **particle collision constraints must be enabled in the solver**. The reason for this is that queries rely on the spatial data structures
generated by the collision detection pipeline. Obi will not build these structures when particle collision constraints are disabled.

The **ObiSolver** class exposes several spatial query methods. All of them are synchronous: they will be executed as soon as you call them and return results immediately.

Most query methods take one or more **QueryShape** structs as input, and one or more **AffineTransform** structs (there must be *exactly* one AffineTransform per QueryShape). All query methods output one or more **QueryResult** structs.

Here's a list of query methods available:

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

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

// Multiple raycasts: pass a list of rays, returns list of hits. All raycasts are performed in parallel. QueryResult[] Raycast(Listrays, int filter, float maxDistance, float rayThickness);

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

Queries can take one or more **QueryShape** structs as input, that describe the query being performed. These are the
contents of a QueryShape:

- type (QueryType)
- Type of query being performed: Box, Sphere, or Ray.
- center (Vector4)
- Center of the query shape, in the local space defined by the corresponding AffineTransform.
- size (Vector4)
- Size of the query shape, in the local space defined by the corresponding AffineTransform.
- contactOffset (float)
- Additional thickness added to this query's shape.
- maxDistance (float)
- Maximum allowable distance between a simplex and this query shape
- filter ()int
- 32 bit integer describing the collision filter used for this query. Most significant 16 bits encode a
**collision mask**, less significant 16 bits encode a**collision category**. See collisions.

All queries will return one or more **QueryResult** structs, containing information about simplices that are close to a queried shape, or hit by a ray. These are the
contents of a QueryResult:

- simplexIndex (int)
- index of the simplex.
- queryIndex (int)
- index of the query in the input ObiNativeQueryShapeList. Use this to correlate each QueryResult to the query that spawned it.
- simplexBary (Vector4)
- Barycentric coords of nearest point/ray hit in simplex.
- queryPoint (Vector4)
- Nearest point in query shape, expressed in solver space.
- normal (Vector4)
- Shortest direction between simplex and query shape, expressed in solver space.
- distance (float)
- Distance between simplex and query shape.

The trick here is to use a sphere of radius 0 to represent the point. Then use maxDistance to indicate we are interested in all simplices within a given radius of the point. Note that this sample assumes there are only 0-simplices (particles) in the solver.

using UnityEngine; using Obi; public class DistanceToPoint : MonoBehaviour { public ObiSolver solver; public float radius = 1; void Update() { int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0); var query = new QueryShape(QueryShape.QueryType.Sphere, Vector3.zero, Vector3.zero, 0, radius, filter); var xform = new AffineTransform(transform.position, transform.rotation, transform.localScale); QueryResult[] results = solver.SpatialQuery(query, xform); // Iterate over results and draw their distance to the point. // We're assuming the solver only contains 0-simplices (particles). for (int i = 0; i < results.Length; ++i) { if (results[i].distance < radius) { Vector3 pos = solver.transform.TransformPoint(solver.positions[results[i].simplexIndex]); Debug.DrawLine(transform.position, pos, Color.yellow); } } } }

We'll use the generic SpatialQuery method to query multiple boxes at once. Then, we will look for QueryResults with a **negative distance**, as these indicate the simplex is inside one of the boxes. Note that this sample
assumes there are only 0-simplices (particles) in the solver.

using UnityEngine; using Obi; public class OverlapTest : MonoBehaviour { public ObiSolver solver; public Transform[] cubes; ObiNativeQueryShapeList queries; ObiNativeAffineTransformList transforms; ObiNativeQueryResultList results; private void Start() { queries = new ObiNativeQueryShapeList(); transforms = new ObiNativeAffineTransformList(); results = new ObiNativeQueryResultList(); } private void OnDestroy() { queries.Dispose(); transforms.Dispose(); results.Dispose(); } void Update() { int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0); queries.Clear(); transforms.Clear(); for (int i = 0; i < cubes.Length; ++i) { queries.Add(new QueryShape() { type = QueryShape.QueryType.Box, center = Vector3.zero, size = new Vector3(1,1,1), contactOffset = 0, maxDistance = 0, filter = filter }); transforms.Add(new AffineTransform(cubes[i].position,cubes[i].rotation,cubes[i].localScale)); } solver.SpatialQuery(queries,transforms,results); // Iterate over results and draw their distance to the center of the cube. // We're assuming the solver only contains 0-simplices (particles). for (int i = 0; i < results.count; ++i) { if (results[i].distance < 0) { int particleIndex = solver.simplices[results[i].simplexIndex]; Vector3 pos = solver.transform.TransformPoint(solver.positions[particleIndex]); Debug.DrawLine(transforms[results[i].queryIndex].translation, pos, Color.yellow); } } } }

Use the single Raycast method, we'll pass a non-zero *thickness* value to it. When the ray hits a simplex, the QueryResult will contain a point on the surface of the simplex.
When the ray passes merely passes within *thickness* distance of a simplex (without hitting it) the QueryResult will contain the closest point between the ray and the simplex.

using UnityEngine; using Obi; public class SingleRaycast : MonoBehaviour { public ObiSolver solver; void Update() { int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0); // perform a raycast, check if it hit anything: if (solver.Raycast(new Ray(Vector3.zero, Vector3.up), out QueryResult result, filter, 50, 0.1f)) { // get the start and size of the simplex that was hit: int simplexStart = solver.simplexCounts.GetSimplexStartAndSize(result.simplexIndex, out int simplexSize); // Debug draw the simplex: for (int i = 0; i < simplexSize; ++i) { int particleIndex = solver.simplices[simplexStart + i]; Vector3 pos = solver.transform.TransformPoint(solver.positions[particleIndex]); Debug.DrawRay(pos, Vector3.up, Color.yellow); } } } }

We'll perform multiple raycasts at once. This is more efficient than performing many individual raycasts.

using System.Collections.Generic; using UnityEngine; using Obi; public class MultiRaycast : MonoBehaviour { public ObiSolver solver; public Transform[] rayTransforms; List<Ray> rays = new List<Ray>(); void Update() { // build a list of rays from the transform array: rays.Clear(); for (int i = 0; i < rayTransforms.Length; ++i) rays.Add(new Ray(rayTransforms[i].position, rayTransforms[i].up)); int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0); // perform multiple raycasts in parallel var results = solver.Raycast(rays, filter); if (results != null) for (int i = 0; i < results.Length; ++i) { // draw the normal at the hit position if (results[i].simplexIndex >= 0) { var normal = solver.transform.TransformPoint(results[i].normal); Debug.DrawRay(rays[i].GetPoint(results[i].distance), normal, Color.yellow); } } } }