-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
Docs: Update BatchedMesh with addInstance docs #28456
Conversation
docs/api/en/objects/BatchedMesh.html
Outdated
[page:Integer maxIndexCount] - the max number of indices to be used by all geometries.<br /> | ||
[page:Material material] - an instance of [page:Material]. Default is a | ||
new [page:MeshBasicMaterial].<br /> | ||
[page:Integer maxItemCount] - the max number of individual items planned to be added and rendered.<br /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think 'items' will need to be defined, maybe something like:
[page:Integer maxItemCount] - the max number of individual items planned to be added and rendered.<br /> | |
[page:Integer maxItemCount] - the max number of individual items (total instances of any geometry) planned to be added and rendered.<br /> |
I lean slightly toward the name, maxInstanceCount
, with clarification that the max instance count is shared, not per-geometry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Especially given the requirement of instantiating each geometry, I think the term "instances" over "items" makes most sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops - thanks I thought I got rid of "items" everywhere!
[page:Integer maxVertexCount] - the max number of vertices to be used by all unique geometries.<br /> | ||
[page:Integer maxIndexCount] - the max number of indices to be used by all unique geometries.<br /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize it's implied by the word "unique", but just to spell it out:
[page:Integer maxVertexCount] - the max number of vertices to be used by all unique geometries.<br /> | |
[page:Integer maxIndexCount] - the max number of indices to be used by all unique geometries.<br /> | |
[page:Integer maxVertexCount] - the max number of vertices to be used by all unique geometries. Additional instances of a geometry do not contribute toward the vertex limit.<br /> | |
[page:Integer maxIndexCount] - the max number of indices to be used by all unique geometries. Additional instances of a geometry do not contribute toward the index limit.<br /> |
docs/api/en/objects/BatchedMesh.html
Outdated
</p> | ||
|
||
<h3> | ||
[method:Integer setGeometryAt]( [param:Integer index], [param:BufferGeometry geometry] ) | ||
</h3> | ||
<p> | ||
[page:Integer index]: Which geometry index to replace with this geometry. | ||
[page:Integer index]: Which item index to replace with this geometry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't "geometry" be the right term in this case? Questions:
- We're using the terms "index" and "id" interchangeably in this section, which do we prefer?
- When calling
setGeometryAt
for an ID previously allocated withaddGeometry
, are instances of that geometry affected? - When calling
setGeometryAt
for an ID previously allocated withaddInstance
, is any other instance affected? - Is an "instance of an instance" different in any way from an instance created from the same base geometry?
We could consider not supporting creation of "instances of instances" quite yet, if the preferred answers to these questions are non-obvious.
docs/api/en/objects/BatchedMesh.html
Outdated
</p> | ||
<p> | ||
[page:BufferGeometry geometry]: The geometry to substitute at the given geometry index. | ||
</p> | ||
<p> | ||
Replaces the geometry at `index` with the provided geometry. Throws an error if there is not enough space reserved for geometry at the index. | ||
Replaces the geometry used by the item at `index` with the provided geometry. Throws an error if there is not enough space reserved for geometry | ||
at the index. Calling this will change all instances that are rendering that geometry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line answers some of my questions above, but now I'm wondering. Is this by design, by technical requirement, or undecided?
Suppose a BatchedMesh represents a forest with instances of various tree species, each having a high-res and low-res geometry available. Trees within 100m use a high-poly geometry, and those further away use low-res geometries. When an instance crosses the 100m radius I might want to swap out the geometry of that instance alone, which may be difficult within this API. I expect it would be less common to want to the shared geometry of all instances at once, but I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this was one of the reasons for the other notes in #28404:
Another API option might be to enabling managing of geometry vs instances separately ...
There are technically two types of ids at being used here. A "geometry id", which identifies a range in the BatchedMesh geometry to draw, and an "instance id", which internally refers to a geometry to draw via geometry id. Instance ids are, of course, used to set matrices colors, etc of the given id, as well. The current API obscures geometry ids by referencing the geometries via instance id in the interest of making the API easier to understand but it may just make it more confusing in some cases.
In the LoD case you're referring to users could just hide and show associated with different LoDs but I agree that's not ideal if only because of memory usage (unnecessary matrix data storage). And adding / deleting instances will have a performance cost to "fix up" all ids & matrix textures once the "optimize" function is eventually added. We could add a "swap geometry" function to change the underlying geometry pointer but this will lead to other issues like losing the underlying geometry id once all the associated instances lose it.
I'm starting to feel that we should expose the underlying geometry ids (via addGeometry
) in addition to instances (via addInstance
) so the user can be aware of and control both, which I think may be more explainable, too. And then other class abstractions can simplify things further if needed. The downsides are that this will be a bit more verbose and be a breaking change. Using the class would now look like this (copied from the other PR):
const mesh = new BatchedMesh( ... );
// initialize geometries
const geometryId0 = mesh.addGeometry( sphereGeometry );
const geometryId1 = mesh.addGeometry( boxGeometry );
// initialize instances
const instancedId0 = mesh.addInstance( geometryId0 );
mesh.setMatrixAt( instanceId0, ... );
const instancedId1 = mesh.addInstance( geometryId1 );
mesh.setMatrixAt( instanceId1, ... );
const instancedId2 = mesh.addInstance( geometryId0 );
mesh.setMatrixAt( instanceId2, ... );
const instancedId3 = mesh.addInstance( geometryId1 );
mesh.setMatrixAt( instanceId3, ... );
And then we can swap the underlying geometry like so:
mesh.setGeometryForInstance( instanceId0, geometryId1 );
Thanks for thinking about some other use cases!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the LoD case you're referring to users could just hide and show associated with different LoDs but I agree that's not ideal if only because of memory usage ...
Yes, it's currently a similar API InstancedMesh. To support a similar use case there we need multiple InstancedMesh objects, and move the instance matrix between them to 'change' that instance. I think it can be confusing that users must move the instance matrix around to change the geometry of the instance, and this feels similar.
I like the API direction above. Maybe in the interest of getting a first version out sooner, what do you think of saying...
addGeometry
returns a geometry IDaddInstance
takes a geometry ID and returns an instance ID- instance IDs and geometry IDs are not interchangeable (at least for now)
.... and perhaps we do a separate PR for setGeometryForInstance
to keep things simpler. I think the API setGeometryAt( id, geometry )
could also work, we'd just need to keep a WeakMap<BufferGeometry, number>
for the lookups, maybe a little overhead involved.
The downsides are that this will be a bit more verbose and be a breaking change.
What is the breaking change compared to today? That calling addGeometry
alone doesn't cause anything to render?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also keep the API as you first proposed, say that all IDs are instance IDs, and change the behavior of setGeometryAt
to affect only one instance. Either:
// (A) geometry must already be in BatchedMesh, with or without instances
batch.setGeometryAt( id, geometry )
// (B) geometry must already be in BatchedMesh, with >=1 instance
batch.setGeometryAt( targetId, sourceId );
If the last instance of a geometry is removed, all references are lost, and it's eligible for some hypothetical future cleanup process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- addGeometry returns a geometry ID
- addInstance takes a geometry ID and returns an instance ID
- instance IDs and geometry IDs are not interchangeable (at least for now)
Yes this is what I would propose for now if we're going to change the API. We can add more functions later.
I think the API setGeometryAt( id, geometry ) could also work
Perhaps as an option? But it shouldn't be required since once the geometry is in the batched mesh you should want to be able to discard / GC the original geometry to save memory. This can also be done by a user abstraction.
What is the breaking change compared to today? That calling addGeometry alone doesn't cause anything to render?
Yeah - just this. The user must now call "addInstance" after "addGeometry".
If the last instance of a geometry is removed, all references are lost, and it's eligible for some hypothetical future cleanup process.
This is basically what's happening now. But I don't think I love the idea of requiring that users keep geometry around once it's been added to the BatchedMesh. As I mention above I think one expected benefit is that you can GC it after.
I'm inclined to change the API to require calling addInstance
if that sounds good to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the docs to reflect the separation of instanceId and geometryId.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on all points! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All remaining comments are suggestions only, thanks!
const boxGeometryId = batchedMesh.addGeometry( box ); | ||
const sphereGeometryId = batchedMesh.addGeometry( sphere ); | ||
|
||
// create instances of those geometries | ||
const boxInstancedId1 = batchedMesh.addInstance( boxGeometryId ); | ||
const boxInstancedId2 = batchedMesh.addInstance( boxGeometryId ); | ||
|
||
const sphereInstancedId1 = batchedMesh.addInstance( sphereGeometryId ); | ||
const sphereInstancedId2 = batchedMesh.addInstance( sphereGeometryId ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea, certainly not necessary in this release. We could consider having addInstance
take a Matrix4 as a second argument, so that users creating static geometry do not need to explicitly save the instance ID and call setMatrixAt
later.
docs/api/en/objects/BatchedMesh.html
Outdated
[page:Integer maxIndexCount] - the max number of indices to be used by all geometries.<br /> | ||
[page:Material material] - an instance of [page:Material]. Default is a | ||
new [page:MeshBasicMaterial].<br /> | ||
[page:Integer maxItemCount] - the max number of individual items planned to be added and rendered.<br /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Especially given the requirement of instantiating each geometry, I think the term "instances" over "items" makes most sense.
[method:Integer setGeometryAt]( [param:Integer geometryId], [param:BufferGeometry geometry] ) | ||
</h3> | ||
<p> | ||
[page:Integer index]: Which geometry index to replace with this geometry. | ||
[page:Integer geometryId]: Which geometry id to replace with this geometry. | ||
</p> | ||
<p> | ||
[page:BufferGeometry geometry]: The geometry to substitute at the given geometry index. | ||
[page:BufferGeometry geometry]: The geometry to substitute at the given geometry id. | ||
</p> | ||
<p> | ||
Replaces the geometry at `index` with the provided geometry. Throws an error if there is not enough space reserved for geometry at the index. | ||
Replaces the geometry at `geometryId` with the provided geometry. Throws an error if there is not enough space reserved for geometry. | ||
Calling this will change all instances that are rendering that geometry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No changes here, but perhaps the future API is:
setGeometryAt( geometryId, geometry )
overwrites a geometry (up to the allocated vertex count), and affects all instances of that geometrysetInstanceAt( instanceId, geometryId )
changes the geometry instantiated by a specific instance ID.
Related issue: #28404, #28462
Description
Updates the BatchedMesh docs with the new functionality from #28404. Separate PR so it's easier to take a look at. It would be good to get some eyes on it that are less familiar with the concepts here, as well. I've also removed the instancedMultiDraw "getInstancedCountAt" and "setInstanceCountAt" functions to avoid naming confusion.
cc @donmccurdy @RenaudRohlinger @CodyJasonBennett