The Wayback Machine - https://web.archive.org/web/20220511193711/https://github.com/libgdx/libgdx/pull/6229
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Octree #6229

Merged
merged 38 commits into from Jun 11, 2021
Merged

Add Octree #6229

merged 38 commits into from Jun 11, 2021

Conversation

yuripourre
Copy link
Contributor

@yuripourre yuripourre commented Oct 15, 2020

Binary Space Partition (BSP) allows developers to work with just a subset of geometries at a given moment, it can be used to improve rendering and/or check collisions. This is a must-have feature for many 3d games.

I really appreciate all feedbacks here!

@mgsx-dev this is the implementation I mentioned. I removed the triangle part so we can start discussing the structure.

Steps

  • Simple implementation
  • Gdx test
  • Generic implementation
  • BoundingBox octree
  • Add collision methods for frustum and ray to Collider
  • Query methods (AABB, Frustum)
  • Query handlers (optional param) for query methods
  • RayCast implementation (with Handler)
  • Add documentation

References:
https://github.com/mgsx-dev/dl13/blob/master/core/src/net/mgsx/dl13/navmesh/NavMesh.java#L105

Copy link
Contributor

@mgsx-dev mgsx-dev left a comment

I made a first quick review, don't take it badly, i didn't take gloves for the sake of review efficiency :D

I'd like to see a first implementation of a query(Frsutum) method to see how it looks, especially userObject/geometry pairing.

to me the nominal case of geometry is BoundaryBox. Vector3 is an edge case IMO.
i'm not even sure we would need more than boundary box approximation, user code could fine grain check collisions over their original shapes. Even for the rayCast scenario, they could supply a handler to perform perfect collision over their original shape. Octree still an approximation to reduce complexity.

Also, maybe we could make it generic : Octree, it would be cleaner. User could always use Octree if they want to mix apples with oranges.

Also i'm a bit balance between using java collection over libgdx collections. I know java one could performs better in most cases but i think we should stay consistent with libgdx code (i know i'll get a shit storm because of what i say here, i'm ready for it :D )

Feel free to disagree with my comments, i could be wrong. I'll probably be more confident about what i said as we going further with the implementation.

Thank you for your work.

gdx/src/com/badlogic/gdx/math/spatial/SpatialDatabase.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/SpatialDatabase.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/spatial/octree/Octree.java Outdated Show resolved Hide resolved
@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Oct 16, 2020

@mgsx-dev Thank you so much for taking the time to review it. For the next days I will work on your comments, and build a working example.

I made a first quick review, don't take it badly, i didn't take gloves for the sake of review efficiency :D

Don't worry at all, we don't have time for that.

I'd like to see a first implementation of a query(Frsutum) method to see how it looks, especially userObject/geometry pairing.

to me the nominal case of geometry is BoundaryBox. Vector3 is an edge case IMO.

Yep, I didn't think about it before you suggested, I had plans for geometry only and do the mapping outside but makes sense to have it.

i'm not even sure we would need more than boundary box approximation, user code could fine grain check collisions over their original shapes. Even for the rayCast scenario, they could supply a handler to perform perfect collision over their original shape. Octree still an approximation to reduce complexity.

Yes, let's start with AABB and see how it goes. I am still not sure how to integrate this structure for the broad phase and a different structure for the narrow phase. I really would like to hide technical details as much as possible.

Also, maybe we could make it generic : Octree, it would be cleaner. User could always use Octree if they want to mix apples with oranges.

Also i'm a bit balance between using java collection over libgdx collections. I know java one could performs better in most cases but i think we should stay consistent with libgdx code (i know i'll get a shit storm because of what i say here, i'm ready for it :D )

Let's go full libgdx then! I've never tried libgdx collections before that's why I didn't think about it.

Feel free to disagree with my comments, i could be wrong. I'll probably be more confident about what i said as we going further with the implementation.

Thank you for your work.

Thank you, all your points are valid.

@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Oct 17, 2020

@mgsx-dev I just updated the code following your precious comments. I didn't added the maxItemsPerNode logic yet.

I quickly ran a debug and it seems to be working.

I will try to find some time this weekend to create a visual "test" example.

Thanks again!

@yuripourre yuripourre requested a review from mgsx-dev Oct 17, 2020
@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Oct 17, 2020

I just realized that the query method would not work as expected, I will work on that as well.

@yuripourre yuripourre force-pushed the spatial-database branch 3 times, most recently from ae8e82c to 9628be4 Compare Nov 8, 2020
@yuripourre yuripourre marked this pull request as ready for review Nov 8, 2020
Copy link
Member

@tommyettinger tommyettinger left a comment

Mostly, I think there's some rough edges on this but they should be easy to polish up. Making the docs use punctuation would be nice.

gdx/src/com/badlogic/gdx/math/Octree.java Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
Copy link
Contributor

@mgsx-dev mgsx-dev left a comment

we also need to think about the "update" case and a proper "remove" :

  • some dynamic objects could move and we would need to update it in the tree (probably remove it and add it again for now, but it could be optimized in the future).
  • when removing an object, we may want to merge some nodes in order to reduce memory footprint. maxItemsPerNode should be true at any time (except for the last depth level of course).

Considering these 2 use cases, we don't want to generate garbage so we may want to handle nodes lifecycle a bit differently :

  • by using a node pool (Pool)
  • by having final Array children. An empty children array would means it's a leaf node.

gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
@mgsx-dev mgsx-dev marked this pull request as draft Nov 10, 2020
@mgsx-dev
Copy link
Contributor

@mgsx-dev mgsx-dev commented Nov 10, 2020

@yuripourre i prefer to keep it as a draft PR until it becomes stable.

@yuripourre yuripourre force-pushed the spatial-database branch 4 times, most recently from 761f6ca to 86768e1 Compare Nov 14, 2020
Copy link
Member

@NathanSweet NathanSweet left a comment

I gave her a once over. Not sure my comments are terribly useful, but here they are! Overall it looks nice.

gdx/src/com/badlogic/gdx/math/Intersector.java Outdated Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Show resolved Hide resolved
gdx/src/com/badlogic/gdx/math/Octree.java Outdated Show resolved Hide resolved
@NathanSweet
Copy link
Member

@NathanSweet NathanSweet commented Dec 8, 2020

Should I squash the commits?

There's no need to squash PRs. Whoever merges this PR can (should!) choose to squash (if they forget, they will be beaten).

return boxes;
}

protected class OctreeNode {
Copy link
Contributor

@Darkyenus Darkyenus Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why is this nested class not static? If it was, the node pool could be static as well and I suspect that it would allow children to be raw array as well.

Copy link
Contributor

@mgsx-dev mgsx-dev Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to avoid garbage as much as possible especially for dynamic stuff, children as raw array wouldn't be better.

sharing pool across multiple Octrees could be an issue, geometry array could be small in some case an big in other case which could lead to unnecessary heavy allocations. This could happen within a single octree though but it would be less annoying (isolated) than with a shared pool.

Copy link
Contributor

@Darkyenus Darkyenus Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to avoid garbage as much as possible especially for dynamic stuff, children as raw array wouldn't be better.

I was referring to the related discussion around the children above, I am not arguing for or against that change, as I have not read the code that thoroughly. That comment was only about the static-ness of OctreeNode.

Copy link
Contributor

@mgsx-dev mgsx-dev Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry i have deep knowledge of that class now :D, so i got directly to the point of shared pool.
To answer your question, OctreeNode use some information from Octree (collider, maxItems), we could pass them to OctreeNode, but not sure it worth it.

mgsx-dev
Copy link
Contributor

@mgsx-dev mgsx-dev commented on 1480a31 Dec 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, i forgot about duplicates. However this strategy is not optimal : temporary ObjectSet will produce garbage and doing this for each removal is maybe not a good idea, maybe we should introduce a new method "pack" called by user code when it feels necessary to optimize the tree, what do you think?

also, avoid diamond operator, it's not supported by some backends :-)

yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented on 1480a31 Dec 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally missed too. I just found it because the unit test broke.
Yes, it's far from optimal, I will think about it tomorrow.

@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Dec 28, 2020

@mgsx-dev

nice catch, i forgot about duplicates. However this strategy is not optimal : temporary ObjectSet will produce garbage and doing this for each removal is maybe not a good idea, maybe we should introduce a new method "pack" called by user code when it feels necessary to optimize the tree, what do you think?

I think we can leave it as-is atm and update in the future. Do you agree with that?

@yuripourre yuripourre requested a review from mgsx-dev Dec 28, 2020
@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented May 29, 2021

Fixed conflicts.

@NathanSweet
Copy link
Member

@NathanSweet NathanSweet commented Jun 2, 2021

Is she all done? :)

@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Jun 5, 2021

@NathanSweet

Is she all done? :)

I think so, I don't have any plans to change the implementation. I hope it can be useful for other developers

@NathanSweet NathanSweet merged commit b8113bd into libgdx:master Jun 11, 2021
1 check passed
NathanSweet added a commit that referenced this issue Jun 11, 2021
* Use default access to avoid synthetic accessors, compiler warnings.
* Don't initialize geometries at worst case capacity, maxItemsPerNode.
* Use OctreeNode[] for children: fewer objects/memory per node, iteration without Iterator, initialized only when/if needed.
* Use createNode() for root, move to Octree.
* Use leaf field internally. Overriding isLeaf to return a different doesn't seem useful.
* Added remove() to OctreeTest.
* Fixed removal being broken because geometries weren't collected recursively.
* Only create a set and add all geometries to it if an object was removed.
* When child nodes were freed they weren't returned to the pool recursively.

#6229
@NathanSweet
Copy link
Member

@NathanSweet NathanSweet commented Jun 11, 2021

@yuripourre It is neat, thanks for sharing! I made a few updates, see 85db7fa. Let me know if you hate any of my changes and we'll revert them.

@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Jun 12, 2021

@NathanSweet not at all! Thanks for the upgrade! ❤️

@yuripourre yuripourre deleted the spatial-database branch Jun 12, 2021
@obigu
Copy link
Contributor

@obigu obigu commented Jun 19, 2021

There's this warning when compiling libGDX which probably causes Octree not to work on GWT:

Compiling module com.badlogic.gdx.tests.gwt.GdxTestsGwt
   Resolving com.badlogic.gdx.math.Octree.OctreeNode
      Found type 'com.badlogic.gdx.math.Octree.OctreeNode'
         Resolving field children
            [ERROR] Unable to resolve inner class OctreeNode in com.badlogic.gdx.math.Octree[]
com.google.gwt.core.ext.typeinfo.NotFoundException
    at com.google.gwt.dev.javac.typemodel.JArrayType.getNestedType(JArrayType.java:157)
    at com.google.gwt.dev.javac.asm.ResolveTypeSignature.visitInnerClassType(ResolveTypeSignature.java:162)
    at org.objectweb.asm.signature.SignatureReader.a(Unknown Source)
    at org.objectweb.asm.signature.SignatureReader.a(Unknown Source)
    at org.objectweb.asm.signature.SignatureReader.acceptType(Unknown Source)
    at com.google.gwt.dev.javac.CompilationUnitTypeOracleUpdater.resolveField(CompilationUnitTypeOracleUpdater.java:1073)

I don't do GWT or Octrees but setting OctreeNode to public seems to fix it but haven't tested properly.

@yuripourre
Copy link
Contributor Author

@yuripourre yuripourre commented Jun 19, 2021

@obigu thanks for reporting, I will try that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants