Skip to content

Commit

Permalink
[BETA] ESM Multi-Camera script updates (playcanvas#7078)
Browse files Browse the repository at this point in the history
* Converted camera entity to camera component

* Reorganised class attributes and removed target

* Reimported script from model viewer and attached script to camera

* Added zoom reset flag to example

* Name refactor

* Added error handling to script

* Used distance instead of length
  • Loading branch information
kpal81xd authored Oct 30, 2024
1 parent b57ede0 commit fb685f2
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 239 deletions.
35 changes: 22 additions & 13 deletions examples/src/examples/camera/multi.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
* @returns {JSX.Element} The returned JSX Element.
*/
export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
const { BindingTwoWay, LabelGroup, Panel, SliderInput } = ReactPCUI;
const { BindingTwoWay, LabelGroup, Panel, BooleanInput, SliderInput } = ReactPCUI;

return fragment(
jsx(
LabelGroup,
{ text: 'Zoom reset' },
jsx(BooleanInput, {
type: 'toggle',
binding: new BindingTwoWay(),
link: { observer, path: 'example.zoomReset' }
})
),
jsx(
Panel,
{ headerText: 'Attributes' },
Expand All @@ -14,7 +23,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Focus FOV' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.focusFov' },
link: { observer, path: 'attr.focusFov' },
min: 30,
max: 120
})
Expand All @@ -24,7 +33,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Look sensitivity' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.lookSensitivity' },
link: { observer, path: 'attr.lookSensitivity' },
min: 0.1,
max: 1,
step: 0.01
Expand All @@ -35,7 +44,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Look damping' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.lookDamping' },
link: { observer, path: 'attr.lookDamping' },
min: 0,
max: 0.99,
step: 0.01
Expand All @@ -46,7 +55,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Move damping' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.moveDamping' },
link: { observer, path: 'attr.moveDamping' },
min: 0,
max: 0.99,
step: 0.01
Expand All @@ -57,7 +66,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Pinch speed' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.pinchSpeed' },
link: { observer, path: 'attr.pinchSpeed' },
min: 1,
max: 10
})
Expand All @@ -67,7 +76,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Wheel speed' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.wheelSpeed' },
link: { observer, path: 'attr.wheelSpeed' },
min: 0.001,
max: 0.01,
step: 0.001
Expand All @@ -78,7 +87,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Zoom min' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.zoomMin' },
link: { observer, path: 'attr.zoomMin' },
min: 0.001,
max: 0.01,
step: 0.001
Expand All @@ -89,7 +98,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Zoom max' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.zoomMax' },
link: { observer, path: 'attr.zoomMax' },
min: 1,
max: 10
})
Expand All @@ -99,7 +108,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Zoom scale min' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.zoomScaleMin' },
link: { observer, path: 'attr.zoomScaleMin' },
min: 0.001,
max: 0.01,
step: 0.001
Expand All @@ -110,7 +119,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Move speed' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.moveSpeed' },
link: { observer, path: 'attr.moveSpeed' },
min: 1,
max: 10
})
Expand All @@ -120,7 +129,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Sprint speed' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.sprintSpeed' },
link: { observer, path: 'attr.sprintSpeed' },
min: 1,
max: 10
})
Expand All @@ -130,7 +139,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
{ text: 'Crouch speed' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'camera.crouchSpeed' },
link: { observer, path: 'attr.crouchSpeed' },
min: 1,
max: 10
})
Expand Down
56 changes: 41 additions & 15 deletions examples/src/examples/camera/multi.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,51 @@ await new Promise((resolve) => {
new pc.AssetListLoader(Object.values(assets), app.assets).load(resolve);
});

/**
* Calculate the bounding box of an entity.
*
* @param {pc.BoundingBox} bbox - The bounding box.
* @param {pc.Entity} entity - The entity.
* @returns {pc.BoundingBox} The bounding box.
*/
const calcEntityAABB = (bbox, entity) => {
bbox.center.set(0, 0, 0);
bbox.halfExtents.set(0, 0, 0);
entity.findComponents('render').forEach((render) => {
render.meshInstances.forEach((/** @type {pc.MeshInstance} */ mi) => {
bbox.add(mi.aabb);
});
});
return bbox;
};

/**
* @param {pc.Entity} focus - The entity to focus the camera on.
* @returns {pc.Entity} The multi-camera entity.
* @returns {MultiCamera} The multi-camera script.
*/
const createMultiCamera = (focus) => {
const camera = new pc.Entity();
camera.addComponent('camera');
camera.addComponent('script');

const start = new pc.Vec3(0, 20, 30);
const bbox = calcEntityAABB(new pc.BoundingBox(), focus);
const cameraDist = start.distance(bbox.center);

const multiCamera = new pc.Entity();
multiCamera.addComponent('script');
const script = /** @type {MultiCamera} */ (multiCamera.script.create(MultiCamera, {
/** @type {MultiCamera} */
const script = camera.script.create(MultiCamera, {
attributes: {
target: canvas
sceneSize: bbox.halfExtents.length()
}
}));
script.attach(camera);
});

// focus on entity when 'f' key is pressed
const onKeyDown = (/** @type {KeyboardEvent} */ e) => {
if (e.key === 'f') {
script.focusOnEntity(focus);
if (data.get('example.zoomReset')) {
script.resetZoom(cameraDist);
}
script.focus(bbox.center);
}
};
window.addEventListener('keydown', onKeyDown);
Expand All @@ -90,12 +114,12 @@ const createMultiCamera = (focus) => {

// wait until after canvas resized to focus on entity
const resize = new ResizeObserver(() => {
script.focusOnEntity(focus, true);
resize.disconnect();
script.focus(bbox.center, start);
});
resize.observe(canvas);

return multiCamera;
return script;
};

app.start();
Expand All @@ -116,11 +140,13 @@ const statue = assets.statue.resource.instantiateRenderEntity();
statue.setLocalPosition(0, -0.5, 0);
app.root.addChild(statue);

const multiCamera = createMultiCamera(statue);
app.root.addChild(multiCamera);
const multiCameraScript = createMultiCamera(statue);

// Bind controls to camera attributes
data.set('camera', {
data.set('example', {
zoomReset: true
});
data.set('attr', {
focusFov: 75,
lookSensitivity: 0.2,
lookDamping: 0.97,
Expand All @@ -136,10 +162,10 @@ data.set('camera', {
});
data.on('*:set', (/** @type {string} */ path, /** @type {any} */ value) => {
const [category, key] = path.split('.');
if (category !== 'camera') {
if (category !== 'attr') {
return;
}
multiCamera.script.multiCamera[key] = value;
multiCameraScript[key] = value;
});

export { app };
Loading

0 comments on commit fb685f2

Please sign in to comment.