-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Add back ModelInstance class #12588
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
base: main
Are you sure you want to change the base?
Add back ModelInstance class #12588
Conversation
Thank you for the pull request, @lukemckinstry! ✅ We can confirm we have a CLA on file for you. |
}; | ||
|
||
/** | ||
* Process a node. This modifies the following parts of the render resources: |
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 was copied over from the previous instancing stage, and will need to be updated to reflect the actual workflow.
Thanks for getting this updated and itemized @lukemckinstry! Just a few notes on what we should make sure to test. Since the workflow was touched by some refactoring, we should confirm the following are still working for non-instances models:
We also need to check scaling with |
@@ -471,13 +546,14 @@ ModelSceneGraph.prototype.buildDrawCommands = function (frameState) { | |||
modelPipelineStage.process(modelRenderResources, model, frameState); | |||
} | |||
|
|||
const modelPositionMin = Cartesian3.fromElements( | |||
// Positions are in local glTF scene coordinates |
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.
Is this a terminology change, is there a difference between model coordinates and local glTF scene coordinates?
e3a2676
to
1e40c93
Compare
All these are resolved.
Scaling is fixed for regular models, |
I did not add these to the code yet
|
Yes. Let's (a) ignore any other value that it might be set to, and (b) document this behavior in model instances property.
Yes, although a |
I think that would be fine. |
window.startup = async function (Cesium) { | ||
"use strict"; | ||
//Sandcastle_Begin | ||
const viewer = new Cesium.Viewer("cesiumContainer"); |
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.
@lukemckinstry Once the public API is where you'd like it, a reminder to clean up the Sandcastle example.
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.
@lukemckinstry Thanks for resolving the last few features!
I looked over the unit tests so far and left a few comments. Let me know if there are any concerns around those.
}); | ||
|
||
afterAll(function () { | ||
scene = createScene(); |
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.
Should this be scene.destroyForSpecs()
?
}); | ||
|
||
it("model instances update stage updates transform vertex attributes", function () { | ||
return loadGltf(sampleGltfUrl).then(function (gltfLoader) { |
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.
Let's prefer async/await
syntax to promise chains. It flattens the test code and helps makes things more readable.
_modelResources: [], | ||
_pipelineResources: [], | ||
statistics: new ModelStatistics(), | ||
sceneGraph: { |
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.
Let's have this function only set up the mocks unrelated to the functionality we're testing. For instance, would probably expected to see something like the following line in the actual test block.
renderResources.sceneGraph.modelInstances = [sampleInstance1, sampleInstance2];
- It helps make the test more readable for those who are working with them later.
- It allows the mock and the test code to change independently.
0.8146623373031616, 0, 0.5799355506896973, 0, 0, 0, 0, 20, 20, 20, | ||
]); | ||
|
||
const expectedTransformsBuffer = Buffer.createVertexBuffer({ |
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.
It looks like this test is jumping through a few hoops to validate the buffer contents.
Instead of creating a new buffer, perhaps consider validating the output of RuntimeModelInstancingPipelineStage._getTransformsTypedArray
against expectedTransformsTypedArray
, and then checking runtimeNode.instancingTransformsBuffer
's usage
and byteLength
properties directly.
_modelResources: [], | ||
_pipelineResources: [], | ||
statistics: new ModelStatistics(), | ||
sceneGraph: { |
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.
Are we able to use an instance of ModelSceneGraph
here rather than just a mock?
fixedFrameTransform, | ||
); | ||
|
||
const instanceModelMatrix2 = new Transforms.headingPitchRollToFixedFrame( |
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.
Do either of these test scale transforms?
ModelInstance.prototype.getPrimitiveBoundingSphere, | ||
).toHaveBeenCalled(); | ||
|
||
console.log("boundingSphere --> ", boundingSphere); |
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.
Make sure to remove all console.log
calls.
const boundingSphere = instance.getBoundingSphere(model); | ||
expect( | ||
ModelInstance.prototype.getPrimitiveBoundingSphere, | ||
).toHaveBeenCalled(); |
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.
So checking whether a specific function is called is always going to be more fragile than confirm the output is what we'd expect. In this case, instead of validating that getPrimitiveBoundingSphere
is called, maybe a less fragile test would be to confirm the camera has zoomed to the expected position and orientation?
samplePrimitiveBoundingSphere, | ||
); | ||
|
||
expect(ModelInstance.prototype.computeModelMatrix).toHaveBeenCalled(); |
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.
Do we need this expect case? I don't think the bounding sphere values will be correct without it, so we can assume it was called correctly.
Another limitation that I should have included in the above comment but was not top of mind was that the current runtime model instancing implementation does not support changing the size of the instances buffer ie. number of instances arbitrarily over time. We support changing the instances themselves, but not the number available. I believe we talked about this being OK for initial release earlier in development , but implementing the collection ( |
If this is the case, I believe we should re-run the model pipeline with |
That works. But it raises some question about API design
|
@lukemckinstry and I discussed this offline— We'll still need Updates can be managed on the |
Is it possible to summarize why this constraint exists? One could argue that it's possible to "emulate" any |
@javagl I think mostly for API simplicity. We're accounting for double precision for any instance locations on the globe. So a localized |
The question was unrelated to precision. (Precision is a tricky issue here, but ... unrelated for now). I rather thought about cases where people want to create instances in a known, local coordinate space. Think about an airport runway where 200 lights are left and right of the runway, along a straight line, 10 meters apart. And then, users want to put these instanced models at a certain position on the globe, and use the In terms of convenience, the "best" API certainly depends on the use case. For example, in this screenshot, I used lat/lon/height as the input. In other cases, people might only have the local transforms. There may also be cases where the instancing information is given as TRS properties. And people will have to write quite a bit of boilerplate code to assemble these into Matrix4 objects, put them into the array, and maybe squeeze in the ENU-to-FF-matrix for the desired geolocation here. (API design is often sort of a trade-off ... about shifting the responsibility for doing things (correctly!) between the implementor and the user. I think that the API should be easy to use correctly, and hard to use incorrectly. A seemingly(!) very specific aspect here is: People will create instances. They will set the |
Description
Adds back a ModelInstance Class for runtime instancing.
Supply transformation matrices (Matrix4) to the Model for each instance by calling
model.instance.add(transform)
. The transform should be in world space and the modelMatrix of the model should be the default identify Matrix4, which corresponds to the earth center.In this way, the transformation matrix for each instance can be computed from a cartographic coordinate as shown below.
Example:
Still TODO:
ext_mesh_gpu_instance extension
, throw developer errorIssue number and link
#10846
Testing plan
Author checklist
CONTRIBUTORS.md
CHANGES.md
with a short summary of my change