Skip to content

Commit 81a93d6

Browse files
committed
package trial and error
1 parent b40eebc commit 81a93d6

File tree

6 files changed

+167
-26
lines changed

6 files changed

+167
-26
lines changed

Packages/NativeTrees/README.md

Lines changed: 144 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
![NativeTrees](NativeTrees.png "NativeTrees")
1+
![NativeTrees](./Packages/NativeTrees/NativeTrees.png "NativeTrees")
22

3-
Generic sparse octree and quadtree
3+
Generic sparse octree and quadtree
44
that store objects together with their axis aligned bounding boxes (AABB's)
55

66
Written in C# for Unity's burst compiler and the ECS/DOTS framework.
@@ -11,7 +11,6 @@ Tested with Unity 2021.1.3.11f1
1111
- Range (AABB overlap)
1212
- K-Nearest neighbours
1313

14-
1514
### Other features:
1615
- Implemented as a sparse tree, so only stores nodes that are occupied. Memory usage is therefore relatively low.
1716
The octree has a max depth of 10 and the quad tree a max depth of 15.
@@ -22,30 +21,36 @@ Tested with Unity 2021.1.3.11f1
2221
### Limitations:
2322
- Objects must be structs (Burst limitation)
2423
- No remove or update. Tried several approaches but they either left an unbalanced tree or doing a
25-
full clear and re-insert was faster.
24+
full clear and re-insert was faster.
2625
- No foreach support, we leverage the stack and "struct" delegates, which suits the recursive
27-
nature of the tree better.
28-
26+
nature of the tree better.
27+
2928
- Insertion can only be done on a single thread. Queries ofcourse can be across multiple threads.
3029

3130
### Future todo's:
3231
- Frustrum query
3332
- 'Fat' raycast (virtually expand AABB's of nodes and objects when testing for ray intersections)
3433

34+
## Installation
35+
36+
Using the Unity package manager, choose Addd Package from git URL and enter:
37+
38+
https://github.com/bartofzo/NativeTrees.git?path=/Packages/NativeTrees
39+
3540
## Performance
3641

37-
The trees are heaviliy optimized to make use of SIMD instructions.
42+
The trees are heaviliy optimized to make use of SIMD instructions.
3843
Therefore performance is best when used in burst compiled code.
3944

40-
Queries are *very* fast.
45+
Queries are *very* fast.
4146
The raycast never visits more nodes than absolutely neccessary.
4247
The overlap (and insertion) use a technique where to test in which child nodes AABB's should go, only
4348
two comparisons are made followed up by some bitwise operations. (See the source for an explanation).
4449

4550
Nearest neighbour is the slowest of the bunch (but still fast) as it has some overhead in keeping track of a priority queue.
4651

4752
Actual performance can vary wildly depending on the structure of your tree and it's
48-
settings. For the sake of cool stats, here are some numbers on insertion times and raycasts for random points and AABB's,
53+
settings. For the sake of cool stats, here are some numbers on insertion times and raycasts for random points and AABB's,
4954
Single thread burst compiled, maxDepth of 8. These numbers should not be taken too seriously because it's random data
5055
and the tree will be divided almost equally everywhere, which in most realistic scenarios is not the case.
5156

@@ -63,15 +68,139 @@ Run on a MBP 16" i9 2.3 GHz from 2019.
6368
|500K |51.00ms |131.58ms |60.08ms |164.17ms |0.84ms |0.45ms
6469
|1M |129.03ms |260.55ms |152.50ms |301.99ms |1.04ms |0.55ms
6570

71+
## Usage
72+
73+
There are two samples included that show how to use the octree and quadtree.
74+
The extension classes provide readymade solutions for AABB only checking. For more complicated
75+
shapes you must provide your own ray/overlap/distance calculations.
76+
77+
### Insertion
78+
The objects can be of any unmanaged type, when inserting, an AABB must be provided:
79+
80+
// Insert a bunch of triangles
81+
for (int i = 0; i < tris.Length; i++)
82+
{
83+
var triangle = tris[i];
84+
octree.Insert(triangle, triangle.GetAABB());
85+
}
86+
87+
Often times however, it's more efficient to insert Id's that map to something outside of
88+
the tree (like DOTS entities).
89+
90+
If you know your objects are points, you can insert them faster by using:
91+
92+
// Insert entities that are 'points'
93+
for (int i = 0; i < entities.Length; i++)
94+
{
95+
var entity = entities[i];
96+
octree.InsertPoint(entities[i], positions[i]);
97+
}
98+
99+
Note that objects inserted as points only support range and nearest neighbour queries.
100+
101+
### Queries
102+
103+
All of the supported queries use the same pattern which is
104+
to (ab)use structs as a sort of delegate. This separates collision/intersection
105+
code from the type of objects, allowing you to insert even primitive types or types from another assembly.
106+
This turned out to be the most efficent and easiest to implement while keeping things fully compatibly with Unity's Burst compiler.
107+
108+
### Raycast
109+
110+
A raycast query for example, requires you to implement IOctreeRayIntersecter which
111+
acts as a delegate to determine if a ray intersects with an object that's in the tree.
112+
113+
public static bool RaycastAABB<T>(this NativeOctree<T> octree, Ray ray, out OctreeRaycastHit<T> hit) where T : unmanaged
114+
{
115+
return octree.Raycast<RayAABBIntersecter<T>>(ray, out hit);
116+
}
117+
118+
struct RayAABBIntersecter<T> : IOctreeRayIntersecter<T>
119+
{
120+
public bool IntersectRay(in PrecomputedRay ray, T obj, AABB objBounds, out float distance)
121+
{
122+
return objBounds.IntersectsRay(ray, out distance);
123+
}
124+
}
125+
126+
The example above just tests the ray against the object's bounds. (See NativeOctreeExtensions) But you could go a step further
127+
and test it against a triangle, a collider and so forth. Note that the tree itself
128+
does not automatically test for Ray-AABB intersections on the objects, so it's usually a good decision to early exit
129+
if the ray doesn't exit with the object's bounds since those checks are cheap.
130+
131+
### Nearest Neighbour
132+
NativeTrees support nearest neighbour queries. You should implement IOctreeNearestVisitor and IOctreeDistanceProvider.
133+
134+
struct AABBDistanceSquaredProvider<T> : IOctreeDistanceProvider<T>
135+
{
136+
// Just return the distance squared to our bounds
137+
public float DistanceSquared(float3 point, T obj, AABB bounds) => bounds.DistanceSquared(point);
138+
}
139+
140+
struct OctreeNearestAABBVisitor<T> : IOctreeNearestVisitor<T>
141+
{
142+
public T nearest;
143+
public bool found;
144+
145+
public bool OnVist(T obj)
146+
{
147+
this.found = true;
148+
this.nearest = obj;
149+
150+
return false; // immediately stop iterating at first hit
151+
// if we want the 2nd or 3rd neighbour, we could iterate on and keep track of the count!
152+
}
153+
}
154+
155+
The extensions classes show an example of these implementation. But only for AABB's.
156+
If you need more detail on your distance, you can implement your type specific behaviour using these interfaces.
157+
158+
To get the nearest neighbour:
159+
160+
var visitor = new OctreeNearestAABBVisitor<Entity>();
161+
octree.Nearest(point, maxDistance, ref visitor, default(AABBDistanceSquaredProvider<Entity>));
162+
Entity nearestEntity = visitor.nearest;
163+
164+
### Range
165+
166+
Here's an example that adds unique objects that overlap with a range to a hashset:
167+
168+
public static void RangeAABBUnique<T>(this NativeOctree<T> octree, AABB range, NativeParallelHashSet<T> results)
169+
where T : unmanaged, IEquatable<T>
170+
{
171+
var vistor = new RangeAABBUniqueVisitor<T>()
172+
{
173+
results = results
174+
};
175+
176+
octree.Range(range, ref vistor);
177+
}
178+
179+
struct RangeAABBUniqueVisitor<T> : IOctreeRangeVisitor<T> where T : unmanaged, IEquatable<T>
180+
{
181+
public NativeParallelHashSet<T> results;
182+
183+
public bool OnVisit(T obj, AABB objBounds, AABB queryRange)
184+
{
185+
// check if our object's AABB overlaps with the query AABB
186+
if (objBounds.Overlaps(queryRange))
187+
results.Add(obj);
188+
189+
return true; // always keep iterating, we want to catch all objects
190+
}
191+
}
192+
193+
It's important to note that the query itself iterates all of the objects that are in nodes that overlap with
194+
the input range. An extra check should be performed to test if the object overlaps.
195+
Also, if the objects aren't points, it's possible for them to be visited multiple times as they reside in more than one node.
196+
A hashset can be used to only visit each object once.
197+
66198
### Support
67199
Feel free to raise an issue or contact me for any questions.
200+
The code is free to use in your project(s).
201+
If this was helpful to you, consider buying me a coffee ;)
68202

69-
### Donate
70-
The code is free to use in your project(s).
71-
If this was helpful to you and you're feeling generous, you can support me with a donation to help me pay for my energy bills and making
72-
more of these projects in the future ;)
73-
74-
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/bartofzo?country.x=NL&locale.x=nl_NL)
203+
https://ko-fi.com/bartofzo
75204

76205
Thank you!
77206

Packages/NativeTrees/Runtime/bartofzo.nativetrees.asmdef

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"name": "com.bartofzo.nativetrees",
33
"rootNamespace": "NativeTrees",
44
"references": [
5-
"GUID:e0cd26848372d4e5c891c569017e11f1",
6-
"GUID:d8b63aba1907145bea998dd612889d6b"
5+
"Unity.Collections",
6+
"Unity.Mathematics"
77
],
88
"includePlatforms": [],
99
"excludePlatforms": [],

Packages/NativeTrees/Samples/bartofzo.nativetrees.samples.asmdef

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
"name": "com.bartofzo.nativetrees.samples",
33
"rootNamespace": "NativeTrees.Samples",
44
"references": [
5-
"GUID:2665a8d13d1b3f18800f46e256720795",
6-
"GUID:17f495d31df1b4f1c90363273beccc77",
7-
"GUID:e0cd26848372d4e5c891c569017e11f1",
8-
"GUID:d8b63aba1907145bea998dd612889d6b"
5+
"Unity.Burst",
6+
"com.bartofzo.nativetrees",
7+
"Unity.Collections",
8+
"Unity.Mathematics"
99
],
1010
"includePlatforms": [],
1111
"excludePlatforms": [],

Packages/NativeTrees/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@
1010
"spatial partitioning"
1111
],
1212
"category": "Unity",
13-
"type": "library"
13+
"type": "library",
14+
"dependencies": {
15+
"com.unity.collections": "",
16+
"com.unity.burst": "",
17+
"com.unity.mathematics": ""
18+
}
1419
}

Packages/packages-lock.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
"version": "file:NativeTrees",
55
"depth": 0,
66
"source": "embedded",
7-
"dependencies": {}
7+
"dependencies": {
8+
"com.unity.collections": "",
9+
"com.unity.burst": "",
10+
"com.unity.mathematics": ""
11+
}
812
},
913
"com.unity.burst": {
1014
"version": "1.6.6",

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Tested with Unity 2021.1.3.11f1
1111
- Range (AABB overlap)
1212
- K-Nearest neighbours
1313

14-
1514
### Other features:
1615
- Implemented as a sparse tree, so only stores nodes that are occupied. Memory usage is therefore relatively low.
1716
The octree has a max depth of 10 and the quad tree a max depth of 15.
@@ -32,6 +31,12 @@ nature of the tree better.
3231
- Frustrum query
3332
- 'Fat' raycast (virtually expand AABB's of nodes and objects when testing for ray intersections)
3433

34+
## Installation
35+
36+
Using the Unity package manager, choose Addd Package from git URL and enter:
37+
38+
https://github.com/bartofzo/NativeTrees.git?path=/Packages/NativeTrees
39+
3540
## Performance
3641

3742
The trees are heaviliy optimized to make use of SIMD instructions.
@@ -192,8 +197,6 @@ A hashset can be used to only visit each object once.
192197

193198
### Support
194199
Feel free to raise an issue or contact me for any questions.
195-
196-
### Donate
197200
The code is free to use in your project(s).
198201
If this was helpful to you, consider buying me a coffee ;)
199202

0 commit comments

Comments
 (0)