-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Animations, reframeCamera, pick multiple models #16145
base: master
Are you sure you want to change the base?
Animations, reframeCamera, pick multiple models #16145
Conversation
Add support for multiple models for animations, reframing the camera and picking
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
Snapshot stored with reference name: Test environment: To test a playground add it to the URL, for example: https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/16145/merge/index.html#WGZLGJ#4600 Links to test babylon tools with this snapshot: https://playground.babylonjs.com/?snapshot=refs/pull/16145/merge To test the snapshot in the playground with a playground ID add it after the snapshot query string: https://playground.babylonjs.com/?snapshot=refs/pull/16145/merge#BCU1XR#0 |
WebGL2 visualization test reporter: |
Visualization tests for WebGPU |
Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). |
Snapshot stored with reference name: Test environment: To test a playground add it to the URL, for example: https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/16145/merge/index.html#WGZLGJ#4600 Links to test babylon tools with this snapshot: https://playground.babylonjs.com/?snapshot=refs/pull/16145/merge To test the snapshot in the playground with a playground ID add it after the snapshot query string: https://playground.babylonjs.com/?snapshot=refs/pull/16145/merge#BCU1XR#0 |
Visualization tests for WebGPU |
WebGL2 visualization test reporter: |
packages/tools/viewer/src/viewer.ts
Outdated
*/ | ||
public get animations(): readonly string[] { | ||
return this._activeModelBacking?.assetContainer.animationGroups.map((group) => group.name) ?? []; | ||
return this._loadedModelsContainer.animationGroups.map((group) => group.name) ?? []; |
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 the base Viewer class should remain single-model-centric in the public interface. The rest of the public interface basically operates on a single (active) model - playing an animation, selecting a material variant, etc. If we change this, then if there are multiple models you'd see animations for all the models in the animation list, but when playing an animation it would target the active model, which wouldn't work.
Can we leave this as is, and if a subclass needs to enumerate all animations across all models, this can still be done through the _loadedModels
property?
packages/tools/viewer/src/viewer.ts
Outdated
if (this._activeModel) { | ||
const model = this._activeModel?.assetContainer; | ||
if (this._loadedModels.length > 0) { | ||
const container = this._loadedModelsContainer; |
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.
Having the extra _loadedModelsContainer
state seems like it is mostly here for convenience in enumerating all meshes, all animations, etc. But the added (redundant) state seems like it creates more opportunity for bugs and to me makes it a bit harder to understand the code.
Instead of adding this state, whenever we need to enumerate meshes/animations/etc. across multiple models, can we just do something like:
this._loadedModels.map((model) => model.meshes).flat().foreach((mesh) => {
I think this bit of code is simple enough to use as is and not introduce additional state (_loadedModelsContainer
).
packages/tools/viewer/src/viewer.ts
Outdated
protected _reframeCamera(interpolateCamera?: boolean): void; | ||
protected _reframeCamera(interpolateCamera: boolean, models?: Model[]): void { | ||
if (!models) { | ||
const selectedAnimation = this._selectedAnimation === -1 ? 0 : this._selectedAnimation; | ||
const worldBounds = this._activeModel?.getWorldBounds(selectedAnimation); | ||
this._reframeCameraFromBounds(worldBounds, interpolateCamera); | ||
return; | ||
} | ||
|
||
const container = models.length === this._loadedModelsBacking.length ? this._loadedModelsContainer : this._reduceModels(models); | ||
const worldBounds = computeBoundingInfos(container); | ||
this._reframeCameraFromBounds(worldBounds, interpolateCamera); | ||
} |
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 don't think computing bounds without specifying an animation group (when there is at least one animation group) totally makes sense here. Depending on which animation group is active, the bounds of the model (even when paused on the first frame of the animation) can be wildly different (UFO model for example). I think it would make more sense to always include an animation group in the calculation when a model has animation groups.
To make this work, I think it would make sense to add a selectedAnimation
to Model
, and move ~half of the _selectedAnimation
setter logic to this new model specific property (e.g. initializing the animation group). This way, every model with at least one animation group would have the concept of a selected animation and could be properly framed in the camera.
Once this is in place, then the logic for framing the camera could always be the same - just frame a set of models. It would default to _loadedModels
, which in the case of the base/default Viewer would be the single loaded model. E.g.
protected _reframeCamera(interpolateCamera?: boolean, models = this._loadedModels)
WebGL2 visualization test reporter: |
Visualization tests for WebGPU |
@@ -77,7 +77,7 @@ | |||
<div id="viewerContainer" style="width: 100vw; height: 100vh"> | |||
<babylon-viewer | |||
engine="WebGPU" | |||
source="https://raw.githubusercontent.com/BabylonJS/Assets/master/meshes/ufo.glb" | |||
source="http://localhost:3000/glb/fox.glb" |
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.
Revert :)
packages/tools/viewer/src/viewer.ts
Outdated
get selectedAnimation() { | ||
return selectedAnimation; | ||
}, | ||
selectAnimation: (index: number) => { |
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 it would be fine for this to be a property setter. The class level selectAnimation
is a function because it needed additional params.
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.
Agree that it should be a setter but Model is marked as Readonly so I cannot change it.
This is why I left it as a function.
I could make change so this._activeModel
return a Readonly<Model>
and this._activeModelBacking
return just a Model
. Should I do it ?
packages/tools/viewer/src/viewer.ts
Outdated
@@ -1063,7 +1081,7 @@ export class Viewer implements IDisposable { | |||
getWorldBounds: (animationIndex: number): Nullable<ViewerBoundingInfo> => { | |||
let worldBounds: Nullable<ViewerBoundingInfo> = cachedWorldBounds[animationIndex]; | |||
if (!worldBounds) { | |||
worldBounds = computeBoundingInfos(assetContainer, assetContainer.animationGroups[animationIndex]); | |||
worldBounds = computeBoundingInfos(assetContainer.meshes, assetContainer.animationGroups[animationIndex]); |
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 could be:
worldBounds = computeModelsBoundingInfos(model);
and then we could remove computeBoundingInfos
, right?
Visualization tests for WebGPU |
WebGL2 visualization test reporter: |
Add support for multiple models for :
animations
_reframeCamera
with option to use a subset of models_pick