From f5e784b025a9bbfca6581ca3f97f653a18ed6bcc Mon Sep 17 00:00:00 2001 From: KPal <48248865+kpal81xd@users.noreply.github.com> Date: Fri, 24 May 2024 14:51:50 +0100 Subject: [PATCH] Gizmo example update (#6622) * separated out grid code * split out selector and disabled deselection on orbiting * added type for observer set callback * fixed example page --- examples/iframe/example.css | 2 + examples/src/examples/misc/gizmos.example.mjs | 55 ++++------- examples/src/examples/misc/gizmos.grid.mjs | 77 +++++++++++++++ .../src/examples/misc/gizmos.selector.mjs | 93 +++++++++++++++++++ 4 files changed, 190 insertions(+), 37 deletions(-) create mode 100644 examples/src/examples/misc/gizmos.grid.mjs create mode 100644 examples/src/examples/misc/gizmos.selector.mjs diff --git a/examples/iframe/example.css b/examples/iframe/example.css index 27fa50e21b9..ed6f4a306f3 100644 --- a/examples/iframe/example.css +++ b/examples/iframe/example.css @@ -1,7 +1,9 @@ body { margin: 0; overflow-y: hidden; + background-color: #000; } + #application-canvas { width: 100%; height: 100%; diff --git a/examples/src/examples/misc/gizmos.example.mjs b/examples/src/examples/misc/gizmos.example.mjs index c94b91794d6..d7bcde3765d 100644 --- a/examples/src/examples/misc/gizmos.example.mjs +++ b/examples/src/examples/misc/gizmos.example.mjs @@ -10,6 +10,8 @@ if (!(canvas instanceof HTMLCanvasElement)) { // class for handling gizmo const { GizmoHandler } = await localImport('gizmo-handler.mjs'); +const { Grid } = await localImport('grid.mjs'); +const { Selector } = await localImport('selector.mjs'); const gfxOptions = { deviceTypes: [deviceType], @@ -162,14 +164,14 @@ const setType = (/** @type {string} */ value) => { // call method from top context (same as controls) // @ts-ignore - window.top.setType(value); + window.top.setType?.(value); }; const setProj = (/** @type {number} */ value) => { data.set('camera.proj', value + 1); // call method from top context (same as controls) // @ts-ignore - window.top.setProj(value); + window.top.setProj?.(value); }; // key event handlers @@ -209,7 +211,7 @@ window.addEventListener('keypress', keypress); // gizmo and camera set handler const tmpC = new pc.Color(); -data.on('*:set', (/** @type {string} */ path, value) => { +data.on('*:set', (/** @type {string} */ path, /** @type {any} */ value) => { const pathArray = path.split('.'); switch (pathArray[0]) { @@ -246,55 +248,34 @@ data.on('*:set', (/** @type {string} */ path, value) => { } }); -// picker -const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight); -const worldLayer = layers.getLayerByName('World'); -const pickerLayers = [worldLayer]; - -const onPointerDown = (/** @type {PointerEvent} */ e) => { +// selector +const selector = new Selector(app, camera.camera, [layers.getLayerByName('World')]); +selector.on('select', (/** @type {pc.GraphNode} */ node, /** @type {boolean} */ clear) => { if (gizmoHandler.ignorePicker) { return; } - if (picker) { - picker.resize(canvas.clientWidth, canvas.clientHeight); - picker.prepare(camera.camera, app.scene, pickerLayers); - } - - picker.getSelectionAsync(e.clientX - 1, e.clientY - 1, 2, 2).then((selection) => { - if (!selection[0]) { - gizmoHandler.clear(); - return; - } - - gizmoHandler.add(selection[0].node, !e.ctrlKey && !e.metaKey); - }); -}; -window.addEventListener('pointerdown', onPointerDown); + gizmoHandler.add(node, clear); +}); +selector.on('deselect', () => { + gizmoHandler.clear(); +}); // grid -const gridColor = new pc.Color(1, 1, 1, 0.5); -const gridHalfSize = 4; -/** - * @type {pc.Vec3[]} - */ -const gridLines = []; -for (let i = 0; i < gridHalfSize * 2 + 1; i++) { - gridLines.push(new pc.Vec3(-gridHalfSize, 0, i - gridHalfSize), new pc.Vec3(gridHalfSize, 0, i - gridHalfSize)); - gridLines.push(new pc.Vec3(i - gridHalfSize, 0, -gridHalfSize), new pc.Vec3(i - gridHalfSize, 0, gridHalfSize)); -} -app.on('update', () => { - app.drawLines(gridLines, gridColor); +const grid = new Grid(); + +app.on('update', (/** @type {number} */ dt) => { + grid.draw(app); }); app.on('destroy', () => { gizmoHandler.destroy(); + selector.destroy(); window.removeEventListener('resize', resize); window.removeEventListener('keydown', keydown); window.removeEventListener('keyup', keyup); window.removeEventListener('keypress', keypress); - window.removeEventListener('pointerdown', onPointerDown); }); export { app }; diff --git a/examples/src/examples/misc/gizmos.grid.mjs b/examples/src/examples/misc/gizmos.grid.mjs new file mode 100644 index 00000000000..101b4299769 --- /dev/null +++ b/examples/src/examples/misc/gizmos.grid.mjs @@ -0,0 +1,77 @@ +import * as pc from 'playcanvas'; + +class Grid { + /** + * @type {pc.Vec3[]} + * @private + */ + _lines = []; + + /** + * @type {pc.Color} + * @private + */ + _color = new pc.Color(1, 1, 1, 0.5); + + /** + * @type {pc.Vec2} + * @private + */ + _halfExtents = new pc.Vec2(4, 4); + + constructor() { + this._setLines(); + } + + set halfExtents(value) { + this._halfExtents.copy(value); + this._setLines(); + } + + get halfExtents() { + return this._halfExtents; + } + + set color(value) { + this._color.copy(value); + } + + get color() { + return this._color; + } + + /** + * @private + */ + _setLines() { + this._lines = [ + new pc.Vec3(-this._halfExtents.x, 0, 0), + new pc.Vec3(this._halfExtents.x, 0, 0), + new pc.Vec3(0, 0, -this._halfExtents.y), + new pc.Vec3(0, 0, this._halfExtents.y) + ]; + for (let i = -this._halfExtents.x; i <= this._halfExtents.x; i++) { + if (i === 0) { + continue; + } + this._lines.push(new pc.Vec3(i, 0, -this._halfExtents.y)); + this._lines.push(new pc.Vec3(i, 0, this._halfExtents.y)); + } + for (let i = -this._halfExtents.y; i <= this._halfExtents.y; i++) { + if (i === 0) { + continue; + } + this._lines.push(new pc.Vec3(-this._halfExtents.x, 0, i)); + this._lines.push(new pc.Vec3(this._halfExtents.x, 0, i)); + } + } + + /** + * @param {pc.AppBase} app - The app. + */ + draw(app) { + app.drawLines(this._lines, this._color); + } +} + +export { Grid }; diff --git a/examples/src/examples/misc/gizmos.selector.mjs b/examples/src/examples/misc/gizmos.selector.mjs new file mode 100644 index 00000000000..f3c55d1938b --- /dev/null +++ b/examples/src/examples/misc/gizmos.selector.mjs @@ -0,0 +1,93 @@ +import * as pc from 'playcanvas'; + +class Selector extends pc.EventHandler { + /** + * @type {pc.CameraComponent} + * @private + */ + _camera; + + /** + * @type {pc.Scene} + * @private + */ + _scene; + + /** + * @type {pc.Picker} + * @private + */ + _picker; + + /** + * @type {pc.Layer[]} + * @private + */ + _layers; + + /** + * @type {pc.Vec2} + * @private + */ + _start = new pc.Vec2(); + + /** + * @param {pc.AppBase} app - The app. + * @param {pc.CameraComponent} camera - The camera to pick from. + * @param {pc.Layer[]} [layers] - The layers to pick from. + */ + constructor(app, camera, layers = []) { + super(); + this._camera = camera; + this._scene = app.scene; + const device = app.graphicsDevice; + this._picker = new pc.Picker(app, device.canvas.width, device.canvas.height); + this._layers = layers; + + this._onPointerDown = this._onPointerDown.bind(this); + this._onPointerUp = this._onPointerUp.bind(this); + + window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointerup', this._onPointerUp); + } + + /** + * @param {MouseEvent} e - The event. + * @private + */ + _onPointerDown(e) { + this._start.set(e.clientX, e.clientY); + } + + /** + * @param {MouseEvent} e - The event. + * @private + */ + _onPointerUp(e) { + if (e.clientX !== this._start.x || e.clientY !== this._start.y) { + return; + } + + if (this._picker) { + const device = this._picker.device; + this._picker.resize(device.canvas.width, device.canvas.height); + this._picker.prepare(this._camera, this._scene, this._layers); + } + + const selection = this._picker.getSelection(e.clientX, e.clientY, 2, 2); + + if (!selection[0]) { + this.fire('deselect'); + return; + } + + this.fire('select', selection[0].node, !e.ctrlKey && !e.metaKey); + } + + destroy() { + window.removeEventListener('pointerdown', this._onPointerDown); + window.removeEventListener('pointerup', this._onPointerUp); + } +} + +export { Selector };