-
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
Viewer: add a way to match POV of other cameras (from gltf file loaded for example) #16076
base: master
Are you sure you want to change the base?
Conversation
1a18638
to
471b841
Compare
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/16076/merge/index.html#WGZLGJ#4600 Links to test babylon tools with this snapshot: https://playground.babylonjs.com/?snapshot=refs/pull/16076/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/16076/merge#BCU1XR#0 |
WebGL2 visualization test reporter: |
Visualization tests for WebGPU (Experimental) |
I was thinking from our initial discussions that we would simply translate these cameras directly to hotspot entries on the element (rather than adding a new cameras concept). Any reason not to go that route? If we do, then I think a few changes to this PR would be good:
|
Not really any specific reason. Initially, during development, I wanted a distinct visual indication so the user could understand that this behavior in the viewer was tied to the cameras in the scene. But once I finished my code and realized that focusing on a hotspot or matching a camera’s POV ultimately relies on the same principle (an interpolation) I understood that we could merge the two. :) That said, I’m still wondering how we could clarify for users where the names and values of these "undeclared hotspots" originate. |
I had a similar feeling about that, so it works perfectly for me.
Yes, yes, yes! Having the bounding info in
We will aiming for behavior 4, so i'll try to do something that stay consistent with the Babylon viewer approach and let us fit with our needed 👍 |
Ok, I guess in this case we can just check |
Remove useless ray parameters Factor code on radius calculation Use _tempVectors for computation Handle mesh predicate as parameter of function getArcRotateCameraInfos
Add property camerasAsHotSpots to active cameras hot spots Fix camera forward ray
Add bounding infos into the model
packages/tools/viewer/src/viewer.ts
Outdated
export type ModelBoundingInfo = { | ||
worldExtents: { | ||
min: Vector3; | ||
max: Vector3; | ||
}; | ||
worldSize: Vector3; | ||
worldCenter: Vector3; | ||
}; |
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.
A few thoughts on this new type:
- We don't expose
Vector3
directly for other parts of the interface (like hotspots). Could we switch these to[x: number, y: number, z: number]
like we have elsewhere? - Make it readonly either in the definition or in the usage (since it is a stored/persistent value, not a transient value that is just returned from a function with a single usage)? I think definition would be ok, but either works.
- To make this more re-usable (in case in the future we want to use it for something not in world space, or for something other than models), could we rename to not include "world" or "model"?
export type ModelBoundingInfo = { | |
worldExtents: { | |
min: Vector3; | |
max: Vector3; | |
}; | |
worldSize: Vector3; | |
worldCenter: Vector3; | |
}; | |
export type BoundingInfo = { | |
extents: Readonly<{ | |
readonly min: readonly [x: number, y: number, z: number]; | |
readonly max: readonly [x: number, y: number, z: number]; | |
}>; | |
readonly size: readonly [x: number, y: number, z: number]; | |
readonly center: readonly [x: number, y: number, z: number]; | |
}; |
And then in the usage below, have worldBounds?: BoundingInfo
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.
Yes good changes! For the type name, aren't you worried it might overlap with the existing BoundingInfo
class in core/Culling
?
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.
Though the user can always do an import as.
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.
That's a good point, it could be confusing. We could call it ViewerBoundingInfo
or something?
packages/tools/viewer/src/viewer.ts
Outdated
/** | ||
* Updates the bounding info for the model by computing its maximum extents, size, and center considering animation, skeleton, and morph targets. | ||
*/ | ||
private _updateModelBoundingInfo(): void { |
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.
Instead of having this function mutate this._modelInfo
, can we:
- Move it out to a utility function (within this file, but not inside the class) that takes an
AssetContainer
and returns aBoundingInfo
- Call it in
loadModel
and set the bounding info for the model when it is loaded - Make the
worldBounds
property of theModelInfo
part of the readonly section (since it will always be available as it would be computed when the model loads)
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.
Indeed, we can move it out to a utility function. However, I don’t think we can move worldBounds
to the readonly section because it needs to be refreshed when another animation is selected (as the camera relies on it for proper placement). Alternatively, we could compute all the worldBounds
for every animation when the asset loads.
Improve type ModelBoundingInfo and mode bounding infos Move getArcRotateCameraInfos into viewerElement and switch it to private Split basic hot spots to hot spots from cameras
Rename _getArcRotateCameraInfos into _cameraToHotSpot
Factorise the code to add or remove a camera hot spot
Rename _updateModelBoundingInfo into computeBoundingInfos
get hotSpots() { | ||
return { ...this._hotSpots, ...this.camerasHotSpots }; | ||
} |
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'm a little worried about this because I can see scenarios where someone reads the hotSpots
property on every frame tick for doing custom hotspot visualizations, and this would cause a lot of GC. Can we have just a single instance, and modify the instance when either a camera is added/removed or when the camerasAsHotspots property changes?
@@ -848,6 +890,7 @@ export class Viewer implements IDisposable { | |||
assetContainer.dispose(); | |||
this._snapshotHelper.enableSnapshotRendering(); | |||
}, | |||
worldBounds: [computeBoundingInfos(assetContainer, assetContainer.animationGroups[0])], |
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 could be confusing that the world bounds for a particular animation are not available until _selectAnimation
is called. I think instead we should change the Model
type slightly such that instead of a worldBounds
property that is an array, have a getWorldBounds(animationIndex: number)
function, then what we can do here is something like:
const worldBounds: BoundingInfo[] = [];
return {
assetContainer,
...,
getWorldBounds: (animationIndex: number) => {
if (!worldBounds[animationIndex]) {
worldBounds[animationIndex] = computeBoundingInfos(assetContainer, animationIndex);
}
return worldBounds[animationIndex];
},
};
This way the computation is still cached, but anyone can access the world bounds any time (not dependent on a previous call to _selectAnimation
).
* The calculated hot spots for the scene cameras. | ||
*/ | ||
@state() | ||
public camerasHotSpots: Record<string, HotSpot> = {}; |
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.
From my other comment, maybe this will be removed, but if we need to keep it, it seems like it should be private.
This change introduces a way to interpolate the viewer's default camera to other existing cameras in the scene. The primary goal of this feature is to enable matching the POV of cameras defined in a GLTF file, without switching the active camera to try to keep a "simple" experience for non-expert users.
_cameraToHotSpot
function compute thealpha
,beta
, andradius
angles, as well as the target point for a camera that is not anArcRotateCamera
.meshes
fromAssetContainer
is found, the first hit point is used as the target. If no intersection is detected, a fallback target is calculated by projecting the distance between the camera and theAssetContainer
world center along the forward ray.