From d764b9e40a11d47bb9db178922cceb0b8f1978c5 Mon Sep 17 00:00:00 2001 From: kpal Date: Sat, 23 Dec 2023 15:04:49 +0000 Subject: [PATCH 001/178] gizmo colliison example setup --- examples/src/examples/misc/gizmos.mjs | 81 ++++++++++++++ examples/src/examples/misc/index.mjs | 1 + extras/gizmo/gizmo.js | 150 ++++++++++++++++++++++++++ extras/index.js | 3 + 4 files changed, 235 insertions(+) create mode 100644 examples/src/examples/misc/gizmos.mjs create mode 100644 extras/gizmo/gizmo.js diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs new file mode 100644 index 00000000000..09d99b14029 --- /dev/null +++ b/examples/src/examples/misc/gizmos.mjs @@ -0,0 +1,81 @@ +import * as pc from 'playcanvas'; + +/** + * @param {import('../../options.mjs').ExampleOptions} options - The example options. + * @returns {Promise} The example application. + */ +async function example({ canvas, deviceType, glslangPath, twgslPath }) { + + const gfxOptions = { + deviceTypes: [deviceType], + glslangUrl: glslangPath + 'glslang.js', + twgslUrl: twgslPath + 'twgsl.js' + }; + + const device = await pc.createGraphicsDevice(canvas, gfxOptions); + const createOptions = new pc.AppOptions(); + createOptions.graphicsDevice = device; + + createOptions.componentSystems = [ + pc.RenderComponentSystem, + pc.CameraComponentSystem, + pc.LightComponentSystem + ]; + createOptions.resourceHandlers = [ + // @ts-ignore + pc.TextureHandler, + // @ts-ignore + pc.ContainerHandler + ]; + + const app = new pc.AppBase(canvas); + app.init(createOptions); + app.start(); + + // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size + app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); + app.setCanvasResolution(pc.RESOLUTION_AUTO); + + // Ensure canvas is resized when window changes size + const resize = () => app.resizeCanvas(); + window.addEventListener('resize', resize); + app.on('destroy', () => { + window.removeEventListener('resize', resize); + }); + + // create box entity + const box = new pc.Entity('cube'); + box.addComponent('render', { + type: 'box' + }); + app.root.addChild(box); + box.setEulerAngles(0, 45, 0); + + // create camera entity + const camera = new pc.Entity('camera'); + camera.addComponent('camera', { + clearColor: new pc.Color(0.5, 0.6, 0.9) + }); + app.root.addChild(camera); + camera.translate(0, 3, 7); + camera.lookAt(0, 0, 0); + + // create directional light entity + const light = new pc.Entity('light'); + light.addComponent('light'); + app.root.addChild(light); + light.setEulerAngles(45, -20, 0); + + // create gizmo + const gizmo = new pcx.Gizmo(app, camera, [box]); + + return app; +} + +class GizmosExample { + static CATEGORY = 'Misc'; + static WEBGPU_ENABLED = true; + static example = example; +} + +export { GizmosExample }; diff --git a/examples/src/examples/misc/index.mjs b/examples/src/examples/misc/index.mjs index 552c51cf31f..f4e123576c4 100644 --- a/examples/src/examples/misc/index.mjs +++ b/examples/src/examples/misc/index.mjs @@ -1,3 +1,4 @@ export * from "./hello-world.mjs"; export * from "./mini-stats.mjs"; export * from "./spineboy.mjs"; +export * from "./gizmos.mjs"; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js new file mode 100644 index 00000000000..1e1066fa4bf --- /dev/null +++ b/extras/gizmo/gizmo.js @@ -0,0 +1,150 @@ +import { + SORTMODE_NONE, + Layer, + Color, + StandardMaterial, + Entity, + Vec3 +} from 'playcanvas' + +class Gizmo { + constructor(app, camera, nodes) { + this.app = app; + this.camera = camera; + this.nodes = nodes; + + this._createLayer(); + this._createGizmo(); + + const self = this; + window.addEventListener('pointermove', (e) => { + const start = self.camera.camera.screenToWorld(e.clientX, e.clientY, 1); + const end = self.camera.camera.screenToWorld(e.clientX, e.clientY, self.camera.camera.farClip); + const dir = end.clone().sub(start).normalize(); + + const xstart = new Vec3(); + const xdir = new Vec3(); + + const selection = []; + const renderComponents = self.app.root.findComponents('render'); + for (let i = 0; i < renderComponents.length; i++) { + const meshInstances = renderComponents[i].meshInstances; + for (let j = 0; j < meshInstances.length; j++) { + const meshInstance = meshInstances[j]; + const mesh = meshInstance.mesh; + const wtm = meshInstance.node.getWorldTransform().clone(); + wtm.invert(); + + wtm.transformPoint(start, xstart); + wtm.transformVector(dir, xdir); + xdir.normalize(); + + const pos = []; + const idx = []; + mesh.getPositions(pos); + mesh.getIndices(idx); + + const v1 = new Vec3(); + const v2 = new Vec3(); + const v3 = new Vec3(); + const out = new Vec3(); + for (let k = 0; k < idx.length; k += 3) { + const i1 = idx[k]; + const i2 = idx[k + 1]; + const i3 = idx[k + 2]; + + v1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + + if (self._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, out)) { + selection.push(meshInstance); + return; + } + } + } + } + console.log('SELECTION', selection); + + }, { passive: false }); + } + + _createLayer() { + this.layerGizmo = new Layer({ + name: 'Gizmo', + clearDepthBuffer: true, + opaqueSortMode: SORTMODE_NONE, + transparentSortMode: SORTMODE_NONE + }); + this.app.scene.layers.push(this.layerGizmo); + this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); + } + + _createGizmo() { + const material = new StandardMaterial(); + material.diffuse = new Color(1, 0.3, 0.3); + + const root = new Entity('gizmo'); + + // lighting + const light = new Entity('light'); + light.addComponent('light', { + layers: [this.layerGizmo.id] + }); + root.addChild(light); + light.setEulerAngles(45, -20, 0); + + // center + const center = new Entity('center'); + center.addComponent('render', { + type: 'box', + layers: [this.layerGizmo.id], + material: material + }); + center.setEulerAngles(0, 45, 0); + center.setLocalScale(0.1, 0.1, 0.1); + root.addChild(center); + + this.app.root.addChild(root); + } + + _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { + const EPSILON = 1e-6; + const e1 = new Vec3(); + const e2 = new Vec3(); + const h = new Vec3(); + const s = new Vec3(); + const q = new Vec3(); + + e1.sub2(v1, v0); + e2.sub2(v2, v0); + h.cross(dir, e2); + const a = e1.dot(h); + if (a > -EPSILON && a < EPSILON) { + return false; // ray is parallel to triangle + } + + const f = 1.0 / a; + s.sub2(origin, v0); + const u = f * s.dot(h); + if (u < 0.0 || u > 1.0) { + return false; + } + + q.cross(s, e1); + const v = f * dir.dot(q); + if (v < 0.0 || u + v > 1.0) { + return false; + } + + const t = f * e1.dot(q); + if (t > EPSILON) { + out.copy(dir).scale(t).add(origin); + return true; + } + + return false; + } +} + +export { Gizmo }; diff --git a/extras/index.js b/extras/index.js index 5b8e4a34391..7a189f1bc21 100644 --- a/extras/index.js +++ b/extras/index.js @@ -16,3 +16,6 @@ export { RenderPassCompose } from './render-passes/render-pass-compose.js'; export { RenderPassDownSample } from './render-passes/render-pass-downsample.js'; export { RenderPassUpSample } from './render-passes/render-pass-upsample.js'; export { RenderPassBloom } from './render-passes/render-pass-bloom.js'; + +// gizmo +export { Gizmo } from "./gizmo/gizmo.js"; From 991e943fbe048e83aec9d635045765df9359f362 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 24 Dec 2023 14:42:16 +0000 Subject: [PATCH 002/178] fixed bug in ray triangle intersection code --- extras/gizmo/gizmo.js | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 1e1066fa4bf..d4919f68d0e 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -8,6 +8,9 @@ import { } from 'playcanvas' class Gizmo { + gizmo; + + constructor(app, camera, nodes) { this.app = app; this.camera = camera; @@ -16,19 +19,18 @@ class Gizmo { this._createLayer(); this._createGizmo(); - const self = this; window.addEventListener('pointermove', (e) => { - const start = self.camera.camera.screenToWorld(e.clientX, e.clientY, 1); - const end = self.camera.camera.screenToWorld(e.clientX, e.clientY, self.camera.camera.farClip); + const start = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); + const end = this.camera.camera.screenToWorld(e.clientX, e.clientY, this.camera.camera.farClip); const dir = end.clone().sub(start).normalize(); const xstart = new Vec3(); const xdir = new Vec3(); const selection = []; - const renderComponents = self.app.root.findComponents('render'); - for (let i = 0; i < renderComponents.length; i++) { - const meshInstances = renderComponents[i].meshInstances; + const renders = this.gizmo.findComponents('render'); + for (let i = 0; i < renders.length; i++) { + const meshInstances = renders[i].meshInstances; for (let j = 0; j < meshInstances.length; j++) { const meshInstance = meshInstances[j]; const mesh = meshInstance.mesh; @@ -48,6 +50,7 @@ class Gizmo { const v2 = new Vec3(); const v3 = new Vec3(); const out = new Vec3(); + for (let k = 0; k < idx.length; k += 3) { const i1 = idx[k]; const i2 = idx[k + 1]; @@ -57,14 +60,15 @@ class Gizmo { v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - if (self._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, out)) { + if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, out)) { selection.push(meshInstance); - return; } } + } } - console.log('SELECTION', selection); + + console.log("SELECTION", selection); }, { passive: false }); } @@ -84,14 +88,14 @@ class Gizmo { const material = new StandardMaterial(); material.diffuse = new Color(1, 0.3, 0.3); - const root = new Entity('gizmo'); + this.gizmo = new Entity('gizmo'); // lighting const light = new Entity('light'); light.addComponent('light', { layers: [this.layerGizmo.id] }); - root.addChild(light); + this.gizmo.addChild(light); light.setEulerAngles(45, -20, 0); // center @@ -103,9 +107,9 @@ class Gizmo { }); center.setEulerAngles(0, 45, 0); center.setLocalScale(0.1, 0.1, 0.1); - root.addChild(center); + this.gizmo.addChild(center); - this.app.root.addChild(root); + this.app.root.addChild(this.gizmo); } _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { @@ -121,23 +125,23 @@ class Gizmo { h.cross(dir, e2); const a = e1.dot(h); if (a > -EPSILON && a < EPSILON) { - return false; // ray is parallel to triangle + return false; } - const f = 1.0 / a; + const f = 1 / a; s.sub2(origin, v0); const u = f * s.dot(h); - if (u < 0.0 || u > 1.0) { + if (u < 0 || u > 1) { return false; } q.cross(s, e1); const v = f * dir.dot(q); - if (v < 0.0 || u + v > 1.0) { + if (v < 0 || u + v > 1) { return false; } - const t = f * e1.dot(q); + const t = f * e2.dot(q); if (t > EPSILON) { out.copy(dir).scale(t).add(origin); return true; From e502e39e50f4eca5e8bd7d12ae4a2a458c73f2bc Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 24 Dec 2023 15:08:15 +0000 Subject: [PATCH 003/178] extracted light and material creation --- extras/gizmo/gizmo.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d4919f68d0e..6ab9eac51a8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -10,7 +10,6 @@ import { class Gizmo { gizmo; - constructor(app, camera, nodes) { this.app = app; this.camera = camera; @@ -68,7 +67,10 @@ class Gizmo { } } - console.log("SELECTION", selection); + const hover = selection[0] ?? null; + if (hover) { + console.log(hover); + } }, { passive: false }); } @@ -84,26 +86,34 @@ class Gizmo { this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); } - _createGizmo() { + _createMaterial(r, g, b) { const material = new StandardMaterial(); - material.diffuse = new Color(1, 0.3, 0.3); - - this.gizmo = new Entity('gizmo'); + material.diffuse = new Color(r, g, b); + return material; + } - // lighting + _createLight(x, y, z) { const light = new Entity('light'); light.addComponent('light', { layers: [this.layerGizmo.id] }); + light.setEulerAngles(x, y, z); + return light; + } + + _createGizmo() { + this.gizmo = new Entity('gizmo'); + + // lighting + const light = this._createLight(45, -20, 0); this.gizmo.addChild(light); - light.setEulerAngles(45, -20, 0); // center const center = new Entity('center'); center.addComponent('render', { type: 'box', layers: [this.layerGizmo.id], - material: material + material: this._createMaterial(1, 0.3, 0.3) }); center.setEulerAngles(0, 45, 0); center.setLocalScale(0.1, 0.1, 0.1); From 374b406c97008ea29a95dafa07bed0d0b05b6da3 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Mon, 25 Dec 2023 18:18:47 +0000 Subject: [PATCH 004/178] added child transform class with axes and props in examples to change shape --- examples/src/examples/misc/gizmos.mjs | 59 +++++- extras/gizmo/gizmo-transform.js | 271 ++++++++++++++++++++++++++ extras/gizmo/gizmo.js | 51 ++--- extras/index.js | 1 + 4 files changed, 341 insertions(+), 41 deletions(-) create mode 100644 extras/gizmo/gizmo-transform.js diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 09d99b14029..69cc4ae2dc5 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -1,10 +1,53 @@ import * as pc from 'playcanvas'; +/** + * @param {import('../../app/example.mjs').ControlOptions} options - The options. + * @returns {JSX.Element} The returned JSX Element. + */ +function controls({ observer, ReactPCUI, React, jsx, fragment }) { + const { BindingTwoWay, LabelGroup, Panel, SliderInput } = ReactPCUI; + return fragment( + jsx(Panel, { headerText: 'Gizmo Transform' }, + jsx(LabelGroup, { text: 'Axis Gap' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisGap' } + }) + ), + jsx(LabelGroup, { text: 'Axis Line Thickness' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisLineThickness' } + }) + ), + jsx(LabelGroup, { text: 'Axis Line Length' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisLineLength' } + }) + ), + jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisArrowThickness' } + }) + ), + jsx(LabelGroup, { text: 'Axis Arrow Length' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisArrowLength' } + }) + ) + ) + ); +} + + /** * @param {import('../../options.mjs').ExampleOptions} options - The example options. * @returns {Promise} The example application. */ -async function example({ canvas, deviceType, glslangPath, twgslPath }) { +async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const gfxOptions = { deviceTypes: [deviceType], @@ -67,7 +110,18 @@ async function example({ canvas, deviceType, glslangPath, twgslPath }) { light.setEulerAngles(45, -20, 0); // create gizmo - const gizmo = new pcx.Gizmo(app, camera, [box]); + const gizmo = new pcx.GizmoTransform(app, camera, []); + data.set('settings', { + axisGap: gizmo.axisGap, + axisLineThickness: gizmo.axisLineThickness, + axisLineLength: gizmo.axisLineLength, + axisArrowThickness: gizmo.axisArrowThickness, + axisArrowLength: gizmo.axisArrowLength + }); + data.on('*:set', (/** @type {string} */ path, value) => { + const pathArray = path.split('.'); + gizmo[pathArray[1]] = value; + }); return app; } @@ -75,6 +129,7 @@ async function example({ canvas, deviceType, glslangPath, twgslPath }) { class GizmosExample { static CATEGORY = 'Misc'; static WEBGPU_ENABLED = true; + static controls = controls; static example = example; } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js new file mode 100644 index 00000000000..94384b8515f --- /dev/null +++ b/extras/gizmo/gizmo-transform.js @@ -0,0 +1,271 @@ +import { + BLEND_NORMAL, + Color, + StandardMaterial, + Entity, + Vec3 +} from 'playcanvas' + +import { Gizmo } from "./gizmo.js"; + +class Axis { + entity; + + meshInstances = []; + + constructor(options = {}) { + this._position = options.position ?? new Vec3(); + this._rotation = options.rotation ?? new Vec3(); + this._scale = options.scale ?? new Vec3(1, 1, 1); + + this._defaultColor = options.defaultColor ?? Color.BLACK; + this._hoverColor = options.hoverColor ?? Color.WHITE; + + this._gap = 0; + this._lineThickness = 0.04; + this._lineLength = 0.5; + this._arrowThickness = 0.15; + this._arrowLength = 0.2; + + this._createAxis(options.layers ?? []); + } + + set gap(value) { + this._gap = value ?? 0; + this._updateLine(); + this._updateArrow(); + } + + get gap() { + return this._gap; + } + + set lineThickness(value) { + this._lineThickness = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineThickness() { + return this._lineThickness; + } + + set lineLength(value) { + this._lineLength = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineLength() { + return this._lineLength; + } + + set arrowThickness(value) { + this._arrowThickness = value ?? 1; + this._updateArrow(); + } + + get arrowThickness() { + return this._arrowThickness; + } + + set arrowLength(value) { + this._arrowLength = value ?? 1; + this._updateArrow(); + } + + get arrowLength() { + return this._arrowLength; + } + + _createAxis(layers) { + this.entity = new Entity('axis'); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + + this._line = new Entity('line'); + this._line.addComponent('render', { + type: 'cylinder', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateLine(); + this.entity.addChild(this._line); + this.meshInstances.push(...this._line.render.meshInstances); + + this._arrow = new Entity('arrow'); + this._arrow.addComponent('render', { + type: 'cone', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateArrow(); + this.entity.addChild(this._arrow); + this.meshInstances.push(...this._arrow.render.meshInstances); + } + + _updateLine() { + this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); + this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + } + + _updateArrow() { + this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); + this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); + } + + hover(state) { + const material = state ? this._hoverColor : this._defaultColor; + for (let i = 0; i < this.meshInstances.length; i++) { + this.meshInstances[i].material = material; + } + } +} + +class GizmoTransform extends Gizmo { + constructor(app, camera, nodes) { + super(app, camera, nodes); + + this.materials = { + opaque: { + red: this._createMaterial(new Color(1, 0.3, 0.3)), + green: this._createMaterial(new Color(0.3, 1, 0.3)), + blue: this._createMaterial(new Color(0.3, 0.3, 1)) + }, + semi: { + red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), + green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), + blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)) + } + }; + + this.axes = { + x: new Axis({ + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 90), + defaultColor: this.materials.semi.red, + hoverColor: this.materials.opaque.red + }), + y: new Axis({ + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this.materials.semi.green, + hoverColor: this.materials.opaque.green + }), + z: new Axis({ + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this.materials.semi.blue, + hoverColor: this.materials.opaque.blue + }) + }; + this.axisMap = new Map(); + + this._createTransform(); + } + + set axisGap(value) { + this._updateAxisProp('gap', value ?? 0); + } + + get axisGap() { + return this.axes.x.gap; + } + + set axisLineThickness(value) { + this._updateAxisProp('lineThickness', value ?? 1); + } + + get axisLineThickness() { + return this.axes.x.lineThickness; + } + + set axisLineLength(value) { + this._updateAxisProp('lineLength', value ?? 1); + } + + get axisLineLength() { + return this.axes.x.lineLength; + } + + set axisArrowThickness(value) { + this._updateAxisProp('arrowThickness', value ?? 1); + } + + get axisArrowThickness() { + return this.axes.x.arrowThickness; + } + + set axisArrowLength(value) { + this._axisArrowLength = value ?? 1; + this._updateAxisProp('arrowLength', this._axisArrowLength); + } + + get axisArrowLength() { + return this.axes.x.arrowLength; + } + + _createMaterial(color) { + const material = new StandardMaterial(); + material.diffuse = color; + if (color.a !== 1) { + material.opacity = color.a; + material.blendType = BLEND_NORMAL; + } + return material; + } + + _createLight(angles) { + const light = new Entity('light'); + light.addComponent('light', { + layers: [this.layerGizmo.id] + }); + light.setEulerAngles(angles); + return light; + } + + _createTransform() { + // lighting + const light = this._createLight(new Vec3(45, -20, 0)); + this.gizmo.addChild(light); + + // center + const center = new Entity('center'); + center.setEulerAngles(0, 45, 0); + this.gizmo.addChild(center); + + // axes + for (const key in this.axes) { + const axis = this.axes[key]; + center.addChild(axis.entity); + for (let i = 0; i < axis.meshInstances.length; i++) { + this.axisMap.set(axis.meshInstances[i], axis); + } + } + } + + _updateAxisProp(propName, value) { + this.axes.x[propName] = value; + this.axes.y[propName] = value; + this.axes.z[propName] = value; + } + + _handleHover(selection) { + const axis = this.axisMap.get(selection); + if (axis === this._dirtyAxis) { + return; + } + if (this._dirtyAxis) { + this._dirtyAxis.hover(false); + this._dirtyAxis = null; + } + if (axis) { + axis.hover(true); + this._dirtyAxis = axis; + } + } +} + +export { GizmoTransform }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 6ab9eac51a8..7e586dc0df9 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -1,8 +1,6 @@ import { SORTMODE_NONE, Layer, - Color, - StandardMaterial, Entity, Vec3 } from 'playcanvas' @@ -18,7 +16,7 @@ class Gizmo { this._createLayer(); this._createGizmo(); - window.addEventListener('pointermove', (e) => { + const onPointerMove = (e) => { const start = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); const end = this.camera.camera.screenToWorld(e.clientX, e.clientY, this.camera.camera.farClip); const dir = end.clone().sub(start).normalize(); @@ -67,12 +65,14 @@ class Gizmo { } } - const hover = selection[0] ?? null; - if (hover) { - console.log(hover); - } + this._handleHover(selection[0] ?? null); + + }; - }, { passive: false }); + window.addEventListener('pointermove', onPointerMove); + app.on('destroy', () => { + window.removeEventListener('pointermove', onPointerMove); + }); } _createLayer() { @@ -86,42 +86,15 @@ class Gizmo { this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); } - _createMaterial(r, g, b) { - const material = new StandardMaterial(); - material.diffuse = new Color(r, g, b); - return material; - } - - _createLight(x, y, z) { - const light = new Entity('light'); - light.addComponent('light', { - layers: [this.layerGizmo.id] - }); - light.setEulerAngles(x, y, z); - return light; - } - _createGizmo() { this.gizmo = new Entity('gizmo'); - - // lighting - const light = this._createLight(45, -20, 0); - this.gizmo.addChild(light); - - // center - const center = new Entity('center'); - center.addComponent('render', { - type: 'box', - layers: [this.layerGizmo.id], - material: this._createMaterial(1, 0.3, 0.3) - }); - center.setEulerAngles(0, 45, 0); - center.setLocalScale(0.1, 0.1, 0.1); - this.gizmo.addChild(center); - this.app.root.addChild(this.gizmo); } + _handleHover(selection) { + // override in child class + } + _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { const EPSILON = 1e-6; const e1 = new Vec3(); diff --git a/extras/index.js b/extras/index.js index adc96fc887c..ce80b842604 100644 --- a/extras/index.js +++ b/extras/index.js @@ -20,3 +20,4 @@ export { RenderPassTAA } from './render-passes/render-pass-taa.js'; // gizmo export { Gizmo } from "./gizmo/gizmo.js"; +export { GizmoTransform } from "./gizmo/gizmo-transform.js"; From a570c388648e07054eb2523729d9d72fdf81fa2c Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 28 Dec 2023 15:23:56 +0000 Subject: [PATCH 005/178] added planes; added basic movement for arrows and planes --- examples/src/examples/misc/gizmos.mjs | 22 ++- extras/gizmo/gizmo-transform.js | 214 ++++++++++++++++++++------ extras/gizmo/gizmo.js | 204 +++++++++++++++++------- 3 files changed, 330 insertions(+), 110 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 69cc4ae2dc5..7843414d03e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -86,13 +86,18 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { window.removeEventListener('resize', resize); }); - // create box entity - const box = new pc.Entity('cube'); - box.addComponent('render', { + // create box entities + const boxA = new pc.Entity('cubeA'); + boxA.addComponent('render', { type: 'box' }); - app.root.addChild(box); - box.setEulerAngles(0, 45, 0); + app.root.addChild(boxA); + // const boxB = new pc.Entity('cubeB'); + // boxB.addComponent('render', { + // type: 'box' + // }); + // boxB.setPosition(1, 0, 0); + // app.root.addChild(boxB); // create camera entity const camera = new pc.Entity('camera'); @@ -100,17 +105,18 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { clearColor: new pc.Color(0.5, 0.6, 0.9) }); app.root.addChild(camera); - camera.translate(0, 3, 7); + camera.translate(5, 3, 5); camera.lookAt(0, 0, 0); // create directional light entity const light = new pc.Entity('light'); light.addComponent('light'); app.root.addChild(light); - light.setEulerAngles(45, -20, 0); + light.setEulerAngles(45, 20, 0); // create gizmo - const gizmo = new pcx.GizmoTransform(app, camera, []); + const gizmo = new pcx.GizmoTransform(app, camera); + gizmo.attach([boxA]); data.set('settings', { axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 94384b8515f..b09ca324c2d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -8,24 +8,79 @@ import { import { Gizmo } from "./gizmo.js"; -class Axis { +// temporary variables +const position = new Vec3(); + +class TransformElement { + _position; + + _rotation; + + _scale; + + _defaultColor; + + _hoverColor; + + name; + entity; meshInstances = []; - constructor(options = {}) { + constructor(options) { + this.name = options.name ?? 'INVALID'; this._position = options.position ?? new Vec3(); this._rotation = options.rotation ?? new Vec3(); this._scale = options.scale ?? new Vec3(1, 1, 1); this._defaultColor = options.defaultColor ?? Color.BLACK; this._hoverColor = options.hoverColor ?? Color.WHITE; + } - this._gap = 0; - this._lineThickness = 0.04; - this._lineLength = 0.5; - this._arrowThickness = 0.15; - this._arrowLength = 0.2; + hover(state) { + const material = state ? this._hoverColor : this._defaultColor; + for (let i = 0; i < this.meshInstances.length; i++) { + this.meshInstances[i].material = material; + } + } +} + +class Plane extends TransformElement { + constructor(options) { + super(options); + + this._createPlane(options.layers ?? []); + } + + _createPlane(layers) { + this.entity = new Entity('plane'); + this.entity.addComponent('render', { + type: 'plane', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + this.meshInstances.push(...this.entity.render.meshInstances); + } +} + +class Axis extends TransformElement { + _gap = 0; + + _lineThickness = 0.04; + + _lineLength = 0.5; + + _arrowThickness = 0.15; + + _arrowLength = 0.2; + + constructor(options = {}) { + super(options); this._createAxis(options.layers ?? []); } @@ -82,6 +137,7 @@ class Axis { this.entity = new Entity('axis'); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); this._line = new Entity('line'); this._line.addComponent('render', { @@ -115,18 +171,19 @@ class Axis { this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); } - - hover(state) { - const material = state ? this._hoverColor : this._defaultColor; - for (let i = 0; i < this.meshInstances.length; i++) { - this.meshInstances[i].material = material; - } - } } class GizmoTransform extends Gizmo { - constructor(app, camera, nodes) { - super(app, camera, nodes); + materials; + + elements; + + elementMap = new Map(); + + _dirtyElement; + + constructor(app, camera) { + super(app, camera); this.materials = { opaque: { @@ -141,29 +198,102 @@ class GizmoTransform extends Gizmo { } }; - this.axes = { + this.elements = { x: new Axis({ + name: 'x', layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 90), + rotation: new Vec3(0, 0, -90), defaultColor: this.materials.semi.red, hoverColor: this.materials.opaque.red }), y: new Axis({ + name: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this.materials.semi.green, hoverColor: this.materials.opaque.green }), z: new Axis({ + name: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this.materials.semi.blue, + hoverColor: this.materials.opaque.blue + }), + yz: new Plane({ + name: 'yz', + layers: [this.layerGizmo.id], + position: new Vec3(0, 0.1, 0.1), + rotation: new Vec3(0, 0, -90), + scale: new Vec3(0.2, 0.2, 0.2), + defaultColor: this.materials.semi.red, + hoverColor: this.materials.opaque.red + }), + xz: new Plane({ + name: 'xz', layers: [this.layerGizmo.id], + position: new Vec3(0.1, 0, 0.1), + rotation: new Vec3(0, 0, 0), + scale: new Vec3(0.2, 0.2, 0.2), + defaultColor: this.materials.semi.green, + hoverColor: this.materials.opaque.green + }), + xy: new Plane({ + name: 'xy', + layers: [this.layerGizmo.id], + position: new Vec3(0.1, 0.1, 0), rotation: new Vec3(90, 0, 0), + scale: new Vec3(0.2, 0.2, 0.2), defaultColor: this.materials.semi.blue, hoverColor: this.materials.opaque.blue }) }; - this.axisMap = new Map(); this._createTransform(); + + this.on('gizmo:hover', (meshInstance) => { + const element = this.elementMap.get(meshInstance); + if (element === this._dirtyElement) { + return; + } + if (this._dirtyElement) { + this._dirtyElement.hover(false); + this._dirtyElement = null; + } + if (element) { + element.hover(true); + this._dirtyElement = element; + } + }); + + this.on('gizmo:hold', (meshInstance, target) => { + const element = this.elementMap.get(meshInstance); + position.copy(this.gizmo.getPosition()); + switch (element.name) { + case 'x': + position.x = target.x; + break; + case 'y': + position.y = target.y; + break; + case 'z': + position.z = target.z; + break; + case 'yz': + position.y = target.y; + position.z = target.z; + break; + case 'xz': + position.x = target.x; + position.z = target.z; + break; + case 'xy': + position.x = target.x; + position.y = target.y; + break; + } + this.gizmo.setPosition(position); + }); } set axisGap(value) { @@ -171,7 +301,7 @@ class GizmoTransform extends Gizmo { } get axisGap() { - return this.axes.x.gap; + return this.elements.x.gap; } set axisLineThickness(value) { @@ -179,7 +309,7 @@ class GizmoTransform extends Gizmo { } get axisLineThickness() { - return this.axes.x.lineThickness; + return this.elements.x.lineThickness; } set axisLineLength(value) { @@ -187,7 +317,7 @@ class GizmoTransform extends Gizmo { } get axisLineLength() { - return this.axes.x.lineLength; + return this.elements.x.lineLength; } set axisArrowThickness(value) { @@ -195,7 +325,7 @@ class GizmoTransform extends Gizmo { } get axisArrowThickness() { - return this.axes.x.arrowThickness; + return this.elements.x.arrowThickness; } set axisArrowLength(value) { @@ -204,7 +334,7 @@ class GizmoTransform extends Gizmo { } get axisArrowLength() { - return this.axes.x.arrowLength; + return this.elements.x.arrowLength; } _createMaterial(color) { @@ -228,43 +358,27 @@ class GizmoTransform extends Gizmo { _createTransform() { // lighting - const light = this._createLight(new Vec3(45, -20, 0)); + const light = this._createLight(new Vec3(45, 0, -45)); this.gizmo.addChild(light); // center const center = new Entity('center'); - center.setEulerAngles(0, 45, 0); this.gizmo.addChild(center); - // axes - for (const key in this.axes) { - const axis = this.axes[key]; - center.addChild(axis.entity); - for (let i = 0; i < axis.meshInstances.length; i++) { - this.axisMap.set(axis.meshInstances[i], axis); + // elements + for (const key in this.elements) { + const element = this.elements[key]; + center.addChild(element.entity); + for (let i = 0; i < element.meshInstances.length; i++) { + this.elementMap.set(element.meshInstances[i], element); } } } _updateAxisProp(propName, value) { - this.axes.x[propName] = value; - this.axes.y[propName] = value; - this.axes.z[propName] = value; - } - - _handleHover(selection) { - const axis = this.axisMap.get(selection); - if (axis === this._dirtyAxis) { - return; - } - if (this._dirtyAxis) { - this._dirtyAxis.hover(false); - this._dirtyAxis = null; - } - if (axis) { - axis.hover(true); - this._dirtyAxis = axis; - } + this.elements.x[propName] = value; + this.elements.y[propName] = value; + this.elements.z[propName] = value; } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 7e586dc0df9..0ba9f48884e 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -1,80 +1,128 @@ import { SORTMODE_NONE, + EventHandler, Layer, Entity, + Vec2, Vec3 } from 'playcanvas' -class Gizmo { +// temp variables +const xstart = new Vec3(); +const xdir = new Vec3(); +const v1 = new Vec3(); +const v2 = new Vec3(); +const v3 = new Vec3(); +const intersect = new Vec3(); +const worldIntersect = new Vec3(); +const gizmoTarget = new Vec3(); + +class Gizmo extends EventHandler { + _nodeParents = new Map(); + + app; + + camera; + + nodes = []; + gizmo; - constructor(app, camera, nodes) { + constructor(app, camera) { + super(); + this.app = app; this.camera = camera; - this.nodes = nodes; this._createLayer(); this._createGizmo(); - const onPointerMove = (e) => { - const start = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); - const end = this.camera.camera.screenToWorld(e.clientX, e.clientY, this.camera.camera.farClip); - const dir = end.clone().sub(start).normalize(); - - const xstart = new Vec3(); - const xdir = new Vec3(); - - const selection = []; - const renders = this.gizmo.findComponents('render'); - for (let i = 0; i < renders.length; i++) { - const meshInstances = renders[i].meshInstances; - for (let j = 0; j < meshInstances.length; j++) { - const meshInstance = meshInstances[j]; - const mesh = meshInstance.mesh; - const wtm = meshInstance.node.getWorldTransform().clone(); - wtm.invert(); - - wtm.transformPoint(start, xstart); - wtm.transformVector(dir, xdir); - xdir.normalize(); - - const pos = []; - const idx = []; - mesh.getPositions(pos); - mesh.getIndices(idx); - - const v1 = new Vec3(); - const v2 = new Vec3(); - const v3 = new Vec3(); - const out = new Vec3(); - - for (let k = 0; k < idx.length; k += 3) { - const i1 = idx[k]; - const i2 = idx[k + 1]; - const i3 = idx[k + 2]; - - v1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); - v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); - v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - - if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, out)) { - selection.push(meshInstance); - } - } + const pointerInfo = { + down: false, + meshInstance: null, + localPosition: new Vec3(), + cameraDist: 1 + }; - } + const onPointerMove = (e) => { + if (!this.gizmo.enabled) { + return; } + const selection = this._getSelection(e.clientX, e.clientY); + this.fire('gizmo:hover', selection[0]?.meshInstance); - this._handleHover(selection[0] ?? null); + if (pointerInfo.down) { + const screenPoint = this.camera.camera.screenToWorld(e.clientX, e.clientY, pointerInfo.cameraDist); + gizmoTarget.sub2(screenPoint, pointerInfo.localPosition); + this.fire('gizmo:hold', pointerInfo.meshInstance, gizmoTarget); + } + }; + const onPointerDown = (e) => { + if (!this.gizmo.enabled) { + return; + } + const selection = this._getSelection(e.clientX, e.clientY); + const first = selection[0]; + if (first) { + const meshInstance = first.meshInstance; + const wtm = meshInstance.node.getWorldTransform(); + wtm.transformPoint(first.intersect, worldIntersect); + + pointerInfo.down = true; + pointerInfo.meshInstance = meshInstance; + pointerInfo.localPosition.sub2(worldIntersect, this.gizmo.getPosition()); + pointerInfo.cameraDist = this._distanceFromCamera(worldIntersect); + } + }; + const onPointerUp = (e) => { + if (!this.gizmo.enabled) { + return; + } + pointerInfo.down = false; }; window.addEventListener('pointermove', onPointerMove); + window.addEventListener('pointerdown', onPointerDown); + window.addEventListener('pointerup', onPointerUp); app.on('destroy', () => { window.removeEventListener('pointermove', onPointerMove); + window.removeEventListener('pointerdown', onPointerDown); + window.removeEventListener('pointerup', onPointerUp); }); } + attach(nodes) { + this.nodes = nodes; + this.gizmo.enabled = true; + + // calculate center + const center = new Vec3(); + for (let i = 0; i < this.nodes.length; i++) { + center.add(this.nodes[i].getPosition()); + } + center.divScalar(this.nodes.length); + this.gizmo.setPosition(center); + + // add nodes to held entity + for (let i = 0; i < this.nodes.length; i++) { + this._nodeParents.set(this.nodes[i], this.nodes[i].parent); + this.held.addChild(this.nodes[i]); + } + } + + detach() { + // fetch parents and reattach + for (let i = 0; i < this.nodes.length; i++) { + const parent = this._nodeParents.get(this.nodes[i]); + parent.addChild(this.nodes[i]); + } + + this._nodeParents.clear(); + this.nodes = []; + this.gizmo.enabled = false; + } + _createLayer() { this.layerGizmo = new Layer({ name: 'Gizmo', @@ -87,12 +135,64 @@ class Gizmo { } _createGizmo() { + // gizmo root entity this.gizmo = new Entity('gizmo'); this.app.root.addChild(this.gizmo); + this.gizmo.enabled = false; + + // entity for holding nodes + this.held = new Entity('held'); + this.gizmo.addChild(this.held); } - _handleHover(selection) { - // override in child class + _distanceFromCamera(position, dist = new Vec3()) { + return dist.sub2(this.camera.getPosition(), position).length(); + } + + _getSelection(x, y) { + const start = this.camera.camera.screenToWorld(x, y, 1); + const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); + const dir = end.clone().sub(start).normalize(); + + const selection = []; + const renders = this.gizmo.findComponents('render'); + for (let i = 0; i < renders.length; i++) { + if (renders[i].entity.parent === this.held) { + continue; + } + const meshInstances = renders[i].meshInstances; + for (let j = 0; j < meshInstances.length; j++) { + const meshInstance = meshInstances[j]; + const mesh = meshInstance.mesh; + const wtm = meshInstance.node.getWorldTransform().clone(); + wtm.invert(); + + wtm.transformPoint(start, xstart); + wtm.transformVector(dir, xdir); + xdir.normalize(); + + const pos = []; + const idx = []; + mesh.getPositions(pos); + mesh.getIndices(idx); + + for (let k = 0; k < idx.length; k += 3) { + const i1 = idx[k]; + const i2 = idx[k + 1]; + const i3 = idx[k + 2]; + + v1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + + if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, intersect)) { + selection.push({ meshInstance, intersect }); + } + } + + } + } + return selection; } _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { From 1b85ed13a3782af3c8406795da04849249720cda Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 28 Dec 2023 15:30:02 +0000 Subject: [PATCH 006/178] fixed y plane transform movement --- extras/gizmo/gizmo.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 0ba9f48884e..3727b866580 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -40,8 +40,7 @@ class Gizmo extends EventHandler { const pointerInfo = { down: false, meshInstance: null, - localPosition: new Vec3(), - cameraDist: 1 + localPosition: new Vec3() }; const onPointerMove = (e) => { @@ -52,7 +51,8 @@ class Gizmo extends EventHandler { this.fire('gizmo:hover', selection[0]?.meshInstance); if (pointerInfo.down) { - const screenPoint = this.camera.camera.screenToWorld(e.clientX, e.clientY, pointerInfo.cameraDist); + const cameraDist = this._distanceFromCamera(); + const screenPoint = this.camera.camera.screenToWorld(e.clientX, e.clientY, cameraDist); gizmoTarget.sub2(screenPoint, pointerInfo.localPosition); this.fire('gizmo:hold', pointerInfo.meshInstance, gizmoTarget); } @@ -71,8 +71,6 @@ class Gizmo extends EventHandler { pointerInfo.down = true; pointerInfo.meshInstance = meshInstance; pointerInfo.localPosition.sub2(worldIntersect, this.gizmo.getPosition()); - pointerInfo.cameraDist = this._distanceFromCamera(worldIntersect); - } }; const onPointerUp = (e) => { @@ -145,8 +143,8 @@ class Gizmo extends EventHandler { this.gizmo.addChild(this.held); } - _distanceFromCamera(position, dist = new Vec3()) { - return dist.sub2(this.camera.getPosition(), position).length(); + _distanceFromCamera(dist = new Vec3()) { + return dist.sub2(this.camera.getPosition(), this.gizmo.getPosition()).length(); } _getSelection(x, y) { From e4aea84a05ec13481135c33e830b59848566344a Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Thu, 28 Dec 2023 23:26:43 +0000 Subject: [PATCH 007/178] reworking intersection for gizmo transform --- extras/gizmo/gizmo-transform.js | 150 ++++++++++++++++++++++---------- extras/gizmo/gizmo.js | 48 +--------- 2 files changed, 107 insertions(+), 91 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index b09ca324c2d..6c13a764857 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,5 +1,7 @@ import { BLEND_NORMAL, + Ray, + Plane, Color, StandardMaterial, Entity, @@ -9,7 +11,7 @@ import { import { Gizmo } from "./gizmo.js"; // temporary variables -const position = new Vec3(); +const worldIntersect = new Vec3(); class TransformElement { _position; @@ -46,7 +48,7 @@ class TransformElement { } } -class Plane extends TransformElement { +class PlaneElement extends TransformElement { constructor(options) { super(options); @@ -68,7 +70,7 @@ class Plane extends TransformElement { } } -class Axis extends TransformElement { +class AxisElement extends TransformElement { _gap = 0; _lineThickness = 0.04; @@ -199,28 +201,28 @@ class GizmoTransform extends Gizmo { }; this.elements = { - x: new Axis({ + x: new AxisElement({ name: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this.materials.semi.red, hoverColor: this.materials.opaque.red }), - y: new Axis({ + y: new AxisElement({ name: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this.materials.semi.green, hoverColor: this.materials.opaque.green }), - z: new Axis({ + z: new AxisElement({ name: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), defaultColor: this.materials.semi.blue, hoverColor: this.materials.opaque.blue }), - yz: new Plane({ + yz: new PlaneElement({ name: 'yz', layers: [this.layerGizmo.id], position: new Vec3(0, 0.1, 0.1), @@ -229,7 +231,7 @@ class GizmoTransform extends Gizmo { defaultColor: this.materials.semi.red, hoverColor: this.materials.opaque.red }), - xz: new Plane({ + xz: new PlaneElement({ name: 'xz', layers: [this.layerGizmo.id], position: new Vec3(0.1, 0, 0.1), @@ -238,7 +240,7 @@ class GizmoTransform extends Gizmo { defaultColor: this.materials.semi.green, hoverColor: this.materials.opaque.green }), - xy: new Plane({ + xy: new PlaneElement({ name: 'xy', layers: [this.layerGizmo.id], position: new Vec3(0.1, 0.1, 0), @@ -251,51 +253,107 @@ class GizmoTransform extends Gizmo { this._createTransform(); - this.on('gizmo:hover', (meshInstance) => { - const element = this.elementMap.get(meshInstance); - if (element === this._dirtyElement) { + this.dragging = false; + this._pointStart = new Vec3(); + this._pointEnd = new Vec3(); + this._offset = new Vec3(); + + const plane = new Plane(); + + this.on('pointermove', (e, selected) => { + this._hover(selected?.meshInstance); + + // if (this.dragging) { + // const mouseWPos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); + // const rayOrigin = this.camera.getPosition(); + // const rayDir = new Vec3(); + // rayDir.sub(mouseWPos, rayOrigin).normalize(); + // const ray = new Ray(rayOrigin, rayDir); + // plane.intersectsRay(ray, this._pointEnd); + // this._offset.sub2(this._pointStart, this._pointEnd); + + // const position = this.gizmo.getPosition(); + // position.add(this._offset); + // this.gizmo.setPosition(position); + + + // } + // if (selected && this.dragging) { + // const meshInstance = selected.meshInstance; + // const wtm = meshInstance.node.getWorldTransform(); + // wtm.transformPoint(selected.intersect, worldIntersect); + + // this._pointEnd.sub2(worldIntersect, this.gizmo.getPosition()); + + // this._offset.sub2(this._pointEnd, this._pointStart); + + // const element = this.elementMap.get(meshInstance); + // const position = this.gizmo.getPosition(); + // if (element.name.indexOf('x') === -1) { + // this._offset.x = 0; + // } + // if (element.name.indexOf('y') === -1) { + // this._offset.y = 0; + // } + // if (element.name.indexOf('z') === -1) { + // this._offset.z = 0; + // } + // position.add(this._offset); + // this.gizmo.setPosition(position); + // } + }); + + this.on('pointerdown', (e, selected) => { + if (this.dragging) { return; } - if (this._dirtyElement) { - this._dirtyElement.hover(false); - this._dirtyElement = null; - } - if (element) { - element.hover(true); - this._dirtyElement = element; + if (selected) { + const meshInstance = selected.meshInstance; + const rot = meshInstance.node.getRotation().clone(); + rot.transformVector(Vec3.UP, plane.normal); + + const mouseWPos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); + const rayOrigin = this.camera.getPosition(); + const rayDir = new Vec3(); + rayDir.sub(mouseWPos, rayOrigin).normalize(); + const ray = new Ray(rayOrigin, rayDir); + const hit = plane.intersectsRay(ray, this._pointStart); + console.log(this._pointStart); + + // const mousePos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); + // this.gizmo.setPosition(mousePos); + // const meshInstance = selected.meshInstance; + // const wtm = meshInstance.node.getWorldTransform(); + // wtm.transformPoint(selected.intersect, worldIntersect); + // this._pointStart.sub2(worldIntersect, this.gizmo.getPosition()); } + this.dragging = true; }); - this.on('gizmo:hold', (meshInstance, target) => { - const element = this.elementMap.get(meshInstance); - position.copy(this.gizmo.getPosition()); - switch (element.name) { - case 'x': - position.x = target.x; - break; - case 'y': - position.y = target.y; - break; - case 'z': - position.z = target.z; - break; - case 'yz': - position.y = target.y; - position.z = target.z; - break; - case 'xz': - position.x = target.x; - position.z = target.z; - break; - case 'xy': - position.x = target.x; - position.y = target.y; - break; - } - this.gizmo.setPosition(position); + this.on('pointerup', (e) => { + this.dragging = false; }); } + _hover(selected) { + const element = this.elementMap.get(selected); + if (element === this._dirtyElement) { + return; + } + if (this._dirtyElement) { + this._dirtyElement.hover(false); + this._dirtyElement = null; + } + if (element) { + element.hover(true); + this._dirtyElement = element; + } + } + + _pickPlane(selected) { + + } + set axisGap(value) { this._updateAxisProp('gap', value ?? 0); } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 3727b866580..2e81ff808b8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -3,7 +3,6 @@ import { EventHandler, Layer, Entity, - Vec2, Vec3 } from 'playcanvas' @@ -14,8 +13,6 @@ const v1 = new Vec3(); const v2 = new Vec3(); const v3 = new Vec3(); const intersect = new Vec3(); -const worldIntersect = new Vec3(); -const gizmoTarget = new Vec3(); class Gizmo extends EventHandler { _nodeParents = new Map(); @@ -37,47 +34,25 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - const pointerInfo = { - down: false, - meshInstance: null, - localPosition: new Vec3() - }; - const onPointerMove = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); - this.fire('gizmo:hover', selection[0]?.meshInstance); - - if (pointerInfo.down) { - const cameraDist = this._distanceFromCamera(); - const screenPoint = this.camera.camera.screenToWorld(e.clientX, e.clientY, cameraDist); - gizmoTarget.sub2(screenPoint, pointerInfo.localPosition); - this.fire('gizmo:hold', pointerInfo.meshInstance, gizmoTarget); - } + this.fire('pointermove', e, selection[0]); }; const onPointerDown = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); - const first = selection[0]; - if (first) { - const meshInstance = first.meshInstance; - const wtm = meshInstance.node.getWorldTransform(); - wtm.transformPoint(first.intersect, worldIntersect); - - pointerInfo.down = true; - pointerInfo.meshInstance = meshInstance; - pointerInfo.localPosition.sub2(worldIntersect, this.gizmo.getPosition()); - } + this.fire('pointerdown', e, selection[0]); }; const onPointerUp = (e) => { if (!this.gizmo.enabled) { return; } - pointerInfo.down = false; + this.fire('pointerup', e); }; window.addEventListener('pointermove', onPointerMove); @@ -101,22 +76,9 @@ class Gizmo extends EventHandler { } center.divScalar(this.nodes.length); this.gizmo.setPosition(center); - - // add nodes to held entity - for (let i = 0; i < this.nodes.length; i++) { - this._nodeParents.set(this.nodes[i], this.nodes[i].parent); - this.held.addChild(this.nodes[i]); - } } detach() { - // fetch parents and reattach - for (let i = 0; i < this.nodes.length; i++) { - const parent = this._nodeParents.get(this.nodes[i]); - parent.addChild(this.nodes[i]); - } - - this._nodeParents.clear(); this.nodes = []; this.gizmo.enabled = false; } @@ -137,10 +99,6 @@ class Gizmo extends EventHandler { this.gizmo = new Entity('gizmo'); this.app.root.addChild(this.gizmo); this.gizmo.enabled = false; - - // entity for holding nodes - this.held = new Entity('held'); - this.gizmo.addChild(this.held); } _distanceFromCamera(dist = new Vec3()) { From 810be60c642a2edbaa598fbce9b14452c375289e Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 14:53:42 +0000 Subject: [PATCH 008/178] ported axis and plane transforms from editor gizmo code --- extras/gizmo/gizmo-transform.js | 136 ++++++++++++++++---------------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 6c13a764857..1e0b9d4a0a1 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,17 +1,18 @@ import { + PROJECTION_PERSPECTIVE, BLEND_NORMAL, - Ray, - Plane, Color, StandardMaterial, Entity, - Vec3 + Vec3, + Quat, } from 'playcanvas' import { Gizmo } from "./gizmo.js"; // temporary variables -const worldIntersect = new Vec3(); +const tmpV = new Vec3(); +const tmpQ = new Quat(); class TransformElement { _position; @@ -56,7 +57,7 @@ class PlaneElement extends TransformElement { } _createPlane(layers) { - this.entity = new Entity('plane'); + this.entity = new Entity('plane_' + this.name); this.entity.addComponent('render', { type: 'plane', layers: layers, @@ -136,12 +137,12 @@ class AxisElement extends TransformElement { } _createAxis(layers) { - this.entity = new Entity('axis'); + this.entity = new Entity('axis_' + this.name); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); - this._line = new Entity('line'); + this._line = new Entity('line_' + this.name); this._line.addComponent('render', { type: 'cylinder', layers: layers, @@ -152,7 +153,7 @@ class AxisElement extends TransformElement { this.entity.addChild(this._line); this.meshInstances.push(...this._line.render.meshInstances); - this._arrow = new Entity('arrow'); + this._arrow = new Entity('arrow_' + this.name); this._arrow.addComponent('render', { type: 'cone', layers: layers, @@ -200,6 +201,12 @@ class GizmoTransform extends Gizmo { } }; + this._planeAxis = { + xz: 'y', + xy: 'z', + yz: 'x' + }; + this.elements = { x: new AxisElement({ name: 'x', @@ -254,53 +261,20 @@ class GizmoTransform extends Gizmo { this._createTransform(); this.dragging = false; + this._axisName = ''; + this._isPlane = false; this._pointStart = new Vec3(); - this._pointEnd = new Vec3(); this._offset = new Vec3(); - - const plane = new Plane(); + this._gizmoStart = new Vec3(); this.on('pointermove', (e, selected) => { this._hover(selected?.meshInstance); - // if (this.dragging) { - // const mouseWPos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); - // const rayOrigin = this.camera.getPosition(); - // const rayDir = new Vec3(); - // rayDir.sub(mouseWPos, rayOrigin).normalize(); - // const ray = new Ray(rayOrigin, rayDir); - // plane.intersectsRay(ray, this._pointEnd); - // this._offset.sub2(this._pointStart, this._pointEnd); - - // const position = this.gizmo.getPosition(); - // position.add(this._offset); - // this.gizmo.setPosition(position); - - - // } - // if (selected && this.dragging) { - // const meshInstance = selected.meshInstance; - // const wtm = meshInstance.node.getWorldTransform(); - // wtm.transformPoint(selected.intersect, worldIntersect); - - // this._pointEnd.sub2(worldIntersect, this.gizmo.getPosition()); - - // this._offset.sub2(this._pointEnd, this._pointStart); - - // const element = this.elementMap.get(meshInstance); - // const position = this.gizmo.getPosition(); - // if (element.name.indexOf('x') === -1) { - // this._offset.x = 0; - // } - // if (element.name.indexOf('y') === -1) { - // this._offset.y = 0; - // } - // if (element.name.indexOf('z') === -1) { - // this._offset.z = 0; - // } - // position.add(this._offset); - // this.gizmo.setPosition(position); - // } + if (this.dragging) { + this._offset.copy(this._pickPlane(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._offset.sub(this._pointStart); + this.gizmo.setPosition(this._gizmoStart.clone().add(this._offset)); + } }); this.on('pointerdown', (e, selected) => { @@ -309,23 +283,11 @@ class GizmoTransform extends Gizmo { } if (selected) { const meshInstance = selected.meshInstance; - const rot = meshInstance.node.getRotation().clone(); - rot.transformVector(Vec3.UP, plane.normal); - - const mouseWPos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); - const rayOrigin = this.camera.getPosition(); - const rayDir = new Vec3(); - rayDir.sub(mouseWPos, rayOrigin).normalize(); - const ray = new Ray(rayOrigin, rayDir); - const hit = plane.intersectsRay(ray, this._pointStart); - console.log(this._pointStart); - - // const mousePos = this.camera.camera.screenToWorld(e.clientX, e.clientY, 1); - // this.gizmo.setPosition(mousePos); - // const meshInstance = selected.meshInstance; - // const wtm = meshInstance.node.getWorldTransform(); - // wtm.transformPoint(selected.intersect, worldIntersect); - // this._pointStart.sub2(worldIntersect, this.gizmo.getPosition()); + const axisName = meshInstance.node.name.split("_")[1]; + this._isPlane = axisName.length === 2; + this._currAxis = this._planeAxis[axisName] ?? axisName; + this._pointStart.copy(this._pickPlane(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._gizmoStart.copy(this.gizmo.getPosition()); } this.dragging = true; }); @@ -350,8 +312,48 @@ class GizmoTransform extends Gizmo { } } - _pickPlane(selected) { + _pickPlane(x, y, axis, isPlane) { + const gizmoPos = this.gizmo.getPosition(); + const mouseWPos = this.camera.camera.screenToWorld(x, y, 1); + const rayOrigin = this.camera.getPosition(); + const rayDir = new Vec3(); + const planeNormal = new Vec3(); + planeNormal[axis] = 1; + + if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + rayDir.copy(mouseWPos).sub(rayOrigin).normalize(); + } else { + rayOrigin.add(mouseWPos); + this.camera.getWorldTransform().transformVector(tmpV.set(0, 0, -1), rayDir); + } + + tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); + + if (!isPlane) { + tmpV.copy(rayOrigin).sub(gizmoPos).normalize(); + planeNormal.copy(tmpV.sub(planeNormal.scale(planeNormal.dot(tmpV))).normalize()); + } + const rayPlaneDot = planeNormal.dot(rayDir); + const planeDist = gizmoPos.dot(planeNormal); + const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; + const pickedPos = rayDir.scale(-pointPlaneDist).add(rayOrigin); + + if (!isPlane) { + planeNormal.set(0, 0, 0); + planeNormal[axis] = 1; + tmpQ.transformVector(planeNormal, planeNormal); + pickedPos.copy(planeNormal.scale(planeNormal.dot(pickedPos))); + } + + tmpQ.invert().transformVector(pickedPos, pickedPos); + + if (!isPlane) { + var v = pickedPos[axis]; + pickedPos.set(0, 0, 0); + pickedPos[axis] = v; + } + return pickedPos; } set axisGap(value) { From 0f85ed86f5f0fb13acaa2e073a1763b5a816145c Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 14:55:13 +0000 Subject: [PATCH 009/178] removed unused camera distance function --- extras/gizmo/gizmo.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 2e81ff808b8..c071ef43208 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -101,10 +101,6 @@ class Gizmo extends EventHandler { this.gizmo.enabled = false; } - _distanceFromCamera(dist = new Vec3()) { - return dist.sub2(this.camera.getPosition(), this.gizmo.getPosition()).length(); - } - _getSelection(x, y) { const start = this.camera.camera.screenToWorld(x, y, 1); const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); From 72a1332e69d416fd04af0c178fdddf6cc2dae0fc Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 15:04:12 +0000 Subject: [PATCH 010/178] removed planeaxis mapping object --- extras/gizmo/gizmo-transform.js | 57 +++++++++++++++------------------ 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 1e0b9d4a0a1..597f2a31f42 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -14,7 +14,7 @@ import { Gizmo } from "./gizmo.js"; const tmpV = new Vec3(); const tmpQ = new Quat(); -class TransformElement { +class AxisShape { _position; _rotation; @@ -25,14 +25,14 @@ class TransformElement { _hoverColor; - name; + axis; entity; meshInstances = []; constructor(options) { - this.name = options.name ?? 'INVALID'; + this.axis = options.axis ?? 'x'; this._position = options.position ?? new Vec3(); this._rotation = options.rotation ?? new Vec3(); this._scale = options.scale ?? new Vec3(1, 1, 1); @@ -49,7 +49,7 @@ class TransformElement { } } -class PlaneElement extends TransformElement { +class AxisPlane extends AxisShape { constructor(options) { super(options); @@ -57,7 +57,7 @@ class PlaneElement extends TransformElement { } _createPlane(layers) { - this.entity = new Entity('plane_' + this.name); + this.entity = new Entity('plane_' + this.axis); this.entity.addComponent('render', { type: 'plane', layers: layers, @@ -71,7 +71,7 @@ class PlaneElement extends TransformElement { } } -class AxisElement extends TransformElement { +class AxisArrow extends AxisShape { _gap = 0; _lineThickness = 0.04; @@ -85,7 +85,7 @@ class AxisElement extends TransformElement { constructor(options = {}) { super(options); - this._createAxis(options.layers ?? []); + this._createArrow(options.layers ?? []); } set gap(value) { @@ -136,13 +136,13 @@ class AxisElement extends TransformElement { return this._arrowLength; } - _createAxis(layers) { - this.entity = new Entity('axis_' + this.name); + _createArrow(layers) { + this.entity = new Entity('axis_' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); - this._line = new Entity('line_' + this.name); + this._line = new Entity('line_' + this.axis); this._line.addComponent('render', { type: 'cylinder', layers: layers, @@ -153,7 +153,7 @@ class AxisElement extends TransformElement { this.entity.addChild(this._line); this.meshInstances.push(...this._line.render.meshInstances); - this._arrow = new Entity('arrow_' + this.name); + this._arrow = new Entity('arrow_' + this.axis); this._arrow.addComponent('render', { type: 'cone', layers: layers, @@ -201,36 +201,30 @@ class GizmoTransform extends Gizmo { } }; - this._planeAxis = { - xz: 'y', - xy: 'z', - yz: 'x' - }; - this.elements = { - x: new AxisElement({ - name: 'x', + x: new AxisArrow({ + axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this.materials.semi.red, hoverColor: this.materials.opaque.red }), - y: new AxisElement({ - name: 'y', + y: new AxisArrow({ + axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this.materials.semi.green, hoverColor: this.materials.opaque.green }), - z: new AxisElement({ - name: 'z', + z: new AxisArrow({ + axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), defaultColor: this.materials.semi.blue, hoverColor: this.materials.opaque.blue }), - yz: new PlaneElement({ - name: 'yz', + yz: new AxisPlane({ + axis: 'x', layers: [this.layerGizmo.id], position: new Vec3(0, 0.1, 0.1), rotation: new Vec3(0, 0, -90), @@ -238,8 +232,8 @@ class GizmoTransform extends Gizmo { defaultColor: this.materials.semi.red, hoverColor: this.materials.opaque.red }), - xz: new PlaneElement({ - name: 'xz', + xz: new AxisPlane({ + axis: 'y', layers: [this.layerGizmo.id], position: new Vec3(0.1, 0, 0.1), rotation: new Vec3(0, 0, 0), @@ -247,8 +241,8 @@ class GizmoTransform extends Gizmo { defaultColor: this.materials.semi.green, hoverColor: this.materials.opaque.green }), - xy: new PlaneElement({ - name: 'xy', + xy: new AxisPlane({ + axis: 'z', layers: [this.layerGizmo.id], position: new Vec3(0.1, 0.1, 0), rotation: new Vec3(90, 0, 0), @@ -283,9 +277,8 @@ class GizmoTransform extends Gizmo { } if (selected) { const meshInstance = selected.meshInstance; - const axisName = meshInstance.node.name.split("_")[1]; - this._isPlane = axisName.length === 2; - this._currAxis = this._planeAxis[axisName] ?? axisName; + this._currAxis = meshInstance.node.name.split("_")[1]; + this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; this._pointStart.copy(this._pickPlane(e.clientX, e.clientY, this._currAxis, this._isPlane)); this._gizmoStart.copy(this.gizmo.getPosition()); } From e158336234a0cdfff00296d0b8599edfc94ca2bb Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 15:14:10 +0000 Subject: [PATCH 011/178] renamed methods and added comments for point calc --- extras/gizmo/gizmo-transform.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 597f2a31f42..48b1c4572c8 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -265,7 +265,7 @@ class GizmoTransform extends Gizmo { this._hover(selected?.meshInstance); if (this.dragging) { - this._offset.copy(this._pickPlane(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._offset.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); this._offset.sub(this._pointStart); this.gizmo.setPosition(this._gizmoStart.clone().add(this._offset)); } @@ -279,7 +279,7 @@ class GizmoTransform extends Gizmo { const meshInstance = selected.meshInstance; this._currAxis = meshInstance.node.name.split("_")[1]; this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; - this._pointStart.copy(this._pickPlane(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._pointStart.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); this._gizmoStart.copy(this.gizmo.getPosition()); } this.dragging = true; @@ -305,14 +305,17 @@ class GizmoTransform extends Gizmo { } } - _pickPlane(x, y, axis, isPlane) { + _calcPoint(x, y, axis, isPlane) { const gizmoPos = this.gizmo.getPosition(); const mouseWPos = this.camera.camera.screenToWorld(x, y, 1); const rayOrigin = this.camera.getPosition(); const rayDir = new Vec3(); + + // set plane normal based on axis const planeNormal = new Vec3(); planeNormal[axis] = 1; + // calculate ray direction from mouse position if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { rayDir.copy(mouseWPos).sub(rayOrigin).normalize(); } else { @@ -320,33 +323,39 @@ class GizmoTransform extends Gizmo { this.camera.getWorldTransform().transformVector(tmpV.set(0, 0, -1), rayDir); } + // rotate plane normal by gizmo rotation tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); if (!isPlane) { tmpV.copy(rayOrigin).sub(gizmoPos).normalize(); planeNormal.copy(tmpV.sub(planeNormal.scale(planeNormal.dot(tmpV))).normalize()); } + + // ray intersection with plane const rayPlaneDot = planeNormal.dot(rayDir); const planeDist = gizmoPos.dot(planeNormal); const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; - const pickedPos = rayDir.scale(-pointPlaneDist).add(rayOrigin); + const point = rayDir.scale(-pointPlaneDist).add(rayOrigin); if (!isPlane) { + // reset normal based on axis and project position from plane onto normal planeNormal.set(0, 0, 0); planeNormal[axis] = 1; tmpQ.transformVector(planeNormal, planeNormal); - pickedPos.copy(planeNormal.scale(planeNormal.dot(pickedPos))); + point.copy(planeNormal.scale(planeNormal.dot(point))); } - tmpQ.invert().transformVector(pickedPos, pickedPos); + // rotate point back to world coords + tmpQ.invert().transformVector(point, point); if (!isPlane) { - var v = pickedPos[axis]; - pickedPos.set(0, 0, 0); - pickedPos[axis] = v; + // set other axes to zero if not plane point + const v = point[axis]; + point.set(0, 0, 0); + point[axis] = v; } - return pickedPos; + return point; } set axisGap(value) { From 49f5fc8ebd7c35600216b0bbc4a4c7f01a45cd14 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 16:41:41 +0000 Subject: [PATCH 012/178] add guidlines when moving translate gizmo --- extras/gizmo/gizmo-transform.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 48b1c4572c8..5d912455215 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -201,6 +201,8 @@ class GizmoTransform extends Gizmo { } }; + this._guideLineColor = new Color(1, 1, 1, 0.5); + this.elements = { x: new AxisArrow({ axis: 'x', @@ -255,12 +257,29 @@ class GizmoTransform extends Gizmo { this._createTransform(); this.dragging = false; - this._axisName = ''; + this._currAxis = ''; this._isPlane = false; this._pointStart = new Vec3(); this._offset = new Vec3(); this._gizmoStart = new Vec3(); + const guildAxes = ['x', 'y', 'z']; + this.app.on('update', () => { + const gizmoPos = this.gizmo.getPosition(); + for (let i = 0; i < guildAxes.length; i++) { + const axis = guildAxes[i]; + if (this._isPlane) { + if (axis !== this._currAxis) { + this._drawGuideLine(gizmoPos, axis); + } + } else { + if (axis === this._currAxis) { + this._drawGuideLine(gizmoPos, axis); + } + } + } + }); + this.on('pointermove', (e, selected) => { this._hover(selected?.meshInstance); @@ -281,12 +300,14 @@ class GizmoTransform extends Gizmo { this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; this._pointStart.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); this._gizmoStart.copy(this.gizmo.getPosition()); + this.dragging = true; } - this.dragging = true; }); this.on('pointerup', (e) => { this.dragging = false; + this._currAxis = ''; + this._isPlane = false; }); } @@ -358,6 +379,14 @@ class GizmoTransform extends Gizmo { return point; } + _drawGuideLine(pos, axis) { + const startDir = new Vec3(); + startDir[axis] = 1; + startDir.scale(1000); + const endDir = startDir.clone().scale(-1); + this.app.drawLine(startDir.add(pos), endDir.add(pos), this._guideLineColor, true, this.layerGizmo); + } + set axisGap(value) { this._updateAxisProp('gap', value ?? 0); } From 969c0e2089d525a8411be071e2016dbbd28f8fbd Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 29 Dec 2023 16:52:13 +0000 Subject: [PATCH 013/178] starting setting gizmo nodes positions based on translation --- extras/gizmo/gizmo.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c071ef43208..48cef0e04e3 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -23,6 +23,8 @@ class Gizmo extends EventHandler { nodes = []; + nodesStart = new Map(); + gizmo; constructor(app, camera) { @@ -69,20 +71,31 @@ class Gizmo extends EventHandler { this.nodes = nodes; this.gizmo.enabled = true; - // calculate center - const center = new Vec3(); + // set nodes starting positions for (let i = 0; i < this.nodes.length; i++) { - center.add(this.nodes[i].getPosition()); + const node = this.nodes[i]; + this.nodesStart.set(node, node.getPosition()); } - center.divScalar(this.nodes.length); - this.gizmo.setPosition(center); + + this.gizmo.setPosition(this.getGizmoPosition()); } detach() { this.nodes = []; + this.nodesStart.clear(); this.gizmo.enabled = false; } + getGizmoPosition() { + const center = new Vec3(); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + const nodePos = node.getPosition(); + center.add(nodePos); + } + return center.scale(1.0 / this.nodes.length); + } + _createLayer() { this.layerGizmo = new Layer({ name: 'Gizmo', From 41353f4b79e6a40b8c8a754828dfbfeef22cffd9 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 29 Dec 2023 22:48:30 +0000 Subject: [PATCH 014/178] applied offsets to selected nodes and update gizmo position based on nodes --- extras/gizmo/gizmo-transform.js | 74 +++++++++++++++++---------------- extras/gizmo/gizmo.js | 33 +++++++++------ 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 5d912455215..39d80f17c8f 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -14,6 +14,9 @@ import { Gizmo } from "./gizmo.js"; const tmpV = new Vec3(); const tmpQ = new Quat(); +// constants +const GUIDELINE_SIZE = 1e3; + class AxisShape { _position; @@ -188,7 +191,7 @@ class GizmoTransform extends Gizmo { constructor(app, camera) { super(app, camera); - this.materials = { + this._materials = { opaque: { red: this._createMaterial(new Color(1, 0.3, 0.3)), green: this._createMaterial(new Color(0.3, 1, 0.3)), @@ -203,27 +206,27 @@ class GizmoTransform extends Gizmo { this._guideLineColor = new Color(1, 1, 1, 0.5); - this.elements = { + this._axisShapes = { x: new AxisArrow({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this.materials.semi.red, - hoverColor: this.materials.opaque.red + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red }), y: new AxisArrow({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this.materials.semi.green, - hoverColor: this.materials.opaque.green + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green }), z: new AxisArrow({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this.materials.semi.blue, - hoverColor: this.materials.opaque.blue + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue }), yz: new AxisPlane({ axis: 'x', @@ -231,8 +234,8 @@ class GizmoTransform extends Gizmo { position: new Vec3(0, 0.1, 0.1), rotation: new Vec3(0, 0, -90), scale: new Vec3(0.2, 0.2, 0.2), - defaultColor: this.materials.semi.red, - hoverColor: this.materials.opaque.red + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red }), xz: new AxisPlane({ axis: 'y', @@ -240,8 +243,8 @@ class GizmoTransform extends Gizmo { position: new Vec3(0.1, 0, 0.1), rotation: new Vec3(0, 0, 0), scale: new Vec3(0.2, 0.2, 0.2), - defaultColor: this.materials.semi.green, - hoverColor: this.materials.opaque.green + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green }), xy: new AxisPlane({ axis: 'z', @@ -249,8 +252,8 @@ class GizmoTransform extends Gizmo { position: new Vec3(0.1, 0.1, 0), rotation: new Vec3(90, 0, 0), scale: new Vec3(0.2, 0.2, 0.2), - defaultColor: this.materials.semi.blue, - hoverColor: this.materials.opaque.blue + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue }) }; @@ -261,7 +264,6 @@ class GizmoTransform extends Gizmo { this._isPlane = false; this._pointStart = new Vec3(); this._offset = new Vec3(); - this._gizmoStart = new Vec3(); const guildAxes = ['x', 'y', 'z']; this.app.on('update', () => { @@ -286,7 +288,7 @@ class GizmoTransform extends Gizmo { if (this.dragging) { this._offset.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); this._offset.sub(this._pointStart); - this.gizmo.setPosition(this._gizmoStart.clone().add(this._offset)); + this.updateNodePositions(this._offset); } }); @@ -299,7 +301,7 @@ class GizmoTransform extends Gizmo { this._currAxis = meshInstance.node.name.split("_")[1]; this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; this._pointStart.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); - this._gizmoStart.copy(this.gizmo.getPosition()); + this.storeNodePositions(); this.dragging = true; } }); @@ -312,17 +314,17 @@ class GizmoTransform extends Gizmo { } _hover(selected) { - const element = this.elementMap.get(selected); - if (element === this._dirtyElement) { + const shape = this.elementMap.get(selected); + if (shape === this._dirtyElement) { return; } if (this._dirtyElement) { this._dirtyElement.hover(false); this._dirtyElement = null; } - if (element) { - element.hover(true); - this._dirtyElement = element; + if (shape) { + shape.hover(true); + this._dirtyElement = shape; } } @@ -382,7 +384,7 @@ class GizmoTransform extends Gizmo { _drawGuideLine(pos, axis) { const startDir = new Vec3(); startDir[axis] = 1; - startDir.scale(1000); + startDir.scale(GUIDELINE_SIZE); const endDir = startDir.clone().scale(-1); this.app.drawLine(startDir.add(pos), endDir.add(pos), this._guideLineColor, true, this.layerGizmo); } @@ -392,7 +394,7 @@ class GizmoTransform extends Gizmo { } get axisGap() { - return this.elements.x.gap; + return this._axisShapes.x.gap; } set axisLineThickness(value) { @@ -400,7 +402,7 @@ class GizmoTransform extends Gizmo { } get axisLineThickness() { - return this.elements.x.lineThickness; + return this._axisShapes.x.lineThickness; } set axisLineLength(value) { @@ -408,7 +410,7 @@ class GizmoTransform extends Gizmo { } get axisLineLength() { - return this.elements.x.lineLength; + return this._axisShapes.x.lineLength; } set axisArrowThickness(value) { @@ -416,7 +418,7 @@ class GizmoTransform extends Gizmo { } get axisArrowThickness() { - return this.elements.x.arrowThickness; + return this._axisShapes.x.arrowThickness; } set axisArrowLength(value) { @@ -425,7 +427,7 @@ class GizmoTransform extends Gizmo { } get axisArrowLength() { - return this.elements.x.arrowLength; + return this._axisShapes.x.arrowLength; } _createMaterial(color) { @@ -457,19 +459,19 @@ class GizmoTransform extends Gizmo { this.gizmo.addChild(center); // elements - for (const key in this.elements) { - const element = this.elements[key]; - center.addChild(element.entity); - for (let i = 0; i < element.meshInstances.length; i++) { - this.elementMap.set(element.meshInstances[i], element); + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + center.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this.elementMap.set(shape.meshInstances[i], shape); } } } _updateAxisProp(propName, value) { - this.elements.x[propName] = value; - this.elements.y[propName] = value; - this.elements.z[propName] = value; + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 48cef0e04e3..d06938acce1 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -23,7 +23,7 @@ class Gizmo extends EventHandler { nodes = []; - nodesStart = new Map(); + nodePositions = new Map(); gizmo; @@ -70,30 +70,39 @@ class Gizmo extends EventHandler { attach(nodes) { this.nodes = nodes; this.gizmo.enabled = true; - - // set nodes starting positions - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - this.nodesStart.set(node, node.getPosition()); - } - this.gizmo.setPosition(this.getGizmoPosition()); } detach() { this.nodes = []; - this.nodesStart.clear(); + this.nodePositions.clear(); this.gizmo.enabled = false; } + storeNodePositions() { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + const pos = node.getPosition(); + this.nodePositions.set(node, pos.clone()); + } + } + + updateNodePositions(point) { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + const pos = this.nodePositions.get(node); + node.setPosition(pos.clone().add(point)); + } + this.gizmo.setPosition(this.getGizmoPosition()); + } + getGizmoPosition() { const center = new Vec3(); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - const nodePos = node.getPosition(); - center.add(nodePos); + center.add(node.getPosition()); } - return center.scale(1.0 / this.nodes.length); + return center.scale(1.0 / this.nodes.length).clone(); } _createLayer() { From e479b2d48d22030fc61707ba95f5cc2b1e942ef6 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 29 Dec 2023 22:50:10 +0000 Subject: [PATCH 015/178] extracted guildine axes constant outside class --- extras/gizmo/gizmo-transform.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 39d80f17c8f..9756cd66b0a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -16,6 +16,7 @@ const tmpQ = new Quat(); // constants const GUIDELINE_SIZE = 1e3; +const GUILDLINE_AXES = ['x', 'y', 'z']; class AxisShape { _position; @@ -265,11 +266,10 @@ class GizmoTransform extends Gizmo { this._pointStart = new Vec3(); this._offset = new Vec3(); - const guildAxes = ['x', 'y', 'z']; this.app.on('update', () => { const gizmoPos = this.gizmo.getPosition(); - for (let i = 0; i < guildAxes.length; i++) { - const axis = guildAxes[i]; + for (let i = 0; i < GUILDLINE_AXES.length; i++) { + const axis = GUILDLINE_AXES[i]; if (this._isPlane) { if (axis !== this._currAxis) { this._drawGuideLine(gizmoPos, axis); From 6140dd1f134568a26b3f9aeb8e6a0ae107849e5f Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 29 Dec 2023 22:54:02 +0000 Subject: [PATCH 016/178] replaced new vector with reusing tmp vector --- extras/gizmo/gizmo-transform.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 9756cd66b0a..ce2e0d23fcc 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -382,11 +382,11 @@ class GizmoTransform extends Gizmo { } _drawGuideLine(pos, axis) { - const startDir = new Vec3(); - startDir[axis] = 1; - startDir.scale(GUIDELINE_SIZE); - const endDir = startDir.clone().scale(-1); - this.app.drawLine(startDir.add(pos), endDir.add(pos), this._guideLineColor, true, this.layerGizmo); + tmpV.set(0, 0, 0); + tmpV[axis] = 1; + tmpV.scale(GUIDELINE_SIZE); + const tmpV2 = tmpV.clone().scale(-1); + this.app.drawLine(tmpV.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); } set axisGap(value) { From 7aeb8c8e77dd29b783f10408944cbdfedaa06726 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 29 Dec 2023 23:02:26 +0000 Subject: [PATCH 017/178] updated commented box for testing --- examples/src/examples/misc/gizmos.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 7843414d03e..f3d5cbffe24 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -96,7 +96,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { // boxB.addComponent('render', { // type: 'box' // }); - // boxB.setPosition(1, 0, 0); + // boxB.setPosition(1, 0, 1); // app.root.addChild(boxB); // create camera entity From 54f26e749c5204101aadb5f61bede703b89d8f46 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Fri, 29 Dec 2023 23:30:24 +0000 Subject: [PATCH 018/178] added plane size setting to controls --- examples/src/examples/misc/gizmos.mjs | 9 +++- extras/gizmo/gizmo-transform.js | 67 ++++++++++++++++++++------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index f3d5cbffe24..6b4ea8af439 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -37,6 +37,12 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'settings.axisArrowLength' } }) + ), + jsx(LabelGroup, { text: 'Axis Plane Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisPlaneSize' } + }) ) ) ); @@ -122,7 +128,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisLineThickness: gizmo.axisLineThickness, axisLineLength: gizmo.axisLineLength, axisArrowThickness: gizmo.axisArrowThickness, - axisArrowLength: gizmo.axisArrowLength + axisArrowLength: gizmo.axisArrowLength, + axisPlaneSize: gizmo.axisPlaneSize }); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index ce2e0d23fcc..b1427bf54c6 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -15,8 +15,8 @@ const tmpV = new Vec3(); const tmpQ = new Quat(); // constants +const VEC3_AXES = Object.keys(tmpV); const GUIDELINE_SIZE = 1e3; -const GUILDLINE_AXES = ['x', 'y', 'z']; class AxisShape { _position; @@ -54,12 +54,26 @@ class AxisShape { } class AxisPlane extends AxisShape { + _size = 0.2; + constructor(options) { super(options); this._createPlane(options.layers ?? []); } + _getPosition() { + const position = new Vec3(); + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; + if (axis === this.axis) { + continue; + } + position[axis] = this._size / 2; + } + return position; + } + _createPlane(layers) { this.entity = new Entity('plane_' + this.axis); this.entity.addComponent('render', { @@ -68,11 +82,21 @@ class AxisPlane extends AxisShape { material: this._defaultColor, castShadows: false }); - this.entity.setLocalPosition(this._position); + this.entity.setLocalPosition(this._getPosition()); this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); + this.entity.setLocalScale(this._size, this._size, this._size); this.meshInstances.push(...this.entity.render.meshInstances); } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } } class AxisArrow extends AxisShape { @@ -232,27 +256,21 @@ class GizmoTransform extends Gizmo { yz: new AxisPlane({ axis: 'x', layers: [this.layerGizmo.id], - position: new Vec3(0, 0.1, 0.1), rotation: new Vec3(0, 0, -90), - scale: new Vec3(0.2, 0.2, 0.2), defaultColor: this._materials.semi.red, hoverColor: this._materials.opaque.red }), xz: new AxisPlane({ axis: 'y', layers: [this.layerGizmo.id], - position: new Vec3(0.1, 0, 0.1), rotation: new Vec3(0, 0, 0), - scale: new Vec3(0.2, 0.2, 0.2), defaultColor: this._materials.semi.green, hoverColor: this._materials.opaque.green }), xy: new AxisPlane({ axis: 'z', layers: [this.layerGizmo.id], - position: new Vec3(0.1, 0.1, 0), rotation: new Vec3(90, 0, 0), - scale: new Vec3(0.2, 0.2, 0.2), defaultColor: this._materials.semi.blue, hoverColor: this._materials.opaque.blue }) @@ -268,8 +286,8 @@ class GizmoTransform extends Gizmo { this.app.on('update', () => { const gizmoPos = this.gizmo.getPosition(); - for (let i = 0; i < GUILDLINE_AXES.length; i++) { - const axis = GUILDLINE_AXES[i]; + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; if (this._isPlane) { if (axis !== this._currAxis) { this._drawGuideLine(gizmoPos, axis); @@ -390,7 +408,7 @@ class GizmoTransform extends Gizmo { } set axisGap(value) { - this._updateAxisProp('gap', value ?? 0); + this._updateArrowProp('gap', value ?? 0); } get axisGap() { @@ -398,7 +416,7 @@ class GizmoTransform extends Gizmo { } set axisLineThickness(value) { - this._updateAxisProp('lineThickness', value ?? 1); + this._updateArrowProp('lineThickness', value ?? 1); } get axisLineThickness() { @@ -406,7 +424,7 @@ class GizmoTransform extends Gizmo { } set axisLineLength(value) { - this._updateAxisProp('lineLength', value ?? 1); + this._updateArrowProp('lineLength', value ?? 1); } get axisLineLength() { @@ -414,7 +432,7 @@ class GizmoTransform extends Gizmo { } set axisArrowThickness(value) { - this._updateAxisProp('arrowThickness', value ?? 1); + this._updateArrowProp('arrowThickness', value ?? 1); } get axisArrowThickness() { @@ -423,13 +441,22 @@ class GizmoTransform extends Gizmo { set axisArrowLength(value) { this._axisArrowLength = value ?? 1; - this._updateAxisProp('arrowLength', this._axisArrowLength); + this._updateArrowProp('arrowLength', this._axisArrowLength); } get axisArrowLength() { return this._axisShapes.x.arrowLength; } + set axisPlaneSize(value) { + this._axisPlaneSize = value ?? 1; + this._updatePlaneProp('size', this._axisPlaneSize); + } + + get axisPlaneSize() { + return this._axisShapes.yz.size; + } + _createMaterial(color) { const material = new StandardMaterial(); material.diffuse = color; @@ -468,11 +495,17 @@ class GizmoTransform extends Gizmo { } } - _updateAxisProp(propName, value) { + _updateArrowProp(propName, value) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; } + + _updatePlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } } export { GizmoTransform }; From b46ca783be90b86b579bcc5726455c3e73881698 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sat, 30 Dec 2023 13:49:41 +0000 Subject: [PATCH 019/178] comma removal --- extras/gizmo/gizmo-transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index b1427bf54c6..e5c3e154e34 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -5,7 +5,7 @@ import { StandardMaterial, Entity, Vec3, - Quat, + Quat } from 'playcanvas' import { Gizmo } from "./gizmo.js"; From aab2101c268023991aed1728f4e419051222a943 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sat, 30 Dec 2023 14:18:13 +0000 Subject: [PATCH 020/178] abstracted parameters for gizmo base class --- extras/gizmo/gizmo-transform.js | 10 +++++----- extras/gizmo/gizmo.js | 7 +++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index e5c3e154e34..ae2fb46aa9b 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -300,17 +300,17 @@ class GizmoTransform extends Gizmo { } }); - this.on('pointermove', (e, selected) => { + this.on('pointermove', (x, y, selected) => { this._hover(selected?.meshInstance); if (this.dragging) { - this._offset.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._offset.copy(this._calcPoint(x, y, this._currAxis, this._isPlane)); this._offset.sub(this._pointStart); this.updateNodePositions(this._offset); } }); - this.on('pointerdown', (e, selected) => { + this.on('pointerdown', (x, y, selected) => { if (this.dragging) { return; } @@ -318,13 +318,13 @@ class GizmoTransform extends Gizmo { const meshInstance = selected.meshInstance; this._currAxis = meshInstance.node.name.split("_")[1]; this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; - this._pointStart.copy(this._calcPoint(e.clientX, e.clientY, this._currAxis, this._isPlane)); + this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._isPlane)); this.storeNodePositions(); this.dragging = true; } }); - this.on('pointerup', (e) => { + this.on('pointerup', () => { this.dragging = false; this._currAxis = ''; this._isPlane = false; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d06938acce1..ab34a64fbf8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -41,20 +41,20 @@ class Gizmo extends EventHandler { return; } const selection = this._getSelection(e.clientX, e.clientY); - this.fire('pointermove', e, selection[0]); + this.fire('pointermove', e.clientX, e.clientY, selection[0]); }; const onPointerDown = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); - this.fire('pointerdown', e, selection[0]); + this.fire('pointerdown', e.clientX, e.clientY, selection[0]); }; const onPointerUp = (e) => { if (!this.gizmo.enabled) { return; } - this.fire('pointerup', e); + this.fire('pointerup'); }; window.addEventListener('pointermove', onPointerMove); @@ -117,7 +117,6 @@ class Gizmo extends EventHandler { } _createGizmo() { - // gizmo root entity this.gizmo = new Entity('gizmo'); this.app.root.addChild(this.gizmo); this.gizmo.enabled = false; From bd6bbb39516554c93cb5cc59de45ca716fd36f08 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 11:40:38 +0000 Subject: [PATCH 021/178] gizmo axis rotation when set coord to local --- examples/src/examples/misc/gizmos.mjs | 1 + extras/gizmo/gizmo-transform.js | 68 ++++++++++++++++++--------- extras/gizmo/gizmo.js | 37 +++++++++++++-- 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 6b4ea8af439..c810937ad0f 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -97,6 +97,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { boxA.addComponent('render', { type: 'box' }); + boxA.rotate(0, 45, 0); app.root.addChild(boxA); // const boxB = new pc.Entity('cubeB'); // boxB.addComponent('render', { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index ae2fb46aa9b..37e289169e4 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -11,11 +11,12 @@ import { import { Gizmo } from "./gizmo.js"; // temporary variables -const tmpV = new Vec3(); +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); const tmpQ = new Quat(); // constants -const VEC3_AXES = Object.keys(tmpV); +const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; class AxisShape { @@ -279,21 +280,26 @@ class GizmoTransform extends Gizmo { this._createTransform(); this.dragging = false; + this._hoverAxis = ''; + this._hoverIsPlane = false; this._currAxis = ''; - this._isPlane = false; + this._currIsPlane = false; this._pointStart = new Vec3(); this._offset = new Vec3(); this.app.on('update', () => { const gizmoPos = this.gizmo.getPosition(); + tmpQ.copy(this.gizmo.getRotation()); + const checkAxis = this._hoverAxis || this._currAxis; + const checkIsPlane = this._hoverIsPlane || this._currIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { const axis = VEC3_AXES[i]; - if (this._isPlane) { - if (axis !== this._currAxis) { + if (checkIsPlane) { + if (axis !== checkAxis) { this._drawGuideLine(gizmoPos, axis); } } else { - if (axis === this._currAxis) { + if (axis === checkAxis) { this._drawGuideLine(gizmoPos, axis); } } @@ -304,9 +310,11 @@ class GizmoTransform extends Gizmo { this._hover(selected?.meshInstance); if (this.dragging) { - this._offset.copy(this._calcPoint(x, y, this._currAxis, this._isPlane)); + this._offset.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); this._offset.sub(this._pointStart); this.updateNodePositions(this._offset); + this._hoverAxis = ''; + this._hoverIsPlane = false; } }); @@ -316,9 +324,9 @@ class GizmoTransform extends Gizmo { } if (selected) { const meshInstance = selected.meshInstance; - this._currAxis = meshInstance.node.name.split("_")[1]; - this._isPlane = meshInstance.node.name.indexOf('plane') !== -1; - this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._isPlane)); + this._currAxis = this._getAxis(meshInstance); + this._currIsPlane = this._getIsPlane(meshInstance); + this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); this.storeNodePositions(); this.dragging = true; } @@ -327,12 +335,28 @@ class GizmoTransform extends Gizmo { this.on('pointerup', () => { this.dragging = false; this._currAxis = ''; - this._isPlane = false; + this._currIsPlane = false; }); } - _hover(selected) { - const shape = this.elementMap.get(selected); + _getAxis(meshInstance) { + if (!meshInstance) { + return ''; + } + return meshInstance.node.name.split("_")[1]; + } + + _getIsPlane(meshInstance) { + if (!meshInstance) { + return false; + } + return meshInstance.node.name.indexOf('plane') !== -1; + } + + _hover(meshInstance) { + this._hoverAxis = this._getAxis(meshInstance); + this._hoverIsPlane = this._getIsPlane(meshInstance); + const shape = this.elementMap.get(meshInstance); if (shape === this._dirtyElement) { return; } @@ -361,15 +385,15 @@ class GizmoTransform extends Gizmo { rayDir.copy(mouseWPos).sub(rayOrigin).normalize(); } else { rayOrigin.add(mouseWPos); - this.camera.getWorldTransform().transformVector(tmpV.set(0, 0, -1), rayDir); + this.camera.getWorldTransform().transformVector(tmpV1.set(0, 0, -1), rayDir); } // rotate plane normal by gizmo rotation tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); if (!isPlane) { - tmpV.copy(rayOrigin).sub(gizmoPos).normalize(); - planeNormal.copy(tmpV.sub(planeNormal.scale(planeNormal.dot(tmpV))).normalize()); + tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); + planeNormal.copy(tmpV1.sub(planeNormal.scale(planeNormal.dot(tmpV1))).normalize()); } // ray intersection with plane @@ -400,11 +424,13 @@ class GizmoTransform extends Gizmo { } _drawGuideLine(pos, axis) { - tmpV.set(0, 0, 0); - tmpV[axis] = 1; - tmpV.scale(GUIDELINE_SIZE); - const tmpV2 = tmpV.clone().scale(-1); - this.app.drawLine(tmpV.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + tmpV1.scale(GUIDELINE_SIZE); + tmpV2.copy(tmpV1).scale(-1); + tmpQ.transformVector(tmpV1, tmpV1); + tmpQ.transformVector(tmpV2, tmpV2); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); } set axisGap(value) { diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index ab34a64fbf8..6aba5bf6702 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -3,6 +3,7 @@ import { EventHandler, Layer, Entity, + Quat, Vec3 } from 'playcanvas' @@ -13,6 +14,8 @@ const v1 = new Vec3(); const v2 = new Vec3(); const v3 = new Vec3(); const intersect = new Vec3(); +const tmpV = new Vec3(); +const tmpQ = new Quat(); class Gizmo extends EventHandler { _nodeParents = new Map(); @@ -23,10 +26,14 @@ class Gizmo extends EventHandler { nodes = []; + nodeLocalPositions = new Map(); + nodePositions = new Map(); gizmo; + mode = 'local'; + constructor(app, camera) { super(); @@ -71,10 +78,18 @@ class Gizmo extends EventHandler { this.nodes = nodes; this.gizmo.enabled = true; this.gizmo.setPosition(this.getGizmoPosition()); + + // TODO: fix temporary hack for gizmo rotation + if (this.mode === 'local') { + this.gizmo.setEulerAngles(this.getGizmoRotation()); + } else { + this.gizmo.setEulerAngles(0, 0, 0); + } } detach() { this.nodes = []; + this.nodeLocalPositions.clear(); this.nodePositions.clear(); this.gizmo.enabled = false; } @@ -82,16 +97,21 @@ class Gizmo extends EventHandler { storeNodePositions() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - const pos = node.getPosition(); - this.nodePositions.set(node, pos.clone()); + this.nodeLocalPositions.set(node, node.getLocalPosition().clone()); + this.nodePositions.set(node, node.getPosition().clone()); } } updateNodePositions(point) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - const pos = this.nodePositions.get(node); - node.setPosition(pos.clone().add(point)); + if (this.mode === 'local') { + tmpV.copy(point); + tmpQ.copy(node.getLocalRotation()).transformVector(tmpV, tmpV); + node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV)); + } else { + node.setPosition(this.nodePositions.get(node).clone().add(point)); + } } this.gizmo.setPosition(this.getGizmoPosition()); } @@ -105,6 +125,15 @@ class Gizmo extends EventHandler { return center.scale(1.0 / this.nodes.length).clone(); } + getGizmoRotation() { + const rot = new Vec3(); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + rot.add(node.getEulerAngles()); + } + return rot.scale(1.0 / this.nodes.length).clone(); + } + _createLayer() { this.layerGizmo = new Layer({ name: 'Gizmo', From 249c417c145c2d47df72c75987fa25ba582c002f Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 11:48:55 +0000 Subject: [PATCH 022/178] removed unused intersect --- extras/gizmo/gizmo-transform.js | 9 ++++----- extras/gizmo/gizmo.js | 23 +++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 37e289169e4..991c0ec5278 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -306,8 +306,8 @@ class GizmoTransform extends Gizmo { } }); - this.on('pointermove', (x, y, selected) => { - this._hover(selected?.meshInstance); + this.on('pointermove', (x, y, meshInstance) => { + this._hover(meshInstance); if (this.dragging) { this._offset.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); @@ -318,12 +318,11 @@ class GizmoTransform extends Gizmo { } }); - this.on('pointerdown', (x, y, selected) => { + this.on('pointerdown', (x, y, meshInstance) => { if (this.dragging) { return; } - if (selected) { - const meshInstance = selected.meshInstance; + if (meshInstance) { this._currAxis = this._getAxis(meshInstance); this._currIsPlane = this._getIsPlane(meshInstance); this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 6aba5bf6702..ee358063af3 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -13,7 +13,6 @@ const xdir = new Vec3(); const v1 = new Vec3(); const v2 = new Vec3(); const v3 = new Vec3(); -const intersect = new Vec3(); const tmpV = new Vec3(); const tmpQ = new Quat(); @@ -77,9 +76,8 @@ class Gizmo extends EventHandler { attach(nodes) { this.nodes = nodes; this.gizmo.enabled = true; - this.gizmo.setPosition(this.getGizmoPosition()); - // TODO: fix temporary hack for gizmo rotation + this.gizmo.setPosition(this.getGizmoPosition()); if (this.mode === 'local') { this.gizmo.setEulerAngles(this.getGizmoRotation()); } else { @@ -117,21 +115,21 @@ class Gizmo extends EventHandler { } getGizmoPosition() { - const center = new Vec3(); + tmpV.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - center.add(node.getPosition()); + tmpV.add(node.getPosition()); } - return center.scale(1.0 / this.nodes.length).clone(); + return tmpV.scale(1.0 / this.nodes.length).clone(); } getGizmoRotation() { - const rot = new Vec3(); + tmpV.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - rot.add(node.getEulerAngles()); + tmpV.add(node.getEulerAngles()); } - return rot.scale(1.0 / this.nodes.length).clone(); + return tmpV.scale(1.0 / this.nodes.length).clone(); } _createLayer() { @@ -159,9 +157,6 @@ class Gizmo extends EventHandler { const selection = []; const renders = this.gizmo.findComponents('render'); for (let i = 0; i < renders.length; i++) { - if (renders[i].entity.parent === this.held) { - continue; - } const meshInstances = renders[i].meshInstances; for (let j = 0; j < meshInstances.length; j++) { const meshInstance = meshInstances[j]; @@ -187,8 +182,8 @@ class Gizmo extends EventHandler { v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, intersect)) { - selection.push({ meshInstance, intersect }); + if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, tmpV)) { + selection.push(meshInstance); } } From fc7a3a1eb8e0d4d5ef0ec042ea28074d5412d8f7 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 11:50:25 +0000 Subject: [PATCH 023/178] moved selection variables inside function --- extras/gizmo/gizmo.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index ee358063af3..45fb89e156c 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -8,11 +8,6 @@ import { } from 'playcanvas' // temp variables -const xstart = new Vec3(); -const xdir = new Vec3(); -const v1 = new Vec3(); -const v2 = new Vec3(); -const v3 = new Vec3(); const tmpV = new Vec3(); const tmpQ = new Quat(); @@ -154,6 +149,12 @@ class Gizmo extends EventHandler { const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); const dir = end.clone().sub(start).normalize(); + const xstart = new Vec3(); + const xdir = new Vec3(); + const v1 = new Vec3(); + const v2 = new Vec3(); + const v3 = new Vec3(); + const selection = []; const renders = this.gizmo.findComponents('render'); for (let i = 0; i < renders.length; i++) { From 51d46d0b93f510e959e4ff17bea79a57e6ff7e84 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 12:00:22 +0000 Subject: [PATCH 024/178] change gizmo position and rotation getters to internal setters --- extras/gizmo/gizmo.js | 44 +++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 45fb89e156c..92ef2d8f6c8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -12,8 +12,6 @@ const tmpV = new Vec3(); const tmpQ = new Quat(); class Gizmo extends EventHandler { - _nodeParents = new Map(); - app; camera; @@ -26,7 +24,7 @@ class Gizmo extends EventHandler { gizmo; - mode = 'local'; + _mode = 'local'; constructor(app, camera) { super(); @@ -68,16 +66,21 @@ class Gizmo extends EventHandler { }); } + set mode(value) { + this._mode = value ?? 'world'; + this.setGizmoRotation(); + } + + get mode() { + return this._mode; + } + attach(nodes) { this.nodes = nodes; this.gizmo.enabled = true; - this.gizmo.setPosition(this.getGizmoPosition()); - if (this.mode === 'local') { - this.gizmo.setEulerAngles(this.getGizmoRotation()); - } else { - this.gizmo.setEulerAngles(0, 0, 0); - } + this.setGizmoPosition(); + this.setGizmoRotation(); } detach() { @@ -106,25 +109,30 @@ class Gizmo extends EventHandler { node.setPosition(this.nodePositions.get(node).clone().add(point)); } } - this.gizmo.setPosition(this.getGizmoPosition()); + + this.setGizmoPosition(); } - getGizmoPosition() { + setGizmoPosition() { tmpV.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; tmpV.add(node.getPosition()); } - return tmpV.scale(1.0 / this.nodes.length).clone(); + this.gizmo.setPosition(tmpV.scale(1.0 / this.nodes.length)); } - getGizmoRotation() { - tmpV.set(0, 0, 0); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV.add(node.getEulerAngles()); + setGizmoRotation() { + if (this._mode === 'local') { + tmpV.set(0, 0, 0); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + tmpV.add(node.getEulerAngles()); + } + this.gizmo.setEulerAngles(tmpV.scale(1.0 / this.nodes.length)); + } else { + this.gizmo.setEulerAngles(0, 0, 0); } - return tmpV.scale(1.0 / this.nodes.length).clone(); } _createLayer() { From 93b30c51ae55d9a1063e54794edfb497bd40e701 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 12:08:25 +0000 Subject: [PATCH 025/178] add setting in examples for coord space --- examples/src/examples/misc/gizmos.mjs | 13 ++++++++++++- extras/gizmo/gizmo.js | 14 +++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index c810937ad0f..9e7376267e1 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -5,9 +5,19 @@ import * as pc from 'playcanvas'; * @returns {JSX.Element} The returned JSX Element. */ function controls({ observer, ReactPCUI, React, jsx, fragment }) { - const { BindingTwoWay, LabelGroup, Panel, SliderInput } = ReactPCUI; + const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( jsx(Panel, { headerText: 'Gizmo Transform' }, + jsx(LabelGroup, { text: 'Coord Space' }, + jsx(SelectInput, { + options: [ + { v: 'world', t: 'World' }, + { v: 'local', t: 'Local' } + ], + binding: new BindingTwoWay(), + link: { observer, path: 'settings.coordSpace' } + }) + ), jsx(LabelGroup, { text: 'Axis Gap' }, jsx(SliderInput, { binding: new BindingTwoWay(), @@ -125,6 +135,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const gizmo = new pcx.GizmoTransform(app, camera); gizmo.attach([boxA]); data.set('settings', { + coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, axisLineLength: gizmo.axisLineLength, diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 92ef2d8f6c8..881afcd955e 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -24,7 +24,7 @@ class Gizmo extends EventHandler { gizmo; - _mode = 'local'; + _coordSpace = 'world'; constructor(app, camera) { super(); @@ -66,13 +66,13 @@ class Gizmo extends EventHandler { }); } - set mode(value) { - this._mode = value ?? 'world'; + set coordSpace(value) { + this._coordSpace = value ?? 'world'; this.setGizmoRotation(); } - get mode() { - return this._mode; + get coordSpace() { + return this._coordSpace; } attach(nodes) { @@ -101,7 +101,7 @@ class Gizmo extends EventHandler { updateNodePositions(point) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - if (this.mode === 'local') { + if (this._coordSpace === 'local') { tmpV.copy(point); tmpQ.copy(node.getLocalRotation()).transformVector(tmpV, tmpV); node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV)); @@ -123,7 +123,7 @@ class Gizmo extends EventHandler { } setGizmoRotation() { - if (this._mode === 'local') { + if (this._coordSpace === 'local') { tmpV.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; From c6753ae24e055219b33a77fd42556847757d6457 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 13:45:14 +0000 Subject: [PATCH 026/178] extracted transform superclass and implemented translate and scale classes --- examples/src/examples/misc/gizmos.mjs | 39 +-- extras/gizmo/gizmo-scale.js | 311 ++++++++++++++++++++++++ extras/gizmo/gizmo-transform.js | 282 +--------------------- extras/gizmo/gizmo-translate.js | 331 ++++++++++++++++++++++++++ extras/gizmo/gizmo.js | 50 ++-- extras/index.js | 2 + 6 files changed, 711 insertions(+), 304 deletions(-) create mode 100644 extras/gizmo/gizmo-scale.js create mode 100644 extras/gizmo/gizmo-translate.js diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 9e7376267e1..7de2141b69e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -36,24 +36,30 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { link: { observer, path: 'settings.axisLineLength' } }) ), - jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + jsx(LabelGroup, { text: 'Axis Box Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisArrowThickness' } - }) - ), - jsx(LabelGroup, { text: 'Axis Arrow Length' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisArrowLength' } - }) - ), - jsx(LabelGroup, { text: 'Axis Plane Size' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisPlaneSize' } + link: { observer, path: 'settings.axisBoxSize' } }) ) + // jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + // jsx(SliderInput, { + // binding: new BindingTwoWay(), + // link: { observer, path: 'settings.axisArrowThickness' } + // }) + // ), + // jsx(LabelGroup, { text: 'Axis Arrow Length' }, + // jsx(SliderInput, { + // binding: new BindingTwoWay(), + // link: { observer, path: 'settings.axisArrowLength' } + // }) + // ), + // jsx(LabelGroup, { text: 'Axis Plane Size' }, + // jsx(SliderInput, { + // binding: new BindingTwoWay(), + // link: { observer, path: 'settings.axisPlaneSize' } + // }) + // ) ) ); } @@ -107,7 +113,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { boxA.addComponent('render', { type: 'box' }); - boxA.rotate(0, 45, 0); + // boxA.rotate(0, 45, 0); app.root.addChild(boxA); // const boxB = new pc.Entity('cubeB'); // boxB.addComponent('render', { @@ -132,7 +138,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - const gizmo = new pcx.GizmoTransform(app, camera); + const gizmo = new pcx.GizmoScale(app, camera); gizmo.attach([boxA]); data.set('settings', { coordSpace: gizmo.coordSpace, @@ -141,6 +147,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisLineLength: gizmo.axisLineLength, axisArrowThickness: gizmo.axisArrowThickness, axisArrowLength: gizmo.axisArrowLength, + axisBoxSize: gizmo.axisBoxSize, axisPlaneSize: gizmo.axisPlaneSize }); data.on('*:set', (/** @type {string} */ path, value) => { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js new file mode 100644 index 00000000000..ebc3c18b110 --- /dev/null +++ b/extras/gizmo/gizmo-scale.js @@ -0,0 +1,311 @@ +import { + Entity, + Vec3, + Quat +} from 'playcanvas' + +import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; + +// temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpQ = new Quat(); + +// constants +const VEC3_AXES = Object.keys(tmpV1); +const GUIDELINE_SIZE = 1e3; + +class AxisPlane extends AxisShape { + _size = 0.2; + + constructor(options) { + super(options); + + this._createPlane(options.layers ?? []); + } + + _getPosition() { + const position = new Vec3(); + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; + if (axis === this.axis) { + continue; + } + position[axis] = this._size / 2; + } + return position; + } + + _createPlane(layers) { + this.entity = new Entity('plane_' + this.axis); + this.entity.addComponent('render', { + type: 'plane', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._size, this._size, this._size); + this.meshInstances.push(...this.entity.render.meshInstances); + } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } +} + +class AxisBoxLine extends AxisShape { + _gap = 0; + + _lineThickness = 0.04; + + _lineLength = 0.5; + + _boxSize = 0.15; + + constructor(options = {}) { + super(options); + + this._createBoxLine(options.layers ?? []); + } + + set gap(value) { + this._gap = value ?? 0; + this._updateLine(); + this._updateBox(); + } + + get gap() { + return this._gap; + } + + set lineThickness(value) { + this._lineThickness = value ?? 1; + this._updateLine(); + this._updateBox(); + } + + get lineThickness() { + return this._lineThickness; + } + + set lineLength(value) { + this._lineLength = value ?? 1; + this._updateLine(); + this._updateBox(); + } + + get lineLength() { + return this._lineLength; + } + + set boxSize(value) { + this._boxSize = value ?? 1; + this._updateBox(); + } + + get boxSize() { + return this._boxSize; + } + + _createBoxLine(layers) { + this.entity = new Entity('axis_' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._line = new Entity('line_' + this.axis); + this._line.addComponent('render', { + type: 'cylinder', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateLine(); + this.entity.addChild(this._line); + this.meshInstances.push(...this._line.render.meshInstances); + + this._box = new Entity('box_' + this.axis); + this._box.addComponent('render', { + type: 'box', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateBox(); + this.entity.addChild(this._box); + this.meshInstances.push(...this._box.render.meshInstances); + } + + _updateLine() { + this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); + this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + } + + _updateBox() { + this._box.setLocalPosition(new Vec3(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0)); + this._box.setLocalScale(new Vec3(this._boxSize, this._boxSize, this._boxSize)); + } +} + +class GizmoScale extends GizmoTransform { + materials; + + elements; + + elementMap = new Map(); + + _dirtyElement; + + constructor(app, camera) { + super(app, camera); + + this._axisShapes = { + x: new AxisBoxLine({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red + }), + y: new AxisBoxLine({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green + }), + z: new AxisBoxLine({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue + }), + yz: new AxisPlane({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red + }), + xz: new AxisPlane({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green + }), + xy: new AxisPlane({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue + }) + }; + + this._createTransform(); + + this.dragging = false; + this._hoverAxis = ''; + this._hoverIsPlane = false; + this._currAxis = ''; + this._currIsPlane = false; + this._pointStart = new Vec3(); + this._offset = new Vec3(); + + this.on('transform:start', () => { + this.storeNodeScale(); + }); + + this.on('transform:move', (offset) => { + this.updateNodeScale(offset); + }); + } + + _drawGuideLine(pos, axis) { + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + tmpV1.scale(GUIDELINE_SIZE); + tmpV2.copy(tmpV1).scale(-1); + tmpQ.transformVector(tmpV1, tmpV1); + tmpQ.transformVector(tmpV2, tmpV2); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); + } + + set axisGap(value) { + this._updateArrowProp('gap', value ?? 0); + } + + get axisGap() { + return this._axisShapes.x.gap; + } + + set axisLineThickness(value) { + this._updateArrowProp('lineThickness', value ?? 1); + } + + get axisLineThickness() { + return this._axisShapes.x.lineThickness; + } + + set axisLineLength(value) { + this._updateArrowProp('lineLength', value ?? 1); + } + + get axisLineLength() { + return this._axisShapes.x.lineLength; + } + + set axisBoxSize(value) { + this._updateArrowProp('boxSize', value ?? 1); + } + + get axisBoxSize() { + return this._axisShapes.x.boxSize; + } + + set axisPlaneSize(value) { + this._axisPlaneSize = value ?? 1; + this._updatePlaneProp('size', this._axisPlaneSize); + } + + get axisPlaneSize() { + return this._axisShapes.yz.size; + } + + _createTransform() { + super._createTransform(); + + // elements + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + this._center.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this.elementMap.set(shape.meshInstances[i], shape); + } + } + } + + _updateArrowProp(propName, value) { + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; + } + + _updatePlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } +} + +export { GizmoScale }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 991c0ec5278..e801b177b90 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -54,157 +54,6 @@ class AxisShape { } } -class AxisPlane extends AxisShape { - _size = 0.2; - - constructor(options) { - super(options); - - this._createPlane(options.layers ?? []); - } - - _getPosition() { - const position = new Vec3(); - for (let i = 0; i < VEC3_AXES.length; i++) { - const axis = VEC3_AXES[i]; - if (axis === this.axis) { - continue; - } - position[axis] = this._size / 2; - } - return position; - } - - _createPlane(layers) { - this.entity = new Entity('plane_' + this.axis); - this.entity.addComponent('render', { - type: 'plane', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); - } - - set size(value) { - this._size = value ?? 1; - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalScale(this._size, this._size, this._size); - } - - get size() { - return this._size; - } -} - -class AxisArrow extends AxisShape { - _gap = 0; - - _lineThickness = 0.04; - - _lineLength = 0.5; - - _arrowThickness = 0.15; - - _arrowLength = 0.2; - - constructor(options = {}) { - super(options); - - this._createArrow(options.layers ?? []); - } - - set gap(value) { - this._gap = value ?? 0; - this._updateLine(); - this._updateArrow(); - } - - get gap() { - return this._gap; - } - - set lineThickness(value) { - this._lineThickness = value ?? 1; - this._updateLine(); - this._updateArrow(); - } - - get lineThickness() { - return this._lineThickness; - } - - set lineLength(value) { - this._lineLength = value ?? 1; - this._updateLine(); - this._updateArrow(); - } - - get lineLength() { - return this._lineLength; - } - - set arrowThickness(value) { - this._arrowThickness = value ?? 1; - this._updateArrow(); - } - - get arrowThickness() { - return this._arrowThickness; - } - - set arrowLength(value) { - this._arrowLength = value ?? 1; - this._updateArrow(); - } - - get arrowLength() { - return this._arrowLength; - } - - _createArrow(layers) { - this.entity = new Entity('axis_' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - - this._line = new Entity('line_' + this.axis); - this._line.addComponent('render', { - type: 'cylinder', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateLine(); - this.entity.addChild(this._line); - this.meshInstances.push(...this._line.render.meshInstances); - - this._arrow = new Entity('arrow_' + this.axis); - this._arrow.addComponent('render', { - type: 'cone', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateArrow(); - this.entity.addChild(this._arrow); - this.meshInstances.push(...this._arrow.render.meshInstances); - } - - _updateLine() { - this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); - this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); - } - - _updateArrow() { - this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); - this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); - } -} - class GizmoTransform extends Gizmo { materials; @@ -232,51 +81,6 @@ class GizmoTransform extends Gizmo { this._guideLineColor = new Color(1, 1, 1, 0.5); - this._axisShapes = { - x: new AxisArrow({ - axis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red - }), - y: new AxisArrow({ - axis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green - }), - z: new AxisArrow({ - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue - }), - yz: new AxisPlane({ - axis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red - }), - xz: new AxisPlane({ - axis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green - }), - xy: new AxisPlane({ - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue - }) - }; - this._createTransform(); this.dragging = false; @@ -306,19 +110,19 @@ class GizmoTransform extends Gizmo { } }); - this.on('pointermove', (x, y, meshInstance) => { + this.on('pointer:move', (x, y, meshInstance) => { this._hover(meshInstance); if (this.dragging) { this._offset.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); this._offset.sub(this._pointStart); - this.updateNodePositions(this._offset); + this.fire('transform:move', this._offset); this._hoverAxis = ''; this._hoverIsPlane = false; } }); - this.on('pointerdown', (x, y, meshInstance) => { + this.on('pointer:down', (x, y, meshInstance) => { if (this.dragging) { return; } @@ -326,12 +130,13 @@ class GizmoTransform extends Gizmo { this._currAxis = this._getAxis(meshInstance); this._currIsPlane = this._getIsPlane(meshInstance); this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); + this.fire('transform:start'); this.storeNodePositions(); this.dragging = true; } }); - this.on('pointerup', () => { + this.on('pointer:up', () => { this.dragging = false; this._currAxis = ''; this._currIsPlane = false; @@ -432,56 +237,6 @@ class GizmoTransform extends Gizmo { this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); } - set axisGap(value) { - this._updateArrowProp('gap', value ?? 0); - } - - get axisGap() { - return this._axisShapes.x.gap; - } - - set axisLineThickness(value) { - this._updateArrowProp('lineThickness', value ?? 1); - } - - get axisLineThickness() { - return this._axisShapes.x.lineThickness; - } - - set axisLineLength(value) { - this._updateArrowProp('lineLength', value ?? 1); - } - - get axisLineLength() { - return this._axisShapes.x.lineLength; - } - - set axisArrowThickness(value) { - this._updateArrowProp('arrowThickness', value ?? 1); - } - - get axisArrowThickness() { - return this._axisShapes.x.arrowThickness; - } - - set axisArrowLength(value) { - this._axisArrowLength = value ?? 1; - this._updateArrowProp('arrowLength', this._axisArrowLength); - } - - get axisArrowLength() { - return this._axisShapes.x.arrowLength; - } - - set axisPlaneSize(value) { - this._axisPlaneSize = value ?? 1; - this._updatePlaneProp('size', this._axisPlaneSize); - } - - get axisPlaneSize() { - return this._axisShapes.yz.size; - } - _createMaterial(color) { const material = new StandardMaterial(); material.diffuse = color; @@ -507,30 +262,9 @@ class GizmoTransform extends Gizmo { this.gizmo.addChild(light); // center - const center = new Entity('center'); - this.gizmo.addChild(center); - - // elements - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; - center.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this.elementMap.set(shape.meshInstances[i], shape); - } - } - } - - _updateArrowProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; - } - - _updatePlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; + this._center = new Entity('center'); + this.gizmo.addChild(this._center); } } -export { GizmoTransform }; +export { AxisShape, GizmoTransform }; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js new file mode 100644 index 00000000000..39c07b02869 --- /dev/null +++ b/extras/gizmo/gizmo-translate.js @@ -0,0 +1,331 @@ +import { + Entity, + Vec3, + Quat +} from 'playcanvas' + +import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; + +// temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpQ = new Quat(); + +// constants +const VEC3_AXES = Object.keys(tmpV1); +const GUIDELINE_SIZE = 1e3; + +class AxisPlane extends AxisShape { + _size = 0.2; + + constructor(options) { + super(options); + + this._createPlane(options.layers ?? []); + } + + _getPosition() { + const position = new Vec3(); + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; + if (axis === this.axis) { + continue; + } + position[axis] = this._size / 2; + } + return position; + } + + _createPlane(layers) { + this.entity = new Entity('plane_' + this.axis); + this.entity.addComponent('render', { + type: 'plane', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._size, this._size, this._size); + this.meshInstances.push(...this.entity.render.meshInstances); + } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } +} + +class AxisArrow extends AxisShape { + _gap = 0; + + _lineThickness = 0.04; + + _lineLength = 0.5; + + _arrowThickness = 0.15; + + _arrowLength = 0.2; + + constructor(options = {}) { + super(options); + + this._createArrow(options.layers ?? []); + } + + set gap(value) { + this._gap = value ?? 0; + this._updateLine(); + this._updateArrow(); + } + + get gap() { + return this._gap; + } + + set lineThickness(value) { + this._lineThickness = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineThickness() { + return this._lineThickness; + } + + set lineLength(value) { + this._lineLength = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineLength() { + return this._lineLength; + } + + set arrowThickness(value) { + this._arrowThickness = value ?? 1; + this._updateArrow(); + } + + get arrowThickness() { + return this._arrowThickness; + } + + set arrowLength(value) { + this._arrowLength = value ?? 1; + this._updateArrow(); + } + + get arrowLength() { + return this._arrowLength; + } + + _createArrow(layers) { + this.entity = new Entity('axis_' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._line = new Entity('line_' + this.axis); + this._line.addComponent('render', { + type: 'cylinder', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateLine(); + this.entity.addChild(this._line); + this.meshInstances.push(...this._line.render.meshInstances); + + this._arrow = new Entity('arrow_' + this.axis); + this._arrow.addComponent('render', { + type: 'cone', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateArrow(); + this.entity.addChild(this._arrow); + this.meshInstances.push(...this._arrow.render.meshInstances); + } + + _updateLine() { + this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); + this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + } + + _updateArrow() { + this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); + this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); + } +} + +class GizmoTranslate extends GizmoTransform { + materials; + + elements; + + elementMap = new Map(); + + _dirtyElement; + + constructor(app, camera) { + super(app, camera); + + this._axisShapes = { + x: new AxisArrow({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red + }), + y: new AxisArrow({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green + }), + z: new AxisArrow({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue + }), + yz: new AxisPlane({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red + }), + xz: new AxisPlane({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green + }), + xy: new AxisPlane({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue + }) + }; + + this._createTransform(); + + this.dragging = false; + this._hoverAxis = ''; + this._hoverIsPlane = false; + this._currAxis = ''; + this._currIsPlane = false; + this._pointStart = new Vec3(); + this._offset = new Vec3(); + + this.on('transform:start', () => { + this.storeNodePositions(); + }); + + this.on('transform:move', (offset) => { + this.updateNodePositions(offset); + }); + } + + _drawGuideLine(pos, axis) { + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + tmpV1.scale(GUIDELINE_SIZE); + tmpV2.copy(tmpV1).scale(-1); + tmpQ.transformVector(tmpV1, tmpV1); + tmpQ.transformVector(tmpV2, tmpV2); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); + } + + set axisGap(value) { + this._updateArrowProp('gap', value ?? 0); + } + + get axisGap() { + return this._axisShapes.x.gap; + } + + set axisLineThickness(value) { + this._updateArrowProp('lineThickness', value ?? 1); + } + + get axisLineThickness() { + return this._axisShapes.x.lineThickness; + } + + set axisLineLength(value) { + this._updateArrowProp('lineLength', value ?? 1); + } + + get axisLineLength() { + return this._axisShapes.x.lineLength; + } + + set axisArrowThickness(value) { + this._updateArrowProp('arrowThickness', value ?? 1); + } + + get axisArrowThickness() { + return this._axisShapes.x.arrowThickness; + } + + set axisArrowLength(value) { + this._axisArrowLength = value ?? 1; + this._updateArrowProp('arrowLength', this._axisArrowLength); + } + + get axisArrowLength() { + return this._axisShapes.x.arrowLength; + } + + set axisPlaneSize(value) { + this._axisPlaneSize = value ?? 1; + this._updatePlaneProp('size', this._axisPlaneSize); + } + + get axisPlaneSize() { + return this._axisShapes.yz.size; + } + + _createTransform() { + super._createTransform(); + + // elements + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + this._center.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this.elementMap.set(shape.meshInstances[i], shape); + } + } + } + + _updateArrowProp(propName, value) { + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; + } + + _updatePlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } +} + +export { GizmoTranslate }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 881afcd955e..cb1867d99c8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -8,7 +8,8 @@ import { } from 'playcanvas' // temp variables -const tmpV = new Vec3(); +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); const tmpQ = new Quat(); class Gizmo extends EventHandler { @@ -22,6 +23,8 @@ class Gizmo extends EventHandler { nodePositions = new Map(); + nodeScale = new Map(); + gizmo; _coordSpace = 'world'; @@ -40,20 +43,20 @@ class Gizmo extends EventHandler { return; } const selection = this._getSelection(e.clientX, e.clientY); - this.fire('pointermove', e.clientX, e.clientY, selection[0]); + this.fire('pointer:move', e.clientX, e.clientY, selection[0]); }; const onPointerDown = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); - this.fire('pointerdown', e.clientX, e.clientY, selection[0]); + this.fire('pointer:down', e.clientX, e.clientY, selection[0]); }; const onPointerUp = (e) => { if (!this.gizmo.enabled) { return; } - this.fire('pointerup'); + this.fire('pointer:up'); }; window.addEventListener('pointermove', onPointerMove); @@ -102,9 +105,14 @@ class Gizmo extends EventHandler { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; if (this._coordSpace === 'local') { - tmpV.copy(point); - tmpQ.copy(node.getLocalRotation()).transformVector(tmpV, tmpV); - node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV)); + tmpV1.copy(point); + node.parent.getWorldTransform().getScale(tmpV2); + tmpV2.x = 1 / tmpV2.x; + tmpV2.y = 1 / tmpV2.y; + tmpV2.z = 1 / tmpV2.z; + tmpQ.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1); + tmpV1.mul(tmpV2); + node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV1)); } else { node.setPosition(this.nodePositions.get(node).clone().add(point)); } @@ -113,23 +121,37 @@ class Gizmo extends EventHandler { this.setGizmoPosition(); } + storeNodeScale() { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + this.nodeScale.set(node, node.getLocalScale().clone()); + } + } + + updateNodeScale(scale) { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + node.setLocalScale(this.nodeScale.get(node).clone().add(scale)); + } + } + setGizmoPosition() { - tmpV.set(0, 0, 0); + tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - tmpV.add(node.getPosition()); + tmpV1.add(node.getPosition()); } - this.gizmo.setPosition(tmpV.scale(1.0 / this.nodes.length)); + this.gizmo.setPosition(tmpV1.scale(1.0 / this.nodes.length)); } setGizmoRotation() { if (this._coordSpace === 'local') { - tmpV.set(0, 0, 0); + tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - tmpV.add(node.getEulerAngles()); + tmpV1.add(node.getEulerAngles()); } - this.gizmo.setEulerAngles(tmpV.scale(1.0 / this.nodes.length)); + this.gizmo.setEulerAngles(tmpV1.scale(1.0 / this.nodes.length)); } else { this.gizmo.setEulerAngles(0, 0, 0); } @@ -191,7 +213,7 @@ class Gizmo extends EventHandler { v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, tmpV)) { + if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, tmpV1)) { selection.push(meshInstance); } } diff --git a/extras/index.js b/extras/index.js index ce80b842604..d425f50760d 100644 --- a/extras/index.js +++ b/extras/index.js @@ -21,3 +21,5 @@ export { RenderPassTAA } from './render-passes/render-pass-taa.js'; // gizmo export { Gizmo } from "./gizmo/gizmo.js"; export { GizmoTransform } from "./gizmo/gizmo-transform.js"; +export { GizmoTranslate } from "./gizmo/gizmo-translate.js"; +export { GizmoScale } from "./gizmo/gizmo-scale.js"; From bb0b1f9614b86bbb1f96054c9a9ec44970d65fb0 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 16:17:08 +0000 Subject: [PATCH 027/178] added all axes entity scaling and removed unused variables --- examples/src/examples/misc/gizmos.mjs | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 7de2141b69e..731f311d124 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -41,25 +41,25 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'settings.axisBoxSize' } }) + ), + jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisArrowThickness' } + }) + ), + jsx(LabelGroup, { text: 'Axis Arrow Length' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisArrowLength' } + }) + ), + jsx(LabelGroup, { text: 'Axis Plane Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.axisPlaneSize' } + }) ) - // jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, - // jsx(SliderInput, { - // binding: new BindingTwoWay(), - // link: { observer, path: 'settings.axisArrowThickness' } - // }) - // ), - // jsx(LabelGroup, { text: 'Axis Arrow Length' }, - // jsx(SliderInput, { - // binding: new BindingTwoWay(), - // link: { observer, path: 'settings.axisArrowLength' } - // }) - // ), - // jsx(LabelGroup, { text: 'Axis Plane Size' }, - // jsx(SliderInput, { - // binding: new BindingTwoWay(), - // link: { observer, path: 'settings.axisPlaneSize' } - // }) - // ) ) ); } From 837f1ea8ed8730b47604964910116c47c644fb6d Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 16:18:11 +0000 Subject: [PATCH 028/178] added all axes entity scaling and removed unused variables --- extras/gizmo/gizmo-scale.js | 66 ++++++++++++++++++++++------- extras/gizmo/gizmo-transform.js | 75 +++++++++++++++++++++------------ extras/gizmo/gizmo-translate.js | 16 +------ extras/gizmo/gizmo.js | 4 +- 4 files changed, 101 insertions(+), 60 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index ebc3c18b110..e4ce8c3acd1 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -1,19 +1,15 @@ import { Entity, - Vec3, - Quat + Vec3 } from 'playcanvas' import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpQ = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); -const GUIDELINE_SIZE = 1e3; class AxisPlane extends AxisShape { _size = 0.2; @@ -31,7 +27,7 @@ class AxisPlane extends AxisShape { if (axis === this.axis) { continue; } - position[axis] = this._size / 2; + position[axis] = this._size * 2; } return position; } @@ -155,6 +151,39 @@ class AxisBoxLine extends AxisShape { } } +class AxisCenter extends AxisShape { + _size = 0.2; + + constructor(options = {}) { + super(options); + + this._createCenter(options.layers ?? []); + } + + _createCenter(layers) { + this.entity = new Entity('center_' + this.axis); + this.entity.addComponent('render', { + type: 'box', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._size, this._size, this._size); + this.meshInstances.push(...this.entity.render.meshInstances); + } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } +} + class GizmoScale extends GizmoTransform { materials; @@ -167,6 +196,8 @@ class GizmoScale extends GizmoTransform { constructor(app, camera) { super(app, camera); + this._coordSpace = 'local'; + this._axisShapes = { x: new AxisBoxLine({ axis: 'x', @@ -209,6 +240,12 @@ class GizmoScale extends GizmoTransform { rotation: new Vec3(90, 0, 0), defaultColor: this._materials.semi.blue, hoverColor: this._materials.opaque.blue + }), + xyz: new AxisCenter({ + axis: 'xyz', + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.yellow, + hoverColor: this._materials.opaque.yellow }) }; @@ -222,7 +259,8 @@ class GizmoScale extends GizmoTransform { this._pointStart = new Vec3(); this._offset = new Vec3(); - this.on('transform:start', () => { + this.on('transform:start', (start) => { + start.sub(Vec3.ONE); this.storeNodeScale(); }); @@ -231,14 +269,12 @@ class GizmoScale extends GizmoTransform { }); } - _drawGuideLine(pos, axis) { - tmpV1.set(0, 0, 0); - tmpV1[axis] = 1; - tmpV1.scale(GUIDELINE_SIZE); - tmpV2.copy(tmpV1).scale(-1); - tmpQ.transformVector(tmpV1, tmpV1); - tmpQ.transformVector(tmpV2, tmpV2); - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); + set coordSpace(value) { + // disallow changing coordSpace for scale + } + + get coordSpace() { + return this._coordSpace; } set axisGap(value) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index e801b177b90..029017cbc8d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,4 +1,5 @@ import { + math, PROJECTION_PERSPECTIVE, BLEND_NORMAL, Color, @@ -70,19 +71,19 @@ class GizmoTransform extends Gizmo { opaque: { red: this._createMaterial(new Color(1, 0.3, 0.3)), green: this._createMaterial(new Color(0.3, 1, 0.3)), - blue: this._createMaterial(new Color(0.3, 0.3, 1)) + blue: this._createMaterial(new Color(0.3, 0.3, 1)), + yellow: this._createMaterial(new Color(1, 1, 0.3, 1)) }, semi: { red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), - blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)) + blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), + yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)) } }; this._guideLineColor = new Color(1, 1, 1, 0.5); - this._createTransform(); - this.dragging = false; this._hoverAxis = ''; this._hoverIsPlane = false; @@ -98,6 +99,10 @@ class GizmoTransform extends Gizmo { const checkIsPlane = this._hoverIsPlane || this._currIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { const axis = VEC3_AXES[i]; + if (checkAxis === 'xyz') { + this._drawGuideLine(gizmoPos, axis); + continue; + } if (checkIsPlane) { if (axis !== checkAxis) { this._drawGuideLine(gizmoPos, axis); @@ -130,7 +135,7 @@ class GizmoTransform extends Gizmo { this._currAxis = this._getAxis(meshInstance); this._currIsPlane = this._getIsPlane(meshInstance); this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); - this.fire('transform:start'); + this.fire('transform:start', this._pointStart); this.storeNodePositions(); this.dragging = true; } @@ -164,7 +169,7 @@ class GizmoTransform extends Gizmo { if (shape === this._dirtyElement) { return; } - if (this._dirtyElement) { + if (this._dirtyElement && !this.dragging) { this._dirtyElement.hover(false); this._dirtyElement = null; } @@ -179,10 +184,8 @@ class GizmoTransform extends Gizmo { const mouseWPos = this.camera.camera.screenToWorld(x, y, 1); const rayOrigin = this.camera.getPosition(); const rayDir = new Vec3(); - - // set plane normal based on axis const planeNormal = new Vec3(); - planeNormal[axis] = 1; + const isCenter = axis === 'xyz'; // calculate ray direction from mouse position if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { @@ -192,12 +195,20 @@ class GizmoTransform extends Gizmo { this.camera.getWorldTransform().transformVector(tmpV1.set(0, 0, -1), rayDir); } - // rotate plane normal by gizmo rotation - tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); + if (isCenter) { + // all axes so set normal to plane facing camera + planeNormal.copy(rayOrigin).sub(gizmoPos).normalize(); + } else { + // set plane normal based on axis + planeNormal[axis] = 1; + + // rotate plane normal by gizmo rotation + tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); - if (!isPlane) { - tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); - planeNormal.copy(tmpV1.sub(planeNormal.scale(planeNormal.dot(tmpV1))).normalize()); + if (!isPlane) { + tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); + planeNormal.copy(tmpV1.sub(planeNormal.scale(planeNormal.dot(tmpV1))).normalize()); + } } // ray intersection with plane @@ -206,22 +217,30 @@ class GizmoTransform extends Gizmo { const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; const point = rayDir.scale(-pointPlaneDist).add(rayOrigin); - if (!isPlane) { - // reset normal based on axis and project position from plane onto normal - planeNormal.set(0, 0, 0); - planeNormal[axis] = 1; - tmpQ.transformVector(planeNormal, planeNormal); - point.copy(planeNormal.scale(planeNormal.dot(point))); - } + if (isCenter) { + tmpV1.copy(point).sub(gizmoPos).normalize(); + tmpV2.copy(this.camera.up).add(this.camera.right).normalize(); + + const v = point.sub(gizmoPos).length() * tmpV1.dot(tmpV2); + point.set(v, v, v); + } else { + if (!isPlane) { + // reset normal based on axis and project position from plane onto normal + planeNormal.set(0, 0, 0); + planeNormal[axis] = 1; + tmpQ.transformVector(planeNormal, planeNormal); + point.copy(planeNormal.scale(planeNormal.dot(point))); + } - // rotate point back to world coords - tmpQ.invert().transformVector(point, point); + // rotate point back to world coords + tmpQ.invert().transformVector(point, point); - if (!isPlane) { - // set other axes to zero if not plane point - const v = point[axis]; - point.set(0, 0, 0); - point[axis] = v; + if (!isPlane) { + // set other axes to zero if not plane point + const v = point[axis]; + point.set(0, 0, 0); + point[axis] = v; + } } return point; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 39c07b02869..8115486fa89 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -1,19 +1,15 @@ import { Entity, - Vec3, - Quat + Vec3 } from 'playcanvas' import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpQ = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); -const GUIDELINE_SIZE = 1e3; class AxisPlane extends AxisShape { _size = 0.2; @@ -242,16 +238,6 @@ class GizmoTranslate extends GizmoTransform { }); } - _drawGuideLine(pos, axis) { - tmpV1.set(0, 0, 0); - tmpV1[axis] = 1; - tmpV1.scale(GUIDELINE_SIZE); - tmpV2.copy(tmpV1).scale(-1); - tmpQ.transformVector(tmpV1, tmpV1); - tmpQ.transformVector(tmpV2, tmpV2); - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); - } - set axisGap(value) { this._updateArrowProp('gap', value ?? 0); } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index cb1867d99c8..d30269e2749 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -128,10 +128,10 @@ class Gizmo extends EventHandler { } } - updateNodeScale(scale) { + updateNodeScale(point) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - node.setLocalScale(this.nodeScale.get(node).clone().add(scale)); + node.setLocalScale(this.nodeScale.get(node).clone().mul(point)); } } From 94e2848f9002b52168f6c05b6488ead0bee43665 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 16:18:23 +0000 Subject: [PATCH 029/178] removed math import --- extras/gizmo/gizmo-transform.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 029017cbc8d..bb9b637bb9d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,5 +1,4 @@ import { - math, PROJECTION_PERSPECTIVE, BLEND_NORMAL, Color, From a4c205f6921036d7ef4c74ce24cc853bfef503ac Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 2 Jan 2024 17:54:10 +0000 Subject: [PATCH 030/178] added camera controls and gizmo scaling for perspective --- examples/src/examples/misc/gizmos.mjs | 36 ++++++++++++++++++++++++++- extras/gizmo/gizmo.js | 35 ++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 731f311d124..ad6246f888d 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -7,6 +7,24 @@ import * as pc from 'playcanvas'; function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( + jsx(Panel, { headerText: 'Camera Transform' }, + jsx(LabelGroup, { text: 'Distance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.dist' }, + min: 1, + max: 10 + }) + ), + jsx(LabelGroup, { text: 'FOV' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.fov' }, + min: 30, + max: 100 + }) + ) + ), jsx(Panel, { headerText: 'Gizmo Transform' }, jsx(LabelGroup, { text: 'Coord Space' }, jsx(SelectInput, { @@ -128,7 +146,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { clearColor: new pc.Color(0.5, 0.6, 0.9) }); app.root.addChild(camera); - camera.translate(5, 3, 5); + camera.setPosition(5, 3, 5); camera.lookAt(0, 0, 0); // create directional light entity @@ -150,8 +168,24 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisBoxSize: gizmo.axisBoxSize, axisPlaneSize: gizmo.axisPlaneSize }); + data.set('camera', { + dist: 1, + fov: 45 + }); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); + if (pathArray[0] === 'camera') { + switch (pathArray[1]) { + case "dist": + camera.setPosition(5 * value, 3 * value, 5 * value); + break; + case 'fov': + camera.camera.fov = value; + break; + } + gizmo.updateGizmoScale(); + return; + } gizmo[pathArray[1]] = value; }); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d30269e2749..34af12807c4 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -1,4 +1,6 @@ import { + math, + PROJECTION_PERSPECTIVE, SORTMODE_NONE, EventHandler, Layer, @@ -12,6 +14,10 @@ const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ = new Quat(); +// constants +const MIN_GIZMO_SCALE = 1e-4; +const EPSILON = 1e-6; + class Gizmo extends EventHandler { app; @@ -27,6 +33,8 @@ class Gizmo extends EventHandler { gizmo; + _startProjFrustWidth; + _coordSpace = 'world'; constructor(app, camera) { @@ -38,6 +46,8 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); + this._startProjFrustWidth = this._getProjFrustumWidth(); + const onPointerMove = (e) => { if (!this.gizmo.enabled) { return; @@ -62,6 +72,7 @@ class Gizmo extends EventHandler { window.addEventListener('pointermove', onPointerMove); window.addEventListener('pointerdown', onPointerDown); window.addEventListener('pointerup', onPointerUp); + app.on('destroy', () => { window.removeEventListener('pointermove', onPointerMove); window.removeEventListener('pointerdown', onPointerDown); @@ -141,7 +152,8 @@ class Gizmo extends EventHandler { const node = this.nodes[i]; tmpV1.add(node.getPosition()); } - this.gizmo.setPosition(tmpV1.scale(1.0 / this.nodes.length)); + tmpV1.scale(1.0 / this.nodes.length); + this.gizmo.setPosition(tmpV1); } setGizmoRotation() { @@ -157,6 +169,26 @@ class Gizmo extends EventHandler { } } + updateGizmoScale() { + // scale to screen space + let scale = 1; + if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + scale = this._getProjFrustumWidth() / this._startProjFrustWidth; + } else { + scale = this.camera.camera.orthoHeight / 3; + } + // scale *= this.size; + scale = Math.max(scale, MIN_GIZMO_SCALE); + this.gizmo.setLocalScale(scale, scale, scale); + } + + _getProjFrustumWidth() { + const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.getPosition(); + const dist = tmpV1.copy(gizmoPos).sub(cameraPos).dot(this.camera.forward); + return dist * Math.tan(this.camera.camera.fov * math.DEG_TO_RAD / 2); + } + _createLayer() { this.layerGizmo = new Layer({ name: 'Gizmo', @@ -224,7 +256,6 @@ class Gizmo extends EventHandler { } _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { - const EPSILON = 1e-6; const e1 = new Vec3(); const e2 = new Vec3(); const h = new Vec3(); From a68ae2385262848a196d587cfd06c1a5a406e763 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 11:04:08 +0000 Subject: [PATCH 031/178] added fixed gizmo scaling constants for persp and otho camera --- examples/src/examples/misc/gizmos.mjs | 40 +++++++++++++++++++++++++-- extras/gizmo/gizmo.js | 29 +++++++++++-------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index ad6246f888d..d45df2cb636 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -8,6 +8,16 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( jsx(Panel, { headerText: 'Camera Transform' }, + jsx(LabelGroup, { text: 'Projection' }, + jsx(SelectInput, { + options: [ + { v: pc.PROJECTION_PERSPECTIVE + 1, t: 'Perspective' }, + { v: pc.PROJECTION_ORTHOGRAPHIC + 1, t: 'Orthographic' } + ], + binding: new BindingTwoWay(), + link: { observer, path: 'camera.proj' } + }) + ), jsx(LabelGroup, { text: 'Distance' }, jsx(SliderInput, { binding: new BindingTwoWay(), @@ -23,9 +33,25 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { min: 30, max: 100 }) + ), + jsx(LabelGroup, { text: 'Ortho Height' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.orthoHeight' }, + min: 1, + max: 20 + }) ) ), jsx(Panel, { headerText: 'Gizmo Transform' }, + jsx(LabelGroup, { text: 'Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'settings.size' }, + min: 0.1, + max: 2.0 + }) + ), jsx(LabelGroup, { text: 'Coord Space' }, jsx(SelectInput, { options: [ @@ -159,6 +185,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const gizmo = new pcx.GizmoScale(app, camera); gizmo.attach([boxA]); data.set('settings', { + size: gizmo.size, coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, @@ -169,19 +196,28 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisPlaneSize: gizmo.axisPlaneSize }); data.set('camera', { + proj: pc.PROJECTION_PERSPECTIVE + 1, dist: 1, - fov: 45 + fov: 45, + orthoHeight: 10 }); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); + // camera properties if (pathArray[0] === 'camera') { switch (pathArray[1]) { - case "dist": + case 'proj': + camera.camera.projection = value - 1; + break; + case 'dist': camera.setPosition(5 * value, 3 * value, 5 * value); break; case 'fov': camera.camera.fov = value; break; + case 'orthoHeight': + camera.camera.orthoHeight = value; + break; } gizmo.updateGizmoScale(); return; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 34af12807c4..01f58803536 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -17,8 +17,14 @@ const tmpQ = new Quat(); // constants const MIN_GIZMO_SCALE = 1e-4; const EPSILON = 1e-6; +const PERS_SCALE_RATIO = 0.3; +const ORTHO_SCALE_RATIO = 0.32; class Gizmo extends EventHandler { + _size = 1; + + _coordSpace = 'world'; + app; camera; @@ -33,10 +39,6 @@ class Gizmo extends EventHandler { gizmo; - _startProjFrustWidth; - - _coordSpace = 'world'; - constructor(app, camera) { super(); @@ -46,8 +48,6 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this._startProjFrustWidth = this._getProjFrustumWidth(); - const onPointerMove = (e) => { if (!this.gizmo.enabled) { return; @@ -89,6 +89,15 @@ class Gizmo extends EventHandler { return this._coordSpace; } + set size(value) { + this._size = value; + this.updateGizmoScale(); + } + + get size() { + return this._size; + } + attach(nodes) { this.nodes = nodes; this.gizmo.enabled = true; @@ -170,15 +179,13 @@ class Gizmo extends EventHandler { } updateGizmoScale() { - // scale to screen space let scale = 1; if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { - scale = this._getProjFrustumWidth() / this._startProjFrustWidth; + scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; } else { - scale = this.camera.camera.orthoHeight / 3; + scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; } - // scale *= this.size; - scale = Math.max(scale, MIN_GIZMO_SCALE); + scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); this.gizmo.setLocalScale(scale, scale, scale); } From e86080d1b15c70a8cd66c7dff04085120724ecbb Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 11:09:07 +0000 Subject: [PATCH 032/178] increased guideline size ;fixed hover drag issue --- extras/gizmo/gizmo-transform.js | 7 +++++-- extras/gizmo/gizmo.js | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index bb9b637bb9d..274b18b769d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -17,7 +17,7 @@ const tmpQ = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); -const GUIDELINE_SIZE = 1e3; +const GUIDELINE_SIZE = 1e6; class AxisShape { _position; @@ -162,13 +162,16 @@ class GizmoTransform extends Gizmo { } _hover(meshInstance) { + if (this.dragging) { + return; + } this._hoverAxis = this._getAxis(meshInstance); this._hoverIsPlane = this._getIsPlane(meshInstance); const shape = this.elementMap.get(meshInstance); if (shape === this._dirtyElement) { return; } - if (this._dirtyElement && !this.dragging) { + if (this._dirtyElement) { this._dirtyElement.hover(false); this._dirtyElement = null; } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 01f58803536..0d140f66ec0 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -48,6 +48,8 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); + this.updateGizmoScale(); + const onPointerMove = (e) => { if (!this.gizmo.enabled) { return; From b2851a440004c76fafed5f840f1c36b43540b51f Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 12:13:34 +0000 Subject: [PATCH 033/178] add gizmo type changing in example; disabled drawLine update if gizmo disabled --- examples/src/examples/misc/gizmos.mjs | 126 ++++++++++++++++---------- extras/gizmo/gizmo-transform.js | 3 + extras/gizmo/gizmo.js | 57 +++++++----- 3 files changed, 113 insertions(+), 73 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index d45df2cb636..0f985608b6e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -7,47 +7,21 @@ import * as pc from 'playcanvas'; function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( - jsx(Panel, { headerText: 'Camera Transform' }, - jsx(LabelGroup, { text: 'Projection' }, + jsx(Panel, { headerText: 'Gizmo' }, + jsx(LabelGroup, { text: 'Type' }, jsx(SelectInput, { options: [ - { v: pc.PROJECTION_PERSPECTIVE + 1, t: 'Perspective' }, - { v: pc.PROJECTION_ORTHOGRAPHIC + 1, t: 'Orthographic' } + { v: 'translate', t: 'Translate' }, + { v: 'scale', t: 'Scale' } ], binding: new BindingTwoWay(), - link: { observer, path: 'camera.proj' } + link: { observer, path: 'gizmo.type' } }) ), - jsx(LabelGroup, { text: 'Distance' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.dist' }, - min: 1, - max: 10 - }) - ), - jsx(LabelGroup, { text: 'FOV' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.fov' }, - min: 30, - max: 100 - }) - ), - jsx(LabelGroup, { text: 'Ortho Height' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.orthoHeight' }, - min: 1, - max: 20 - }) - ) - ), - jsx(Panel, { headerText: 'Gizmo Transform' }, jsx(LabelGroup, { text: 'Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.size' }, + link: { observer, path: 'gizmo.size' }, min: 0.1, max: 2.0 }) @@ -59,49 +33,85 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { { v: 'local', t: 'Local' } ], binding: new BindingTwoWay(), - link: { observer, path: 'settings.coordSpace' } + link: { observer, path: 'gizmo.coordSpace' } }) ), jsx(LabelGroup, { text: 'Axis Gap' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisGap' } + link: { observer, path: 'gizmo.axisGap' } }) ), jsx(LabelGroup, { text: 'Axis Line Thickness' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisLineThickness' } + link: { observer, path: 'gizmo.axisLineThickness' } }) ), jsx(LabelGroup, { text: 'Axis Line Length' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisLineLength' } + link: { observer, path: 'gizmo.axisLineLength' } }) ), jsx(LabelGroup, { text: 'Axis Box Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisBoxSize' } + link: { observer, path: 'gizmo.axisBoxSize' } }) ), jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisArrowThickness' } + link: { observer, path: 'gizmo.axisArrowThickness' } }) ), jsx(LabelGroup, { text: 'Axis Arrow Length' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisArrowLength' } + link: { observer, path: 'gizmo.axisArrowLength' } }) ), jsx(LabelGroup, { text: 'Axis Plane Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'settings.axisPlaneSize' } + link: { observer, path: 'gizmo.axisPlaneSize' } + }) + ) + ), + jsx(Panel, { headerText: 'Camera' }, + jsx(LabelGroup, { text: 'Projection' }, + jsx(SelectInput, { + options: [ + { v: pc.PROJECTION_PERSPECTIVE + 1, t: 'Perspective' }, + { v: pc.PROJECTION_ORTHOGRAPHIC + 1, t: 'Orthographic' } + ], + binding: new BindingTwoWay(), + link: { observer, path: 'camera.proj' } + }) + ), + jsx(LabelGroup, { text: 'Distance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.dist' }, + min: 1, + max: 10 + }) + ), + jsx(LabelGroup, { text: 'FOV' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.fov' }, + min: 30, + max: 100 + }) + ), + jsx(LabelGroup, { text: 'Ortho Height' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.orthoHeight' }, + min: 1, + max: 20 }) ) ) @@ -167,6 +177,12 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { // app.root.addChild(boxB); // create camera entity + data.set('camera', { + proj: pc.PROJECTION_PERSPECTIVE + 1, + dist: 1, + fov: 45, + orthoHeight: 10 + }); const camera = new pc.Entity('camera'); camera.addComponent('camera', { clearColor: new pc.Color(0.5, 0.6, 0.9) @@ -182,9 +198,14 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - const gizmo = new pcx.GizmoScale(app, camera); + const GIZMOS = { + translate: new pcx.GizmoTranslate(app, camera), + scale: new pcx.GizmoScale(app, camera) + }; + let gizmo = GIZMOS.translate; gizmo.attach([boxA]); - data.set('settings', { + data.set('gizmo', { + type: 'translate', size: gizmo.size, coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, @@ -195,15 +216,11 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisBoxSize: gizmo.axisBoxSize, axisPlaneSize: gizmo.axisPlaneSize }); - data.set('camera', { - proj: pc.PROJECTION_PERSPECTIVE + 1, - dist: 1, - fov: 45, - orthoHeight: 10 - }); + data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); - // camera properties + + // camera if (pathArray[0] === 'camera') { switch (pathArray[1]) { case 'proj': @@ -222,7 +239,16 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { gizmo.updateGizmoScale(); return; } - gizmo[pathArray[1]] = value; + + // gizmo + if (pathArray[1] === 'type') { + gizmo.detach(); + gizmo = GIZMOS[value]; + gizmo.attach([boxA]); + } else { + gizmo[pathArray[1]] = value; + } + }); return app; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 274b18b769d..95a723da749 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -92,6 +92,9 @@ class GizmoTransform extends Gizmo { this._offset = new Vec3(); this.app.on('update', () => { + if (!this.gizmo.enabled) { + return; + } const gizmoPos = this.gizmo.getPosition(); tmpQ.copy(this.gizmo.getRotation()); const checkAxis = this._hoverAxis || this._currAxis; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 0d140f66ec0..1edf57f01f7 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -15,6 +15,7 @@ const tmpV2 = new Vec3(); const tmpQ = new Quat(); // constants +const GIZMO_LAYER_ID = 1e5; const MIN_GIZMO_SCALE = 1e-4; const EPSILON = 1e-6; const PERS_SCALE_RATIO = 0.3; @@ -50,36 +51,28 @@ class Gizmo extends EventHandler { this.updateGizmoScale(); - const onPointerMove = (e) => { + this._onPointerMove = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); this.fire('pointer:move', e.clientX, e.clientY, selection[0]); }; - const onPointerDown = (e) => { + this._onPointerDown = (e) => { if (!this.gizmo.enabled) { return; } const selection = this._getSelection(e.clientX, e.clientY); this.fire('pointer:down', e.clientX, e.clientY, selection[0]); }; - const onPointerUp = (e) => { + this._onPointerUp = (e) => { if (!this.gizmo.enabled) { return; } this.fire('pointer:up'); }; - window.addEventListener('pointermove', onPointerMove); - window.addEventListener('pointerdown', onPointerDown); - window.addEventListener('pointerup', onPointerUp); - - app.on('destroy', () => { - window.removeEventListener('pointermove', onPointerMove); - window.removeEventListener('pointerdown', onPointerDown); - window.removeEventListener('pointerup', onPointerUp); - }); + app.on('destroy', () => this.detach()); } set coordSpace(value) { @@ -102,17 +95,27 @@ class Gizmo extends EventHandler { attach(nodes) { this.nodes = nodes; - this.gizmo.enabled = true; - this.setGizmoPosition(); this.setGizmoRotation(); + + window.addEventListener('pointermove', this._onPointerMove); + window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointerup', this._onPointerUp); + + this.gizmo.enabled = true; } detach() { + this.gizmo.enabled = false; + this.nodes = []; this.nodeLocalPositions.clear(); this.nodePositions.clear(); - this.gizmo.enabled = false; + this.nodeScale.clear(); + + window.removeEventListener('pointermove', this._onPointerMove); + window.removeEventListener('pointerdown', this._onPointerDown); + window.removeEventListener('pointerup', this._onPointerUp); } storeNodePositions() { @@ -199,14 +202,22 @@ class Gizmo extends EventHandler { } _createLayer() { - this.layerGizmo = new Layer({ - name: 'Gizmo', - clearDepthBuffer: true, - opaqueSortMode: SORTMODE_NONE, - transparentSortMode: SORTMODE_NONE - }); - this.app.scene.layers.push(this.layerGizmo); - this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); + const layerMap = this.app.scene.layers.layerIdMap; + if (layerMap.has(GIZMO_LAYER_ID)) { + this.layerGizmo = layerMap.get(GIZMO_LAYER_ID); + } else { + this.layerGizmo = new Layer({ + id: GIZMO_LAYER_ID, + name: 'Gizmo', + clearDepthBuffer: true, + opaqueSortMode: SORTMODE_NONE, + transparentSortMode: SORTMODE_NONE + }); + this.app.scene.layers.push(this.layerGizmo); + } + if (this.camera.camera.layers.indexOf(this.layerGizmo.id) === -1) { + this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); + } } _createGizmo() { From 6aa74551fea41678db60c32dc5159f0394d92724 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 12:32:54 +0000 Subject: [PATCH 034/178] add plane gap setting --- examples/src/examples/misc/gizmos.mjs | 9 ++++++- extras/gizmo/gizmo-scale.js | 32 ++++++++++++++++++------ extras/gizmo/gizmo-translate.js | 35 ++++++++++++++++++++------- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 0f985608b6e..9b8ce0ba71a 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -77,6 +77,12 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisPlaneSize' } }) + ), + jsx(LabelGroup, { text: 'Axis Plane Gap' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisPlaneGap' } + }) ) ), jsx(Panel, { headerText: 'Camera' }, @@ -214,7 +220,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisArrowThickness: gizmo.axisArrowThickness, axisArrowLength: gizmo.axisArrowLength, axisBoxSize: gizmo.axisBoxSize, - axisPlaneSize: gizmo.axisPlaneSize + axisPlaneSize: gizmo.axisPlaneSize, + aaxisPlaneGap: gizmo.axisPlaneGap }); data.on('*:set', (/** @type {string} */ path, value) => { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index e4ce8c3acd1..08def654e1c 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -14,6 +14,8 @@ const VEC3_AXES = Object.keys(tmpV1); class AxisPlane extends AxisShape { _size = 0.2; + _gap = 0.2; + constructor(options) { super(options); @@ -27,7 +29,7 @@ class AxisPlane extends AxisShape { if (axis === this.axis) { continue; } - position[axis] = this._size * 2; + position[axis] = this._size / 2 + this._gap; } return position; } @@ -55,6 +57,15 @@ class AxisPlane extends AxisShape { get size() { return this._size; } + + set gap(value) { + this._gap = value ?? 0; + this.entity.setLocalPosition(this._getPosition()); + } + + get gap() { + return this._gap; + } } class AxisBoxLine extends AxisShape { @@ -278,7 +289,7 @@ class GizmoScale extends GizmoTransform { } set axisGap(value) { - this._updateArrowProp('gap', value ?? 0); + this._updateArrowProp('gap'); } get axisGap() { @@ -286,7 +297,7 @@ class GizmoScale extends GizmoTransform { } set axisLineThickness(value) { - this._updateArrowProp('lineThickness', value ?? 1); + this._updateArrowProp('lineThickness'); } get axisLineThickness() { @@ -294,7 +305,7 @@ class GizmoScale extends GizmoTransform { } set axisLineLength(value) { - this._updateArrowProp('lineLength', value ?? 1); + this._updateArrowProp('lineLength'); } get axisLineLength() { @@ -302,7 +313,7 @@ class GizmoScale extends GizmoTransform { } set axisBoxSize(value) { - this._updateArrowProp('boxSize', value ?? 1); + this._updateArrowProp('boxSize'); } get axisBoxSize() { @@ -310,14 +321,21 @@ class GizmoScale extends GizmoTransform { } set axisPlaneSize(value) { - this._axisPlaneSize = value ?? 1; - this._updatePlaneProp('size', this._axisPlaneSize); + this._updatePlaneProp('size', value); } get axisPlaneSize() { return this._axisShapes.yz.size; } + set axisPlaneGap(value) { + this._updatePlaneProp('gap', value); + } + + get axisPlaneGap() { + return this._axisShapes.x.gap; + } + _createTransform() { super._createTransform(); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 8115486fa89..43082a00270 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -14,6 +14,8 @@ const VEC3_AXES = Object.keys(tmpV1); class AxisPlane extends AxisShape { _size = 0.2; + _gap = 0; + constructor(options) { super(options); @@ -27,7 +29,7 @@ class AxisPlane extends AxisShape { if (axis === this.axis) { continue; } - position[axis] = this._size / 2; + position[axis] = this._size / 2 + this._gap; } return position; } @@ -55,6 +57,15 @@ class AxisPlane extends AxisShape { get size() { return this._size; } + + set gap(value) { + this._gap = value ?? 0; + this.entity.setLocalPosition(this._getPosition()); + } + + get gap() { + return this._gap; + } } class AxisArrow extends AxisShape { @@ -239,7 +250,7 @@ class GizmoTranslate extends GizmoTransform { } set axisGap(value) { - this._updateArrowProp('gap', value ?? 0); + this._updateArrowProp('gap', value); } get axisGap() { @@ -247,7 +258,7 @@ class GizmoTranslate extends GizmoTransform { } set axisLineThickness(value) { - this._updateArrowProp('lineThickness', value ?? 1); + this._updateArrowProp('lineThickness', value); } get axisLineThickness() { @@ -255,7 +266,7 @@ class GizmoTranslate extends GizmoTransform { } set axisLineLength(value) { - this._updateArrowProp('lineLength', value ?? 1); + this._updateArrowProp('lineLength', value); } get axisLineLength() { @@ -263,7 +274,7 @@ class GizmoTranslate extends GizmoTransform { } set axisArrowThickness(value) { - this._updateArrowProp('arrowThickness', value ?? 1); + this._updateArrowProp('arrowThickness', value); } get axisArrowThickness() { @@ -271,8 +282,7 @@ class GizmoTranslate extends GizmoTransform { } set axisArrowLength(value) { - this._axisArrowLength = value ?? 1; - this._updateArrowProp('arrowLength', this._axisArrowLength); + this._updateArrowProp('arrowLength', value); } get axisArrowLength() { @@ -280,14 +290,21 @@ class GizmoTranslate extends GizmoTransform { } set axisPlaneSize(value) { - this._axisPlaneSize = value ?? 1; - this._updatePlaneProp('size', this._axisPlaneSize); + this._updatePlaneProp('size', value); } get axisPlaneSize() { return this._axisShapes.yz.size; } + set axisPlaneGap(value) { + this._updatePlaneProp('gap', value); + } + + get axisPlaneGap() { + return this._axisShapes.x.gap; + } + _createTransform() { super._createTransform(); From a17a9b9e80610f04e3ccedef4ac2c88d9a89a080 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 16:46:14 +0000 Subject: [PATCH 035/178] added gizmo rotation and cleaned up inherit classes --- examples/src/examples/misc/gizmos.mjs | 42 ++++----- extras/gizmo/gizmo-rotate.js | 124 ++++++++++++++++++++++++++ extras/gizmo/gizmo-scale.js | 40 ++++----- extras/gizmo/gizmo-transform.js | 80 ++++++++++++----- extras/gizmo/gizmo-translate.js | 20 +---- extras/gizmo/gizmo.js | 49 +++++++++- extras/index.js | 1 + 7 files changed, 268 insertions(+), 88 deletions(-) create mode 100644 extras/gizmo/gizmo-rotate.js diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 9b8ce0ba71a..0f7cc23f2ac 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -8,16 +8,16 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( jsx(Panel, { headerText: 'Gizmo' }, - jsx(LabelGroup, { text: 'Type' }, - jsx(SelectInput, { - options: [ - { v: 'translate', t: 'Translate' }, - { v: 'scale', t: 'Scale' } - ], - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.type' } - }) - ), + // jsx(LabelGroup, { text: 'Type' }, + // jsx(SelectInput, { + // options: [ + // { v: 'translate', t: 'Translate' }, + // { v: 'scale', t: 'Scale' } + // ], + // binding: new BindingTwoWay(), + // link: { observer, path: 'gizmo.type' } + // }) + // ), jsx(LabelGroup, { text: 'Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), @@ -83,6 +83,12 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisPlaneGap' } }) + ), + jsx(LabelGroup, { text: 'Axis Center Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisCenterSize' } + }) ) ), jsx(Panel, { headerText: 'Camera' }, @@ -204,14 +210,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - const GIZMOS = { - translate: new pcx.GizmoTranslate(app, camera), - scale: new pcx.GizmoScale(app, camera) - }; - let gizmo = GIZMOS.translate; + const gizmo = new pcx.GizmoRotate(app, camera); gizmo.attach([boxA]); data.set('gizmo', { - type: 'translate', size: gizmo.size, coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, @@ -221,7 +222,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisArrowLength: gizmo.axisArrowLength, axisBoxSize: gizmo.axisBoxSize, axisPlaneSize: gizmo.axisPlaneSize, - aaxisPlaneGap: gizmo.axisPlaneGap + axisPlaneGap: gizmo.axisPlaneGap, + axisCenterSize: gizmo.axisCenterSize }); data.on('*:set', (/** @type {string} */ path, value) => { @@ -249,9 +251,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { // gizmo if (pathArray[1] === 'type') { - gizmo.detach(); - gizmo = GIZMOS[value]; - gizmo.attach([boxA]); + // gizmo.detach(); + // gizmo = GIZMOS[value]; + // gizmo.attach([boxA]); } else { gizmo[pathArray[1]] = value; } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js new file mode 100644 index 00000000000..a89a1f42ad5 --- /dev/null +++ b/extras/gizmo/gizmo-rotate.js @@ -0,0 +1,124 @@ +import { + createTorus, + MeshInstance, + Entity, + Vec3 +} from 'playcanvas' + +import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; + +// temporary variables +const tmpV1 = new Vec3(); + +class AxisDisk extends AxisShape { + _tubeRadius = 0.02; + + _ringRadius = 0.55; + + constructor(options = {}) { + super(options); + + this._createDisk(options.app, options.layers ?? []); + } + + _createDisk(app, layers) { + const mesh = createTorus(app.graphicsDevice, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + + this.entity = new Entity('disk_' + this.axis); + this.entity.addComponent('render', { + meshInstances: [meshInstance], + layers: layers, + castShadows: false + }); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + this.meshInstances.push(meshInstance); + } +} + +class GizmoRotate extends GizmoTransform { + _rotation = true; + + constructor(app, camera) { + super(app, camera); + + this._axisShapes = { + x: new AxisDisk({ + app: app, + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.semi.red, + hoverColor: this._materials.opaque.red + }), + y: new AxisDisk({ + app: app, + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.semi.green, + hoverColor: this._materials.opaque.green + }), + z: new AxisDisk({ + app: app, + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.semi.blue, + hoverColor: this._materials.opaque.blue + }), + // xyz: new AxisDisk({ + // app: app, + // axis: 'xyz', + // layers: [this.layerGizmo.id], + // defaultColor: this._materials.semi.white, + // hoverColor: this._materials.semi.white + // }) + }; + + // this._axisShapes.xyz.entity.lookAt(this.camera.getPosition()); + // this._axisShapes.xyz.entity.rotateLocal(90, 0, 0); + + this._createTransform(); + + this.on('transform:start', () => { + this.storeNodeRotations(); + }); + + this.on('transform:move', (axis, offset, angle) => { + this.updateNodeRotations(axis, angle); + }); + } + + _createTransform() { + super._createTransform(); + + // elements + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + this._center.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this.elementMap.set(shape.meshInstances[i], shape); + } + } + } + + _updateArrowProp(propName, value) { + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; + } + + _updatePlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } +} + +export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 08def654e1c..50c91907ea3 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -69,7 +69,7 @@ class AxisPlane extends AxisShape { } class AxisBoxLine extends AxisShape { - _gap = 0; + _gap = 0.1; _lineThickness = 0.04; @@ -162,7 +162,7 @@ class AxisBoxLine extends AxisShape { } } -class AxisCenter extends AxisShape { +class AxisBoxCenter extends AxisShape { _size = 0.2; constructor(options = {}) { @@ -196,14 +196,6 @@ class AxisCenter extends AxisShape { } class GizmoScale extends GizmoTransform { - materials; - - elements; - - elementMap = new Map(); - - _dirtyElement; - constructor(app, camera) { super(app, camera); @@ -252,7 +244,7 @@ class GizmoScale extends GizmoTransform { defaultColor: this._materials.semi.blue, hoverColor: this._materials.opaque.blue }), - xyz: new AxisCenter({ + xyz: new AxisBoxCenter({ axis: 'xyz', layers: [this.layerGizmo.id], defaultColor: this._materials.semi.yellow, @@ -262,20 +254,12 @@ class GizmoScale extends GizmoTransform { this._createTransform(); - this.dragging = false; - this._hoverAxis = ''; - this._hoverIsPlane = false; - this._currAxis = ''; - this._currIsPlane = false; - this._pointStart = new Vec3(); - this._offset = new Vec3(); - this.on('transform:start', (start) => { start.sub(Vec3.ONE); this.storeNodeScale(); }); - this.on('transform:move', (offset) => { + this.on('transform:move', (axis, offset) => { this.updateNodeScale(offset); }); } @@ -289,7 +273,7 @@ class GizmoScale extends GizmoTransform { } set axisGap(value) { - this._updateArrowProp('gap'); + this._updateArrowProp('gap', value); } get axisGap() { @@ -297,7 +281,7 @@ class GizmoScale extends GizmoTransform { } set axisLineThickness(value) { - this._updateArrowProp('lineThickness'); + this._updateArrowProp('lineThickness', value); } get axisLineThickness() { @@ -305,7 +289,7 @@ class GizmoScale extends GizmoTransform { } set axisLineLength(value) { - this._updateArrowProp('lineLength'); + this._updateArrowProp('lineLength', value); } get axisLineLength() { @@ -313,7 +297,7 @@ class GizmoScale extends GizmoTransform { } set axisBoxSize(value) { - this._updateArrowProp('boxSize'); + this._updateArrowProp('boxSize', value); } get axisBoxSize() { @@ -336,6 +320,14 @@ class GizmoScale extends GizmoTransform { return this._axisShapes.x.gap; } + set axisCenterSize(value) { + this._axisShapes.xyz.size = value; + } + + get axisCenterSize() { + return this._axisShapes.xyz.size; + } + _createTransform() { super._createTransform(); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 95a723da749..a04b8ba8b8c 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -55,14 +55,34 @@ class AxisShape { } class GizmoTransform extends Gizmo { - materials; + _rotation = false; - elements; + _materials; - elementMap = new Map(); + _guideLineColor = new Color(1, 1, 1, 0.5); + + _hoverAxis = ''; + + _hoverIsPlane = false; + + _currAxis = ''; + + _currIsPlane = false; + + _pointStart = new Vec3(); + + _angleStart = 0; + + _offset = new Vec3(); _dirtyElement; + dragging = false; + + elements; + + elementMap = new Map(); + constructor(app, camera) { super(app, camera); @@ -71,26 +91,18 @@ class GizmoTransform extends Gizmo { red: this._createMaterial(new Color(1, 0.3, 0.3)), green: this._createMaterial(new Color(0.3, 1, 0.3)), blue: this._createMaterial(new Color(0.3, 0.3, 1)), - yellow: this._createMaterial(new Color(1, 1, 0.3, 1)) + yellow: this._createMaterial(new Color(1, 1, 0.3)), + white: this._createMaterial(new Color(1, 1, 1)) }, semi: { red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), - yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)) + yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), + white: this._createMaterial(new Color(1, 1, 1, 0.25)) } }; - this._guideLineColor = new Color(1, 1, 1, 0.5); - - this.dragging = false; - this._hoverAxis = ''; - this._hoverIsPlane = false; - this._currAxis = ''; - this._currIsPlane = false; - this._pointStart = new Vec3(); - this._offset = new Vec3(); - this.app.on('update', () => { if (!this.gizmo.enabled) { return; @@ -121,9 +133,10 @@ class GizmoTransform extends Gizmo { this._hover(meshInstance); if (this.dragging) { - this._offset.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); + const pointInfo = this._calcPoint(x, y); + this._offset.copy(pointInfo.point); this._offset.sub(this._pointStart); - this.fire('transform:move', this._offset); + this.fire('transform:move', this._currAxis, this._offset, pointInfo.angle - this._angleStart); this._hoverAxis = ''; this._hoverIsPlane = false; } @@ -136,9 +149,10 @@ class GizmoTransform extends Gizmo { if (meshInstance) { this._currAxis = this._getAxis(meshInstance); this._currIsPlane = this._getIsPlane(meshInstance); - this._pointStart.copy(this._calcPoint(x, y, this._currAxis, this._currIsPlane)); + const pointInfo = this._calcPoint(x, y); + this._pointStart.copy(pointInfo.point); + this._angleStart = pointInfo.angle; this.fire('transform:start', this._pointStart); - this.storeNodePositions(); this.dragging = true; } }); @@ -184,12 +198,15 @@ class GizmoTransform extends Gizmo { } } - _calcPoint(x, y, axis, isPlane) { + _calcPoint(x, y) { const gizmoPos = this.gizmo.getPosition(); const mouseWPos = this.camera.camera.screenToWorld(x, y, 1); const rayOrigin = this.camera.getPosition(); const rayDir = new Vec3(); const planeNormal = new Vec3(); + const axis = this._currAxis; + const isPlane = this._currIsPlane; + const isRotation = this._rotation; const isCenter = axis === 'xyz'; // calculate ray direction from mouse position @@ -210,7 +227,7 @@ class GizmoTransform extends Gizmo { // rotate plane normal by gizmo rotation tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); - if (!isPlane) { + if (!isPlane && !isRotation) { tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); planeNormal.copy(tmpV1.sub(planeNormal.scale(planeNormal.dot(tmpV1))).normalize()); } @@ -229,7 +246,7 @@ class GizmoTransform extends Gizmo { const v = point.sub(gizmoPos).length() * tmpV1.dot(tmpV2); point.set(v, v, v); } else { - if (!isPlane) { + if (!isPlane && !isRotation) { // reset normal based on axis and project position from plane onto normal planeNormal.set(0, 0, 0); planeNormal[axis] = 1; @@ -240,7 +257,7 @@ class GizmoTransform extends Gizmo { // rotate point back to world coords tmpQ.invert().transformVector(point, point); - if (!isPlane) { + if (!isPlane && !isRotation) { // set other axes to zero if not plane point const v = point[axis]; point.set(0, 0, 0); @@ -248,7 +265,22 @@ class GizmoTransform extends Gizmo { } } - return point; + let angle = 0; + if (isRotation) { + switch (axis) { + case 'x': + angle = Math.atan2(point.z, point.y) / (Math.PI / 180); + break; + case 'y': + angle = Math.atan2(point.x, point.z) / (Math.PI / 180); + break; + case 'z': + angle = Math.atan2(point.y, point.x) / (Math.PI / 180); + break; + } + } + + return { point, angle }; } _drawGuideLine(pos, axis) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 43082a00270..f0f936d950d 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -69,7 +69,7 @@ class AxisPlane extends AxisShape { } class AxisArrow extends AxisShape { - _gap = 0; + _gap = 0.2; _lineThickness = 0.04; @@ -174,14 +174,6 @@ class AxisArrow extends AxisShape { } class GizmoTranslate extends GizmoTransform { - materials; - - elements; - - elementMap = new Map(); - - _dirtyElement; - constructor(app, camera) { super(app, camera); @@ -232,19 +224,11 @@ class GizmoTranslate extends GizmoTransform { this._createTransform(); - this.dragging = false; - this._hoverAxis = ''; - this._hoverIsPlane = false; - this._currAxis = ''; - this._currIsPlane = false; - this._pointStart = new Vec3(); - this._offset = new Vec3(); - this.on('transform:start', () => { this.storeNodePositions(); }); - this.on('transform:move', (offset) => { + this.on('transform:move', (axis, offset) => { this.updateNodePositions(offset); }); } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 1edf57f01f7..4c06a5bcea7 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -12,7 +12,9 @@ import { // temp variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); -const tmpQ = new Quat(); +const tmpQ1 = new Quat(); +const tmpQ2 = new Quat(); + // constants const GIZMO_LAYER_ID = 1e5; @@ -36,8 +38,14 @@ class Gizmo extends EventHandler { nodePositions = new Map(); + nodeLocalRotations = new Map(); + + nodeRotations = new Map(); + nodeScale = new Map(); + nodeOffset = new Map(); + gizmo; constructor(app, camera) { @@ -135,7 +143,7 @@ class Gizmo extends EventHandler { tmpV2.x = 1 / tmpV2.x; tmpV2.y = 1 / tmpV2.y; tmpV2.z = 1 / tmpV2.z; - tmpQ.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1); + tmpQ1.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1); tmpV1.mul(tmpV2); node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV1)); } else { @@ -146,6 +154,43 @@ class Gizmo extends EventHandler { this.setGizmoPosition(); } + storeNodeRotations() { + const gizmoPos = this.gizmo.getPosition(); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + this.nodeLocalRotations.set(node, node.getLocalRotation().clone()); + this.nodeRotations.set(node, node.getRotation().clone()); + this.nodeOffset.set(node, node.getPosition().clone().sub(gizmoPos)); + } + } + + updateNodeRotations(axis, angle) { + const gizmoPos = this.gizmo.getPosition(); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + + tmpQ1.setFromAxisAngle(tmpV1, angle); + + if (this._coordSpace === 'local') { + tmpQ2.copy(this.nodeLocalRotations.get(node)).mul(tmpQ1); + node.setLocalRotation(tmpQ2); + } else { + tmpV1.copy(this.nodeOffset.get(node)); + tmpQ1.transformVector(tmpV1, tmpV1); + tmpQ2.copy(tmpQ1).mul(this.nodeRotations.get(node)); + node.setRotation(tmpQ2); + node.setPosition(tmpV1.add(gizmoPos)); + } + } + + if (this._coordSpace === 'local') { + this.setGizmoRotation(); + } + } + storeNodeScale() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; diff --git a/extras/index.js b/extras/index.js index d425f50760d..d7855c41763 100644 --- a/extras/index.js +++ b/extras/index.js @@ -22,4 +22,5 @@ export { RenderPassTAA } from './render-passes/render-pass-taa.js'; export { Gizmo } from "./gizmo/gizmo.js"; export { GizmoTransform } from "./gizmo/gizmo-transform.js"; export { GizmoTranslate } from "./gizmo/gizmo-translate.js"; +export { GizmoRotate } from "./gizmo/gizmo-rotate.js"; export { GizmoScale } from "./gizmo/gizmo-scale.js"; From 757f416d7a4a102719c56d9b285f94c136395cdb Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 17:00:21 +0000 Subject: [PATCH 036/178] added gizmo handler to example for type switching --- examples/src/examples/misc/gizmos.mjs | 89 +++++++++++++++++---------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 0f7cc23f2ac..16ed63934c1 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -8,16 +8,17 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; return fragment( jsx(Panel, { headerText: 'Gizmo' }, - // jsx(LabelGroup, { text: 'Type' }, - // jsx(SelectInput, { - // options: [ - // { v: 'translate', t: 'Translate' }, - // { v: 'scale', t: 'Scale' } - // ], - // binding: new BindingTwoWay(), - // link: { observer, path: 'gizmo.type' } - // }) - // ), + jsx(LabelGroup, { text: 'Type' }, + jsx(SelectInput, { + options: [ + { v: 'translate', t: 'Translate' }, + { v: 'rotate', t: 'Rotate' }, + { v: 'scale', t: 'Scale' } + ], + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.type' } + }) + ), jsx(LabelGroup, { text: 'Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), @@ -130,13 +131,11 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { ); } - /** * @param {import('../../options.mjs').ExampleOptions} options - The example options. * @returns {Promise} The example application. */ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { - const gfxOptions = { deviceTypes: [deviceType], glslangUrl: glslangPath + 'glslang.js', @@ -210,21 +209,49 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - const gizmo = new pcx.GizmoRotate(app, camera); - gizmo.attach([boxA]); - data.set('gizmo', { - size: gizmo.size, - coordSpace: gizmo.coordSpace, - axisGap: gizmo.axisGap, - axisLineThickness: gizmo.axisLineThickness, - axisLineLength: gizmo.axisLineLength, - axisArrowThickness: gizmo.axisArrowThickness, - axisArrowLength: gizmo.axisArrowLength, - axisBoxSize: gizmo.axisBoxSize, - axisPlaneSize: gizmo.axisPlaneSize, - axisPlaneGap: gizmo.axisPlaneGap, - axisCenterSize: gizmo.axisCenterSize - }); + class GizmoHandler { + _type = 'translate'; + + constructor(app, camera) { + this.gizmos = { + translate: new pcx.GizmoTranslate(app, camera), + rotate: new pcx.GizmoRotate(app, camera), + scale: new pcx.GizmoScale(app, camera) + }; + } + + get gizmo() { + return this.gizmos[this._type]; + } + + switch(type, nodes, data) { + this.gizmo.detach(); + this._type = type; + const gizmo = this.gizmo; + gizmo.attach(nodes); + + // TODO: fix controls updating + if (data) { + data.set('gizmo', { + type: type, + size: gizmo.size, + coordSpace: gizmo.coordSpace, + axisGap: gizmo.axisGap, + axisLineThickness: gizmo.axisLineThickness, + axisLineLength: gizmo.axisLineLength, + axisArrowThickness: gizmo.axisArrowThickness, + axisArrowLength: gizmo.axisArrowLength, + axisBoxSize: gizmo.axisBoxSize, + axisPlaneSize: gizmo.axisPlaneSize, + axisPlaneGap: gizmo.axisPlaneGap, + axisCenterSize: gizmo.axisCenterSize + }); + } + } + } + + const gizmoHandler = new GizmoHandler(app, camera); + gizmoHandler.switch('translate', [boxA], data); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); @@ -245,17 +272,15 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { camera.camera.orthoHeight = value; break; } - gizmo.updateGizmoScale(); + gizmoHandler.gizmo.updateGizmoScale(); return; } // gizmo if (pathArray[1] === 'type') { - // gizmo.detach(); - // gizmo = GIZMOS[value]; - // gizmo.attach([boxA]); + gizmoHandler.switch(value, [boxA]); } else { - gizmo[pathArray[1]] = value; + gizmoHandler.gizmo[pathArray[1]] = value; } }); From ca44896fde6107fd576d258b280042ce025b1016 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 3 Jan 2024 17:35:19 +0000 Subject: [PATCH 037/178] added extra box in examples --- examples/src/examples/misc/gizmos.mjs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 16ed63934c1..793f59f6a33 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -178,14 +178,14 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { boxA.addComponent('render', { type: 'box' }); - // boxA.rotate(0, 45, 0); + boxA.setPosition(0.5, 0, -0.5); app.root.addChild(boxA); - // const boxB = new pc.Entity('cubeB'); - // boxB.addComponent('render', { - // type: 'box' - // }); - // boxB.setPosition(1, 0, 1); - // app.root.addChild(boxB); + const boxB = new pc.Entity('cubeB'); + boxB.addComponent('render', { + type: 'box' + }); + boxB.setPosition(-0.5, 0, 0.5); + app.root.addChild(boxB); // create camera entity data.set('camera', { @@ -251,7 +251,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } const gizmoHandler = new GizmoHandler(app, camera); - gizmoHandler.switch('translate', [boxA], data); + gizmoHandler.switch('translate', [boxA, boxB], data); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); @@ -278,7 +278,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { // gizmo if (pathArray[1] === 'type') { - gizmoHandler.switch(value, [boxA]); + gizmoHandler.switch(value, [boxA, boxB]); } else { gizmoHandler.gizmo[pathArray[1]] = value; } From 1df2de80ab62d620be9ece86badeb26c6f8130d8 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 10:55:45 +0000 Subject: [PATCH 038/178] cleaned up example and disabled set firing when switching gizmos --- examples/src/examples/misc/gizmos.mjs | 105 ++++++++++++++------------ 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 793f59f6a33..5df9cd317ad 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -210,9 +210,14 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { // create gizmo class GizmoHandler { + data; + _type = 'translate'; - constructor(app, camera) { + skipSetFire = false; + + constructor(app, camera, data) { + this.data = data; this.gizmos = { translate: new pcx.GizmoTranslate(app, camera), rotate: new pcx.GizmoRotate(app, camera), @@ -224,65 +229,67 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { return this.gizmos[this._type]; } - switch(type, nodes, data) { + switch(type, nodes) { this.gizmo.detach(); this._type = type; const gizmo = this.gizmo; gizmo.attach(nodes); - - // TODO: fix controls updating - if (data) { - data.set('gizmo', { - type: type, - size: gizmo.size, - coordSpace: gizmo.coordSpace, - axisGap: gizmo.axisGap, - axisLineThickness: gizmo.axisLineThickness, - axisLineLength: gizmo.axisLineLength, - axisArrowThickness: gizmo.axisArrowThickness, - axisArrowLength: gizmo.axisArrowLength, - axisBoxSize: gizmo.axisBoxSize, - axisPlaneSize: gizmo.axisPlaneSize, - axisPlaneGap: gizmo.axisPlaneGap, - axisCenterSize: gizmo.axisCenterSize - }); - } + this.skipSetFire = true; + this.data.set('gizmo', { + type: type, + size: gizmo.size, + coordSpace: gizmo.coordSpace, + axisGap: gizmo.axisGap, + axisLineThickness: gizmo.axisLineThickness, + axisLineLength: gizmo.axisLineLength, + axisArrowThickness: gizmo.axisArrowThickness, + axisArrowLength: gizmo.axisArrowLength, + axisBoxSize: gizmo.axisBoxSize, + axisPlaneSize: gizmo.axisPlaneSize, + axisPlaneGap: gizmo.axisPlaneGap, + axisCenterSize: gizmo.axisCenterSize + }); + this.skipSetFire = false; } } - - const gizmoHandler = new GizmoHandler(app, camera); - gizmoHandler.switch('translate', [boxA, boxB], data); + const gizmoHandler = new GizmoHandler(app, camera, data); + gizmoHandler.switch('translate', [boxA, boxB]); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); - // camera - if (pathArray[0] === 'camera') { - switch (pathArray[1]) { - case 'proj': - camera.camera.projection = value - 1; - break; - case 'dist': - camera.setPosition(5 * value, 3 * value, 5 * value); - break; - case 'fov': - camera.camera.fov = value; - break; - case 'orthoHeight': - camera.camera.orthoHeight = value; - break; - } - gizmoHandler.gizmo.updateGizmoScale(); - return; + switch (pathArray[0]) { + case 'camera': + switch (pathArray[1]) { + case 'proj': + camera.camera.projection = value - 1; + break; + case 'dist': + camera.setPosition(5 * value, 3 * value, 5 * value); + break; + case 'fov': + camera.camera.fov = value; + break; + case 'orthoHeight': + camera.camera.orthoHeight = value; + break; + } + gizmoHandler.gizmo.updateGizmoScale(); + return; + case 'gizmo': + if (gizmoHandler.skipSetFire) { + return; + } + switch (pathArray[1]) { + case 'type': + gizmoHandler.switch(value, [boxA, boxB]); + break; + default: + gizmoHandler.gizmo[pathArray[1]] = value; + break; + } + break; } - - // gizmo - if (pathArray[1] === 'type') { - gizmoHandler.switch(value, [boxA, boxB]); - } else { - gizmoHandler.gizmo[pathArray[1]] = value; - } - }); return app; From 66426f52e1dcd5989c0402071cf3bfff4da1d0f1 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 12:00:14 +0000 Subject: [PATCH 039/178] add dynamic controls to gizmo example --- examples/src/examples/misc/gizmos.mjs | 149 +++++++++++++++----------- extras/gizmo/gizmo-rotate.js | 63 ++++++++--- 2 files changed, 139 insertions(+), 73 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 5df9cd317ad..2d6880f57f0 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -6,6 +6,9 @@ import * as pc from 'playcanvas'; */ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; + + const [type, setType] = React.useState('translate'); + return fragment( jsx(Panel, { headerText: 'Gizmo' }, jsx(LabelGroup, { text: 'Type' }, @@ -16,15 +19,8 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { { v: 'scale', t: 'Scale' } ], binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.type' } - }) - ), - jsx(LabelGroup, { text: 'Size' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.size' }, - min: 0.1, - max: 2.0 + link: { observer, path: 'gizmo.type' }, + onSelect: value => setType(value) }) ), jsx(LabelGroup, { text: 'Coord Space' }, @@ -37,60 +33,91 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { link: { observer, path: 'gizmo.coordSpace' } }) ), - jsx(LabelGroup, { text: 'Axis Gap' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisGap' } - }) - ), - jsx(LabelGroup, { text: 'Axis Line Thickness' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisLineThickness' } - }) - ), - jsx(LabelGroup, { text: 'Axis Line Length' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisLineLength' } - }) - ), - jsx(LabelGroup, { text: 'Axis Box Size' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisBoxSize' } - }) - ), - jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisArrowThickness' } - }) - ), - jsx(LabelGroup, { text: 'Axis Arrow Length' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisArrowLength' } - }) - ), - jsx(LabelGroup, { text: 'Axis Plane Size' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisPlaneSize' } - }) - ), - jsx(LabelGroup, { text: 'Axis Plane Gap' }, + jsx(LabelGroup, { text: 'Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisPlaneGap' } + link: { observer, path: 'gizmo.size' }, + min: 0.1, + max: 2.0 }) ), - jsx(LabelGroup, { text: 'Axis Center Size' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisCenterSize' } - }) - ) + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Axis Gap' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisGap' } + }) + ), + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Axis Line Thickness' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisLineThickness' } + }) + ), + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Axis Line Length' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisLineLength' } + }) + ), + type === 'scale' && + jsx(LabelGroup, { text: 'Axis Box Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisBoxSize' } + }) + ), + type === 'translate' && + jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisArrowThickness' } + }) + ), + type === 'translate' && + jsx(LabelGroup, { text: 'Axis Arrow Length' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisArrowLength' } + }) + ), + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Axis Plane Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisPlaneSize' } + }) + ), + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Axis Plane Gap' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisPlaneGap' } + }) + ), + type === 'scale' && + jsx(LabelGroup, { text: 'Axis Center Size' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisCenterSize' } + }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Tube Radius' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.tubeRadius' } + }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Ring Radius' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.ringRadius' } + }) + ) ), jsx(Panel, { headerText: 'Camera' }, jsx(LabelGroup, { text: 'Projection' }, @@ -247,7 +274,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisBoxSize: gizmo.axisBoxSize, axisPlaneSize: gizmo.axisPlaneSize, axisPlaneGap: gizmo.axisPlaneGap, - axisCenterSize: gizmo.axisCenterSize + axisCenterSize: gizmo.axisCenterSize, + tubeRadius: gizmo.tubeRadius, + ringRadius: gizmo.ringRadius }); this.skipSetFire = false; } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index a89a1f42ad5..1754f62a688 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -11,6 +11,8 @@ import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; const tmpV1 = new Vec3(); class AxisDisk extends AxisShape { + _device; + _tubeRadius = 0.02; _ringRadius = 0.55; @@ -18,11 +20,12 @@ class AxisDisk extends AxisShape { constructor(options = {}) { super(options); - this._createDisk(options.app, options.layers ?? []); + this._device = options.device; + this._createDisk(options.layers ?? []); } - _createDisk(app, layers) { - const mesh = createTorus(app.graphicsDevice, { + _createDisk(layers) { + const mesh = createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius }); @@ -39,6 +42,30 @@ class AxisDisk extends AxisShape { this.entity.setLocalScale(this._scale); this.meshInstances.push(meshInstance); } + + set tubeRadius(value) { + this._tubeRadius = value; + this.meshInstances[0].mesh = createTorus(this._device, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + } + + get tubeRadius() { + return this._tubeRadius; + } + + set ringRadius(value) { + this._ringRadius = value; + this.meshInstances[0].mesh = createTorus(this._device, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + } + + get ringRadius() { + return this._ringRadius; + } } class GizmoRotate extends GizmoTransform { @@ -49,7 +76,7 @@ class GizmoRotate extends GizmoTransform { this._axisShapes = { x: new AxisDisk({ - app: app, + device: app.graphicsDevice, axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), @@ -57,7 +84,7 @@ class GizmoRotate extends GizmoTransform { hoverColor: this._materials.opaque.red }), y: new AxisDisk({ - app: app, + device: app.graphicsDevice, axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), @@ -65,7 +92,7 @@ class GizmoRotate extends GizmoTransform { hoverColor: this._materials.opaque.green }), z: new AxisDisk({ - app: app, + device: app.graphicsDevice, axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), @@ -108,17 +135,27 @@ class GizmoRotate extends GizmoTransform { } } - _updateArrowProp(propName, value) { + set tubeRadius(value) { + this._updateDiskProp('tubeRadius', value); + } + + get tubeRadius() { + return this._axisShapes.x.tubeRadius; + } + + set ringRadius(value) { + this._updateDiskProp('ringRadius', value); + } + + get ringRadius() { + return this._axisShapes.x.ringRadius; + } + + _updateDiskProp(propName, value) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; } - - _updatePlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; - } } export { GizmoRotate }; From 61662671aeb4f14ccbb9bfc50a1a25fc5f7a586f Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 12:01:34 +0000 Subject: [PATCH 040/178] refactor spaces and removed unused temporary vector --- extras/gizmo/gizmo-rotate.js | 3 --- extras/gizmo/gizmo.js | 1 - 2 files changed, 4 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 1754f62a688..0eaf31ecd80 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -7,9 +7,6 @@ import { import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; -// temporary variables -const tmpV1 = new Vec3(); - class AxisDisk extends AxisShape { _device; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 4c06a5bcea7..1eb8b668e00 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -15,7 +15,6 @@ const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); const tmpQ2 = new Quat(); - // constants const GIZMO_LAYER_ID = 1e5; const MIN_GIZMO_SCALE = 1e-4; From 54ec1170842556094605bc4ccd7f9dd644e62c7d Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 14:18:37 +0000 Subject: [PATCH 041/178] added facing ring for rotation and updated colors --- extras/gizmo/gizmo-rotate.js | 58 +++++++++++++++++++++++---------- extras/gizmo/gizmo-scale.js | 42 ++++++++++++------------ extras/gizmo/gizmo-transform.js | 43 ++++++++++++------------ extras/gizmo/gizmo-translate.js | 28 ++++++++-------- extras/gizmo/gizmo.js | 12 +++++-- 5 files changed, 107 insertions(+), 76 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 0eaf31ecd80..8e11a9f43aa 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -18,6 +18,7 @@ class AxisDisk extends AxisShape { super(options); this._device = options.device; + this._ringRadius = options.ringRadius ?? this._ringRadius; this._createDisk(options.layers ?? []); } @@ -41,7 +42,7 @@ class AxisDisk extends AxisShape { } set tubeRadius(value) { - this._tubeRadius = value; + this._tubeRadius = value ?? 0.1; this.meshInstances[0].mesh = createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius @@ -53,7 +54,7 @@ class AxisDisk extends AxisShape { } set ringRadius(value) { - this._ringRadius = value; + this._ringRadius = value ?? 0.1; this.meshInstances[0].mesh = createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius @@ -68,6 +69,8 @@ class AxisDisk extends AxisShape { class GizmoRotate extends GizmoTransform { _rotation = true; + _ring; + constructor(app, camera) { super(app, camera); @@ -77,44 +80,45 @@ class GizmoRotate extends GizmoTransform { axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red + defaultColor: this._materials.opaque.red, + hoverColor: this._materials.opaque.yellow }), y: new AxisDisk({ device: app.graphicsDevice, axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green + defaultColor: this._materials.opaque.green, + hoverColor: this._materials.opaque.yellow }), z: new AxisDisk({ device: app.graphicsDevice, axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue + defaultColor: this._materials.opaque.blue, + hoverColor: this._materials.opaque.yellow }), - // xyz: new AxisDisk({ - // app: app, - // axis: 'xyz', - // layers: [this.layerGizmo.id], - // defaultColor: this._materials.semi.white, - // hoverColor: this._materials.semi.white - // }) + face: new AxisDisk({ + app: app, + axis: 'face', + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.yellow, + hoverColor: this._materials.opaque.yellow, + ringRadius: 0.8 + }) }; - // this._axisShapes.xyz.entity.lookAt(this.camera.getPosition()); - // this._axisShapes.xyz.entity.rotateLocal(90, 0, 0); - this._createTransform(); + this._setFacingDisks(); this.on('transform:start', () => { + this._setFacingDisks(); this.storeNodeRotations(); }); this.on('transform:move', (axis, offset, angle) => { + this._setFacingDisks(); this.updateNodeRotations(axis, angle); }); } @@ -122,6 +126,14 @@ class GizmoRotate extends GizmoTransform { _createTransform() { super._createTransform(); + // guide ring + this._ring = new AxisDisk({ + app: this.app, + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.white + }); + this._center.addChild(this._ring.entity); + // elements for (const key in this._axisShapes) { const shape = this._axisShapes[key]; @@ -153,6 +165,16 @@ class GizmoRotate extends GizmoTransform { this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; } + + _setFacingDisks() { + this._faceDiskToCamera(this._ring.entity); + this._faceDiskToCamera(this._axisShapes.face.entity); + } + + _faceDiskToCamera(entity) { + entity.lookAt(this.camera.getPosition()); + entity.rotateLocal(90, 0, 0); + } } export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 50c91907ea3..85a118415ba 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -14,7 +14,7 @@ const VEC3_AXES = Object.keys(tmpV1); class AxisPlane extends AxisShape { _size = 0.2; - _gap = 0.2; + _gap = 0.1; constructor(options) { super(options); @@ -69,13 +69,13 @@ class AxisPlane extends AxisShape { } class AxisBoxLine extends AxisShape { - _gap = 0.1; + _gap = 0; _lineThickness = 0.04; _lineLength = 0.5; - _boxSize = 0.15; + _boxSize = 0.14; constructor(options = {}) { super(options); @@ -163,7 +163,7 @@ class AxisBoxLine extends AxisShape { } class AxisBoxCenter extends AxisShape { - _size = 0.2; + _size = 0.14; constructor(options = {}) { super(options); @@ -202,52 +202,52 @@ class GizmoScale extends GizmoTransform { this._coordSpace = 'local'; this._axisShapes = { + xyz: new AxisBoxCenter({ + axis: 'xyz', + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.white, + hoverColor: this._materials.opaque.yellow + }), x: new AxisBoxLine({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red + defaultColor: this._materials.opaque.red, + hoverColor: this._materials.opaque.yellow }), y: new AxisBoxLine({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green + defaultColor: this._materials.opaque.green, + hoverColor: this._materials.opaque.yellow }), z: new AxisBoxLine({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue + defaultColor: this._materials.opaque.blue, + hoverColor: this._materials.opaque.yellow }), yz: new AxisPlane({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red + defaultColor: this._materials.opaque.red, + hoverColor: this._materials.opaque.yellow }), xz: new AxisPlane({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green + defaultColor: this._materials.opaque.green, + hoverColor: this._materials.opaque.yellow }), xy: new AxisPlane({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue - }), - xyz: new AxisBoxCenter({ - axis: 'xyz', - layers: [this.layerGizmo.id], - defaultColor: this._materials.semi.yellow, + defaultColor: this._materials.opaque.blue, hoverColor: this._materials.opaque.yellow }) }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index a04b8ba8b8c..c3861d961d3 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,4 +1,5 @@ import { + math, PROJECTION_PERSPECTIVE, BLEND_NORMAL, Color, @@ -99,7 +100,7 @@ class GizmoTransform extends Gizmo { green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), - white: this._createMaterial(new Color(1, 1, 1, 0.25)) + white: this._createMaterial(new Color(1, 1, 1, 0.5)) } }; @@ -207,7 +208,8 @@ class GizmoTransform extends Gizmo { const axis = this._currAxis; const isPlane = this._currIsPlane; const isRotation = this._rotation; - const isCenter = axis === 'xyz'; + const isAllAxes = axis === 'xyz'; + const isFacing = axis === 'face'; // calculate ray direction from mouse position if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { @@ -217,7 +219,7 @@ class GizmoTransform extends Gizmo { this.camera.getWorldTransform().transformVector(tmpV1.set(0, 0, -1), rayDir); } - if (isCenter) { + if (isAllAxes || isFacing) { // all axes so set normal to plane facing camera planeNormal.copy(rayOrigin).sub(gizmoPos).normalize(); } else { @@ -239,13 +241,14 @@ class GizmoTransform extends Gizmo { const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; const point = rayDir.scale(-pointPlaneDist).add(rayOrigin); - if (isCenter) { + if (isAllAxes) { + // calculate point distance from gizmo tmpV1.copy(point).sub(gizmoPos).normalize(); tmpV2.copy(this.camera.up).add(this.camera.right).normalize(); const v = point.sub(gizmoPos).length() * tmpV1.dot(tmpV2); point.set(v, v, v); - } else { + } else if (!isFacing) { if (!isPlane && !isRotation) { // reset normal based on axis and project position from plane onto normal planeNormal.set(0, 0, 0); @@ -269,17 +272,17 @@ class GizmoTransform extends Gizmo { if (isRotation) { switch (axis) { case 'x': - angle = Math.atan2(point.z, point.y) / (Math.PI / 180); + angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; break; case 'y': - angle = Math.atan2(point.x, point.z) / (Math.PI / 180); + angle = Math.atan2(point.x, point.z) * math.RAD_TO_DEG; break; case 'z': - angle = Math.atan2(point.y, point.x) / (Math.PI / 180); + case 'face': + angle = Math.atan2(point.y, point.x) * math.RAD_TO_DEG; break; } } - return { point, angle }; } @@ -295,7 +298,7 @@ class GizmoTransform extends Gizmo { _createMaterial(color) { const material = new StandardMaterial(); - material.diffuse = color; + material.emissive = color; if (color.a !== 1) { material.opacity = color.a; material.blendType = BLEND_NORMAL; @@ -303,19 +306,19 @@ class GizmoTransform extends Gizmo { return material; } - _createLight(angles) { - const light = new Entity('light'); - light.addComponent('light', { - layers: [this.layerGizmo.id] - }); - light.setEulerAngles(angles); - return light; - } + // _createLight(angles) { + // const light = new Entity('light'); + // light.addComponent('light', { + // layers: [this.layerGizmo.id] + // }); + // light.setEulerAngles(angles); + // return light; + // } _createTransform() { // lighting - const light = this._createLight(new Vec3(45, 0, -45)); - this.gizmo.addChild(light); + // const light = this._createLight(new Vec3(45, 0, -45)); + // this.gizmo.addChild(light); // center this._center = new Entity('center'); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index f0f936d950d..5f272dbf1db 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -14,7 +14,7 @@ const VEC3_AXES = Object.keys(tmpV1); class AxisPlane extends AxisShape { _size = 0.2; - _gap = 0; + _gap = 0.1; constructor(options) { super(options); @@ -69,7 +69,7 @@ class AxisPlane extends AxisShape { } class AxisArrow extends AxisShape { - _gap = 0.2; + _gap = 0; _lineThickness = 0.04; @@ -182,43 +182,43 @@ class GizmoTranslate extends GizmoTransform { axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red + defaultColor: this._materials.opaque.red, + hoverColor: this._materials.opaque.yellow }), y: new AxisArrow({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green + defaultColor: this._materials.opaque.green, + hoverColor: this._materials.opaque.yellow }), z: new AxisArrow({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue + defaultColor: this._materials.opaque.blue, + hoverColor: this._materials.opaque.yellow }), yz: new AxisPlane({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.semi.red, - hoverColor: this._materials.opaque.red + defaultColor: this._materials.opaque.red, + hoverColor: this._materials.opaque.yellow }), xz: new AxisPlane({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.semi.green, - hoverColor: this._materials.opaque.green + defaultColor: this._materials.opaque.green, + hoverColor: this._materials.opaque.yellow }), xy: new AxisPlane({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.semi.blue, - hoverColor: this._materials.opaque.blue + defaultColor: this._materials.opaque.blue, + hoverColor: this._materials.opaque.yellow }) }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 1eb8b668e00..c57a069d8a8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -165,15 +165,21 @@ class Gizmo extends EventHandler { updateNodeRotations(axis, angle) { const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.getPosition(); + const isFacing = axis === 'face'; for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - tmpV1.set(0, 0, 0); - tmpV1[axis] = 1; + if (isFacing) { + tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); + } else { + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + } tmpQ1.setFromAxisAngle(tmpV1, angle); - if (this._coordSpace === 'local') { + if (!isFacing && this._coordSpace === 'local') { tmpQ2.copy(this.nodeLocalRotations.get(node)).mul(tmpQ1); node.setLocalRotation(tmpQ2); } else { From dc7696734b156ba11702d957f14a2a40e105b00e Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 15:02:42 +0000 Subject: [PATCH 042/178] refactor and renaming methods from using update to set --- extras/gizmo/gizmo-rotate.js | 48 ++++++++++++++++----------------- extras/gizmo/gizmo-scale.js | 36 ++++++++++++------------- extras/gizmo/gizmo-translate.js | 38 +++++++++++++------------- extras/gizmo/gizmo.js | 6 ++--- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 8e11a9f43aa..d58a723bd7e 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -123,29 +123,8 @@ class GizmoRotate extends GizmoTransform { }); } - _createTransform() { - super._createTransform(); - - // guide ring - this._ring = new AxisDisk({ - app: this.app, - layers: [this.layerGizmo.id], - defaultColor: this._materials.semi.white - }); - this._center.addChild(this._ring.entity); - - // elements - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; - this._center.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this.elementMap.set(shape.meshInstances[i], shape); - } - } - } - set tubeRadius(value) { - this._updateDiskProp('tubeRadius', value); + this._setDiskProp('tubeRadius', value); } get tubeRadius() { @@ -153,14 +132,14 @@ class GizmoRotate extends GizmoTransform { } set ringRadius(value) { - this._updateDiskProp('ringRadius', value); + this._setDiskProp('ringRadius', value); } get ringRadius() { return this._axisShapes.x.ringRadius; } - _updateDiskProp(propName, value) { + _setDiskProp(propName, value) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; @@ -175,6 +154,27 @@ class GizmoRotate extends GizmoTransform { entity.lookAt(this.camera.getPosition()); entity.rotateLocal(90, 0, 0); } + + _createTransform() { + super._createTransform(); + + // guide ring + this._ring = new AxisDisk({ + app: this.app, + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.white + }); + this._center.addChild(this._ring.entity); + + // elements + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + this._center.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this.elementMap.set(shape.meshInstances[i], shape); + } + } + } } export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 85a118415ba..f7a36cffa96 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -273,7 +273,7 @@ class GizmoScale extends GizmoTransform { } set axisGap(value) { - this._updateArrowProp('gap', value); + this._setArrowProp('gap', value); } get axisGap() { @@ -281,7 +281,7 @@ class GizmoScale extends GizmoTransform { } set axisLineThickness(value) { - this._updateArrowProp('lineThickness', value); + this._setArrowProp('lineThickness', value); } get axisLineThickness() { @@ -289,7 +289,7 @@ class GizmoScale extends GizmoTransform { } set axisLineLength(value) { - this._updateArrowProp('lineLength', value); + this._setArrowProp('lineLength', value); } get axisLineLength() { @@ -297,7 +297,7 @@ class GizmoScale extends GizmoTransform { } set axisBoxSize(value) { - this._updateArrowProp('boxSize', value); + this._setArrowProp('boxSize', value); } get axisBoxSize() { @@ -305,7 +305,7 @@ class GizmoScale extends GizmoTransform { } set axisPlaneSize(value) { - this._updatePlaneProp('size', value); + this._setPlaneProp('size', value); } get axisPlaneSize() { @@ -313,7 +313,7 @@ class GizmoScale extends GizmoTransform { } set axisPlaneGap(value) { - this._updatePlaneProp('gap', value); + this._setPlaneProp('gap', value); } get axisPlaneGap() { @@ -328,6 +328,18 @@ class GizmoScale extends GizmoTransform { return this._axisShapes.xyz.size; } + _setArrowProp(propName, value) { + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; + } + + _setPlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } + _createTransform() { super._createTransform(); @@ -340,18 +352,6 @@ class GizmoScale extends GizmoTransform { } } } - - _updateArrowProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; - } - - _updatePlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; - } } export { GizmoScale }; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 5f272dbf1db..cbf2c0dae41 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -234,7 +234,7 @@ class GizmoTranslate extends GizmoTransform { } set axisGap(value) { - this._updateArrowProp('gap', value); + this._setArrowProp('gap', value); } get axisGap() { @@ -242,7 +242,7 @@ class GizmoTranslate extends GizmoTransform { } set axisLineThickness(value) { - this._updateArrowProp('lineThickness', value); + this._setArrowProp('lineThickness', value); } get axisLineThickness() { @@ -250,7 +250,7 @@ class GizmoTranslate extends GizmoTransform { } set axisLineLength(value) { - this._updateArrowProp('lineLength', value); + this._setArrowProp('lineLength', value); } get axisLineLength() { @@ -258,7 +258,7 @@ class GizmoTranslate extends GizmoTransform { } set axisArrowThickness(value) { - this._updateArrowProp('arrowThickness', value); + this._setArrowProp('arrowThickness', value); } get axisArrowThickness() { @@ -266,7 +266,7 @@ class GizmoTranslate extends GizmoTransform { } set axisArrowLength(value) { - this._updateArrowProp('arrowLength', value); + this._setArrowProp('arrowLength', value); } get axisArrowLength() { @@ -274,7 +274,7 @@ class GizmoTranslate extends GizmoTransform { } set axisPlaneSize(value) { - this._updatePlaneProp('size', value); + this._setPlaneProp('size', value); } get axisPlaneSize() { @@ -282,13 +282,25 @@ class GizmoTranslate extends GizmoTransform { } set axisPlaneGap(value) { - this._updatePlaneProp('gap', value); + this._setPlaneProp('gap', value); } get axisPlaneGap() { return this._axisShapes.x.gap; } + _setArrowProp(propName, value) { + this._axisShapes.x[propName] = value; + this._axisShapes.y[propName] = value; + this._axisShapes.z[propName] = value; + } + + _setPlaneProp(propName, value) { + this._axisShapes.yz[propName] = value; + this._axisShapes.xz[propName] = value; + this._axisShapes.xy[propName] = value; + } + _createTransform() { super._createTransform(); @@ -301,18 +313,6 @@ class GizmoTranslate extends GizmoTransform { } } } - - _updateArrowProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; - } - - _updatePlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; - } } export { GizmoTranslate }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c57a069d8a8..fb9cb903362 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -56,7 +56,7 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this.updateGizmoScale(); + this.setGizmoScale(); this._onPointerMove = (e) => { if (!this.gizmo.enabled) { @@ -93,7 +93,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this.updateGizmoScale(); + this.setGizmoScale(); } get size() { @@ -233,7 +233,7 @@ class Gizmo extends EventHandler { } } - updateGizmoScale() { + setGizmoScale() { let scale = 1; if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; From 60c27d3715bf278656fd302980e1cb93b8f9a545 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 15:25:55 +0000 Subject: [PATCH 043/178] moved node updates to child classes --- extras/gizmo/gizmo-rotate.js | 67 +++++++++++++++++- extras/gizmo/gizmo-scale.js | 26 ++++++- extras/gizmo/gizmo-transform.js | 14 ++-- extras/gizmo/gizmo-translate.js | 46 +++++++++++- extras/gizmo/gizmo.js | 122 +++----------------------------- 5 files changed, 149 insertions(+), 126 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index d58a723bd7e..8d9cd27cb67 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -2,11 +2,17 @@ import { createTorus, MeshInstance, Entity, + Quat, Vec3 } from 'playcanvas' import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; +// temporary variables +const tmpV1 = new Vec3(); +const tmpQ1 = new Quat(); +const tmpQ2 = new Quat(); + class AxisDisk extends AxisShape { _device; @@ -71,6 +77,12 @@ class GizmoRotate extends GizmoTransform { _ring; + _nodeLocalRotations = new Map(); + + _nodeRotations = new Map(); + + _nodeOffsets = new Map(); + constructor(app, camera) { super(app, camera); @@ -114,12 +126,12 @@ class GizmoRotate extends GizmoTransform { this.on('transform:start', () => { this._setFacingDisks(); - this.storeNodeRotations(); + this._storeNodeRotations(); }); this.on('transform:move', (axis, offset, angle) => { this._setFacingDisks(); - this.updateNodeRotations(axis, angle); + this._setNodeRotations(axis, angle); }); } @@ -175,6 +187,57 @@ class GizmoRotate extends GizmoTransform { } } } + + _storeNodeRotations() { + const gizmoPos = this.gizmo.getPosition(); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + this._nodeLocalRotations.set(node, node.getLocalRotation().clone()); + this._nodeRotations.set(node, node.getRotation().clone()); + this._nodeOffsets.set(node, node.getPosition().clone().sub(gizmoPos)); + } + } + + _setNodeRotations(axis, angle) { + const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.getPosition(); + const isFacing = axis === 'face'; + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + + if (isFacing) { + tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); + } else { + tmpV1.set(0, 0, 0); + tmpV1[axis] = 1; + } + + tmpQ1.setFromAxisAngle(tmpV1, angle); + + if (!isFacing && this._coordSpace === 'local') { + tmpQ2.copy(this._nodeLocalRotations.get(node)).mul(tmpQ1); + node.setLocalRotation(tmpQ2); + } else { + tmpV1.copy(this._nodeOffsets.get(node)); + tmpQ1.transformVector(tmpV1, tmpV1); + tmpQ2.copy(tmpQ1).mul(this._nodeRotations.get(node)); + node.setRotation(tmpQ2); + node.setPosition(tmpV1.add(gizmoPos)); + } + } + + if (this._coordSpace === 'local') { + this.updateGizmoRotation(); + } + } + + detach() { + super.detach(); + + this._nodeLocalRotations.clear(); + this._nodeRotations.clear(); + this._nodeOffsets.clear(); + } } export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index f7a36cffa96..141ac19826d 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -196,6 +196,8 @@ class AxisBoxCenter extends AxisShape { } class GizmoScale extends GizmoTransform { + _nodeScales = new Map(); + constructor(app, camera) { super(app, camera); @@ -256,11 +258,11 @@ class GizmoScale extends GizmoTransform { this.on('transform:start', (start) => { start.sub(Vec3.ONE); - this.storeNodeScale(); + this._storeNodeScales(); }); this.on('transform:move', (axis, offset) => { - this.updateNodeScale(offset); + this._setNodeScales(offset); }); } @@ -352,6 +354,26 @@ class GizmoScale extends GizmoTransform { } } } + + _storeNodeScales() { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + this._nodeScales.set(node, node.getLocalScale().clone()); + } + } + + _setNodeScales(point) { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + node.setLocalScale(this._nodeScales.get(node).clone().mul(point)); + } + } + + detach() { + super.detach(); + + this._nodeScales.clear(); + } } export { GizmoScale }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index c3861d961d3..cd3984d1f3a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -14,7 +14,7 @@ import { Gizmo } from "./gizmo.js"; // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); -const tmpQ = new Quat(); +const tmpQ1 = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); @@ -109,7 +109,7 @@ class GizmoTransform extends Gizmo { return; } const gizmoPos = this.gizmo.getPosition(); - tmpQ.copy(this.gizmo.getRotation()); + tmpQ1.copy(this.gizmo.getRotation()); const checkAxis = this._hoverAxis || this._currAxis; const checkIsPlane = this._hoverIsPlane || this._currIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { @@ -227,7 +227,7 @@ class GizmoTransform extends Gizmo { planeNormal[axis] = 1; // rotate plane normal by gizmo rotation - tmpQ.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); + tmpQ1.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); if (!isPlane && !isRotation) { tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); @@ -253,12 +253,12 @@ class GizmoTransform extends Gizmo { // reset normal based on axis and project position from plane onto normal planeNormal.set(0, 0, 0); planeNormal[axis] = 1; - tmpQ.transformVector(planeNormal, planeNormal); + tmpQ1.transformVector(planeNormal, planeNormal); point.copy(planeNormal.scale(planeNormal.dot(point))); } // rotate point back to world coords - tmpQ.invert().transformVector(point, point); + tmpQ1.invert().transformVector(point, point); if (!isPlane && !isRotation) { // set other axes to zero if not plane point @@ -291,8 +291,8 @@ class GizmoTransform extends Gizmo { tmpV1[axis] = 1; tmpV1.scale(GUIDELINE_SIZE); tmpV2.copy(tmpV1).scale(-1); - tmpQ.transformVector(tmpV1, tmpV1); - tmpQ.transformVector(tmpV2, tmpV2); + tmpQ1.transformVector(tmpV1, tmpV1); + tmpQ1.transformVector(tmpV2, tmpV2); this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index cbf2c0dae41..374123424c8 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -1,5 +1,6 @@ import { Entity, + Quat, Vec3 } from 'playcanvas' @@ -7,6 +8,8 @@ import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpQ1 = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); @@ -174,6 +177,10 @@ class AxisArrow extends AxisShape { } class GizmoTranslate extends GizmoTransform { + _nodeLocalPositions = new Map(); + + _nodePositions = new Map(); + constructor(app, camera) { super(app, camera); @@ -225,11 +232,11 @@ class GizmoTranslate extends GizmoTransform { this._createTransform(); this.on('transform:start', () => { - this.storeNodePositions(); + this._storeNodePositions(); }); this.on('transform:move', (axis, offset) => { - this.updateNodePositions(offset); + this._setNodePositions(offset); }); } @@ -313,6 +320,41 @@ class GizmoTranslate extends GizmoTransform { } } } + + _storeNodePositions() { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + this._nodeLocalPositions.set(node, node.getLocalPosition().clone()); + this._nodePositions.set(node, node.getPosition().clone()); + } + } + + _setNodePositions(point) { + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + if (this._coordSpace === 'local') { + tmpV1.copy(point); + node.parent.getWorldTransform().getScale(tmpV2); + tmpV2.x = 1 / tmpV2.x; + tmpV2.y = 1 / tmpV2.y; + tmpV2.z = 1 / tmpV2.z; + tmpQ1.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1); + tmpV1.mul(tmpV2); + node.setLocalPosition(this._nodeLocalPositions.get(node).clone().add(tmpV1)); + } else { + node.setPosition(this._nodePositions.get(node).clone().add(point)); + } + } + + this.updateGizmoPosition(); + } + + detach() { + super.detach(); + + this._nodeLocalPositions.clear(); + this._nodePositions.clear(); + } } export { GizmoTranslate }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index fb9cb903362..4dd9498e932 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -5,15 +5,11 @@ import { EventHandler, Layer, Entity, - Quat, Vec3 } from 'playcanvas' -// temp variables +// temporary variables const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpQ1 = new Quat(); -const tmpQ2 = new Quat(); // constants const GIZMO_LAYER_ID = 1e5; @@ -33,18 +29,6 @@ class Gizmo extends EventHandler { nodes = []; - nodeLocalPositions = new Map(); - - nodePositions = new Map(); - - nodeLocalRotations = new Map(); - - nodeRotations = new Map(); - - nodeScale = new Map(); - - nodeOffset = new Map(); - gizmo; constructor(app, camera) { @@ -56,7 +40,7 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this.setGizmoScale(); + this.updateGizmoScale(); this._onPointerMove = (e) => { if (!this.gizmo.enabled) { @@ -84,7 +68,7 @@ class Gizmo extends EventHandler { set coordSpace(value) { this._coordSpace = value ?? 'world'; - this.setGizmoRotation(); + this.updateGizmoRotation(); } get coordSpace() { @@ -93,7 +77,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this.setGizmoScale(); + this.updateGizmoScale(); } get size() { @@ -102,8 +86,8 @@ class Gizmo extends EventHandler { attach(nodes) { this.nodes = nodes; - this.setGizmoPosition(); - this.setGizmoRotation(); + this.updateGizmoPosition(); + this.updateGizmoRotation(); window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerdown', this._onPointerDown); @@ -116,101 +100,13 @@ class Gizmo extends EventHandler { this.gizmo.enabled = false; this.nodes = []; - this.nodeLocalPositions.clear(); - this.nodePositions.clear(); - this.nodeScale.clear(); window.removeEventListener('pointermove', this._onPointerMove); window.removeEventListener('pointerdown', this._onPointerDown); window.removeEventListener('pointerup', this._onPointerUp); } - storeNodePositions() { - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - this.nodeLocalPositions.set(node, node.getLocalPosition().clone()); - this.nodePositions.set(node, node.getPosition().clone()); - } - } - - updateNodePositions(point) { - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - if (this._coordSpace === 'local') { - tmpV1.copy(point); - node.parent.getWorldTransform().getScale(tmpV2); - tmpV2.x = 1 / tmpV2.x; - tmpV2.y = 1 / tmpV2.y; - tmpV2.z = 1 / tmpV2.z; - tmpQ1.copy(node.getLocalRotation()).transformVector(tmpV1, tmpV1); - tmpV1.mul(tmpV2); - node.setLocalPosition(this.nodeLocalPositions.get(node).clone().add(tmpV1)); - } else { - node.setPosition(this.nodePositions.get(node).clone().add(point)); - } - } - - this.setGizmoPosition(); - } - - storeNodeRotations() { - const gizmoPos = this.gizmo.getPosition(); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - this.nodeLocalRotations.set(node, node.getLocalRotation().clone()); - this.nodeRotations.set(node, node.getRotation().clone()); - this.nodeOffset.set(node, node.getPosition().clone().sub(gizmoPos)); - } - } - - updateNodeRotations(axis, angle) { - const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.getPosition(); - const isFacing = axis === 'face'; - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - - if (isFacing) { - tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); - } else { - tmpV1.set(0, 0, 0); - tmpV1[axis] = 1; - } - - tmpQ1.setFromAxisAngle(tmpV1, angle); - - if (!isFacing && this._coordSpace === 'local') { - tmpQ2.copy(this.nodeLocalRotations.get(node)).mul(tmpQ1); - node.setLocalRotation(tmpQ2); - } else { - tmpV1.copy(this.nodeOffset.get(node)); - tmpQ1.transformVector(tmpV1, tmpV1); - tmpQ2.copy(tmpQ1).mul(this.nodeRotations.get(node)); - node.setRotation(tmpQ2); - node.setPosition(tmpV1.add(gizmoPos)); - } - } - - if (this._coordSpace === 'local') { - this.setGizmoRotation(); - } - } - - storeNodeScale() { - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - this.nodeScale.set(node, node.getLocalScale().clone()); - } - } - - updateNodeScale(point) { - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - node.setLocalScale(this.nodeScale.get(node).clone().mul(point)); - } - } - - setGizmoPosition() { + updateGizmoPosition() { tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; @@ -220,7 +116,7 @@ class Gizmo extends EventHandler { this.gizmo.setPosition(tmpV1); } - setGizmoRotation() { + updateGizmoRotation() { if (this._coordSpace === 'local') { tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { @@ -233,7 +129,7 @@ class Gizmo extends EventHandler { } } - setGizmoScale() { + updateGizmoScale() { let scale = 1; if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; From 59741257ed6aac86adef99dd39da346f81d24709 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 15:39:06 +0000 Subject: [PATCH 044/178] gizmo type switching keybinds in example --- examples/src/examples/misc/gizmos.mjs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 2d6880f57f0..769b531319a 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -321,6 +321,21 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } }); + // gizmo type switching keybinds + window.addEventListener('keypress', (e) => { + switch (e.key) { + case '1': + data.set('gizmo.type', 'translate'); + break; + case '2': + data.set('gizmo.type', 'rotate'); + break; + case '3': + data.set('gizmo.type', 'scale'); + break; + } + }); + return app; } From 161fc97acc5ef34ae4e9a3dd4634beb7b1d64279 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 15:53:55 +0000 Subject: [PATCH 045/178] reording detach and faceDisks happens on attach --- extras/gizmo/gizmo-rotate.js | 12 +++++++++--- extras/gizmo/gizmo-scale.js | 4 ++-- extras/gizmo/gizmo-transform.js | 7 +++---- extras/gizmo/gizmo-translate.js | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 8d9cd27cb67..99c05ec6ad2 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -122,7 +122,6 @@ class GizmoRotate extends GizmoTransform { }; this._createTransform(); - this._setFacingDisks(); this.on('transform:start', () => { this._setFacingDisks(); @@ -231,12 +230,19 @@ class GizmoRotate extends GizmoTransform { } } - detach() { - super.detach(); + attach(nodes) { + super.attach(nodes); + + this._setFacingDisks(); + } + detach() { this._nodeLocalRotations.clear(); this._nodeRotations.clear(); this._nodeOffsets.clear(); + + super.detach(); + } } diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 141ac19826d..fcd682626d3 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -370,9 +370,9 @@ class GizmoScale extends GizmoTransform { } detach() { - super.detach(); - this._nodeScales.clear(); + + super.detach(); } } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index cd3984d1f3a..1a7c580f736 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -56,8 +56,6 @@ class AxisShape { } class GizmoTransform extends Gizmo { - _rotation = false; - _materials; _guideLineColor = new Color(1, 1, 1, 0.5); @@ -78,6 +76,8 @@ class GizmoTransform extends Gizmo { _dirtyElement; + _rotation = false; + dragging = false; elements; @@ -135,8 +135,7 @@ class GizmoTransform extends Gizmo { if (this.dragging) { const pointInfo = this._calcPoint(x, y); - this._offset.copy(pointInfo.point); - this._offset.sub(this._pointStart); + this._offset.copy(pointInfo.point).sub(this._pointStart); this.fire('transform:move', this._currAxis, this._offset, pointInfo.angle - this._angleStart); this._hoverAxis = ''; this._hoverIsPlane = false; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 374123424c8..5740b608372 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -350,10 +350,10 @@ class GizmoTranslate extends GizmoTransform { } detach() { - super.detach(); - this._nodeLocalPositions.clear(); this._nodePositions.clear(); + + super.detach(); } } From 2b9f849341c06beac4f7b6a37f0351bb99d42fcf Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 16:24:43 +0000 Subject: [PATCH 046/178] fixed rotation issue when gizmo not at origin --- extras/gizmo/gizmo-rotate.js | 16 ++++++++-------- extras/gizmo/gizmo-transform.js | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 99c05ec6ad2..353fb91c6b9 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -169,14 +169,6 @@ class GizmoRotate extends GizmoTransform { _createTransform() { super._createTransform(); - // guide ring - this._ring = new AxisDisk({ - app: this.app, - layers: [this.layerGizmo.id], - defaultColor: this._materials.semi.white - }); - this._center.addChild(this._ring.entity); - // elements for (const key in this._axisShapes) { const shape = this._axisShapes[key]; @@ -185,6 +177,14 @@ class GizmoRotate extends GizmoTransform { this.elementMap.set(shape.meshInstances[i], shape); } } + + // guide ring + this._ring = new AxisDisk({ + app: this.app, + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.white + }); + this._center.addChild(this._ring.entity); } _storeNodeRotations() { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 1a7c580f736..c29989154fb 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -240,6 +240,11 @@ class GizmoTransform extends Gizmo { const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; const point = rayDir.scale(-pointPlaneDist).add(rayOrigin); + if (isRotation) { + // point needs to be relative to gizmo for angle calculation + point.sub(gizmoPos); + } + if (isAllAxes) { // calculate point distance from gizmo tmpV1.copy(point).sub(gizmoPos).normalize(); From 2840ec506ded39c6cfcd2668c146b6d85c467ca2 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 17:42:28 +0000 Subject: [PATCH 047/178] update facing disk when changing coord space --- examples/src/examples/misc/gizmos.mjs | 5 ++++- extras/gizmo/gizmo-rotate.js | 10 ++++++++++ extras/gizmo/gizmo-transform.js | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 769b531319a..a1777e65315 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -321,9 +321,12 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } }); - // gizmo type switching keybinds + // control keybinds window.addEventListener('keypress', (e) => { switch (e.key) { + case 'x': + data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); + break; case '1': data.set('gizmo.type', 'translate'); break; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 353fb91c6b9..ea5833c14cb 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -244,6 +244,16 @@ class GizmoRotate extends GizmoTransform { super.detach(); } + + set coordSpace(value) { + this._coordSpace = value ?? 'world'; + this.updateGizmoRotation(); + this._setFacingDisks(); + } + + get coordSpace() { + return this._coordSpace; + } } export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index c29989154fb..de5a33396f6 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -18,7 +18,7 @@ const tmpQ1 = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); -const GUIDELINE_SIZE = 1e6; +const GUIDELINE_SIZE = 1e3; class AxisShape { _position; @@ -58,7 +58,7 @@ class AxisShape { class GizmoTransform extends Gizmo { _materials; - _guideLineColor = new Color(1, 1, 1, 0.5); + _guideLineColor = new Color(1, 1, 1, 0.8); _hoverAxis = ''; From b9034827f211d3f433d99998af132d8f4dd636c3 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 4 Jan 2024 17:57:33 +0000 Subject: [PATCH 048/178] added snapping for translate rotate and scale --- extras/gizmo/gizmo-rotate.js | 5 +++++ extras/gizmo/gizmo-scale.js | 7 +++++++ extras/gizmo/gizmo-transform.js | 2 ++ extras/gizmo/gizmo-translate.js | 7 +++++++ extras/gizmo/gizmo.js | 12 ++++++++++++ 5 files changed, 33 insertions(+) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index ea5833c14cb..21c0463d808 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -83,6 +83,8 @@ class GizmoRotate extends GizmoTransform { _nodeOffsets = new Map(); + snapIncrement = 5; + constructor(app, camera) { super(app, camera); @@ -129,6 +131,9 @@ class GizmoRotate extends GizmoTransform { }); this.on('transform:move', (axis, offset, angle) => { + if (this.snap) { + angle = Math.round(angle / this.snapIncrement) * this.snapIncrement; + } this._setFacingDisks(); this._setNodeRotations(axis, angle); }); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index fcd682626d3..77b717f8a67 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -198,6 +198,8 @@ class AxisBoxCenter extends AxisShape { class GizmoScale extends GizmoTransform { _nodeScales = new Map(); + snapIncrement = 1; + constructor(app, camera) { super(app, camera); @@ -262,6 +264,11 @@ class GizmoScale extends GizmoTransform { }); this.on('transform:move', (axis, offset) => { + if (this.snap) { + offset.scale(1 / this.snapIncrement); + offset.round(); + offset.scale(this.snapIncrement); + } this._setNodeScales(offset); }); } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index de5a33396f6..3de7b0ed526 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -84,6 +84,8 @@ class GizmoTransform extends Gizmo { elementMap = new Map(); + snapIncrement = 1; + constructor(app, camera) { super(app, camera); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 5740b608372..9a991064c00 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -181,6 +181,8 @@ class GizmoTranslate extends GizmoTransform { _nodePositions = new Map(); + snapIncrement = 1; + constructor(app, camera) { super(app, camera); @@ -236,6 +238,11 @@ class GizmoTranslate extends GizmoTransform { }); this.on('transform:move', (axis, offset) => { + if (this.snap) { + offset.scale(1 / this.snapIncrement); + offset.round(); + offset.scale(this.snapIncrement); + } this._setNodePositions(offset); }); } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 4dd9498e932..aeb6b73d710 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -31,6 +31,8 @@ class Gizmo extends EventHandler { gizmo; + snap = false; + constructor(app, camera) { super(); @@ -62,6 +64,12 @@ class Gizmo extends EventHandler { } this.fire('pointer:up'); }; + this._onKeyDown = (e) => { + this.snap = e.shiftKey; + }; + this._onKeyUp = (e) => { + this.snap = false; + }; app.on('destroy', () => this.detach()); } @@ -92,6 +100,8 @@ class Gizmo extends EventHandler { window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerdown', this._onPointerDown); window.addEventListener('pointerup', this._onPointerUp); + window.addEventListener('keydown', this._onKeyDown); + window.addEventListener('keyup', this._onKeyUp); this.gizmo.enabled = true; } @@ -104,6 +114,8 @@ class Gizmo extends EventHandler { window.removeEventListener('pointermove', this._onPointerMove); window.removeEventListener('pointerdown', this._onPointerDown); window.removeEventListener('pointerup', this._onPointerUp); + window.removeEventListener('keydown', this._onKeyDown); + window.removeEventListener('keyup', this._onKeyUp); } updateGizmoPosition() { From 7bd66180537dea1ea8a93a826df50fc7b9370c1c Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:22:48 +0000 Subject: [PATCH 049/178] added plane flipping when viewing from underneath; changed coordSpace get override to event fire --- extras/gizmo/gizmo-rotate.js | 31 +++++++----------- extras/gizmo/gizmo-scale.js | 53 ++++++++++++++++++++++++++---- extras/gizmo/gizmo-transform.js | 2 ++ extras/gizmo/gizmo-translate.js | 57 +++++++++++++++++++++++++++++---- extras/gizmo/gizmo.js | 1 + 5 files changed, 113 insertions(+), 31 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 21c0463d808..19bf1e9f7b3 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -89,6 +89,14 @@ class GizmoRotate extends GizmoTransform { super(app, camera); this._axisShapes = { + z: new AxisDisk({ + device: app.graphicsDevice, + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.opaque.blue, + hoverColor: this._materials.opaque.yellow + }), x: new AxisDisk({ device: app.graphicsDevice, axis: 'x', @@ -105,14 +113,6 @@ class GizmoRotate extends GizmoTransform { defaultColor: this._materials.opaque.green, hoverColor: this._materials.opaque.yellow }), - z: new AxisDisk({ - device: app.graphicsDevice, - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow - }), face: new AxisDisk({ app: app, axis: 'face', @@ -137,6 +137,10 @@ class GizmoRotate extends GizmoTransform { this._setFacingDisks(); this._setNodeRotations(axis, angle); }); + + this.on('coordSpace:set', () => { + this._setFacingDisks(); + }); } set tubeRadius(value) { @@ -247,17 +251,6 @@ class GizmoRotate extends GizmoTransform { this._nodeOffsets.clear(); super.detach(); - - } - - set coordSpace(value) { - this._coordSpace = value ?? 'world'; - this.updateGizmoRotation(); - this._setFacingDisks(); - } - - get coordSpace() { - return this._coordSpace; } } diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 77b717f8a67..9f6a3b5a8bd 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -1,5 +1,6 @@ import { Entity, + Quat, Vec3 } from 'playcanvas' @@ -7,6 +8,9 @@ import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); +const tmpQ1 = new Quat(); // constants const VEC3_AXES = Object.keys(tmpV1); @@ -16,9 +20,13 @@ class AxisPlane extends AxisShape { _gap = 0.1; + flip = false; + constructor(options) { super(options); + this._flipAxis = options.flipAxis ?? 'x'; + this._createPlane(options.layers ?? []); } @@ -66,6 +74,18 @@ class AxisPlane extends AxisShape { get gap() { return this._gap; } + + checkForFlip(screenDir) { + tmpV2.set(0, 1, 0); + tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); + const dot = screenDir.dot(tmpV2); + if (dot > 0) { + return; + } + tmpV3.copy(this.entity.getLocalEulerAngles()); + tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; + this.entity.setLocalEulerAngles(tmpV3); + } } class AxisBoxLine extends AxisShape { @@ -212,42 +232,42 @@ class GizmoScale extends GizmoTransform { defaultColor: this._materials.semi.white, hoverColor: this._materials.opaque.yellow }), - x: new AxisBoxLine({ + yz: new AxisPlane({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.opaque.red, hoverColor: this._materials.opaque.yellow }), - y: new AxisBoxLine({ + xz: new AxisPlane({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.opaque.green, hoverColor: this._materials.opaque.yellow }), - z: new AxisBoxLine({ + xy: new AxisPlane({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.opaque.blue, hoverColor: this._materials.opaque.yellow }), - yz: new AxisPlane({ + x: new AxisBoxLine({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.opaque.red, hoverColor: this._materials.opaque.yellow }), - xz: new AxisPlane({ + y: new AxisBoxLine({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.opaque.green, hoverColor: this._materials.opaque.yellow }), - xy: new AxisPlane({ + z: new AxisBoxLine({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), @@ -256,11 +276,21 @@ class GizmoScale extends GizmoTransform { }) }; + this._planes = []; + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + if (!(shape instanceof AxisPlane)) { + continue; + } + this._planes.push(shape); + } + this._createTransform(); this.on('transform:start', (start) => { start.sub(Vec3.ONE); this._storeNodeScales(); + this._checkForPlaneFlip(); }); this.on('transform:move', (axis, offset) => { @@ -270,6 +300,7 @@ class GizmoScale extends GizmoTransform { offset.scale(this.snapIncrement); } this._setNodeScales(offset); + this._checkForPlaneFlip(); }); } @@ -362,6 +393,16 @@ class GizmoScale extends GizmoTransform { } } + _checkForPlaneFlip() { + const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.getPosition(); + tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); + + for (let i = 0; i < this._planes.length; i++) { + this._planes[i].checkForFlip(tmpV1); + } + } + _storeNodeScales() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 3de7b0ed526..d9c6d27422f 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -110,6 +110,8 @@ class GizmoTransform extends Gizmo { if (!this.gizmo.enabled) { return; } + + // draw guidelines const gizmoPos = this.gizmo.getPosition(); tmpQ1.copy(this.gizmo.getRotation()); const checkAxis = this._hoverAxis || this._currAxis; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 9a991064c00..e19888e3251 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -9,6 +9,7 @@ import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); const tmpQ1 = new Quat(); // constants @@ -19,9 +20,13 @@ class AxisPlane extends AxisShape { _gap = 0.1; + flip = false; + constructor(options) { super(options); + this._flipAxis = options.flipAxis ?? 'x'; + this._createPlane(options.layers ?? []); } @@ -69,6 +74,18 @@ class AxisPlane extends AxisShape { get gap() { return this._gap; } + + checkForFlip(screenDir) { + tmpV2.set(0, 1, 0); + tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); + const dot = screenDir.dot(tmpV2); + if (dot > 0) { + return; + } + tmpV3.copy(this.entity.getLocalEulerAngles()); + tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; + this.entity.setLocalEulerAngles(tmpV3); + } } class AxisArrow extends AxisShape { @@ -187,42 +204,45 @@ class GizmoTranslate extends GizmoTransform { super(app, camera); this._axisShapes = { - x: new AxisArrow({ + yz: new AxisPlane({ axis: 'x', + flipAxis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.opaque.red, hoverColor: this._materials.opaque.yellow }), - y: new AxisArrow({ + xz: new AxisPlane({ axis: 'y', + flipAxis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.opaque.green, hoverColor: this._materials.opaque.yellow }), - z: new AxisArrow({ + xy: new AxisPlane({ axis: 'z', + flipAxis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.opaque.blue, hoverColor: this._materials.opaque.yellow }), - yz: new AxisPlane({ + x: new AxisArrow({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.opaque.red, hoverColor: this._materials.opaque.yellow }), - xz: new AxisPlane({ + y: new AxisArrow({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.opaque.green, hoverColor: this._materials.opaque.yellow }), - xy: new AxisPlane({ + z: new AxisArrow({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), @@ -231,10 +251,20 @@ class GizmoTranslate extends GizmoTransform { }) }; + this._planes = []; + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + if (!(shape instanceof AxisPlane)) { + continue; + } + this._planes.push(shape); + } + this._createTransform(); this.on('transform:start', () => { this._storeNodePositions(); + this._checkForPlaneFlip(); }); this.on('transform:move', (axis, offset) => { @@ -244,6 +274,11 @@ class GizmoTranslate extends GizmoTransform { offset.scale(this.snapIncrement); } this._setNodePositions(offset); + this._checkForPlaneFlip(); + }); + + this.on('coordSpace:set', () => { + this._checkForPlaneFlip(); }); } @@ -328,6 +363,16 @@ class GizmoTranslate extends GizmoTransform { } } + _checkForPlaneFlip() { + const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.getPosition(); + tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); + + for (let i = 0; i < this._planes.length; i++) { + this._planes[i].checkForFlip(tmpV1); + } + } + _storeNodePositions() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index aeb6b73d710..c9244f3bf79 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -77,6 +77,7 @@ class Gizmo extends EventHandler { set coordSpace(value) { this._coordSpace = value ?? 'world'; this.updateGizmoRotation(); + this.fire('coordSpace:set', this._coordSpace); } get coordSpace() { From d7dcff93e580a5eba41cabde9869f35e34e0980c Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:24:32 +0000 Subject: [PATCH 050/178] added missing flipAxis for plane flipping --- extras/gizmo/gizmo-scale.js | 5 +++-- extras/gizmo/gizmo-translate.js | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 9f6a3b5a8bd..09a8955a1a5 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -20,8 +20,6 @@ class AxisPlane extends AxisShape { _gap = 0.1; - flip = false; - constructor(options) { super(options); @@ -234,6 +232,7 @@ class GizmoScale extends GizmoTransform { }), yz: new AxisPlane({ axis: 'x', + flipAxis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.opaque.red, @@ -241,6 +240,7 @@ class GizmoScale extends GizmoTransform { }), xz: new AxisPlane({ axis: 'y', + flipAxis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.opaque.green, @@ -248,6 +248,7 @@ class GizmoScale extends GizmoTransform { }), xy: new AxisPlane({ axis: 'z', + flipAxis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.opaque.blue, diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index e19888e3251..f9f4701d896 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -20,8 +20,6 @@ class AxisPlane extends AxisShape { _gap = 0.1; - flip = false; - constructor(options) { super(options); From 5d142da929ab5af6b82cdcb76df42bf3024c40e6 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:26:42 +0000 Subject: [PATCH 051/178] moved plane array initialize and added create transform in ctor --- extras/gizmo/gizmo-scale.js | 5 +++-- extras/gizmo/gizmo-translate.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 09a8955a1a5..fafe912713e 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -277,6 +277,8 @@ class GizmoScale extends GizmoTransform { }) }; + this._createTransform(); + this._planes = []; for (const key in this._axisShapes) { const shape = this._axisShapes[key]; @@ -285,8 +287,7 @@ class GizmoScale extends GizmoTransform { } this._planes.push(shape); } - - this._createTransform(); + this._checkForPlaneFlip(); this.on('transform:start', (start) => { start.sub(Vec3.ONE); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index f9f4701d896..62192032f38 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -249,6 +249,8 @@ class GizmoTranslate extends GizmoTransform { }) }; + this._createTransform(); + this._planes = []; for (const key in this._axisShapes) { const shape = this._axisShapes[key]; @@ -257,8 +259,7 @@ class GizmoTranslate extends GizmoTransform { } this._planes.push(shape); } - - this._createTransform(); + this._checkForPlaneFlip(); this.on('transform:start', () => { this._storeNodePositions(); From 3fc20743f53cf03d11af56a367f90b356a557b0a Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:31:27 +0000 Subject: [PATCH 052/178] fixed axis plane gap getter --- extras/gizmo/gizmo-scale.js | 2 +- extras/gizmo/gizmo-translate.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index fafe912713e..b9b337dde34 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -359,7 +359,7 @@ class GizmoScale extends GizmoTransform { } get axisPlaneGap() { - return this._axisShapes.x.gap; + return this._axisShapes.yz.gap; } set axisCenterSize(value) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 62192032f38..e9964729a7b 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -334,7 +334,7 @@ class GizmoTranslate extends GizmoTransform { } get axisPlaneGap() { - return this._axisShapes.x.gap; + return this._axisShapes.yz.gap; } _setArrowProp(propName, value) { From 468ddf244f1bd9a959de0e1ddcc0a01e2e91a6cd Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:42:03 +0000 Subject: [PATCH 053/178] renamed dirtyElement to hoverElement --- extras/gizmo/gizmo-transform.js | 46 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index d9c6d27422f..deb1b576cc1 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -56,7 +56,22 @@ class AxisShape { } class GizmoTransform extends Gizmo { - _materials; + _materials = { + opaque: { + red: this._createMaterial(new Color(1, 0.3, 0.3)), + green: this._createMaterial(new Color(0.3, 1, 0.3)), + blue: this._createMaterial(new Color(0.3, 0.3, 1)), + yellow: this._createMaterial(new Color(1, 1, 0.3)), + white: this._createMaterial(new Color(1, 1, 1)) + }, + semi: { + red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), + green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), + blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), + yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), + white: this._createMaterial(new Color(1, 1, 1, 0.5)) + } + }; _guideLineColor = new Color(1, 1, 1, 0.8); @@ -74,7 +89,7 @@ class GizmoTransform extends Gizmo { _offset = new Vec3(); - _dirtyElement; + _hoverElement; _rotation = false; @@ -89,23 +104,6 @@ class GizmoTransform extends Gizmo { constructor(app, camera) { super(app, camera); - this._materials = { - opaque: { - red: this._createMaterial(new Color(1, 0.3, 0.3)), - green: this._createMaterial(new Color(0.3, 1, 0.3)), - blue: this._createMaterial(new Color(0.3, 0.3, 1)), - yellow: this._createMaterial(new Color(1, 1, 0.3)), - white: this._createMaterial(new Color(1, 1, 1)) - }, - semi: { - red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), - green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), - blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), - yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), - white: this._createMaterial(new Color(1, 1, 1, 0.5)) - } - }; - this.app.on('update', () => { if (!this.gizmo.enabled) { return; @@ -189,16 +187,16 @@ class GizmoTransform extends Gizmo { this._hoverAxis = this._getAxis(meshInstance); this._hoverIsPlane = this._getIsPlane(meshInstance); const shape = this.elementMap.get(meshInstance); - if (shape === this._dirtyElement) { + if (shape === this._hoverElement) { return; } - if (this._dirtyElement) { - this._dirtyElement.hover(false); - this._dirtyElement = null; + if (this._hoverElement) { + this._hoverElement.hover(false); + this._hoverElement = null; } if (shape) { shape.hover(true); - this._dirtyElement = shape; + this._hoverElement = shape; } } From 4a8b4ce6f5e7bbc52b77d3543c6e4740eb277911 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 14:45:15 +0000 Subject: [PATCH 054/178] added check for plane flip on attach override --- extras/gizmo/gizmo-scale.js | 6 ++++++ extras/gizmo/gizmo-translate.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index b9b337dde34..28c25d61d17 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -419,6 +419,12 @@ class GizmoScale extends GizmoTransform { } } + attach(nodes) { + super.attach(nodes); + + this._checkForPlaneFlip(); + } + detach() { this._nodeScales.clear(); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index e9964729a7b..77868b2f201 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -400,6 +400,12 @@ class GizmoTranslate extends GizmoTransform { this.updateGizmoPosition(); } + attach(nodes) { + super.attach(nodes); + + this._checkForPlaneFlip(); + } + detach() { this._nodeLocalPositions.clear(); this._nodePositions.clear(); From e72c103d0d788b9937e49646084e0fa6066daf37 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 16:06:07 +0000 Subject: [PATCH 055/178] refactored example code --- examples/src/examples/misc/gizmos.mjs | 130 +++++++++++++------------- extras/gizmo/gizmo-rotate.js | 4 +- extras/gizmo/gizmo-scale.js | 4 +- extras/gizmo/gizmo-transform.js | 4 +- extras/gizmo/gizmo-translate.js | 4 +- 5 files changed, 73 insertions(+), 73 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index a1777e65315..f0c3483ed50 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -163,6 +163,50 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { * @returns {Promise} The example application. */ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { + class GizmoHandler { + _type = 'translate'; + + skipSetFire = false; + + constructor(app, camera) { + this.gizmos = { + translate: new pcx.GizmoTranslate(app, camera), + rotate: new pcx.GizmoRotate(app, camera), + scale: new pcx.GizmoScale(app, camera) + }; + } + + get gizmo() { + return this.gizmos[this._type]; + } + + switch(type, nodes) { + this.gizmo.detach(); + this._type = type ?? 'translate'; + const gizmo = this.gizmo; + gizmo.attach(nodes); + this.skipSetFire = true; + data.set('gizmo', { + type: type, + size: gizmo.size, + coordSpace: gizmo.coordSpace, + axisGap: gizmo.axisGap, + axisLineThickness: gizmo.axisLineThickness, + axisLineLength: gizmo.axisLineLength, + axisArrowThickness: gizmo.axisArrowThickness, + axisArrowLength: gizmo.axisArrowLength, + axisBoxSize: gizmo.axisBoxSize, + axisPlaneSize: gizmo.axisPlaneSize, + axisPlaneGap: gizmo.axisPlaneGap, + axisCenterSize: gizmo.axisCenterSize, + tubeRadius: gizmo.tubeRadius, + ringRadius: gizmo.ringRadius + }); + this.skipSetFire = false; + } + } + + const gfxOptions = { deviceTypes: [deviceType], glslangUrl: glslangPath + 'glslang.js', @@ -187,7 +231,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const app = new pc.AppBase(canvas); app.init(createOptions); - app.start(); // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); @@ -200,6 +243,26 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { window.removeEventListener('resize', resize); }); + // control keybinds + window.addEventListener('keypress', (e) => { + switch (e.key) { + case 'x': + data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); + break; + case '1': + data.set('gizmo.type', 'translate'); + break; + case '2': + data.set('gizmo.type', 'rotate'); + break; + case '3': + data.set('gizmo.type', 'scale'); + break; + } + }); + + app.start(); + // create box entities const boxA = new pc.Entity('cubeA'); boxA.addComponent('render', { @@ -236,52 +299,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - class GizmoHandler { - data; - - _type = 'translate'; - - skipSetFire = false; - - constructor(app, camera, data) { - this.data = data; - this.gizmos = { - translate: new pcx.GizmoTranslate(app, camera), - rotate: new pcx.GizmoRotate(app, camera), - scale: new pcx.GizmoScale(app, camera) - }; - } - - get gizmo() { - return this.gizmos[this._type]; - } - - switch(type, nodes) { - this.gizmo.detach(); - this._type = type; - const gizmo = this.gizmo; - gizmo.attach(nodes); - this.skipSetFire = true; - this.data.set('gizmo', { - type: type, - size: gizmo.size, - coordSpace: gizmo.coordSpace, - axisGap: gizmo.axisGap, - axisLineThickness: gizmo.axisLineThickness, - axisLineLength: gizmo.axisLineLength, - axisArrowThickness: gizmo.axisArrowThickness, - axisArrowLength: gizmo.axisArrowLength, - axisBoxSize: gizmo.axisBoxSize, - axisPlaneSize: gizmo.axisPlaneSize, - axisPlaneGap: gizmo.axisPlaneGap, - axisCenterSize: gizmo.axisCenterSize, - tubeRadius: gizmo.tubeRadius, - ringRadius: gizmo.ringRadius - }); - this.skipSetFire = false; - } - } - const gizmoHandler = new GizmoHandler(app, camera, data); + const gizmoHandler = new GizmoHandler(app, camera); gizmoHandler.switch('translate', [boxA, boxB]); data.on('*:set', (/** @type {string} */ path, value) => { @@ -321,24 +339,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } }); - // control keybinds - window.addEventListener('keypress', (e) => { - switch (e.key) { - case 'x': - data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); - break; - case '1': - data.set('gizmo.type', 'translate'); - break; - case '2': - data.set('gizmo.type', 'rotate'); - break; - case '3': - data.set('gizmo.type', 'scale'); - break; - } - }); - return app; } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 19bf1e9f7b3..f8bb823b3ce 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -85,8 +85,8 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(app, camera) { - super(app, camera); + constructor(...args) { + super(...args); this._axisShapes = { z: new AxisDisk({ diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 28c25d61d17..ea2b0562c31 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -218,8 +218,8 @@ class GizmoScale extends GizmoTransform { snapIncrement = 1; - constructor(app, camera) { - super(app, camera); + constructor(...args) { + super(...args); this._coordSpace = 'local'; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index deb1b576cc1..391a79fb18f 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -101,8 +101,8 @@ class GizmoTransform extends Gizmo { snapIncrement = 1; - constructor(app, camera) { - super(app, camera); + constructor(...args) { + super(...args); this.app.on('update', () => { if (!this.gizmo.enabled) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 77868b2f201..a02927033bc 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -198,8 +198,8 @@ class GizmoTranslate extends GizmoTransform { snapIncrement = 1; - constructor(app, camera) { - super(app, camera); + constructor(...args) { + super(...args); this._axisShapes = { yz: new AxisPlane({ From c666a01726925140769a47ae35c1f60b71e8f31a Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 16:39:05 +0000 Subject: [PATCH 056/178] customization for gizmo colors --- examples/src/examples/misc/gizmos.mjs | 23 ++++++++++++- extras/gizmo/gizmo-rotate.js | 18 +++++----- extras/gizmo/gizmo-scale.js | 28 ++++++++-------- extras/gizmo/gizmo-transform.js | 47 ++++++++++++++++++++++----- extras/gizmo/gizmo-translate.js | 24 +++++++------- 5 files changed, 95 insertions(+), 45 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index f0c3483ed50..5f5f637b92f 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -5,7 +5,7 @@ import * as pc from 'playcanvas'; * @returns {JSX.Element} The returned JSX Element. */ function controls({ observer, ReactPCUI, React, jsx, fragment }) { - const { BindingTwoWay, LabelGroup, Panel, SliderInput, SelectInput } = ReactPCUI; + const { BindingTwoWay, LabelGroup, Panel, ColorPicker, SliderInput, SelectInput } = ReactPCUI; const [type, setType] = React.useState('translate'); @@ -41,6 +41,24 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { max: 2.0 }) ), + jsx(LabelGroup, { text: 'Axis X Color' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisXColor' } + }) + ), + jsx(LabelGroup, { text: 'Axis Y Color' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisYColor' } + }) + ), + jsx(LabelGroup, { text: 'Axis Z Color' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisZColor' } + }) + ), (type === 'translate' || type === 'scale') && jsx(LabelGroup, { text: 'Axis Gap' }, jsx(SliderInput, { @@ -189,6 +207,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { data.set('gizmo', { type: type, size: gizmo.size, + axisXColor: gizmo.axisXColor, + axisYColor: gizmo.axisYColor, + axisZColor: gizmo.axisZColor, coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index f8bb823b3ce..199f922aecf 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -85,8 +85,8 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(...args) { - super(...args); + constructor(app, ...args) { + super(app, ...args); this._axisShapes = { z: new AxisDisk({ @@ -94,31 +94,31 @@ class GizmoRotate extends GizmoTransform { axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover }), x: new AxisDisk({ device: app.graphicsDevice, axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.opaque.red, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover }), y: new AxisDisk({ device: app.graphicsDevice, axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.opaque.green, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover }), face: new AxisDisk({ app: app, axis: 'face', layers: [this.layerGizmo.id], defaultColor: this._materials.semi.yellow, - hoverColor: this._materials.opaque.yellow, + hoverColor: this._materials.hover, ringRadius: 0.8 }) }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index ea2b0562c31..b666c065946 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -227,53 +227,53 @@ class GizmoScale extends GizmoTransform { xyz: new AxisBoxCenter({ axis: 'xyz', layers: [this.layerGizmo.id], - defaultColor: this._materials.semi.white, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.center, + hoverColor: this._materials.hover }), yz: new AxisPlane({ axis: 'x', flipAxis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.opaque.red, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover }), xz: new AxisPlane({ axis: 'y', flipAxis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.opaque.green, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover }), xy: new AxisPlane({ axis: 'z', flipAxis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover }), x: new AxisBoxLine({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.opaque.red, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover }), y: new AxisBoxLine({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.opaque.green, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover }), z: new AxisBoxLine({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover }) }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 391a79fb18f..d4bd595add9 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -15,6 +15,7 @@ import { Gizmo } from "./gizmo.js"; const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); +const tmpC1 = new Color(); // constants const VEC3_AXES = Object.keys(tmpV1); @@ -57,17 +58,15 @@ class AxisShape { class GizmoTransform extends Gizmo { _materials = { - opaque: { - red: this._createMaterial(new Color(1, 0.3, 0.3)), - green: this._createMaterial(new Color(0.3, 1, 0.3)), - blue: this._createMaterial(new Color(0.3, 0.3, 1)), - yellow: this._createMaterial(new Color(1, 1, 0.3)), - white: this._createMaterial(new Color(1, 1, 1)) + axis: { + x: this._createMaterial(new Color(1, 0.3, 0.3)), + y: this._createMaterial(new Color(0.3, 1, 0.3)), + z: this._createMaterial(new Color(0.3, 0.3, 1)), + face: this._createMaterial(new Color(1, 1, 0.3, 0.5)) }, + center: this._createMaterial(new Color(1, 1, 1, 0.5)), + hover: this._createMaterial(new Color(1, 1, 0.3)), semi: { - red: this._createMaterial(new Color(1, 0.3, 0.3, 0.5)), - green: this._createMaterial(new Color(0.3, 1, 0.3, 0.5)), - blue: this._createMaterial(new Color(0.3, 0.3, 1, 0.5)), yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), white: this._createMaterial(new Color(1, 1, 1, 0.5)) } @@ -330,6 +329,36 @@ class GizmoTransform extends Gizmo { this._center = new Entity('center'); this.gizmo.addChild(this._center); } + + set axisXColor(value) { + tmpC1.set(...value); + this._materials.axis.x.emissive = tmpC1; + this._materials.axis.x.update(); + } + + get axisXColor() { + return Object.values(this._materials.axis.x.emissive); + } + + set axisYColor(value) { + tmpC1.set(...value); + this._materials.axis.y.emissive = tmpC1; + this._materials.axis.y.update(); + } + + get axisYColor() { + return Object.values(this._materials.axis.y.emissive); + } + + set axisZColor(value) { + tmpC1.set(...value); + this._materials.axis.z.emissive = tmpC1; + this._materials.axis.z.update(); + } + + get axisZColor() { + return Object.values(this._materials.axis.z.emissive); + } } export { AxisShape, GizmoTransform }; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index a02927033bc..d214d09059e 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -207,45 +207,45 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.opaque.red, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover }), xz: new AxisPlane({ axis: 'y', flipAxis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.opaque.green, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover }), xy: new AxisPlane({ axis: 'z', flipAxis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover }), x: new AxisArrow({ axis: 'x', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.opaque.red, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover }), y: new AxisArrow({ axis: 'y', layers: [this.layerGizmo.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.opaque.green, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover }), z: new AxisArrow({ axis: 'z', layers: [this.layerGizmo.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.opaque.blue, - hoverColor: this._materials.opaque.yellow + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover }) }; From 8bc5a88cdd7f545bb3f940ce1a2472f94723d5a5 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 5 Jan 2024 16:51:28 +0000 Subject: [PATCH 057/178] added semi colons to end of import --- extras/gizmo/gizmo-rotate.js | 2 +- extras/gizmo/gizmo-scale.js | 2 +- extras/gizmo/gizmo-transform.js | 2 +- extras/gizmo/gizmo-translate.js | 2 +- extras/gizmo/gizmo.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 199f922aecf..cacc983f2a1 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -4,7 +4,7 @@ import { Entity, Quat, Vec3 -} from 'playcanvas' +} from 'playcanvas'; import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index b666c065946..1fa99dbd56f 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -2,7 +2,7 @@ import { Entity, Quat, Vec3 -} from 'playcanvas' +} from 'playcanvas'; import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index d4bd595add9..276ffaa6127 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -7,7 +7,7 @@ import { Entity, Vec3, Quat -} from 'playcanvas' +} from 'playcanvas'; import { Gizmo } from "./gizmo.js"; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index d214d09059e..534201adf98 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -2,7 +2,7 @@ import { Entity, Quat, Vec3 -} from 'playcanvas' +} from 'playcanvas'; import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c9244f3bf79..d5b503de231 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -6,7 +6,7 @@ import { Layer, Entity, Vec3 -} from 'playcanvas' +} from 'playcanvas'; // temporary variables const tmpV1 = new Vec3(); From 7f0e1a62deb7df2d815b9c79ddf54d7a2dd80af9 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sat, 6 Jan 2024 17:25:59 +0000 Subject: [PATCH 058/178] hoisted temporary vectors outside methods --- extras/gizmo/gizmo.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d5b503de231..aa5cf2d5e2e 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -10,6 +10,18 @@ import { // temporary variables const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); +const tmpV4 = new Vec3(); + +const xstart = new Vec3(); +const xdir = new Vec3(); + +const e1 = new Vec3(); +const e2 = new Vec3(); +const h = new Vec3(); +const s = new Vec3(); +const q = new Vec3(); // constants const GIZMO_LAYER_ID = 1e5; @@ -190,12 +202,6 @@ class Gizmo extends EventHandler { const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); const dir = end.clone().sub(start).normalize(); - const xstart = new Vec3(); - const xdir = new Vec3(); - const v1 = new Vec3(); - const v2 = new Vec3(); - const v3 = new Vec3(); - const selection = []; const renders = this.gizmo.findComponents('render'); for (let i = 0; i < renders.length; i++) { @@ -220,11 +226,11 @@ class Gizmo extends EventHandler { const i2 = idx[k + 1]; const i3 = idx[k + 2]; - v1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); - v2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); - v3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - if (this._rayIntersectsTriangle(xstart, xdir, v1, v2, v3, tmpV1)) { + if (this._rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { selection.push(meshInstance); } } @@ -235,12 +241,6 @@ class Gizmo extends EventHandler { } _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { - const e1 = new Vec3(); - const e2 = new Vec3(); - const h = new Vec3(); - const s = new Vec3(); - const q = new Vec3(); - e1.sub2(v1, v0); e2.sub2(v2, v0); h.cross(dir, e2); From 5287af48c6b0612790137f2cc76419ba583ee37e Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sat, 6 Jan 2024 17:26:46 +0000 Subject: [PATCH 059/178] removed commented lighting code --- extras/gizmo/gizmo-transform.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 276ffaa6127..a8c03de469e 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -311,20 +311,7 @@ class GizmoTransform extends Gizmo { return material; } - // _createLight(angles) { - // const light = new Entity('light'); - // light.addComponent('light', { - // layers: [this.layerGizmo.id] - // }); - // light.setEulerAngles(angles); - // return light; - // } - _createTransform() { - // lighting - // const light = this._createLight(new Vec3(45, 0, -45)); - // this.gizmo.addChild(light); - // center this._center = new Entity('center'); this.gizmo.addChild(this._center); From 4d915855f242cb9af70e67c6d0eb9aa5b38e276a Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sat, 6 Jan 2024 17:36:08 +0000 Subject: [PATCH 060/178] changed getter and setter to use color object instead of array --- examples/src/examples/misc/gizmos.mjs | 13 ++++++++++--- extras/gizmo/gizmo-transform.js | 16 ++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 5f5f637b92f..fa5595cb5d5 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -207,9 +207,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { data.set('gizmo', { type: type, size: gizmo.size, - axisXColor: gizmo.axisXColor, - axisYColor: gizmo.axisYColor, - axisZColor: gizmo.axisZColor, + axisXColor: Object.values(gizmo.axisXColor), + axisYColor: Object.values(gizmo.axisYColor), + axisZColor: Object.values(gizmo.axisZColor), coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, @@ -323,6 +323,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const gizmoHandler = new GizmoHandler(app, camera); gizmoHandler.switch('translate', [boxA, boxB]); + const tmpC = new pc.Color(); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); @@ -352,6 +353,12 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { case 'type': gizmoHandler.switch(value, [boxA, boxB]); break; + case 'axisXColor': + case 'axisYColor': + case 'axisZColor': + tmpC.set(...value); + gizmoHandler.gizmo[pathArray[1]] = tmpC; + break; default: gizmoHandler.gizmo[pathArray[1]] = value; break; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index a8c03de469e..6b11a9fa992 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -15,7 +15,6 @@ import { Gizmo } from "./gizmo.js"; const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); -const tmpC1 = new Color(); // constants const VEC3_AXES = Object.keys(tmpV1); @@ -318,33 +317,30 @@ class GizmoTransform extends Gizmo { } set axisXColor(value) { - tmpC1.set(...value); - this._materials.axis.x.emissive = tmpC1; + this._materials.axis.x.emissive.copy(value); this._materials.axis.x.update(); } get axisXColor() { - return Object.values(this._materials.axis.x.emissive); + return this._materials.axis.x.emissive; } set axisYColor(value) { - tmpC1.set(...value); - this._materials.axis.y.emissive = tmpC1; + this._materials.axis.y.emissive.copy(value); this._materials.axis.y.update(); } get axisYColor() { - return Object.values(this._materials.axis.y.emissive); + return this._materials.axis.y.emissive; } set axisZColor(value) { - tmpC1.set(...value); - this._materials.axis.z.emissive = tmpC1; + this._materials.axis.z.emissive.copy(value); this._materials.axis.z.update(); } get axisZColor() { - return Object.values(this._materials.axis.z.emissive); + return this._materials.axis.z.emissive; } } From 1374f18810700384ffde6dcb84212abda4016c7b Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 14:41:40 +0000 Subject: [PATCH 061/178] replaced overrides with events for attach and detach. --- extras/gizmo/gizmo-rotate.js | 24 ++++++++++-------------- extras/gizmo/gizmo-scale.js | 20 ++++++++------------ extras/gizmo/gizmo-transform.js | 2 ++ extras/gizmo/gizmo-translate.js | 22 +++++++++------------- extras/gizmo/gizmo.js | 5 +++++ 5 files changed, 34 insertions(+), 39 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index cacc983f2a1..9d7a67f3a4c 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -141,6 +141,16 @@ class GizmoRotate extends GizmoTransform { this.on('coordSpace:set', () => { this._setFacingDisks(); }); + + this.on('nodes:attach', () => { + this._setFacingDisks(); + }); + + this.on('nodes:detach', () => { + this._nodeLocalRotations.clear(); + this._nodeRotations.clear(); + this._nodeOffsets.clear(); + }); } set tubeRadius(value) { @@ -238,20 +248,6 @@ class GizmoRotate extends GizmoTransform { this.updateGizmoRotation(); } } - - attach(nodes) { - super.attach(nodes); - - this._setFacingDisks(); - } - - detach() { - this._nodeLocalRotations.clear(); - this._nodeRotations.clear(); - this._nodeOffsets.clear(); - - super.detach(); - } } export { GizmoRotate }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 1fa99dbd56f..5655e4327ad 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -304,6 +304,14 @@ class GizmoScale extends GizmoTransform { this._setNodeScales(offset); this._checkForPlaneFlip(); }); + + this.on('nodes:attach', () => { + this._checkForPlaneFlip(); + }); + + this.on('nodes:detach', () => { + this._nodeScales.clear(); + }); } set coordSpace(value) { @@ -418,18 +426,6 @@ class GizmoScale extends GizmoTransform { node.setLocalScale(this._nodeScales.get(node).clone().mul(point)); } } - - attach(nodes) { - super.attach(nodes); - - this._checkForPlaneFlip(); - } - - detach() { - this._nodeScales.clear(); - - super.detach(); - } } export { GizmoScale }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 6b11a9fa992..6b9f1f65453 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -91,6 +91,8 @@ class GizmoTransform extends Gizmo { _rotation = false; + _center; + dragging = false; elements; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 534201adf98..069a34e4bdb 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -279,6 +279,15 @@ class GizmoTranslate extends GizmoTransform { this.on('coordSpace:set', () => { this._checkForPlaneFlip(); }); + + this.on('nodes:attach', () => { + this._checkForPlaneFlip(); + }); + + this.on('nodes:detach', () => { + this._nodeLocalPositions.clear(); + this._nodePositions.clear(); + }); } set axisGap(value) { @@ -399,19 +408,6 @@ class GizmoTranslate extends GizmoTransform { this.updateGizmoPosition(); } - - attach(nodes) { - super.attach(nodes); - - this._checkForPlaneFlip(); - } - - detach() { - this._nodeLocalPositions.clear(); - this._nodePositions.clear(); - - super.detach(); - } } export { GizmoTranslate }; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index aa5cf2d5e2e..b571a4282f6 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -99,6 +99,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; this.updateGizmoScale(); + this.fire('size:set', this._size); } get size() { @@ -116,12 +117,16 @@ class Gizmo extends EventHandler { window.addEventListener('keydown', this._onKeyDown); window.addEventListener('keyup', this._onKeyUp); + this.fire('nodes:attach'); + this.gizmo.enabled = true; } detach() { this.gizmo.enabled = false; + this.fire('nodes:detach'); + this.nodes = []; window.removeEventListener('pointermove', this._onPointerMove); From 1526b36bd2b816dbb056b9eac8510665462bd3e1 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 14:50:19 +0000 Subject: [PATCH 062/178] renamed elements to shapes and made private --- extras/gizmo/gizmo-rotate.js | 4 ++-- extras/gizmo/gizmo-scale.js | 4 ++-- extras/gizmo/gizmo-transform.js | 20 +++++++++----------- extras/gizmo/gizmo-translate.js | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 9d7a67f3a4c..1157f03294a 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -188,12 +188,12 @@ class GizmoRotate extends GizmoTransform { _createTransform() { super._createTransform(); - // elements + // shapes for (const key in this._axisShapes) { const shape = this._axisShapes[key]; this._center.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this.elementMap.set(shape.meshInstances[i], shape); + this._shapeMap.set(shape.meshInstances[i], shape); } } diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 5655e4327ad..111fcdf0ea5 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -393,12 +393,12 @@ class GizmoScale extends GizmoTransform { _createTransform() { super._createTransform(); - // elements + // shapes for (const key in this._axisShapes) { const shape = this._axisShapes[key]; this._center.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this.elementMap.set(shape.meshInstances[i], shape); + this._shapeMap.set(shape.meshInstances[i], shape); } } } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 6b9f1f65453..3f8e68308e7 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -87,7 +87,9 @@ class GizmoTransform extends Gizmo { _offset = new Vec3(); - _hoverElement; + _shapeMap = new Map(); + + _hoverShape; _rotation = false; @@ -95,10 +97,6 @@ class GizmoTransform extends Gizmo { dragging = false; - elements; - - elementMap = new Map(); - snapIncrement = 1; constructor(...args) { @@ -186,17 +184,17 @@ class GizmoTransform extends Gizmo { } this._hoverAxis = this._getAxis(meshInstance); this._hoverIsPlane = this._getIsPlane(meshInstance); - const shape = this.elementMap.get(meshInstance); - if (shape === this._hoverElement) { + const shape = this._shapeMap.get(meshInstance); + if (shape === this._hoverShape) { return; } - if (this._hoverElement) { - this._hoverElement.hover(false); - this._hoverElement = null; + if (this._hoverShape) { + this._hoverShape.hover(false); + this._hoverShape = null; } if (shape) { shape.hover(true); - this._hoverElement = shape; + this._hoverShape = shape; } } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 069a34e4bdb..3364f9383d6 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -361,12 +361,12 @@ class GizmoTranslate extends GizmoTransform { _createTransform() { super._createTransform(); - // elements + // shapes for (const key in this._axisShapes) { const shape = this._axisShapes[key]; this._center.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this.elementMap.set(shape.meshInstances[i], shape); + this._shapeMap.set(shape.meshInstances[i], shape); } } } From ce35e10e1f0a7aece2aa9d31e4417451b86a3401 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:04:46 +0000 Subject: [PATCH 063/178] moved predefined attributes to class body --- extras/gizmo/gizmo-rotate.js | 76 +++++++++++----------- extras/gizmo/gizmo-scale.js | 112 ++++++++++++++++---------------- extras/gizmo/gizmo-transform.js | 13 ++-- extras/gizmo/gizmo-translate.js | 96 +++++++++++++-------------- 4 files changed, 149 insertions(+), 148 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 1157f03294a..1116f61af85 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -73,7 +73,42 @@ class AxisDisk extends AxisShape { } class GizmoRotate extends GizmoTransform { - _rotation = true; + _axisShapes = { + z: new AxisDisk({ + device: this.app.graphicsDevice, + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover + }), + x: new AxisDisk({ + device: this.app.graphicsDevice, + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover + }), + y: new AxisDisk({ + device: this.app.graphicsDevice, + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover + }), + face: new AxisDisk({ + device: this.app.graphicsDevice, + axis: 'face', + layers: [this.layerGizmo.id], + defaultColor: this._materials.semi.yellow, + hoverColor: this._materials.hover, + ringRadius: 0.8 + }) + }; + + _isRotation = true; _ring; @@ -85,43 +120,8 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(app, ...args) { - super(app, ...args); - - this._axisShapes = { - z: new AxisDisk({ - device: app.graphicsDevice, - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, - hoverColor: this._materials.hover - }), - x: new AxisDisk({ - device: app.graphicsDevice, - axis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, - hoverColor: this._materials.hover - }), - y: new AxisDisk({ - device: app.graphicsDevice, - axis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, - hoverColor: this._materials.hover - }), - face: new AxisDisk({ - app: app, - axis: 'face', - layers: [this.layerGizmo.id], - defaultColor: this._materials.semi.yellow, - hoverColor: this._materials.hover, - ringRadius: 0.8 - }) - }; + constructor(...args) { + super(...args); this._createTransform(); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 111fcdf0ea5..fbf685b19f4 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -214,6 +214,62 @@ class AxisBoxCenter extends AxisShape { } class GizmoScale extends GizmoTransform { + _axisShapes = { + xyz: new AxisBoxCenter({ + axis: 'xyz', + layers: [this.layerGizmo.id], + defaultColor: this._materials.center, + hoverColor: this._materials.hover + }), + yz: new AxisPlane({ + axis: 'x', + flipAxis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover + }), + xz: new AxisPlane({ + axis: 'y', + flipAxis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover + }), + xy: new AxisPlane({ + axis: 'z', + flipAxis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover + }), + x: new AxisBoxLine({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover + }), + y: new AxisBoxLine({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover + }), + z: new AxisBoxLine({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover + }) + }; + + _coordSpace = 'local'; + _nodeScales = new Map(); snapIncrement = 1; @@ -221,62 +277,6 @@ class GizmoScale extends GizmoTransform { constructor(...args) { super(...args); - this._coordSpace = 'local'; - - this._axisShapes = { - xyz: new AxisBoxCenter({ - axis: 'xyz', - layers: [this.layerGizmo.id], - defaultColor: this._materials.center, - hoverColor: this._materials.hover - }), - yz: new AxisPlane({ - axis: 'x', - flipAxis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, - hoverColor: this._materials.hover - }), - xz: new AxisPlane({ - axis: 'y', - flipAxis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, - hoverColor: this._materials.hover - }), - xy: new AxisPlane({ - axis: 'z', - flipAxis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, - hoverColor: this._materials.hover - }), - x: new AxisBoxLine({ - axis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, - hoverColor: this._materials.hover - }), - y: new AxisBoxLine({ - axis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, - hoverColor: this._materials.hover - }), - z: new AxisBoxLine({ - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, - hoverColor: this._materials.hover - }) - }; - this._createTransform(); this._planes = []; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 3f8e68308e7..608f5cf85d3 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -73,6 +73,10 @@ class GizmoTransform extends Gizmo { _guideLineColor = new Color(1, 1, 1, 0.8); + _shapeMap = new Map(); + + _hoverShape; + _hoverAxis = ''; _hoverIsPlane = false; @@ -87,11 +91,7 @@ class GizmoTransform extends Gizmo { _offset = new Vec3(); - _shapeMap = new Map(); - - _hoverShape; - - _rotation = false; + _isRotation = false; _center; @@ -206,7 +206,7 @@ class GizmoTransform extends Gizmo { const planeNormal = new Vec3(); const axis = this._currAxis; const isPlane = this._currIsPlane; - const isRotation = this._rotation; + const isRotation = this._isRotation; const isAllAxes = axis === 'xyz'; const isFacing = axis === 'face'; @@ -272,6 +272,7 @@ class GizmoTransform extends Gizmo { } } + // calculate angle based on axis let angle = 0; if (isRotation) { switch (axis) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 3364f9383d6..f35c3069889 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -192,6 +192,54 @@ class AxisArrow extends AxisShape { } class GizmoTranslate extends GizmoTransform { + _axisShapes = { + yz: new AxisPlane({ + axis: 'x', + flipAxis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover + }), + xz: new AxisPlane({ + axis: 'y', + flipAxis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover + }), + xy: new AxisPlane({ + axis: 'z', + flipAxis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover + }), + x: new AxisArrow({ + axis: 'x', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, -90), + defaultColor: this._materials.axis.x, + hoverColor: this._materials.hover + }), + y: new AxisArrow({ + axis: 'y', + layers: [this.layerGizmo.id], + rotation: new Vec3(0, 0, 0), + defaultColor: this._materials.axis.y, + hoverColor: this._materials.hover + }), + z: new AxisArrow({ + axis: 'z', + layers: [this.layerGizmo.id], + rotation: new Vec3(90, 0, 0), + defaultColor: this._materials.axis.z, + hoverColor: this._materials.hover + }) + }; + _nodeLocalPositions = new Map(); _nodePositions = new Map(); @@ -201,54 +249,6 @@ class GizmoTranslate extends GizmoTransform { constructor(...args) { super(...args); - this._axisShapes = { - yz: new AxisPlane({ - axis: 'x', - flipAxis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, - hoverColor: this._materials.hover - }), - xz: new AxisPlane({ - axis: 'y', - flipAxis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, - hoverColor: this._materials.hover - }), - xy: new AxisPlane({ - axis: 'z', - flipAxis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, - hoverColor: this._materials.hover - }), - x: new AxisArrow({ - axis: 'x', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, - hoverColor: this._materials.hover - }), - y: new AxisArrow({ - axis: 'y', - layers: [this.layerGizmo.id], - rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, - hoverColor: this._materials.hover - }), - z: new AxisArrow({ - axis: 'z', - layers: [this.layerGizmo.id], - rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, - hoverColor: this._materials.hover - }) - }; - this._createTransform(); this._planes = []; From 43848ed678a33022b9c01a4f7a85d8b4e6bf5027 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:19:31 +0000 Subject: [PATCH 064/178] moved all axis shapes to separate file --- extras/gizmo/axis-shapes.js | 416 ++++++++++++++++++++++++++++++++ extras/gizmo/gizmo-rotate.js | 71 +----- extras/gizmo/gizmo-scale.js | 209 +--------------- extras/gizmo/gizmo-transform.js | 37 +-- extras/gizmo/gizmo-translate.js | 184 +------------- 5 files changed, 426 insertions(+), 491 deletions(-) create mode 100644 extras/gizmo/axis-shapes.js diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js new file mode 100644 index 00000000000..e142779a5c4 --- /dev/null +++ b/extras/gizmo/axis-shapes.js @@ -0,0 +1,416 @@ +import { + createTorus, + Color, + MeshInstance, + Entity, + Vec3, + Quat +} from 'playcanvas'; + +// temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); +const tmpQ1 = new Quat(); + +// constants +const VEC3_AXES = Object.keys(tmpV1); + +class AxisShape { + _position; + + _rotation; + + _scale; + + _defaultColor; + + _hoverColor; + + axis; + + entity; + + meshInstances = []; + + constructor(options) { + this.axis = options.axis ?? 'x'; + this._position = options.position ?? new Vec3(); + this._rotation = options.rotation ?? new Vec3(); + this._scale = options.scale ?? new Vec3(1, 1, 1); + + this._defaultColor = options.defaultColor ?? Color.BLACK; + this._hoverColor = options.hoverColor ?? Color.WHITE; + } + + hover(state) { + const material = state ? this._hoverColor : this._defaultColor; + for (let i = 0; i < this.meshInstances.length; i++) { + this.meshInstances[i].material = material; + } + } +} + +class AxisArrow extends AxisShape { + _gap = 0; + + _lineThickness = 0.04; + + _lineLength = 0.5; + + _arrowThickness = 0.15; + + _arrowLength = 0.2; + + constructor(options = {}) { + super(options); + + this._createArrow(options.layers ?? []); + } + + set gap(value) { + this._gap = value ?? 0; + this._updateLine(); + this._updateArrow(); + } + + get gap() { + return this._gap; + } + + set lineThickness(value) { + this._lineThickness = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineThickness() { + return this._lineThickness; + } + + set lineLength(value) { + this._lineLength = value ?? 1; + this._updateLine(); + this._updateArrow(); + } + + get lineLength() { + return this._lineLength; + } + + set arrowThickness(value) { + this._arrowThickness = value ?? 1; + this._updateArrow(); + } + + get arrowThickness() { + return this._arrowThickness; + } + + set arrowLength(value) { + this._arrowLength = value ?? 1; + this._updateArrow(); + } + + get arrowLength() { + return this._arrowLength; + } + + _createArrow(layers) { + this.entity = new Entity('axis_' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._line = new Entity('line_' + this.axis); + this._line.addComponent('render', { + type: 'cylinder', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateLine(); + this.entity.addChild(this._line); + this.meshInstances.push(...this._line.render.meshInstances); + + this._arrow = new Entity('arrow_' + this.axis); + this._arrow.addComponent('render', { + type: 'cone', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateArrow(); + this.entity.addChild(this._arrow); + this.meshInstances.push(...this._arrow.render.meshInstances); + } + + _updateLine() { + this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); + this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + } + + _updateArrow() { + this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); + this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); + } +} + +class AxisBoxCenter extends AxisShape { + _size = 0.14; + + constructor(options = {}) { + super(options); + + this._createCenter(options.layers ?? []); + } + + _createCenter(layers) { + this.entity = new Entity('center_' + this.axis); + this.entity.addComponent('render', { + type: 'box', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._size, this._size, this._size); + this.meshInstances.push(...this.entity.render.meshInstances); + } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } +} + +class AxisBoxLine extends AxisShape { + _gap = 0; + + _lineThickness = 0.04; + + _lineLength = 0.5; + + _boxSize = 0.14; + + constructor(options = {}) { + super(options); + + this._createBoxLine(options.layers ?? []); + } + + set gap(value) { + this._gap = value ?? 0; + this._updateLine(); + this._updateBox(); + } + + get gap() { + return this._gap; + } + + set lineThickness(value) { + this._lineThickness = value ?? 1; + this._updateLine(); + this._updateBox(); + } + + get lineThickness() { + return this._lineThickness; + } + + set lineLength(value) { + this._lineLength = value ?? 1; + this._updateLine(); + this._updateBox(); + } + + get lineLength() { + return this._lineLength; + } + + set boxSize(value) { + this._boxSize = value ?? 1; + this._updateBox(); + } + + get boxSize() { + return this._boxSize; + } + + _createBoxLine(layers) { + this.entity = new Entity('axis_' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._line = new Entity('line_' + this.axis); + this._line.addComponent('render', { + type: 'cylinder', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateLine(); + this.entity.addChild(this._line); + this.meshInstances.push(...this._line.render.meshInstances); + + this._box = new Entity('box_' + this.axis); + this._box.addComponent('render', { + type: 'box', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this._updateBox(); + this.entity.addChild(this._box); + this.meshInstances.push(...this._box.render.meshInstances); + } + + _updateLine() { + this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); + this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + } + + _updateBox() { + this._box.setLocalPosition(new Vec3(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0)); + this._box.setLocalScale(new Vec3(this._boxSize, this._boxSize, this._boxSize)); + } +} + +class AxisDisk extends AxisShape { + _device; + + _tubeRadius = 0.02; + + _ringRadius = 0.55; + + constructor(options = {}) { + super(options); + + this._device = options.device; + this._ringRadius = options.ringRadius ?? this._ringRadius; + this._createDisk(options.layers ?? []); + } + + _createDisk(layers) { + const mesh = createTorus(this._device, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + + this.entity = new Entity('disk_' + this.axis); + this.entity.addComponent('render', { + meshInstances: [meshInstance], + layers: layers, + castShadows: false + }); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + this.meshInstances.push(meshInstance); + } + + set tubeRadius(value) { + this._tubeRadius = value ?? 0.1; + this.meshInstances[0].mesh = createTorus(this._device, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + } + + get tubeRadius() { + return this._tubeRadius; + } + + set ringRadius(value) { + this._ringRadius = value ?? 0.1; + this.meshInstances[0].mesh = createTorus(this._device, { + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius + }); + } + + get ringRadius() { + return this._ringRadius; + } +} + +class AxisPlane extends AxisShape { + _size = 0.2; + + _gap = 0.1; + + constructor(options) { + super(options); + + this._flipAxis = options.flipAxis ?? 'x'; + + this._createPlane(options.layers ?? []); + } + + _getPosition() { + const position = new Vec3(); + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; + if (axis === this.axis) { + continue; + } + position[axis] = this._size / 2 + this._gap; + } + return position; + } + + _createPlane(layers) { + this.entity = new Entity('plane_' + this.axis); + this.entity.addComponent('render', { + type: 'plane', + layers: layers, + material: this._defaultColor, + castShadows: false + }); + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._size, this._size, this._size); + this.meshInstances.push(...this.entity.render.meshInstances); + } + + set size(value) { + this._size = value ?? 1; + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalScale(this._size, this._size, this._size); + } + + get size() { + return this._size; + } + + set gap(value) { + this._gap = value ?? 0; + this.entity.setLocalPosition(this._getPosition()); + } + + get gap() { + return this._gap; + } + + checkForFlip(screenDir) { + tmpV2.set(0, 1, 0); + tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); + const dot = screenDir.dot(tmpV2); + if (dot > 0) { + return; + } + tmpV3.copy(this.entity.getLocalEulerAngles()); + tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; + this.entity.setLocalEulerAngles(tmpV3); + } +} + +export { AxisShape, AxisArrow, AxisBoxCenter, AxisBoxLine, AxisDisk, AxisPlane }; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 1116f61af85..c69bd94a16f 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,77 +1,16 @@ import { - createTorus, - MeshInstance, - Entity, Quat, Vec3 } from 'playcanvas'; -import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; +import { AxisDisk } from './axis-shapes.js'; +import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); const tmpQ1 = new Quat(); const tmpQ2 = new Quat(); -class AxisDisk extends AxisShape { - _device; - - _tubeRadius = 0.02; - - _ringRadius = 0.55; - - constructor(options = {}) { - super(options); - - this._device = options.device; - this._ringRadius = options.ringRadius ?? this._ringRadius; - this._createDisk(options.layers ?? []); - } - - _createDisk(layers) { - const mesh = createTorus(this._device, { - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius - }); - const meshInstance = new MeshInstance(mesh, this._defaultColor); - - this.entity = new Entity('disk_' + this.axis); - this.entity.addComponent('render', { - meshInstances: [meshInstance], - layers: layers, - castShadows: false - }); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - this.meshInstances.push(meshInstance); - } - - set tubeRadius(value) { - this._tubeRadius = value ?? 0.1; - this.meshInstances[0].mesh = createTorus(this._device, { - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius - }); - } - - get tubeRadius() { - return this._tubeRadius; - } - - set ringRadius(value) { - this._ringRadius = value ?? 0.1; - this.meshInstances[0].mesh = createTorus(this._device, { - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius - }); - } - - get ringRadius() { - return this._ringRadius; - } -} - class GizmoRotate extends GizmoTransform { _axisShapes = { z: new AxisDisk({ @@ -120,8 +59,8 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(...args) { - super(...args); + constructor(app, ...args) { + super(app, ...args); this._createTransform(); @@ -199,7 +138,7 @@ class GizmoRotate extends GizmoTransform { // guide ring this._ring = new AxisDisk({ - app: this.app, + device: this.app.graphicsDevice, layers: [this.layerGizmo.id], defaultColor: this._materials.semi.white }); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index fbf685b19f4..60300ca12ec 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -1,217 +1,12 @@ import { - Entity, - Quat, Vec3 } from 'playcanvas'; -import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; +import { AxisBoxCenter, AxisBoxLine, AxisPlane } from './axis-shapes.js'; +import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpV3 = new Vec3(); -const tmpQ1 = new Quat(); - -// constants -const VEC3_AXES = Object.keys(tmpV1); - -class AxisPlane extends AxisShape { - _size = 0.2; - - _gap = 0.1; - - constructor(options) { - super(options); - - this._flipAxis = options.flipAxis ?? 'x'; - - this._createPlane(options.layers ?? []); - } - - _getPosition() { - const position = new Vec3(); - for (let i = 0; i < VEC3_AXES.length; i++) { - const axis = VEC3_AXES[i]; - if (axis === this.axis) { - continue; - } - position[axis] = this._size / 2 + this._gap; - } - return position; - } - - _createPlane(layers) { - this.entity = new Entity('plane_' + this.axis); - this.entity.addComponent('render', { - type: 'plane', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); - } - - set size(value) { - this._size = value ?? 1; - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalScale(this._size, this._size, this._size); - } - - get size() { - return this._size; - } - - set gap(value) { - this._gap = value ?? 0; - this.entity.setLocalPosition(this._getPosition()); - } - - get gap() { - return this._gap; - } - - checkForFlip(screenDir) { - tmpV2.set(0, 1, 0); - tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); - const dot = screenDir.dot(tmpV2); - if (dot > 0) { - return; - } - tmpV3.copy(this.entity.getLocalEulerAngles()); - tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; - this.entity.setLocalEulerAngles(tmpV3); - } -} - -class AxisBoxLine extends AxisShape { - _gap = 0; - - _lineThickness = 0.04; - - _lineLength = 0.5; - - _boxSize = 0.14; - - constructor(options = {}) { - super(options); - - this._createBoxLine(options.layers ?? []); - } - - set gap(value) { - this._gap = value ?? 0; - this._updateLine(); - this._updateBox(); - } - - get gap() { - return this._gap; - } - - set lineThickness(value) { - this._lineThickness = value ?? 1; - this._updateLine(); - this._updateBox(); - } - - get lineThickness() { - return this._lineThickness; - } - - set lineLength(value) { - this._lineLength = value ?? 1; - this._updateLine(); - this._updateBox(); - } - - get lineLength() { - return this._lineLength; - } - - set boxSize(value) { - this._boxSize = value ?? 1; - this._updateBox(); - } - - get boxSize() { - return this._boxSize; - } - - _createBoxLine(layers) { - this.entity = new Entity('axis_' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - - this._line = new Entity('line_' + this.axis); - this._line.addComponent('render', { - type: 'cylinder', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateLine(); - this.entity.addChild(this._line); - this.meshInstances.push(...this._line.render.meshInstances); - - this._box = new Entity('box_' + this.axis); - this._box.addComponent('render', { - type: 'box', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateBox(); - this.entity.addChild(this._box); - this.meshInstances.push(...this._box.render.meshInstances); - } - - _updateLine() { - this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); - this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); - } - - _updateBox() { - this._box.setLocalPosition(new Vec3(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0)); - this._box.setLocalScale(new Vec3(this._boxSize, this._boxSize, this._boxSize)); - } -} - -class AxisBoxCenter extends AxisShape { - _size = 0.14; - - constructor(options = {}) { - super(options); - - this._createCenter(options.layers ?? []); - } - - _createCenter(layers) { - this.entity = new Entity('center_' + this.axis); - this.entity.addComponent('render', { - type: 'box', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); - } - - set size(value) { - this._size = value ?? 1; - this.entity.setLocalScale(this._size, this._size, this._size); - } - - get size() { - return this._size; - } -} class GizmoScale extends GizmoTransform { _axisShapes = { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 608f5cf85d3..bf84e848563 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -20,41 +20,6 @@ const tmpQ1 = new Quat(); const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; -class AxisShape { - _position; - - _rotation; - - _scale; - - _defaultColor; - - _hoverColor; - - axis; - - entity; - - meshInstances = []; - - constructor(options) { - this.axis = options.axis ?? 'x'; - this._position = options.position ?? new Vec3(); - this._rotation = options.rotation ?? new Vec3(); - this._scale = options.scale ?? new Vec3(1, 1, 1); - - this._defaultColor = options.defaultColor ?? Color.BLACK; - this._hoverColor = options.hoverColor ?? Color.WHITE; - } - - hover(state) { - const material = state ? this._hoverColor : this._defaultColor; - for (let i = 0; i < this.meshInstances.length; i++) { - this.meshInstances[i].material = material; - } - } -} - class GizmoTransform extends Gizmo { _materials = { axis: { @@ -345,4 +310,4 @@ class GizmoTransform extends Gizmo { } } -export { AxisShape, GizmoTransform }; +export { GizmoTransform }; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index f35c3069889..18b250a7677 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -1,196 +1,16 @@ import { - Entity, Quat, Vec3 } from 'playcanvas'; -import { AxisShape, GizmoTransform } from "./gizmo-transform.js"; +import { AxisArrow, AxisPlane } from './axis-shapes.js'; +import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); -const tmpV3 = new Vec3(); const tmpQ1 = new Quat(); -// constants -const VEC3_AXES = Object.keys(tmpV1); - -class AxisPlane extends AxisShape { - _size = 0.2; - - _gap = 0.1; - - constructor(options) { - super(options); - - this._flipAxis = options.flipAxis ?? 'x'; - - this._createPlane(options.layers ?? []); - } - - _getPosition() { - const position = new Vec3(); - for (let i = 0; i < VEC3_AXES.length; i++) { - const axis = VEC3_AXES[i]; - if (axis === this.axis) { - continue; - } - position[axis] = this._size / 2 + this._gap; - } - return position; - } - - _createPlane(layers) { - this.entity = new Entity('plane_' + this.axis); - this.entity.addComponent('render', { - type: 'plane', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); - } - - set size(value) { - this._size = value ?? 1; - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalScale(this._size, this._size, this._size); - } - - get size() { - return this._size; - } - - set gap(value) { - this._gap = value ?? 0; - this.entity.setLocalPosition(this._getPosition()); - } - - get gap() { - return this._gap; - } - - checkForFlip(screenDir) { - tmpV2.set(0, 1, 0); - tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); - const dot = screenDir.dot(tmpV2); - if (dot > 0) { - return; - } - tmpV3.copy(this.entity.getLocalEulerAngles()); - tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; - this.entity.setLocalEulerAngles(tmpV3); - } -} - -class AxisArrow extends AxisShape { - _gap = 0; - - _lineThickness = 0.04; - - _lineLength = 0.5; - - _arrowThickness = 0.15; - - _arrowLength = 0.2; - - constructor(options = {}) { - super(options); - - this._createArrow(options.layers ?? []); - } - - set gap(value) { - this._gap = value ?? 0; - this._updateLine(); - this._updateArrow(); - } - - get gap() { - return this._gap; - } - - set lineThickness(value) { - this._lineThickness = value ?? 1; - this._updateLine(); - this._updateArrow(); - } - - get lineThickness() { - return this._lineThickness; - } - - set lineLength(value) { - this._lineLength = value ?? 1; - this._updateLine(); - this._updateArrow(); - } - - get lineLength() { - return this._lineLength; - } - - set arrowThickness(value) { - this._arrowThickness = value ?? 1; - this._updateArrow(); - } - - get arrowThickness() { - return this._arrowThickness; - } - - set arrowLength(value) { - this._arrowLength = value ?? 1; - this._updateArrow(); - } - - get arrowLength() { - return this._arrowLength; - } - - _createArrow(layers) { - this.entity = new Entity('axis_' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - - this._line = new Entity('line_' + this.axis); - this._line.addComponent('render', { - type: 'cylinder', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateLine(); - this.entity.addChild(this._line); - this.meshInstances.push(...this._line.render.meshInstances); - - this._arrow = new Entity('arrow_' + this.axis); - this._arrow.addComponent('render', { - type: 'cone', - layers: layers, - material: this._defaultColor, - castShadows: false - }); - this._updateArrow(); - this.entity.addChild(this._arrow); - this.meshInstances.push(...this._arrow.render.meshInstances); - } - - _updateLine() { - this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); - this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); - } - - _updateArrow() { - this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); - this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); - } -} - class GizmoTranslate extends GizmoTransform { _axisShapes = { yz: new AxisPlane({ From eb5cb0953bb493b3d07718a7cab498d23bd7c74f Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:22:39 +0000 Subject: [PATCH 065/178] removed VEC3_AXIS --- extras/gizmo/axis-shapes.js | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index e142779a5c4..9cadafaaabb 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -10,12 +10,8 @@ import { // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); -const tmpV3 = new Vec3(); const tmpQ1 = new Quat(); -// constants -const VEC3_AXES = Object.keys(tmpV1); - class AxisShape { _position; @@ -356,14 +352,9 @@ class AxisPlane extends AxisShape { } _getPosition() { - const position = new Vec3(); - for (let i = 0; i < VEC3_AXES.length; i++) { - const axis = VEC3_AXES[i]; - if (axis === this.axis) { - continue; - } - position[axis] = this._size / 2 + this._gap; - } + const offset = this._size / 2 + this._gap; + const position = new Vec3(offset, offset, offset); + position[this.axis] = 0; return position; } @@ -401,15 +392,15 @@ class AxisPlane extends AxisShape { } checkForFlip(screenDir) { - tmpV2.set(0, 1, 0); - tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV2, tmpV2); - const dot = screenDir.dot(tmpV2); + tmpV1.set(0, 1, 0); + tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV1, tmpV1); + const dot = screenDir.dot(tmpV1); if (dot > 0) { return; } - tmpV3.copy(this.entity.getLocalEulerAngles()); - tmpV3[this._flipAxis] = 180 - tmpV3[this._flipAxis]; - this.entity.setLocalEulerAngles(tmpV3); + tmpV2.copy(this.entity.getLocalEulerAngles()); + tmpV2[this._flipAxis] = 180 - tmpV2[this._flipAxis]; + this.entity.setLocalEulerAngles(tmpV2); } } From cfdb59c1a6755871dabc4c779b68019853a4e30b Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:23:20 +0000 Subject: [PATCH 066/178] reordered methods in gizmo base class --- extras/gizmo/gizmo.js | 128 +++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index b571a4282f6..ca98cec9dc6 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -106,70 +106,6 @@ class Gizmo extends EventHandler { return this._size; } - attach(nodes) { - this.nodes = nodes; - this.updateGizmoPosition(); - this.updateGizmoRotation(); - - window.addEventListener('pointermove', this._onPointerMove); - window.addEventListener('pointerdown', this._onPointerDown); - window.addEventListener('pointerup', this._onPointerUp); - window.addEventListener('keydown', this._onKeyDown); - window.addEventListener('keyup', this._onKeyUp); - - this.fire('nodes:attach'); - - this.gizmo.enabled = true; - } - - detach() { - this.gizmo.enabled = false; - - this.fire('nodes:detach'); - - this.nodes = []; - - window.removeEventListener('pointermove', this._onPointerMove); - window.removeEventListener('pointerdown', this._onPointerDown); - window.removeEventListener('pointerup', this._onPointerUp); - window.removeEventListener('keydown', this._onKeyDown); - window.removeEventListener('keyup', this._onKeyUp); - } - - updateGizmoPosition() { - tmpV1.set(0, 0, 0); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV1.add(node.getPosition()); - } - tmpV1.scale(1.0 / this.nodes.length); - this.gizmo.setPosition(tmpV1); - } - - updateGizmoRotation() { - if (this._coordSpace === 'local') { - tmpV1.set(0, 0, 0); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV1.add(node.getEulerAngles()); - } - this.gizmo.setEulerAngles(tmpV1.scale(1.0 / this.nodes.length)); - } else { - this.gizmo.setEulerAngles(0, 0, 0); - } - } - - updateGizmoScale() { - let scale = 1; - if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { - scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; - } else { - scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; - } - scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); - this.gizmo.setLocalScale(scale, scale, scale); - } - _getProjFrustumWidth() { const gizmoPos = this.gizmo.getPosition(); const cameraPos = this.camera.getPosition(); @@ -275,6 +211,70 @@ class Gizmo extends EventHandler { return false; } + + attach(nodes) { + this.nodes = nodes; + this.updateGizmoPosition(); + this.updateGizmoRotation(); + + window.addEventListener('pointermove', this._onPointerMove); + window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointerup', this._onPointerUp); + window.addEventListener('keydown', this._onKeyDown); + window.addEventListener('keyup', this._onKeyUp); + + this.fire('nodes:attach'); + + this.gizmo.enabled = true; + } + + detach() { + this.gizmo.enabled = false; + + this.fire('nodes:detach'); + + this.nodes = []; + + window.removeEventListener('pointermove', this._onPointerMove); + window.removeEventListener('pointerdown', this._onPointerDown); + window.removeEventListener('pointerup', this._onPointerUp); + window.removeEventListener('keydown', this._onKeyDown); + window.removeEventListener('keyup', this._onKeyUp); + } + + updateGizmoPosition() { + tmpV1.set(0, 0, 0); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + tmpV1.add(node.getPosition()); + } + tmpV1.scale(1.0 / this.nodes.length); + this.gizmo.setPosition(tmpV1); + } + + updateGizmoRotation() { + if (this._coordSpace === 'local') { + tmpV1.set(0, 0, 0); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + tmpV1.add(node.getEulerAngles()); + } + this.gizmo.setEulerAngles(tmpV1.scale(1.0 / this.nodes.length)); + } else { + this.gizmo.setEulerAngles(0, 0, 0); + } + } + + updateGizmoScale() { + let scale = 1; + if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; + } else { + scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; + } + scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); + this.gizmo.setLocalScale(scale, scale, scale); + } } export { Gizmo }; From 12578a3b091d4a0019b8cfdcb9201e9b7cbaa423 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:25:23 +0000 Subject: [PATCH 067/178] check if gizmo enabled on snapping check --- extras/gizmo/gizmo.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index ca98cec9dc6..f906114bdac 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -77,9 +77,15 @@ class Gizmo extends EventHandler { this.fire('pointer:up'); }; this._onKeyDown = (e) => { + if (!this.gizmo.enabled) { + return; + } this.snap = e.shiftKey; }; this._onKeyUp = (e) => { + if (!this.gizmo.enabled) { + return; + } this.snap = false; }; From c7dae178b0d5f2533f58c0d723c31eaba3b36329 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:32:23 +0000 Subject: [PATCH 068/178] refactored update methods and added events firing for each --- extras/gizmo/gizmo-rotate.js | 2 +- extras/gizmo/gizmo-translate.js | 2 +- extras/gizmo/gizmo.js | 32 +++++++++++++++++++++----------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index c69bd94a16f..5e6fb3fcfa3 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -184,7 +184,7 @@ class GizmoRotate extends GizmoTransform { } if (this._coordSpace === 'local') { - this.updateGizmoRotation(); + this.updateRotation(); } } } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 18b250a7677..aec910362f2 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -226,7 +226,7 @@ class GizmoTranslate extends GizmoTransform { } } - this.updateGizmoPosition(); + this.updatePosition(); } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index f906114bdac..0f698701e2a 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -54,7 +54,7 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this.updateGizmoScale(); + this.updateScale(); this._onPointerMove = (e) => { if (!this.gizmo.enabled) { @@ -94,7 +94,7 @@ class Gizmo extends EventHandler { set coordSpace(value) { this._coordSpace = value ?? 'world'; - this.updateGizmoRotation(); + this.updateRotation(); this.fire('coordSpace:set', this._coordSpace); } @@ -104,7 +104,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this.updateGizmoScale(); + this.updateScale(); this.fire('size:set', this._size); } @@ -220,8 +220,8 @@ class Gizmo extends EventHandler { attach(nodes) { this.nodes = nodes; - this.updateGizmoPosition(); - this.updateGizmoRotation(); + this.updatePosition(); + this.updateRotation(); window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerdown', this._onPointerDown); @@ -248,7 +248,7 @@ class Gizmo extends EventHandler { window.removeEventListener('keyup', this._onKeyUp); } - updateGizmoPosition() { + updatePosition() { tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; @@ -256,22 +256,28 @@ class Gizmo extends EventHandler { } tmpV1.scale(1.0 / this.nodes.length); this.gizmo.setPosition(tmpV1); + + this.fire('position:set', tmpV1); } - updateGizmoRotation() { + updateRotation() { if (this._coordSpace === 'local') { tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; tmpV1.add(node.getEulerAngles()); } - this.gizmo.setEulerAngles(tmpV1.scale(1.0 / this.nodes.length)); + tmpV1.scale(1.0 / this.nodes.length); + this.gizmo.setEulerAngles(tmpV1); } else { - this.gizmo.setEulerAngles(0, 0, 0); + tmpV1.set(0, 0, 0); + this.gizmo.setEulerAngles(tmpV1); } + + this.fire('eulerAngles:set', tmpV1); } - updateGizmoScale() { + updateScale() { let scale = 1; if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; @@ -279,7 +285,11 @@ class Gizmo extends EventHandler { scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; } scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); - this.gizmo.setLocalScale(scale, scale, scale); + tmpV1.set(scale, scale, scale); + this.gizmo.setLocalScale(tmpV1); + + this.fire('scale:set', tmpV1); + } } From a7b0b313eb050b81cb2230464bd9e09aed2ab706 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:56:43 +0000 Subject: [PATCH 069/178] moved snapping to gizmo transform; made update pos rot and scale private; added jsdoc to gizmo base class --- extras/gizmo/gizmo-rotate.js | 2 +- extras/gizmo/gizmo-transform.js | 10 ++ extras/gizmo/gizmo-translate.js | 2 +- extras/gizmo/gizmo.js | 161 +++++++++++++++++++++----------- 4 files changed, 120 insertions(+), 55 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 5e6fb3fcfa3..606d93b2280 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -184,7 +184,7 @@ class GizmoRotate extends GizmoTransform { } if (this._coordSpace === 'local') { - this.updateRotation(); + this._updateRotation(); } } } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index bf84e848563..f1ddcb9ba79 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -62,6 +62,8 @@ class GizmoTransform extends Gizmo { dragging = false; + snap = false; + snapIncrement = 1; constructor(...args) { @@ -127,6 +129,14 @@ class GizmoTransform extends Gizmo { this._currAxis = ''; this._currIsPlane = false; }); + + this.on('key:down', (key, shiftKey) => { + this.snap = shiftKey; + }); + + this.on('key:up', () => { + this.snap = false; + }); } _getAxis(meshInstance) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index aec910362f2..c20bd2782f4 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -226,7 +226,7 @@ class GizmoTranslate extends GizmoTransform { } } - this.updatePosition(); + this._updatePosition(); } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 0f698701e2a..6f79972db96 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -31,20 +31,59 @@ const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; class Gizmo extends EventHandler { + /** + * Internal version of the gizmo size. + * + * @type {number} + * @private + */ _size = 1; + /** + * Internal version of coordinate space. + * + * @type {string} + * @private + */ _coordSpace = 'world'; + /** + * The application instance containing the gizmo. + * + * @type {import('playcanvas').AppBase} + */ app; + /** + * The camera entity that displays the gizmo. + * + * @type {Entity} + */ camera; + /** + * The graph nodes attached to the gizmo. + * + * @type {import('playcanvas').GraphNode} + */ nodes = []; + /** + * The root gizmo entity. + * + * @type {import('playcanvas').Entity} + */ gizmo; - snap = false; + /** + * Creates a new Gizmo object. + * + * @param {import('playcanvas').AppBase} app - The application instance. + * @param {Entity} camera - The camera entity. + * @example + * const gizmo = new pcx.Gizmo(app, camera); + */ constructor(app, camera) { super(); @@ -54,7 +93,7 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this.updateScale(); + this._updateScale(); this._onPointerMove = (e) => { if (!this.gizmo.enabled) { @@ -80,13 +119,13 @@ class Gizmo extends EventHandler { if (!this.gizmo.enabled) { return; } - this.snap = e.shiftKey; + this.fire('key:down', e.key, e.shiftKey, e.metaKey); }; this._onKeyUp = (e) => { if (!this.gizmo.enabled) { return; } - this.snap = false; + this.fire('key:up'); }; app.on('destroy', () => this.detach()); @@ -94,7 +133,7 @@ class Gizmo extends EventHandler { set coordSpace(value) { this._coordSpace = value ?? 'world'; - this.updateRotation(); + this._updateRotation(); this.fire('coordSpace:set', this._coordSpace); } @@ -104,7 +143,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this.updateScale(); + this._updateScale(); this.fire('size:set', this._size); } @@ -144,6 +183,50 @@ class Gizmo extends EventHandler { this.gizmo.enabled = false; } + _updatePosition() { + tmpV1.set(0, 0, 0); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + tmpV1.add(node.getPosition()); + } + tmpV1.scale(1.0 / this.nodes.length); + this.gizmo.setPosition(tmpV1); + + this.fire('position:set', tmpV1); + } + + _updateRotation() { + if (this._coordSpace === 'local') { + tmpV1.set(0, 0, 0); + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + tmpV1.add(node.getEulerAngles()); + } + tmpV1.scale(1.0 / this.nodes.length); + this.gizmo.setEulerAngles(tmpV1); + } else { + tmpV1.set(0, 0, 0); + this.gizmo.setEulerAngles(tmpV1); + } + + this.fire('eulerAngles:set', tmpV1); + } + + _updateScale() { + let scale = 1; + if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; + } else { + scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; + } + scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); + tmpV1.set(scale, scale, scale); + this.gizmo.setLocalScale(tmpV1); + + this.fire('scale:set', tmpV1); + + } + _getSelection(x, y) { const start = this.camera.camera.screenToWorld(x, y, 1); const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); @@ -218,10 +301,18 @@ class Gizmo extends EventHandler { return false; } - attach(nodes) { + /** + * Attach an array of graph nodes to the gizmo. + * + * @param {import('playcanvas').GraphNode} [nodes] - The graph nodes. Defaults to []. + * @example + * const gizmo = new pcx.Gizmo(); + * gizmo.attach([boxA, boxB]); + */ + attach(nodes = []) { this.nodes = nodes; - this.updatePosition(); - this.updateRotation(); + this._updatePosition(); + this._updateRotation(); window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerdown', this._onPointerDown); @@ -234,6 +325,14 @@ class Gizmo extends EventHandler { this.gizmo.enabled = true; } + /** + * Detaches all graph nodes from the gizmo. + * + * @example + * const gizmo = new pcx.Gizmo(); + * gizmo.attach([boxA, boxB]); + * gizmo.detach(); + */ detach() { this.gizmo.enabled = false; @@ -247,50 +346,6 @@ class Gizmo extends EventHandler { window.removeEventListener('keydown', this._onKeyDown); window.removeEventListener('keyup', this._onKeyUp); } - - updatePosition() { - tmpV1.set(0, 0, 0); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV1.add(node.getPosition()); - } - tmpV1.scale(1.0 / this.nodes.length); - this.gizmo.setPosition(tmpV1); - - this.fire('position:set', tmpV1); - } - - updateRotation() { - if (this._coordSpace === 'local') { - tmpV1.set(0, 0, 0); - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV1.add(node.getEulerAngles()); - } - tmpV1.scale(1.0 / this.nodes.length); - this.gizmo.setEulerAngles(tmpV1); - } else { - tmpV1.set(0, 0, 0); - this.gizmo.setEulerAngles(tmpV1); - } - - this.fire('eulerAngles:set', tmpV1); - } - - updateScale() { - let scale = 1; - if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { - scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; - } else { - scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; - } - scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); - tmpV1.set(scale, scale, scale); - this.gizmo.setLocalScale(tmpV1); - - this.fire('scale:set', tmpV1); - - } } export { Gizmo }; From 2b804413acf945fb3b114e97bdd98aa133424612 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 15:59:17 +0000 Subject: [PATCH 070/178] changed internal coordSpace to protected --- extras/gizmo/gizmo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 6f79972db96..c417b8a4ee3 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -43,7 +43,7 @@ class Gizmo extends EventHandler { * Internal version of coordinate space. * * @type {string} - * @private + * @protected */ _coordSpace = 'world'; From d691cd39489d5f10c39f20daaa116182405d7033 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 7 Jan 2024 16:05:54 +0000 Subject: [PATCH 071/178] removed line drawing from gizmo layer and renamed to layer. --- extras/gizmo/gizmo-rotate.js | 10 +++++----- extras/gizmo/gizmo-scale.js | 14 +++++++------- extras/gizmo/gizmo-transform.js | 2 +- extras/gizmo/gizmo-translate.js | 12 ++++++------ extras/gizmo/gizmo.js | 10 +++++----- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 606d93b2280..660c1e316a4 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -16,7 +16,7 @@ class GizmoRotate extends GizmoTransform { z: new AxisDisk({ device: this.app.graphicsDevice, axis: 'z', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z, hoverColor: this._materials.hover @@ -24,7 +24,7 @@ class GizmoRotate extends GizmoTransform { x: new AxisDisk({ device: this.app.graphicsDevice, axis: 'x', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x, hoverColor: this._materials.hover @@ -32,7 +32,7 @@ class GizmoRotate extends GizmoTransform { y: new AxisDisk({ device: this.app.graphicsDevice, axis: 'y', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y, hoverColor: this._materials.hover @@ -40,7 +40,7 @@ class GizmoRotate extends GizmoTransform { face: new AxisDisk({ device: this.app.graphicsDevice, axis: 'face', - layers: [this.layerGizmo.id], + layers: [this.layer.id], defaultColor: this._materials.semi.yellow, hoverColor: this._materials.hover, ringRadius: 0.8 @@ -139,7 +139,7 @@ class GizmoRotate extends GizmoTransform { // guide ring this._ring = new AxisDisk({ device: this.app.graphicsDevice, - layers: [this.layerGizmo.id], + layers: [this.layer.id], defaultColor: this._materials.semi.white }); this._center.addChild(this._ring.entity); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 60300ca12ec..51c6a38b532 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -12,14 +12,14 @@ class GizmoScale extends GizmoTransform { _axisShapes = { xyz: new AxisBoxCenter({ axis: 'xyz', - layers: [this.layerGizmo.id], + layers: [this.layer.id], defaultColor: this._materials.center, hoverColor: this._materials.hover }), yz: new AxisPlane({ axis: 'x', flipAxis: 'y', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x, hoverColor: this._materials.hover @@ -27,7 +27,7 @@ class GizmoScale extends GizmoTransform { xz: new AxisPlane({ axis: 'y', flipAxis: 'z', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y, hoverColor: this._materials.hover @@ -35,28 +35,28 @@ class GizmoScale extends GizmoTransform { xy: new AxisPlane({ axis: 'z', flipAxis: 'x', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z, hoverColor: this._materials.hover }), x: new AxisBoxLine({ axis: 'x', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x, hoverColor: this._materials.hover }), y: new AxisBoxLine({ axis: 'y', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y, hoverColor: this._materials.hover }), z: new AxisBoxLine({ axis: 'z', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z, hoverColor: this._materials.hover diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index f1ddcb9ba79..2f98b37a789 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -273,7 +273,7 @@ class GizmoTransform extends Gizmo { tmpV2.copy(tmpV1).scale(-1); tmpQ1.transformVector(tmpV1, tmpV1); tmpQ1.transformVector(tmpV2, tmpV2); - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true, this.layerGizmo); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true); } _createMaterial(color) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index c20bd2782f4..ab8b7ada843 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -16,7 +16,7 @@ class GizmoTranslate extends GizmoTransform { yz: new AxisPlane({ axis: 'x', flipAxis: 'y', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x, hoverColor: this._materials.hover @@ -24,7 +24,7 @@ class GizmoTranslate extends GizmoTransform { xz: new AxisPlane({ axis: 'y', flipAxis: 'z', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y, hoverColor: this._materials.hover @@ -32,28 +32,28 @@ class GizmoTranslate extends GizmoTransform { xy: new AxisPlane({ axis: 'z', flipAxis: 'x', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z, hoverColor: this._materials.hover }), x: new AxisArrow({ axis: 'x', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x, hoverColor: this._materials.hover }), y: new AxisArrow({ axis: 'y', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y, hoverColor: this._materials.hover }), z: new AxisArrow({ axis: 'z', - layers: [this.layerGizmo.id], + layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z, hoverColor: this._materials.hover diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c417b8a4ee3..d6c60ea038d 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -161,19 +161,19 @@ class Gizmo extends EventHandler { _createLayer() { const layerMap = this.app.scene.layers.layerIdMap; if (layerMap.has(GIZMO_LAYER_ID)) { - this.layerGizmo = layerMap.get(GIZMO_LAYER_ID); + this.layer = layerMap.get(GIZMO_LAYER_ID); } else { - this.layerGizmo = new Layer({ + this.layer = new Layer({ id: GIZMO_LAYER_ID, name: 'Gizmo', clearDepthBuffer: true, opaqueSortMode: SORTMODE_NONE, transparentSortMode: SORTMODE_NONE }); - this.app.scene.layers.push(this.layerGizmo); + this.app.scene.layers.push(this.layer); } - if (this.camera.camera.layers.indexOf(this.layerGizmo.id) === -1) { - this.camera.camera.layers = this.camera.camera.layers.concat(this.layerGizmo.id); + if (this.camera.camera.layers.indexOf(this.layer.id) === -1) { + this.camera.camera.layers = this.camera.camera.layers.concat(this.layer.id); } } From 647344e707abb70b904ef1bd849be02ab653106e Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 10:25:53 +0000 Subject: [PATCH 072/178] removed unnecessary vec creation in updateLine --- extras/gizmo/axis-shapes.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 9cadafaaabb..5eb0b039267 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -142,13 +142,13 @@ class AxisArrow extends AxisShape { } _updateLine() { - this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); - this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._line.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); } _updateArrow() { - this._arrow.setLocalPosition(new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0)); - this._arrow.setLocalScale(new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness)); + this._arrow.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); + this._arrow.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); } } From fbab09a7ee8a3d4cfc144ac69f78323d1da7f297 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 11:26:51 +0000 Subject: [PATCH 073/178] and segments and sides to rotate rings --- examples/src/examples/misc/gizmos.mjs | 26 +++++++++++++- extras/gizmo/axis-shapes.js | 52 ++++++++++++++++++++------- extras/gizmo/gizmo-rotate.js | 16 +++++++++ 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index fa5595cb5d5..77ab893bddb 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -135,6 +135,28 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.ringRadius' } }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Segments' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.segments' }, + min: 1, + max: 100, + precision: 0, + step: 1 + }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Sides' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.sides' }, + min: 1, + max: 100, + precision: 0, + step: 1 + }) ) ), jsx(Panel, { headerText: 'Camera' }, @@ -221,7 +243,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisPlaneGap: gizmo.axisPlaneGap, axisCenterSize: gizmo.axisCenterSize, tubeRadius: gizmo.tubeRadius, - ringRadius: gizmo.ringRadius + ringRadius: gizmo.ringRadius, + segments: gizmo.segments, + sides: gizmo.sides }); this.skipSetFire = false; } diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 5eb0b039267..6fb84a8ea69 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -286,20 +286,25 @@ class AxisDisk extends AxisShape { _ringRadius = 0.55; + _segments = 30; + + _sides = 20; + constructor(options = {}) { super(options); this._device = options.device; + + this._tubeRadius = options.tubeRadius ?? this._tubeRadius; this._ringRadius = options.ringRadius ?? this._ringRadius; + this._segments = options.segments ?? this._segments; + this._sides = options.sides ?? this._sides; + this._createDisk(options.layers ?? []); } _createDisk(layers) { - const mesh = createTorus(this._device, { - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius - }); - const meshInstance = new MeshInstance(mesh, this._defaultColor); + const meshInstance = new MeshInstance(this._createTorusMesh(), this._defaultColor); this.entity = new Entity('disk_' + this.axis); this.entity.addComponent('render', { @@ -313,29 +318,50 @@ class AxisDisk extends AxisShape { this.meshInstances.push(meshInstance); } - set tubeRadius(value) { - this._tubeRadius = value ?? 0.1; - this.meshInstances[0].mesh = createTorus(this._device, { + _createTorusMesh() { + return createTorus(this._device, { tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius + ringRadius: this._ringRadius, + segments: this._segments, + sides: this._sides }); } + set tubeRadius(value) { + this._tubeRadius = value ?? 0.1; + this.meshInstances[0].mesh = this._createTorusMesh(); + } + get tubeRadius() { return this._tubeRadius; } set ringRadius(value) { this._ringRadius = value ?? 0.1; - this.meshInstances[0].mesh = createTorus(this._device, { - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius - }); + this.meshInstances[0].mesh = this._createTorusMesh(); } get ringRadius() { return this._ringRadius; } + + set segments(value) { + this._segments = value ?? 30; + this.meshInstances[0].mesh = this._createTorusMesh(); + } + + get segments() { + return this._segments; + } + + set sides(value) { + this._sides = value ?? 20; + this.meshInstances[0].mesh = this._createTorusMesh(); + } + + get sides() { + return this._sides; + } } class AxisPlane extends AxisShape { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 660c1e316a4..4c9296b3884 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -108,6 +108,22 @@ class GizmoRotate extends GizmoTransform { return this._axisShapes.x.ringRadius; } + set segments(value) { + this._setDiskProp('segments', value); + } + + get segments() { + return this._axisShapes.x.segments; + } + + set sides(value) { + this._setDiskProp('sides', value); + } + + get sides() { + return this._axisShapes.x.sides; + } + _setDiskProp(propName, value) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; From 2e8fccbb349e6131327108f0703f7264395feef9 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 12:07:33 +0000 Subject: [PATCH 074/178] replaced segment and side settings with ratios to each radius --- examples/src/examples/misc/gizmos.mjs | 22 +++++++++------------- extras/gizmo/axis-shapes.js | 26 ++++++++++++-------------- extras/gizmo/gizmo-rotate.js | 22 +++++++++++++--------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 77ab893bddb..c820be06742 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -137,25 +137,21 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Segments' }, + jsx(LabelGroup, { text: 'Tube Radius Side Ratio' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.segments' }, - min: 1, - max: 100, - precision: 0, - step: 1 + link: { observer, path: 'gizmo.tubeRadiusSideRatio' }, + min: 100, + max: 2000 }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Sides' }, + jsx(LabelGroup, { text: 'Ring Radius Segment Ratio' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.sides' }, + link: { observer, path: 'gizmo.ringRadiusSegmentRatio' }, min: 1, - max: 100, - precision: 0, - step: 1 + max: 100 }) ) ), @@ -244,8 +240,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisCenterSize: gizmo.axisCenterSize, tubeRadius: gizmo.tubeRadius, ringRadius: gizmo.ringRadius, - segments: gizmo.segments, - sides: gizmo.sides + tubeRadiusSideRatio: gizmo.tubeRadiusSideRatio, + ringRadiusSegmentRatio: gizmo.ringRadiusSegmentRatio }); this.skipSetFire = false; } diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 6fb84a8ea69..240eb317970 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -286,9 +286,9 @@ class AxisDisk extends AxisShape { _ringRadius = 0.55; - _segments = 30; + _tubeRadiusSideRatio = 20 / 0.02; - _sides = 20; + _ringRadiusSegmentRatio = 30 / 0.55; constructor(options = {}) { super(options); @@ -297,8 +297,6 @@ class AxisDisk extends AxisShape { this._tubeRadius = options.tubeRadius ?? this._tubeRadius; this._ringRadius = options.ringRadius ?? this._ringRadius; - this._segments = options.segments ?? this._segments; - this._sides = options.sides ?? this._sides; this._createDisk(options.layers ?? []); } @@ -322,8 +320,8 @@ class AxisDisk extends AxisShape { return createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, - segments: this._segments, - sides: this._sides + segments: Math.floor(this._ringRadius * this._ringRadiusSegmentRatio), + sides: Math.floor(this._tubeRadius * this._tubeRadiusSideRatio) }); } @@ -345,22 +343,22 @@ class AxisDisk extends AxisShape { return this._ringRadius; } - set segments(value) { - this._segments = value ?? 30; + set tubeRadiusSideRatio(value) { + this._tubeRadiusSideRatio = value; this.meshInstances[0].mesh = this._createTorusMesh(); } - get segments() { - return this._segments; + get tubeRadiusSideRatio() { + return this._tubeRadiusSideRatio; } - set sides(value) { - this._sides = value ?? 20; + set ringRadiusSegmentRatio(value) { + this._ringRadiusSegmentRatio = value; this.meshInstances[0].mesh = this._createTorusMesh(); } - get sides() { - return this._sides; + get ringRadiusSegmentRatio() { + return this._ringRadiusSegmentRatio; } } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 4c9296b3884..271d86168c7 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -108,26 +108,30 @@ class GizmoRotate extends GizmoTransform { return this._axisShapes.x.ringRadius; } - set segments(value) { - this._setDiskProp('segments', value); + set tubeRadiusSideRatio(value) { + this._setDiskProp('tubeRadiusSideRatio', value, true); } - get segments() { - return this._axisShapes.x.segments; + get tubeRadiusSideRatio() { + return this._axisShapes.x.tubeRadiusSideRatio; } - set sides(value) { - this._setDiskProp('sides', value); + set ringRadiusSegmentRatio(value) { + this._setDiskProp('ringRadiusSegmentRatio', value, true); } - get sides() { - return this._axisShapes.x.sides; + get ringRadiusSegmentRatio() { + return this._axisShapes.x.ringRadiusSegmentRatio; } - _setDiskProp(propName, value) { + _setDiskProp(propName, value, all = false) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; + if (all) { + this._axisShapes.face[propName] = value; + this._ring[propName] = value; + } } _setFacingDisks() { From 54055738af722aecefdd9736b4f25b1a93d8956c Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 12:26:06 +0000 Subject: [PATCH 075/178] window define to update react state using keybinds --- examples/src/examples/misc/gizmos.mjs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index c820be06742..fcb1aef4a8d 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -9,6 +9,8 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const [type, setType] = React.useState('translate'); + this.setType = (value) => setType(value); + return fragment( jsx(Panel, { headerText: 'Gizmo' }, jsx(LabelGroup, { text: 'Type' }, @@ -20,7 +22,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { ], binding: new BindingTwoWay(), link: { observer, path: 'gizmo.type' }, - onSelect: value => setType(value) + onSelect: this.setType }) ), jsx(LabelGroup, { text: 'Coord Space' }, @@ -285,19 +287,23 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { }); // control keybinds + const setType = (value) => { + data.set('gizmo.type', value); + this.top.setType(value); + }; window.addEventListener('keypress', (e) => { switch (e.key) { case 'x': data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); break; case '1': - data.set('gizmo.type', 'translate'); + setType('translate'); break; case '2': - data.set('gizmo.type', 'rotate'); + setType('rotate'); break; case '3': - data.set('gizmo.type', 'scale'); + setType('scale'); break; } }); From 23e4a4f9a3683fd30fe6314f47c02f407adb5276 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 12:42:20 +0000 Subject: [PATCH 076/178] removed tube radius ratio (minimal noticable effect) --- examples/src/examples/misc/gizmos.mjs | 22 ++++++---------------- extras/gizmo/axis-shapes.js | 14 +------------- extras/gizmo/gizmo-rotate.js | 16 ++++------------ 3 files changed, 11 insertions(+), 41 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index fcb1aef4a8d..0131273ce0c 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -125,26 +125,17 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Tube Radius' }, + jsx(LabelGroup, { text: 'Axis Tube Radius' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.tubeRadius' } + link: { observer, path: 'gizmo.axisTubeRadius' } }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Ring Radius' }, + jsx(LabelGroup, { text: 'Axis Ring Radius' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.ringRadius' } - }) - ), - type === 'rotate' && - jsx(LabelGroup, { text: 'Tube Radius Side Ratio' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.tubeRadiusSideRatio' }, - min: 100, - max: 2000 + link: { observer, path: 'gizmo.axisRingRadius' } }) ), type === 'rotate' && @@ -240,9 +231,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisPlaneSize: gizmo.axisPlaneSize, axisPlaneGap: gizmo.axisPlaneGap, axisCenterSize: gizmo.axisCenterSize, - tubeRadius: gizmo.tubeRadius, - ringRadius: gizmo.ringRadius, - tubeRadiusSideRatio: gizmo.tubeRadiusSideRatio, + axisTubeRadius: gizmo.axisTubeRadius, + axisRingRadius: gizmo.axisRingRadius, ringRadiusSegmentRatio: gizmo.ringRadiusSegmentRatio }); this.skipSetFire = false; diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 240eb317970..c4a62cd20bb 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -286,8 +286,6 @@ class AxisDisk extends AxisShape { _ringRadius = 0.55; - _tubeRadiusSideRatio = 20 / 0.02; - _ringRadiusSegmentRatio = 30 / 0.55; constructor(options = {}) { @@ -320,8 +318,7 @@ class AxisDisk extends AxisShape { return createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, - segments: Math.floor(this._ringRadius * this._ringRadiusSegmentRatio), - sides: Math.floor(this._tubeRadius * this._tubeRadiusSideRatio) + segments: Math.floor(this._ringRadius * this._ringRadiusSegmentRatio) }); } @@ -343,15 +340,6 @@ class AxisDisk extends AxisShape { return this._ringRadius; } - set tubeRadiusSideRatio(value) { - this._tubeRadiusSideRatio = value; - this.meshInstances[0].mesh = this._createTorusMesh(); - } - - get tubeRadiusSideRatio() { - return this._tubeRadiusSideRatio; - } - set ringRadiusSegmentRatio(value) { this._ringRadiusSegmentRatio = value; this.meshInstances[0].mesh = this._createTorusMesh(); diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 271d86168c7..5edbc4b4c28 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -92,30 +92,22 @@ class GizmoRotate extends GizmoTransform { }); } - set tubeRadius(value) { + set axisTubeRadius(value) { this._setDiskProp('tubeRadius', value); } - get tubeRadius() { + get axisTubeRadius() { return this._axisShapes.x.tubeRadius; } - set ringRadius(value) { + set axisRingRadius(value) { this._setDiskProp('ringRadius', value); } - get ringRadius() { + get axisRingRadius() { return this._axisShapes.x.ringRadius; } - set tubeRadiusSideRatio(value) { - this._setDiskProp('tubeRadiusSideRatio', value, true); - } - - get tubeRadiusSideRatio() { - return this._axisShapes.x.tubeRadiusSideRatio; - } - set ringRadiusSegmentRatio(value) { this._setDiskProp('ringRadiusSegmentRatio', value, true); } From cb6b91251ee43780580fbe6bcbaf089892921425 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 13:36:53 +0000 Subject: [PATCH 077/178] added face tube and ring radius settings and fixed segments --- examples/src/examples/misc/gizmos.mjs | 17 ++++++++++++----- extras/gizmo/axis-shapes.js | 16 ++++------------ extras/gizmo/gizmo-rotate.js | 23 ++++++++++++++--------- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 0131273ce0c..11f5698639e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -139,12 +139,18 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Ring Radius Segment Ratio' }, + jsx(LabelGroup, { text: 'Face Tube Radius' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.ringRadiusSegmentRatio' }, - min: 1, - max: 100 + link: { observer, path: 'gizmo.faceTubeRadius' } + }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Face Ring Radius' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.faceRingRadius' }, + max: 2 }) ) ), @@ -233,7 +239,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisCenterSize: gizmo.axisCenterSize, axisTubeRadius: gizmo.axisTubeRadius, axisRingRadius: gizmo.axisRingRadius, - ringRadiusSegmentRatio: gizmo.ringRadiusSegmentRatio + faceTubeRadius: gizmo.faceTubeRadius, + faceRingRadius: gizmo.faceRingRadius }); this.skipSetFire = false; } diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index c4a62cd20bb..87f9ec065ab 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -12,6 +12,9 @@ const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); +// constants +const TORUS_SEGMENTS = 80; + class AxisShape { _position; @@ -286,8 +289,6 @@ class AxisDisk extends AxisShape { _ringRadius = 0.55; - _ringRadiusSegmentRatio = 30 / 0.55; - constructor(options = {}) { super(options); @@ -318,7 +319,7 @@ class AxisDisk extends AxisShape { return createTorus(this._device, { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, - segments: Math.floor(this._ringRadius * this._ringRadiusSegmentRatio) + segments: TORUS_SEGMENTS }); } @@ -339,15 +340,6 @@ class AxisDisk extends AxisShape { get ringRadius() { return this._ringRadius; } - - set ringRadiusSegmentRatio(value) { - this._ringRadiusSegmentRatio = value; - this.meshInstances[0].mesh = this._createTorusMesh(); - } - - get ringRadiusSegmentRatio() { - return this._ringRadiusSegmentRatio; - } } class AxisPlane extends AxisShape { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 5edbc4b4c28..b42f843612e 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -108,22 +108,27 @@ class GizmoRotate extends GizmoTransform { return this._axisShapes.x.ringRadius; } - set ringRadiusSegmentRatio(value) { - this._setDiskProp('ringRadiusSegmentRatio', value, true); + set faceTubeRadius(value) { + this._axisShapes.face.tubeRadius = value; } - get ringRadiusSegmentRatio() { - return this._axisShapes.x.ringRadiusSegmentRatio; + get faceTubeRadius() { + return this._axisShapes.face.tubeRadius; } - _setDiskProp(propName, value, all = false) { + set faceRingRadius(value) { + this._axisShapes.face.ringRadius = value; + } + + get faceRingRadius() { + return this._axisShapes.face.ringRadius; + } + + _setDiskProp(propName, value) { this._axisShapes.x[propName] = value; this._axisShapes.y[propName] = value; this._axisShapes.z[propName] = value; - if (all) { - this._axisShapes.face[propName] = value; - this._ring[propName] = value; - } + this._ring[propName] = value; } _setFacingDisks() { From 18647f62b276474fa4f70814341ff434f61d6355 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 14:28:21 +0000 Subject: [PATCH 078/178] added hover and guide line color customization --- examples/src/examples/misc/gizmos.mjs | 16 ++++++++++++++++ extras/gizmo/gizmo-rotate.js | 4 ++-- extras/gizmo/gizmo-transform.js | 22 ++++++++++++++++++---- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 11f5698639e..d3517288e0e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -61,6 +61,18 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { link: { observer, path: 'gizmo.axisZColor' } }) ), + jsx(LabelGroup, { text: 'Hover Color' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.hoverColor' } + }) + ), + jsx(LabelGroup, { text: 'Guide Line Color' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.guideLineColor' } + }) + ), (type === 'translate' || type === 'scale') && jsx(LabelGroup, { text: 'Axis Gap' }, jsx(SliderInput, { @@ -227,6 +239,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisXColor: Object.values(gizmo.axisXColor), axisYColor: Object.values(gizmo.axisYColor), axisZColor: Object.values(gizmo.axisZColor), + hoverColor: Object.values(gizmo.hoverColor), + guideLineColor: Object.values(gizmo.guideLineColor), coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, @@ -379,6 +393,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { case 'axisXColor': case 'axisYColor': case 'axisZColor': + case 'hoverColor': + case 'guideLineColor': tmpC.set(...value); gizmoHandler.gizmo[pathArray[1]] = tmpC; break; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index b42f843612e..75f4dd0f354 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -41,7 +41,7 @@ class GizmoRotate extends GizmoTransform { device: this.app.graphicsDevice, axis: 'face', layers: [this.layer.id], - defaultColor: this._materials.semi.yellow, + defaultColor: this._materials.axis.face, hoverColor: this._materials.hover, ringRadius: 0.8 }) @@ -157,7 +157,7 @@ class GizmoRotate extends GizmoTransform { this._ring = new AxisDisk({ device: this.app.graphicsDevice, layers: [this.layer.id], - defaultColor: this._materials.semi.white + defaultColor: this._materials.guide }); this._center.addChild(this._ring.entity); } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 2f98b37a789..83cac7610e8 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -30,10 +30,7 @@ class GizmoTransform extends Gizmo { }, center: this._createMaterial(new Color(1, 1, 1, 0.5)), hover: this._createMaterial(new Color(1, 1, 0.3)), - semi: { - yellow: this._createMaterial(new Color(1, 1, 0.3, 0.5)), - white: this._createMaterial(new Color(1, 1, 1, 0.5)) - } + guide: this._createMaterial(new Color(1, 1, 1, 0.5)) }; _guideLineColor = new Color(1, 1, 1, 0.8); @@ -318,6 +315,23 @@ class GizmoTransform extends Gizmo { get axisZColor() { return this._materials.axis.z.emissive; } + + set hoverColor(value) { + this._materials.hover.emissive.copy(value); + this._materials.hover.update(); + } + + get hoverColor() { + return this._materials.hover.emissive; + } + + set guideLineColor(value) { + this._guideLineColor.copy(value); + } + + get guideLineColor() { + return this._guideLineColor; + } } export { GizmoTransform }; From 19de376ef79308b84c5251ee168f172979b5355e Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 14:29:58 +0000 Subject: [PATCH 079/178] refactor gizmo-transform getters/setters --- extras/gizmo/gizmo-transform.js | 88 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 83cac7610e8..5b88a966e00 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -136,6 +136,50 @@ class GizmoTransform extends Gizmo { }); } + set axisXColor(value) { + this._materials.axis.x.emissive.copy(value); + this._materials.axis.x.update(); + } + + get axisXColor() { + return this._materials.axis.x.emissive; + } + + set axisYColor(value) { + this._materials.axis.y.emissive.copy(value); + this._materials.axis.y.update(); + } + + get axisYColor() { + return this._materials.axis.y.emissive; + } + + set axisZColor(value) { + this._materials.axis.z.emissive.copy(value); + this._materials.axis.z.update(); + } + + get axisZColor() { + return this._materials.axis.z.emissive; + } + + set hoverColor(value) { + this._materials.hover.emissive.copy(value); + this._materials.hover.update(); + } + + get hoverColor() { + return this._materials.hover.emissive; + } + + set guideLineColor(value) { + this._guideLineColor.copy(value); + } + + get guideLineColor() { + return this._guideLineColor; + } + _getAxis(meshInstance) { if (!meshInstance) { return ''; @@ -288,50 +332,6 @@ class GizmoTransform extends Gizmo { this._center = new Entity('center'); this.gizmo.addChild(this._center); } - - set axisXColor(value) { - this._materials.axis.x.emissive.copy(value); - this._materials.axis.x.update(); - } - - get axisXColor() { - return this._materials.axis.x.emissive; - } - - set axisYColor(value) { - this._materials.axis.y.emissive.copy(value); - this._materials.axis.y.update(); - } - - get axisYColor() { - return this._materials.axis.y.emissive; - } - - set axisZColor(value) { - this._materials.axis.z.emissive.copy(value); - this._materials.axis.z.update(); - } - - get axisZColor() { - return this._materials.axis.z.emissive; - } - - set hoverColor(value) { - this._materials.hover.emissive.copy(value); - this._materials.hover.update(); - } - - get hoverColor() { - return this._materials.hover.emissive; - } - - set guideLineColor(value) { - this._guideLineColor.copy(value); - } - - get guideLineColor() { - return this._guideLineColor; - } } export { GizmoTransform }; From bb3d1ea2380afa6643886bdf4b2d99e26f5ea70e Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 14:30:56 +0000 Subject: [PATCH 080/178] spread argument refactor --- extras/gizmo/gizmo-rotate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 75f4dd0f354..259ec05e4ae 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -59,8 +59,8 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(app, ...args) { - super(app, ...args); + constructor(...args) { + super(...args); this._createTransform(); From 4abaeb844ed8284c85ef8f3489b44490dccd3726 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 14:33:22 +0000 Subject: [PATCH 081/178] refactor axis to xyz for radii names --- examples/src/examples/misc/gizmos.mjs | 12 ++++++------ extras/gizmo/gizmo-rotate.js | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index d3517288e0e..cdf9ea6948e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -137,17 +137,17 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Axis Tube Radius' }, + jsx(LabelGroup, { text: 'XYZ Tube Radius' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisTubeRadius' } + link: { observer, path: 'gizmo.xyzTubeRadius' } }) ), type === 'rotate' && - jsx(LabelGroup, { text: 'Axis Ring Radius' }, + jsx(LabelGroup, { text: 'XYZ Ring Radius' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisRingRadius' } + link: { observer, path: 'gizmo.xyzRingRadius' } }) ), type === 'rotate' && @@ -251,8 +251,8 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { axisPlaneSize: gizmo.axisPlaneSize, axisPlaneGap: gizmo.axisPlaneGap, axisCenterSize: gizmo.axisCenterSize, - axisTubeRadius: gizmo.axisTubeRadius, - axisRingRadius: gizmo.axisRingRadius, + xyzTubeRadius: gizmo.xyzTubeRadius, + xyzRingRadius: gizmo.xyzRingRadius, faceTubeRadius: gizmo.faceTubeRadius, faceRingRadius: gizmo.faceRingRadius }); diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 259ec05e4ae..3235700cb5d 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -92,19 +92,19 @@ class GizmoRotate extends GizmoTransform { }); } - set axisTubeRadius(value) { + set xyzTubeRadius(value) { this._setDiskProp('tubeRadius', value); } - get axisTubeRadius() { + get xyzTubeRadius() { return this._axisShapes.x.tubeRadius; } - set axisRingRadius(value) { + set xyzRingRadius(value) { this._setDiskProp('ringRadius', value); } - get axisRingRadius() { + get xyzRingRadius() { return this._axisShapes.x.ringRadius; } From a1ea4c730f7d885bfebf4f47a14a96f7c8eaa585 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 14:49:21 +0000 Subject: [PATCH 082/178] formatted color property names and organised controls in example --- examples/src/examples/misc/gizmos.mjs | 65 ++++++++++++++++----------- extras/gizmo/gizmo-transform.js | 21 ++++++--- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index cdf9ea6948e..227d40fa646 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -12,7 +12,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { this.setType = (value) => setType(value); return fragment( - jsx(Panel, { headerText: 'Gizmo' }, + jsx(Panel, { headerText: 'Transform' }, jsx(LabelGroup, { text: 'Type' }, jsx(SelectInput, { options: [ @@ -42,95 +42,106 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { min: 0.1, max: 2.0 }) - ), - jsx(LabelGroup, { text: 'Axis X Color' }, + ) + ), + jsx(Panel, { headerText: 'Color' }, + jsx(LabelGroup, { text: 'X Axis' }, jsx(ColorPicker, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisXColor' } + link: { observer, path: 'gizmo.xAxisColor' } }) ), - jsx(LabelGroup, { text: 'Axis Y Color' }, + jsx(LabelGroup, { text: 'Y Axis' }, jsx(ColorPicker, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisYColor' } + link: { observer, path: 'gizmo.yAxisColor' } }) ), - jsx(LabelGroup, { text: 'Axis Z Color' }, + jsx(LabelGroup, { text: 'Z Axis' }, jsx(ColorPicker, { binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.axisZColor' } + link: { observer, path: 'gizmo.zAxisColor' } }) ), - jsx(LabelGroup, { text: 'Hover Color' }, + type === 'rotate' && + jsx(LabelGroup, { text: 'Face Axis' }, + jsx(ColorPicker, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.faceAxisColor' } + }) + ), + jsx(LabelGroup, { text: 'Hover' }, jsx(ColorPicker, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.hoverColor' } }) ), - jsx(LabelGroup, { text: 'Guide Line Color' }, + jsx(LabelGroup, { text: 'Guide Line' }, jsx(ColorPicker, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.guideLineColor' } }) - ), + ) + ), + jsx(Panel, { headerText: 'Mesh' }, (type === 'translate' || type === 'scale') && - jsx(LabelGroup, { text: 'Axis Gap' }, + jsx(LabelGroup, { text: 'Gap' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisGap' } }) ), (type === 'translate' || type === 'scale') && - jsx(LabelGroup, { text: 'Axis Line Thickness' }, + jsx(LabelGroup, { text: 'Line Thickness' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisLineThickness' } }) ), (type === 'translate' || type === 'scale') && - jsx(LabelGroup, { text: 'Axis Line Length' }, + jsx(LabelGroup, { text: 'Line Length' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisLineLength' } }) ), type === 'scale' && - jsx(LabelGroup, { text: 'Axis Box Size' }, + jsx(LabelGroup, { text: 'Box Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisBoxSize' } }) ), type === 'translate' && - jsx(LabelGroup, { text: 'Axis Arrow Thickness' }, + jsx(LabelGroup, { text: 'Arrow Thickness' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisArrowThickness' } }) ), type === 'translate' && - jsx(LabelGroup, { text: 'Axis Arrow Length' }, + jsx(LabelGroup, { text: 'Arrow Length' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisArrowLength' } }) ), (type === 'translate' || type === 'scale') && - jsx(LabelGroup, { text: 'Axis Plane Size' }, + jsx(LabelGroup, { text: 'Plane Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisPlaneSize' } }) ), (type === 'translate' || type === 'scale') && - jsx(LabelGroup, { text: 'Axis Plane Gap' }, + jsx(LabelGroup, { text: 'Plane Gap' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisPlaneGap' } }) ), type === 'scale' && - jsx(LabelGroup, { text: 'Axis Center Size' }, + jsx(LabelGroup, { text: 'Center Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.axisCenterSize' } @@ -236,9 +247,10 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { data.set('gizmo', { type: type, size: gizmo.size, - axisXColor: Object.values(gizmo.axisXColor), - axisYColor: Object.values(gizmo.axisYColor), - axisZColor: Object.values(gizmo.axisZColor), + xAxisColor: Object.values(gizmo.xAxisColor), + yAxisColor: Object.values(gizmo.yAxisColor), + zAxisColor: Object.values(gizmo.zAxisColor), + faceAxisColor: Object.values(gizmo.faceAxisColor), hoverColor: Object.values(gizmo.hoverColor), guideLineColor: Object.values(gizmo.guideLineColor), coordSpace: gizmo.coordSpace, @@ -390,9 +402,10 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { case 'type': gizmoHandler.switch(value, [boxA, boxB]); break; - case 'axisXColor': - case 'axisYColor': - case 'axisZColor': + case 'xAxisColor': + case 'yAxisColor': + case 'zAxisColor': + case 'faceAxisColor': case 'hoverColor': case 'guideLineColor': tmpC.set(...value); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 5b88a966e00..de21bb12976 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -136,33 +136,42 @@ class GizmoTransform extends Gizmo { }); } - set axisXColor(value) { + set xAxisColor(value) { this._materials.axis.x.emissive.copy(value); this._materials.axis.x.update(); } - get axisXColor() { + get xAxisColor() { return this._materials.axis.x.emissive; } - set axisYColor(value) { + set yAxisColor(value) { this._materials.axis.y.emissive.copy(value); this._materials.axis.y.update(); } - get axisYColor() { + get yAxisColor() { return this._materials.axis.y.emissive; } - set axisZColor(value) { + set zAxisColor(value) { this._materials.axis.z.emissive.copy(value); this._materials.axis.z.update(); } - get axisZColor() { + get zAxisColor() { return this._materials.axis.z.emissive; } + set faceAxisColor(value) { + this._materials.axis.face.emissive.copy(value); + this._materials.axis.face.update(); + } + + get faceAxisColor() { + return this._materials.axis.face.emissive; + } + set hoverColor(value) { this._materials.hover.emissive.copy(value); this._materials.hover.update(); From 1426551955f9be9e6f2eb290626a4bd0972a8cd1 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 15:53:39 +0000 Subject: [PATCH 083/178] added jsdoc to transform and refactored attribute names and changed visibility --- extras/gizmo/gizmo-rotate.js | 28 +++-- extras/gizmo/gizmo-scale.js | 33 +++--- extras/gizmo/gizmo-transform.js | 192 ++++++++++++++++++++++++++------ extras/gizmo/gizmo-translate.js | 30 ++--- extras/gizmo/gizmo.js | 3 + 5 files changed, 212 insertions(+), 74 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 3235700cb5d..75f2b63427a 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -11,6 +11,11 @@ const tmpV1 = new Vec3(); const tmpQ1 = new Quat(); const tmpQ2 = new Quat(); +/** + * Rotation gizmo. + * + * @augments GizmoTransform + */ class GizmoRotate extends GizmoTransform { _axisShapes = { z: new AxisDisk({ @@ -59,8 +64,16 @@ class GizmoRotate extends GizmoTransform { snapIncrement = 5; - constructor(...args) { - super(...args); + /** + * Creates a new GizmoRotate object. + * + * @param {import('playcanvas').AppBase} app - The application instance. + * @param {import('playcanvas').Entity} camera - The camera entity. + * @example + * const gizmo = new pcx.GizmoRotate(app, camera); + */ + constructor(app, camera) { + super(app, camera); this._createTransform(); @@ -144,22 +157,13 @@ class GizmoRotate extends GizmoTransform { _createTransform() { super._createTransform(); - // shapes - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; - this._center.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this._shapeMap.set(shape.meshInstances[i], shape); - } - } - // guide ring this._ring = new AxisDisk({ device: this.app.graphicsDevice, layers: [this.layer.id], defaultColor: this._materials.guide }); - this._center.addChild(this._ring.entity); + this._meshRoot.addChild(this._ring.entity); } _storeNodeRotations() { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 51c6a38b532..5059c1a8961 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -8,6 +8,11 @@ import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); +/** + * Scaling gizmo. + * + * @augments GizmoTransform + */ class GizmoScale extends GizmoTransform { _axisShapes = { xyz: new AxisBoxCenter({ @@ -65,16 +70,25 @@ class GizmoScale extends GizmoTransform { _coordSpace = 'local'; + _planes = []; + _nodeScales = new Map(); snapIncrement = 1; - constructor(...args) { - super(...args); + /** + * Creates a new GizmoScale object. + * + * @param {import('playcanvas').AppBase} app - The application instance. + * @param {import('playcanvas').Entity} camera - The camera entity. + * @example + * const gizmo = new pcx.GizmoScale(app, camera); + */ + constructor(app, camera) { + super(app, camera); this._createTransform(); - this._planes = []; for (const key in this._axisShapes) { const shape = this._axisShapes[key]; if (!(shape instanceof AxisPlane)) { @@ -185,19 +199,6 @@ class GizmoScale extends GizmoTransform { this._axisShapes.xy[propName] = value; } - _createTransform() { - super._createTransform(); - - // shapes - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; - this._center.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this._shapeMap.set(shape.meshInstances[i], shape); - } - } - } - _checkForPlaneFlip() { const gizmoPos = this.gizmo.getPosition(); const cameraPos = this.camera.getPosition(); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index de21bb12976..ac3c7f89b08 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -16,11 +16,35 @@ const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); +const pointDelta = new Vec3(); + // constants const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; +/** + * The base class for all transform gizmos. + * + * @augments Gizmo + */ class GizmoTransform extends Gizmo { + /** + * Internal material objects for mesh instances. + * + * @typedef MaterialsObject + * @property {Object} axis - The object containing axis materials. + * @property {StandardMaterial} axis.x - The X axis material. + * @property {StandardMaterial} axis.y - The Y axis material. + * @property {StandardMaterial} axis.z - The Z axis material. + * @property {StandardMaterial} axis.face - The camera facing (face) axis material. Only for rotate + * @property {StandardMaterial} hover - The hover material. + * @property {StandardMaterial} center - The center shape material. Only for scale. + * @property {StandardMaterial} guide - The guide ring axis material. Only for rotate. + */ + /** + * @type {MaterialsObject} + * @protected + */ _materials = { axis: { x: this._createMaterial(new Color(1, 0.3, 0.3)), @@ -28,43 +52,139 @@ class GizmoTransform extends Gizmo { z: this._createMaterial(new Color(0.3, 0.3, 1)), face: this._createMaterial(new Color(1, 1, 0.3, 0.5)) }, - center: this._createMaterial(new Color(1, 1, 1, 0.5)), hover: this._createMaterial(new Color(1, 1, 0.3)), + center: this._createMaterial(new Color(1, 1, 1, 0.5)), guide: this._createMaterial(new Color(1, 1, 1, 0.5)) }; + /** + * Internal version of the guide line color. + * + * @type {Color} + * @private + */ _guideLineColor = new Color(1, 1, 1, 0.8); + /** + * Internal object containing the axis shapes to render. + * + * @type {Object.} + * @protected + */ + _axisShapes = {}; + + /** + * Internal mapping of mesh instances to axis shapes. + * + * @type {Map} + * @private + */ _shapeMap = new Map(); + /** + * Internal currently hovered shape. + * + * @type {import('./axis-shapes.js').AxisShape} + * @private + */ _hoverShape; + /** + * Internal currently hovered axis. + * + * @type {string} + * @private + */ _hoverAxis = ''; + /** + * Internal state of if currently hovered shape is a plane. + * + * @type {boolean} + * @private + */ _hoverIsPlane = false; - _currAxis = ''; - - _currIsPlane = false; - + /** + * Internal currently selected axis. + * + * @type {string} + * @private + */ + _selectedAxis = ''; + + /** + * Internal state of if currently selected shape is a plane. + * + * @type {boolean} + * @private + */ + _selectedIsPlane = false; + + /** + * Internal selection starting coordinates in world space. + * + * @type {Vec3} + * @private + */ _pointStart = new Vec3(); + /** + * Internal selection starting angle in world space. + * + * @type {number} + * @private + */ _angleStart = 0; - _offset = new Vec3(); - + /** + * Internal state if transform is a rotation. + * + * @type {boolean} + * @protected + */ _isRotation = false; - _center; - - dragging = false; - + /** + * Internal entity for mesh root. + * + * @type {Entity} + * @protected + */ + _meshRoot; + + /** + * Internal state for if the gizmo is being dragged. + * + * @type {boolean} + * @private + */ + _dragging = false; + + /** + * State for if snapping is enabled. Defaults to false + * + * @type {boolean} + */ snap = false; + /** + * Snapping increment. Defaults to 1 + * + * @type {number} + */ snapIncrement = 1; - constructor(...args) { - super(...args); + /** + * Creates a new GizmoTransform object. + * + * @param {import('playcanvas').AppBase} app - The application instance. + * @param {Entity} camera - The camera entity. + * @example + * const gizmo = new pcx.GizmoTransform(app, camera); + */ + constructor(app, camera) { + super(app, camera); this.app.on('update', () => { if (!this.gizmo.enabled) { @@ -74,8 +194,8 @@ class GizmoTransform extends Gizmo { // draw guidelines const gizmoPos = this.gizmo.getPosition(); tmpQ1.copy(this.gizmo.getRotation()); - const checkAxis = this._hoverAxis || this._currAxis; - const checkIsPlane = this._hoverIsPlane || this._currIsPlane; + const checkAxis = this._hoverAxis || this._selectedAxis; + const checkIsPlane = this._hoverIsPlane || this._selectedIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { const axis = VEC3_AXES[i]; if (checkAxis === 'xyz') { @@ -97,34 +217,35 @@ class GizmoTransform extends Gizmo { this.on('pointer:move', (x, y, meshInstance) => { this._hover(meshInstance); - if (this.dragging) { + if (this._dragging) { const pointInfo = this._calcPoint(x, y); - this._offset.copy(pointInfo.point).sub(this._pointStart); - this.fire('transform:move', this._currAxis, this._offset, pointInfo.angle - this._angleStart); + pointDelta.copy(pointInfo.point).sub(this._pointStart); + const angleDelta = pointInfo.angle - this._angleStart; + this.fire('transform:move', this._selectedAxis, pointDelta, angleDelta); this._hoverAxis = ''; this._hoverIsPlane = false; } }); this.on('pointer:down', (x, y, meshInstance) => { - if (this.dragging) { + if (this._dragging) { return; } if (meshInstance) { - this._currAxis = this._getAxis(meshInstance); - this._currIsPlane = this._getIsPlane(meshInstance); + this._selectedAxis = this._getAxis(meshInstance); + this._selectedIsPlane = this._getIsPlane(meshInstance); const pointInfo = this._calcPoint(x, y); this._pointStart.copy(pointInfo.point); this._angleStart = pointInfo.angle; this.fire('transform:start', this._pointStart); - this.dragging = true; + this._dragging = true; } }); this.on('pointer:up', () => { - this.dragging = false; - this._currAxis = ''; - this._currIsPlane = false; + this._dragging = false; + this._selectedAxis = ''; + this._selectedIsPlane = false; }); this.on('key:down', (key, shiftKey) => { @@ -204,7 +325,7 @@ class GizmoTransform extends Gizmo { } _hover(meshInstance) { - if (this.dragging) { + if (this._dragging) { return; } this._hoverAxis = this._getAxis(meshInstance); @@ -229,8 +350,8 @@ class GizmoTransform extends Gizmo { const rayOrigin = this.camera.getPosition(); const rayDir = new Vec3(); const planeNormal = new Vec3(); - const axis = this._currAxis; - const isPlane = this._currIsPlane; + const axis = this._selectedAxis; + const isPlane = this._selectedIsPlane; const isRotation = this._isRotation; const isAllAxes = axis === 'xyz'; const isFacing = axis === 'face'; @@ -337,9 +458,18 @@ class GizmoTransform extends Gizmo { } _createTransform() { - // center - this._center = new Entity('center'); - this.gizmo.addChild(this._center); + // mesh root + this._meshRoot = new Entity('center'); + this.gizmo.addChild(this._meshRoot); + + // shapes + for (const key in this._axisShapes) { + const shape = this._axisShapes[key]; + this._meshRoot.addChild(shape.entity); + for (let i = 0; i < shape.meshInstances.length; i++) { + this._shapeMap.set(shape.meshInstances[i], shape); + } + } } } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index ab8b7ada843..afbe897e7fd 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -11,6 +11,11 @@ const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); +/** + * Translation gizmo. + * + * @augments GizmoTransform + */ class GizmoTranslate extends GizmoTransform { _axisShapes = { yz: new AxisPlane({ @@ -66,8 +71,16 @@ class GizmoTranslate extends GizmoTransform { snapIncrement = 1; - constructor(...args) { - super(...args); + /** + * Creates a new GizmoTranslate object. + * + * @param {import('playcanvas').AppBase} app - The application instance. + * @param {import('playcanvas').Entity} camera - The camera entity. + * @example + * const gizmo = new pcx.GizmoTranslate(app, camera); + */ + constructor(app, camera) { + super(app, camera); this._createTransform(); @@ -178,19 +191,6 @@ class GizmoTranslate extends GizmoTransform { this._axisShapes.xy[propName] = value; } - _createTransform() { - super._createTransform(); - - // shapes - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; - this._center.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this._shapeMap.set(shape.meshInstances[i], shape); - } - } - } - _checkForPlaneFlip() { const gizmoPos = this.gizmo.getPosition(); const cameraPos = this.camera.getPosition(); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d6c60ea038d..1e5d6062362 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -30,6 +30,9 @@ const EPSILON = 1e-6; const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; +/** + * The base class for all gizmos. + */ class Gizmo extends EventHandler { /** * Internal version of the gizmo size. From 99b5c66fec954dc50ee1467edbcd75895e74d27f Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 15:54:26 +0000 Subject: [PATCH 084/178] rename entity mesh root --- extras/gizmo/gizmo-transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index ac3c7f89b08..15de0a0132a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -459,7 +459,7 @@ class GizmoTransform extends Gizmo { _createTransform() { // mesh root - this._meshRoot = new Entity('center'); + this._meshRoot = new Entity('meshRoot'); this.gizmo.addChild(this._meshRoot); // shapes From fa5b2dd023073e4f270a88998ef82e2b2b0e47e6 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 15:56:25 +0000 Subject: [PATCH 085/178] refactor shapeMap --- extras/gizmo/gizmo-transform.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 15de0a0132a..a5379ccbe14 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -79,7 +79,7 @@ class GizmoTransform extends Gizmo { * @type {Map} * @private */ - _shapeMap = new Map(); + _axisShapeMap = new Map(); /** * Internal currently hovered shape. @@ -330,7 +330,7 @@ class GizmoTransform extends Gizmo { } this._hoverAxis = this._getAxis(meshInstance); this._hoverIsPlane = this._getIsPlane(meshInstance); - const shape = this._shapeMap.get(meshInstance); + const shape = this._axisShapeMap.get(meshInstance); if (shape === this._hoverShape) { return; } @@ -467,7 +467,7 @@ class GizmoTransform extends Gizmo { const shape = this._axisShapes[key]; this._meshRoot.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this._shapeMap.set(shape.meshInstances[i], shape); + this._axisShapeMap.set(shape.meshInstances[i], shape); } } } From 432dcff4dccc29de825d2d16e783aa90146067b5 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 15:58:07 +0000 Subject: [PATCH 086/178] refactored axisShape to shape --- extras/gizmo/gizmo-rotate.js | 22 ++++++++++----------- extras/gizmo/gizmo-scale.js | 34 ++++++++++++++++----------------- extras/gizmo/gizmo-transform.js | 12 ++++++------ extras/gizmo/gizmo-translate.js | 32 +++++++++++++++---------------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 75f2b63427a..ce046db70e0 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -17,7 +17,7 @@ const tmpQ2 = new Quat(); * @augments GizmoTransform */ class GizmoRotate extends GizmoTransform { - _axisShapes = { + _shapes = { z: new AxisDisk({ device: this.app.graphicsDevice, axis: 'z', @@ -110,7 +110,7 @@ class GizmoRotate extends GizmoTransform { } get xyzTubeRadius() { - return this._axisShapes.x.tubeRadius; + return this._shapes.x.tubeRadius; } set xyzRingRadius(value) { @@ -118,35 +118,35 @@ class GizmoRotate extends GizmoTransform { } get xyzRingRadius() { - return this._axisShapes.x.ringRadius; + return this._shapes.x.ringRadius; } set faceTubeRadius(value) { - this._axisShapes.face.tubeRadius = value; + this._shapes.face.tubeRadius = value; } get faceTubeRadius() { - return this._axisShapes.face.tubeRadius; + return this._shapes.face.tubeRadius; } set faceRingRadius(value) { - this._axisShapes.face.ringRadius = value; + this._shapes.face.ringRadius = value; } get faceRingRadius() { - return this._axisShapes.face.ringRadius; + return this._shapes.face.ringRadius; } _setDiskProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; + this._shapes.x[propName] = value; + this._shapes.y[propName] = value; + this._shapes.z[propName] = value; this._ring[propName] = value; } _setFacingDisks() { this._faceDiskToCamera(this._ring.entity); - this._faceDiskToCamera(this._axisShapes.face.entity); + this._faceDiskToCamera(this._shapes.face.entity); } _faceDiskToCamera(entity) { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 5059c1a8961..bf7f4ca84d8 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -14,7 +14,7 @@ const tmpV1 = new Vec3(); * @augments GizmoTransform */ class GizmoScale extends GizmoTransform { - _axisShapes = { + _shapes = { xyz: new AxisBoxCenter({ axis: 'xyz', layers: [this.layer.id], @@ -89,8 +89,8 @@ class GizmoScale extends GizmoTransform { this._createTransform(); - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; + for (const key in this._shapes) { + const shape = this._shapes[key]; if (!(shape instanceof AxisPlane)) { continue; } @@ -136,7 +136,7 @@ class GizmoScale extends GizmoTransform { } get axisGap() { - return this._axisShapes.x.gap; + return this._shapes.x.gap; } set axisLineThickness(value) { @@ -144,7 +144,7 @@ class GizmoScale extends GizmoTransform { } get axisLineThickness() { - return this._axisShapes.x.lineThickness; + return this._shapes.x.lineThickness; } set axisLineLength(value) { @@ -152,7 +152,7 @@ class GizmoScale extends GizmoTransform { } get axisLineLength() { - return this._axisShapes.x.lineLength; + return this._shapes.x.lineLength; } set axisBoxSize(value) { @@ -160,7 +160,7 @@ class GizmoScale extends GizmoTransform { } get axisBoxSize() { - return this._axisShapes.x.boxSize; + return this._shapes.x.boxSize; } set axisPlaneSize(value) { @@ -168,7 +168,7 @@ class GizmoScale extends GizmoTransform { } get axisPlaneSize() { - return this._axisShapes.yz.size; + return this._shapes.yz.size; } set axisPlaneGap(value) { @@ -176,27 +176,27 @@ class GizmoScale extends GizmoTransform { } get axisPlaneGap() { - return this._axisShapes.yz.gap; + return this._shapes.yz.gap; } set axisCenterSize(value) { - this._axisShapes.xyz.size = value; + this._shapes.xyz.size = value; } get axisCenterSize() { - return this._axisShapes.xyz.size; + return this._shapes.xyz.size; } _setArrowProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; + this._shapes.x[propName] = value; + this._shapes.y[propName] = value; + this._shapes.z[propName] = value; } _setPlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; + this._shapes.yz[propName] = value; + this._shapes.xz[propName] = value; + this._shapes.xy[propName] = value; } _checkForPlaneFlip() { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index a5379ccbe14..1a022b94950 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -71,7 +71,7 @@ class GizmoTransform extends Gizmo { * @type {Object.} * @protected */ - _axisShapes = {}; + _shapes = {}; /** * Internal mapping of mesh instances to axis shapes. @@ -79,7 +79,7 @@ class GizmoTransform extends Gizmo { * @type {Map} * @private */ - _axisShapeMap = new Map(); + _shapeMap = new Map(); /** * Internal currently hovered shape. @@ -330,7 +330,7 @@ class GizmoTransform extends Gizmo { } this._hoverAxis = this._getAxis(meshInstance); this._hoverIsPlane = this._getIsPlane(meshInstance); - const shape = this._axisShapeMap.get(meshInstance); + const shape = this._shapeMap.get(meshInstance); if (shape === this._hoverShape) { return; } @@ -463,11 +463,11 @@ class GizmoTransform extends Gizmo { this.gizmo.addChild(this._meshRoot); // shapes - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; + for (const key in this._shapes) { + const shape = this._shapes[key]; this._meshRoot.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this._axisShapeMap.set(shape.meshInstances[i], shape); + this._shapeMap.set(shape.meshInstances[i], shape); } } } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index afbe897e7fd..054c2b021bf 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -17,7 +17,7 @@ const tmpQ1 = new Quat(); * @augments GizmoTransform */ class GizmoTranslate extends GizmoTransform { - _axisShapes = { + _shapes = { yz: new AxisPlane({ axis: 'x', flipAxis: 'y', @@ -85,8 +85,8 @@ class GizmoTranslate extends GizmoTransform { this._createTransform(); this._planes = []; - for (const key in this._axisShapes) { - const shape = this._axisShapes[key]; + for (const key in this._shapes) { + const shape = this._shapes[key]; if (!(shape instanceof AxisPlane)) { continue; } @@ -128,7 +128,7 @@ class GizmoTranslate extends GizmoTransform { } get axisGap() { - return this._axisShapes.x.gap; + return this._shapes.x.gap; } set axisLineThickness(value) { @@ -136,7 +136,7 @@ class GizmoTranslate extends GizmoTransform { } get axisLineThickness() { - return this._axisShapes.x.lineThickness; + return this._shapes.x.lineThickness; } set axisLineLength(value) { @@ -144,7 +144,7 @@ class GizmoTranslate extends GizmoTransform { } get axisLineLength() { - return this._axisShapes.x.lineLength; + return this._shapes.x.lineLength; } set axisArrowThickness(value) { @@ -152,7 +152,7 @@ class GizmoTranslate extends GizmoTransform { } get axisArrowThickness() { - return this._axisShapes.x.arrowThickness; + return this._shapes.x.arrowThickness; } set axisArrowLength(value) { @@ -160,7 +160,7 @@ class GizmoTranslate extends GizmoTransform { } get axisArrowLength() { - return this._axisShapes.x.arrowLength; + return this._shapes.x.arrowLength; } set axisPlaneSize(value) { @@ -168,7 +168,7 @@ class GizmoTranslate extends GizmoTransform { } get axisPlaneSize() { - return this._axisShapes.yz.size; + return this._shapes.yz.size; } set axisPlaneGap(value) { @@ -176,19 +176,19 @@ class GizmoTranslate extends GizmoTransform { } get axisPlaneGap() { - return this._axisShapes.yz.gap; + return this._shapes.yz.gap; } _setArrowProp(propName, value) { - this._axisShapes.x[propName] = value; - this._axisShapes.y[propName] = value; - this._axisShapes.z[propName] = value; + this._shapes.x[propName] = value; + this._shapes.y[propName] = value; + this._shapes.z[propName] = value; } _setPlaneProp(propName, value) { - this._axisShapes.yz[propName] = value; - this._axisShapes.xz[propName] = value; - this._axisShapes.xy[propName] = value; + this._shapes.yz[propName] = value; + this._shapes.xz[propName] = value; + this._shapes.xy[propName] = value; } _checkForPlaneFlip() { From 53742f6d6c0ffcc979b07c7f62c405d2d40b5f90 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 16:00:55 +0000 Subject: [PATCH 087/178] refactor propName to prop --- extras/gizmo/gizmo-rotate.js | 10 +++++----- extras/gizmo/gizmo-scale.js | 16 ++++++++-------- extras/gizmo/gizmo-translate.js | 19 ++++++++++--------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index ce046db70e0..35f10b3a2c4 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -137,11 +137,11 @@ class GizmoRotate extends GizmoTransform { return this._shapes.face.ringRadius; } - _setDiskProp(propName, value) { - this._shapes.x[propName] = value; - this._shapes.y[propName] = value; - this._shapes.z[propName] = value; - this._ring[propName] = value; + _setDiskProp(prop, value) { + this._shapes.x[prop] = value; + this._shapes.y[prop] = value; + this._shapes.z[prop] = value; + this._ring[prop] = value; } _setFacingDisks() { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index bf7f4ca84d8..586ce019592 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -187,16 +187,16 @@ class GizmoScale extends GizmoTransform { return this._shapes.xyz.size; } - _setArrowProp(propName, value) { - this._shapes.x[propName] = value; - this._shapes.y[propName] = value; - this._shapes.z[propName] = value; + _setArrowProp(prop, value) { + this._shapes.x[prop] = value; + this._shapes.y[prop] = value; + this._shapes.z[prop] = value; } - _setPlaneProp(propName, value) { - this._shapes.yz[propName] = value; - this._shapes.xz[propName] = value; - this._shapes.xy[propName] = value; + _setPlaneProp(prop, value) { + this._shapes.yz[prop] = value; + this._shapes.xz[prop] = value; + this._shapes.xy[prop] = value; } _checkForPlaneFlip() { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 054c2b021bf..403b32c47fb 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -65,6 +65,8 @@ class GizmoTranslate extends GizmoTransform { }) }; + _planes = []; + _nodeLocalPositions = new Map(); _nodePositions = new Map(); @@ -84,7 +86,6 @@ class GizmoTranslate extends GizmoTransform { this._createTransform(); - this._planes = []; for (const key in this._shapes) { const shape = this._shapes[key]; if (!(shape instanceof AxisPlane)) { @@ -179,16 +180,16 @@ class GizmoTranslate extends GizmoTransform { return this._shapes.yz.gap; } - _setArrowProp(propName, value) { - this._shapes.x[propName] = value; - this._shapes.y[propName] = value; - this._shapes.z[propName] = value; + _setArrowProp(prop, value) { + this._shapes.x[prop] = value; + this._shapes.y[prop] = value; + this._shapes.z[prop] = value; } - _setPlaneProp(propName, value) { - this._shapes.yz[propName] = value; - this._shapes.xz[propName] = value; - this._shapes.xy[propName] = value; + _setPlaneProp(prop, value) { + this._shapes.yz[prop] = value; + this._shapes.xz[prop] = value; + this._shapes.xy[prop] = value; } _checkForPlaneFlip() { From 938aeb6f38166c6aa65dd71956a95861024ae9da Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 16:13:50 +0000 Subject: [PATCH 088/178] added jsdoc to translate rotate and scale classes --- extras/gizmo/gizmo-rotate.js | 34 ++++++++++++++++++++++++++++----- extras/gizmo/gizmo-scale.js | 12 ++++++++++++ extras/gizmo/gizmo-translate.js | 18 +++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 35f10b3a2c4..11288b19e76 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -54,12 +54,36 @@ class GizmoRotate extends GizmoTransform { _isRotation = true; - _ring; + /** + * Internal axis shape for guide ring. + * + * @type {AxisDisk} + * @private + */ + _guideRingShape; + /** + * Internal mapping from each attached node to their starting rotation (euler angles) in local space. + * + * @type {Map} + * @private + */ _nodeLocalRotations = new Map(); + /** + * Internal mapping from each attached node to their starting rotation (euler angles) in world space. + * + * @type {Map} + * @private + */ _nodeRotations = new Map(); + /** + * Internal mapping from each attached node to their offset position from the gizmo. + * + * @type {Map} + * @private + */ _nodeOffsets = new Map(); snapIncrement = 5; @@ -141,11 +165,11 @@ class GizmoRotate extends GizmoTransform { this._shapes.x[prop] = value; this._shapes.y[prop] = value; this._shapes.z[prop] = value; - this._ring[prop] = value; + this._guideRingShape[prop] = value; } _setFacingDisks() { - this._faceDiskToCamera(this._ring.entity); + this._faceDiskToCamera(this._guideRingShape.entity); this._faceDiskToCamera(this._shapes.face.entity); } @@ -158,12 +182,12 @@ class GizmoRotate extends GizmoTransform { super._createTransform(); // guide ring - this._ring = new AxisDisk({ + this._guideRingShape = new AxisDisk({ device: this.app.graphicsDevice, layers: [this.layer.id], defaultColor: this._materials.guide }); - this._meshRoot.addChild(this._ring.entity); + this._meshRoot.addChild(this._guideRingShape.entity); } _storeNodeRotations() { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 586ce019592..758bda38e49 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -70,8 +70,20 @@ class GizmoScale extends GizmoTransform { _coordSpace = 'local'; + /** + * Internal object containing the axis planes to flip. + * + * @type {import('./axis-shapes.js').AxisPlane[]} + * @private + */ _planes = []; + /** + * Internal mapping from each attached node to their starting scale. + * + * @type {Map} + * @private + */ _nodeScales = new Map(); snapIncrement = 1; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 403b32c47fb..6e4a54293dd 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -65,10 +65,28 @@ class GizmoTranslate extends GizmoTransform { }) }; + /** + * Internal object containing the axis planes to flip. + * + * @type {import('./axis-shapes.js').AxisPlane[]} + * @private + */ _planes = []; + /** + * Internal mapping from each attached node to their starting position in local space. + * + * @type {Map} + * @private + */ _nodeLocalPositions = new Map(); + /** + * Internal mapping from each attached node to their starting position in world space. + * + * @type {Map} + * @private + */ _nodePositions = new Map(); snapIncrement = 1; From 53990cd313886f850db4b8abaa2230d8d948f6d4 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 16:16:12 +0000 Subject: [PATCH 089/178] added snap increment setting --- examples/src/examples/misc/gizmos.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 227d40fa646..9eb7eca28ae 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -42,6 +42,15 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { min: 0.1, max: 2.0 }) + ), + jsx(LabelGroup, { text: 'Snap Increment' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.snapIncrement' }, + min: 1, + max: 10, + precision: 0 + }) ) ), jsx(Panel, { headerText: 'Color' }, @@ -247,6 +256,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { data.set('gizmo', { type: type, size: gizmo.size, + snapIncrement: gizmo.snapIncrement, xAxisColor: Object.values(gizmo.xAxisColor), yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), From 2cf38d9170f74c0069b2ba8db7db4096ea4c7893 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 16:21:48 +0000 Subject: [PATCH 090/178] fixed potential division by zero --- extras/gizmo/gizmo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 1e5d6062362..072c5f452c2 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -192,7 +192,7 @@ class Gizmo extends EventHandler { const node = this.nodes[i]; tmpV1.add(node.getPosition()); } - tmpV1.scale(1.0 / this.nodes.length); + tmpV1.scale(1.0 / (this.nodes.length || 1)); this.gizmo.setPosition(tmpV1); this.fire('position:set', tmpV1); @@ -205,7 +205,7 @@ class Gizmo extends EventHandler { const node = this.nodes[i]; tmpV1.add(node.getEulerAngles()); } - tmpV1.scale(1.0 / this.nodes.length); + tmpV1.scale(1.0 / (this.nodes.length || 1)); this.gizmo.setEulerAngles(tmpV1); } else { tmpV1.set(0, 0, 0); From 861a923886543c101997394e22a6d9f3688d8392 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 16:26:37 +0000 Subject: [PATCH 091/178] fixed updateScale issue and added jsdoc --- examples/src/examples/misc/gizmos.mjs | 2 +- extras/gizmo/gizmo.js | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 9eb7eca28ae..748fe3923b2 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -402,7 +402,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { camera.camera.orthoHeight = value; break; } - gizmoHandler.gizmo.updateGizmoScale(); + gizmoHandler.gizmo.updateScale(); return; case 'gizmo': if (gizmoHandler.skipSetFire) { diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 072c5f452c2..c613d832b65 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -96,7 +96,7 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this._updateScale(); + this.updateScale(); this._onPointerMove = (e) => { if (!this.gizmo.enabled) { @@ -146,7 +146,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this._updateScale(); + this.updateScale(); this.fire('size:set', this._size); } @@ -215,7 +215,15 @@ class Gizmo extends EventHandler { this.fire('eulerAngles:set', tmpV1); } - _updateScale() { + /** + * Updates the scale of the gizmo based on projection + * and the distance from the camera. + * + * @example + * const gizmo = new pcx.Gizmo(app, camera); + * gizmo.updateScale(); + */ + updateScale() { let scale = 1; if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; From 40419d764cfa0993d5f813616b1a847848b25192 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 17:27:47 +0000 Subject: [PATCH 092/178] used camera component instead of camera entity --- examples/src/examples/misc/gizmos.mjs | 2 +- extras/gizmo/gizmo-rotate.js | 6 +++--- extras/gizmo/gizmo-scale.js | 4 ++-- extras/gizmo/gizmo-transform.js | 12 ++++++------ extras/gizmo/gizmo-translate.js | 4 ++-- extras/gizmo/gizmo.js | 20 ++++++++++---------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 748fe3923b2..728f4aa4ece 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -379,7 +379,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { light.setEulerAngles(45, 20, 0); // create gizmo - const gizmoHandler = new GizmoHandler(app, camera); + const gizmoHandler = new GizmoHandler(app, camera.camera); gizmoHandler.switch('translate', [boxA, boxB]); const tmpC = new pc.Color(); diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 11288b19e76..90349d2f051 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -92,7 +92,7 @@ class GizmoRotate extends GizmoTransform { * Creates a new GizmoRotate object. * * @param {import('playcanvas').AppBase} app - The application instance. - * @param {import('playcanvas').Entity} camera - The camera entity. + * @param {import('playcanvas').CameraComponent} camera - The camera component. * @example * const gizmo = new pcx.GizmoRotate(app, camera); */ @@ -174,7 +174,7 @@ class GizmoRotate extends GizmoTransform { } _faceDiskToCamera(entity) { - entity.lookAt(this.camera.getPosition()); + entity.lookAt(this.camera.entity.getPosition()); entity.rotateLocal(90, 0, 0); } @@ -202,7 +202,7 @@ class GizmoRotate extends GizmoTransform { _setNodeRotations(axis, angle) { const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.getPosition(); + const cameraPos = this.camera.entity.getPosition(); const isFacing = axis === 'face'; for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 758bda38e49..1a4881a40a6 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -92,7 +92,7 @@ class GizmoScale extends GizmoTransform { * Creates a new GizmoScale object. * * @param {import('playcanvas').AppBase} app - The application instance. - * @param {import('playcanvas').Entity} camera - The camera entity. + * @param {import('playcanvas').CameraComponent} camera - The camera component. * @example * const gizmo = new pcx.GizmoScale(app, camera); */ @@ -213,7 +213,7 @@ class GizmoScale extends GizmoTransform { _checkForPlaneFlip() { const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.getPosition(); + const cameraPos = this.camera.entity.getPosition(); tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); for (let i = 0; i < this._planes.length; i++) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 1a022b94950..be7436700d6 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -179,7 +179,7 @@ class GizmoTransform extends Gizmo { * Creates a new GizmoTransform object. * * @param {import('playcanvas').AppBase} app - The application instance. - * @param {Entity} camera - The camera entity. + * @param {import('playcanvas').CameraComponent} camera - The camera component. * @example * const gizmo = new pcx.GizmoTransform(app, camera); */ @@ -346,8 +346,8 @@ class GizmoTransform extends Gizmo { _calcPoint(x, y) { const gizmoPos = this.gizmo.getPosition(); - const mouseWPos = this.camera.camera.screenToWorld(x, y, 1); - const rayOrigin = this.camera.getPosition(); + const mouseWPos = this.camera.screenToWorld(x, y, 1); + const rayOrigin = this.camera.entity.getPosition(); const rayDir = new Vec3(); const planeNormal = new Vec3(); const axis = this._selectedAxis; @@ -357,11 +357,11 @@ class GizmoTransform extends Gizmo { const isFacing = axis === 'face'; // calculate ray direction from mouse position - if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + if (this.camera.projection === PROJECTION_PERSPECTIVE) { rayDir.copy(mouseWPos).sub(rayOrigin).normalize(); } else { rayOrigin.add(mouseWPos); - this.camera.getWorldTransform().transformVector(tmpV1.set(0, 0, -1), rayDir); + this.camera.entity.getWorldTransform().transformVector(tmpV1.set(0, 0, -1), rayDir); } if (isAllAxes || isFacing) { @@ -394,7 +394,7 @@ class GizmoTransform extends Gizmo { if (isAllAxes) { // calculate point distance from gizmo tmpV1.copy(point).sub(gizmoPos).normalize(); - tmpV2.copy(this.camera.up).add(this.camera.right).normalize(); + tmpV2.copy(this.camera.entity.up).add(this.camera.entity.right).normalize(); const v = point.sub(gizmoPos).length() * tmpV1.dot(tmpV2); point.set(v, v, v); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 6e4a54293dd..645997f4817 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -95,7 +95,7 @@ class GizmoTranslate extends GizmoTransform { * Creates a new GizmoTranslate object. * * @param {import('playcanvas').AppBase} app - The application instance. - * @param {import('playcanvas').Entity} camera - The camera entity. + * @param {import('playcanvas').CameraComponent} camera - The camera component. * @example * const gizmo = new pcx.GizmoTranslate(app, camera); */ @@ -212,7 +212,7 @@ class GizmoTranslate extends GizmoTransform { _checkForPlaneFlip() { const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.getPosition(); + const cameraPos = this.camera.entity.getPosition(); tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); for (let i = 0; i < this._planes.length; i++) { diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c613d832b65..2ede0703762 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -83,7 +83,7 @@ class Gizmo extends EventHandler { * Creates a new Gizmo object. * * @param {import('playcanvas').AppBase} app - The application instance. - * @param {Entity} camera - The camera entity. + * @param {import('playcanvas').CameraComponent} camera - The camera component. * @example * const gizmo = new pcx.Gizmo(app, camera); */ @@ -156,9 +156,9 @@ class Gizmo extends EventHandler { _getProjFrustumWidth() { const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.getPosition(); - const dist = tmpV1.copy(gizmoPos).sub(cameraPos).dot(this.camera.forward); - return dist * Math.tan(this.camera.camera.fov * math.DEG_TO_RAD / 2); + const cameraPos = this.camera.entity.getPosition(); + const dist = tmpV1.copy(gizmoPos).sub(cameraPos).dot(this.camera.entity.forward); + return dist * Math.tan(this.camera.fov * math.DEG_TO_RAD / 2); } _createLayer() { @@ -175,8 +175,8 @@ class Gizmo extends EventHandler { }); this.app.scene.layers.push(this.layer); } - if (this.camera.camera.layers.indexOf(this.layer.id) === -1) { - this.camera.camera.layers = this.camera.camera.layers.concat(this.layer.id); + if (this.camera.layers.indexOf(this.layer.id) === -1) { + this.camera.layers = this.camera.layers.concat(this.layer.id); } } @@ -225,10 +225,10 @@ class Gizmo extends EventHandler { */ updateScale() { let scale = 1; - if (this.camera.camera.projection === PROJECTION_PERSPECTIVE) { + if (this.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; } else { - scale = this.camera.camera.orthoHeight * ORTHO_SCALE_RATIO; + scale = this.camera.orthoHeight * ORTHO_SCALE_RATIO; } scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); tmpV1.set(scale, scale, scale); @@ -239,8 +239,8 @@ class Gizmo extends EventHandler { } _getSelection(x, y) { - const start = this.camera.camera.screenToWorld(x, y, 1); - const end = this.camera.camera.screenToWorld(x, y, this.camera.camera.farClip); + const start = this.camera.screenToWorld(x, y, 1); + const end = this.camera.screenToWorld(x, y, this.camera.farClip); const dir = end.clone().sub(start).normalize(); const selection = []; From 37f8daee984df119c5ee16a7801acbd25ebc246c Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 8 Jan 2024 18:14:54 +0000 Subject: [PATCH 093/178] removed check for flip; used material culling --- extras/gizmo/axis-shapes.js | 20 +--------- extras/gizmo/gizmo-rotate.js | 6 +-- extras/gizmo/gizmo-scale.js | 45 +++------------------ extras/gizmo/gizmo-transform.js | 71 +++++++++++++++++++++++---------- extras/gizmo/gizmo-translate.js | 49 +++-------------------- 5 files changed, 67 insertions(+), 124 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 87f9ec065ab..d44d5806b1d 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -3,15 +3,9 @@ import { Color, MeshInstance, Entity, - Vec3, - Quat + Vec3 } from 'playcanvas'; -// temporary variables -const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpQ1 = new Quat(); - // constants const TORUS_SEGMENTS = 80; @@ -394,18 +388,6 @@ class AxisPlane extends AxisShape { get gap() { return this._gap; } - - checkForFlip(screenDir) { - tmpV1.set(0, 1, 0); - tmpQ1.copy(this.entity.getRotation()).transformVector(tmpV1, tmpV1); - const dot = screenDir.dot(tmpV1); - if (dot > 0) { - return; - } - tmpV2.copy(this.entity.getLocalEulerAngles()); - tmpV2[this._flipAxis] = 180 - tmpV2[this._flipAxis]; - this.entity.setLocalEulerAngles(tmpV2); - } } export { AxisShape, AxisArrow, AxisBoxCenter, AxisBoxLine, AxisDisk, AxisPlane }; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 90349d2f051..b0a939c408b 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -23,7 +23,7 @@ class GizmoRotate extends GizmoTransform { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, + defaultColor: this._materials.axis.z.culled, hoverColor: this._materials.hover }), x: new AxisDisk({ @@ -31,7 +31,7 @@ class GizmoRotate extends GizmoTransform { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, + defaultColor: this._materials.axis.x.culled, hoverColor: this._materials.hover }), y: new AxisDisk({ @@ -39,7 +39,7 @@ class GizmoRotate extends GizmoTransform { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, + defaultColor: this._materials.axis.y.culled, hoverColor: this._materials.hover }), face: new AxisDisk({ diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 1a4881a40a6..b5080bd21e1 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -26,7 +26,7 @@ class GizmoScale extends GizmoTransform { flipAxis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, + defaultColor: this._materials.axis.x.noCull, hoverColor: this._materials.hover }), xz: new AxisPlane({ @@ -34,7 +34,7 @@ class GizmoScale extends GizmoTransform { flipAxis: 'z', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, + defaultColor: this._materials.axis.y.noCull, hoverColor: this._materials.hover }), xy: new AxisPlane({ @@ -42,42 +42,34 @@ class GizmoScale extends GizmoTransform { flipAxis: 'x', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, + defaultColor: this._materials.axis.z.noCull, hoverColor: this._materials.hover }), x: new AxisBoxLine({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, + defaultColor: this._materials.axis.x.culled, hoverColor: this._materials.hover }), y: new AxisBoxLine({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, + defaultColor: this._materials.axis.y.culled, hoverColor: this._materials.hover }), z: new AxisBoxLine({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, + defaultColor: this._materials.axis.z.culled, hoverColor: this._materials.hover }) }; _coordSpace = 'local'; - /** - * Internal object containing the axis planes to flip. - * - * @type {import('./axis-shapes.js').AxisPlane[]} - * @private - */ - _planes = []; - /** * Internal mapping from each attached node to their starting scale. * @@ -101,19 +93,9 @@ class GizmoScale extends GizmoTransform { this._createTransform(); - for (const key in this._shapes) { - const shape = this._shapes[key]; - if (!(shape instanceof AxisPlane)) { - continue; - } - this._planes.push(shape); - } - this._checkForPlaneFlip(); - this.on('transform:start', (start) => { start.sub(Vec3.ONE); this._storeNodeScales(); - this._checkForPlaneFlip(); }); this.on('transform:move', (axis, offset) => { @@ -123,11 +105,6 @@ class GizmoScale extends GizmoTransform { offset.scale(this.snapIncrement); } this._setNodeScales(offset); - this._checkForPlaneFlip(); - }); - - this.on('nodes:attach', () => { - this._checkForPlaneFlip(); }); this.on('nodes:detach', () => { @@ -211,16 +188,6 @@ class GizmoScale extends GizmoTransform { this._shapes.xy[prop] = value; } - _checkForPlaneFlip() { - const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.entity.getPosition(); - tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); - - for (let i = 0; i < this._planes.length; i++) { - this._planes[i].checkForFlip(tmpV1); - } - } - _storeNodeScales() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index be7436700d6..e71450308ed 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,5 +1,6 @@ import { math, + CULLFACE_NONE, PROJECTION_PERSPECTIVE, BLEND_NORMAL, Color, @@ -21,6 +22,12 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; +const RED_COLOR = new Color(1, 0.3, 0.3); +const BLUE_COLOR = new Color(0.3, 1, 0.3); +const GREEN_COLOR = new Color(0.3, 0.3, 1); +const YELLOW_COLOR = new Color(1, 1, 0.3); +const SEMI_YELLOW_COLOR = new Color(1, 1, 0.3, 0.5); +const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.5); /** * The base class for all transform gizmos. @@ -33,9 +40,15 @@ class GizmoTransform extends Gizmo { * * @typedef MaterialsObject * @property {Object} axis - The object containing axis materials. - * @property {StandardMaterial} axis.x - The X axis material. - * @property {StandardMaterial} axis.y - The Y axis material. - * @property {StandardMaterial} axis.z - The Z axis material. + * @property {Object} axis.x - The object containing the X axis materials. + * @property {StandardMaterial} axis.x.culled - The X axis material with culling. + * @property {StandardMaterial} axis.x.noCull - The X axis material without culling. + * @property {Object} axis.y - The object containing the Y axis materials. + * @property {StandardMaterial} axis.y.culled - The Y axis material with culling. + * @property {StandardMaterial} axis.y.noCull - The Y axis material without culling. + * @property {Object} axis.z - The object containing the Z axis materials. + * @property {StandardMaterial} axis.z.culled - The Z axis material with culling. + * @property {StandardMaterial} axis.z.noCull - The Z axis material without culling. * @property {StandardMaterial} axis.face - The camera facing (face) axis material. Only for rotate * @property {StandardMaterial} hover - The hover material. * @property {StandardMaterial} center - The center shape material. Only for scale. @@ -47,14 +60,23 @@ class GizmoTransform extends Gizmo { */ _materials = { axis: { - x: this._createMaterial(new Color(1, 0.3, 0.3)), - y: this._createMaterial(new Color(0.3, 1, 0.3)), - z: this._createMaterial(new Color(0.3, 0.3, 1)), - face: this._createMaterial(new Color(1, 1, 0.3, 0.5)) + x: { + culled: this._createMaterial(RED_COLOR), + noCull: this._createMaterial(RED_COLOR, true) + }, + y: { + culled: this._createMaterial(GREEN_COLOR), + noCull: this._createMaterial(GREEN_COLOR, true) + }, + z: { + culled: this._createMaterial(BLUE_COLOR), + noCull: this._createMaterial(BLUE_COLOR, true) + }, + face: this._createMaterial(SEMI_YELLOW_COLOR) }, - hover: this._createMaterial(new Color(1, 1, 0.3)), - center: this._createMaterial(new Color(1, 1, 1, 0.5)), - guide: this._createMaterial(new Color(1, 1, 1, 0.5)) + hover: this._createMaterial(YELLOW_COLOR), + center: this._createMaterial(SEMI_WHITE_COLOR), + guide: this._createMaterial(SEMI_WHITE_COLOR) }; /** @@ -258,30 +280,36 @@ class GizmoTransform extends Gizmo { } set xAxisColor(value) { - this._materials.axis.x.emissive.copy(value); - this._materials.axis.x.update(); + this._materials.axis.x.culled.emissive.copy(value); + this._materials.axis.x.noCull.emissive.copy(value); + this._materials.axis.x.culled.update(); + this._materials.axis.x.noCull.update(); } get xAxisColor() { - return this._materials.axis.x.emissive; + return this._materials.axis.x.culled.emissive; } set yAxisColor(value) { - this._materials.axis.y.emissive.copy(value); - this._materials.axis.y.update(); + this._materials.axis.y.culled.emissive.copy(value); + this._materials.axis.y.noCull.emissive.copy(value); + this._materials.axis.y.culled.update(); + this._materials.axis.y.noCull.update(); } get yAxisColor() { - return this._materials.axis.y.emissive; + return this._materials.axis.y.culled.emissive; } set zAxisColor(value) { - this._materials.axis.z.emissive.copy(value); - this._materials.axis.z.update(); + this._materials.axis.z.culled.emissive.copy(value); + this._materials.axis.z.noCull.emissive.copy(value); + this._materials.axis.z.culled.update(); + this._materials.axis.z.noCull.update(); } get zAxisColor() { - return this._materials.axis.z.emissive; + return this._materials.axis.z.culled.emissive; } set faceAxisColor(value) { @@ -447,9 +475,12 @@ class GizmoTransform extends Gizmo { this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true); } - _createMaterial(color) { + _createMaterial(color, disableCulling = false) { const material = new StandardMaterial(); material.emissive = color; + if (disableCulling) { + material.cull = CULLFACE_NONE; + } if (color.a !== 1) { material.opacity = color.a; material.blendType = BLEND_NORMAL; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 645997f4817..b4263488256 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -23,7 +23,7 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, + defaultColor: this._materials.axis.x.noCull, hoverColor: this._materials.hover }), xz: new AxisPlane({ @@ -31,7 +31,7 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'z', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, + defaultColor: this._materials.axis.y.noCull, hoverColor: this._materials.hover }), xy: new AxisPlane({ @@ -39,40 +39,32 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'x', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, + defaultColor: this._materials.axis.z.noCull, hoverColor: this._materials.hover }), x: new AxisArrow({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x, + defaultColor: this._materials.axis.x.culled, hoverColor: this._materials.hover }), y: new AxisArrow({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y, + defaultColor: this._materials.axis.y.culled, hoverColor: this._materials.hover }), z: new AxisArrow({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z, + defaultColor: this._materials.axis.z.culled, hoverColor: this._materials.hover }) }; - /** - * Internal object containing the axis planes to flip. - * - * @type {import('./axis-shapes.js').AxisPlane[]} - * @private - */ - _planes = []; - /** * Internal mapping from each attached node to their starting position in local space. * @@ -104,18 +96,8 @@ class GizmoTranslate extends GizmoTransform { this._createTransform(); - for (const key in this._shapes) { - const shape = this._shapes[key]; - if (!(shape instanceof AxisPlane)) { - continue; - } - this._planes.push(shape); - } - this._checkForPlaneFlip(); - this.on('transform:start', () => { this._storeNodePositions(); - this._checkForPlaneFlip(); }); this.on('transform:move', (axis, offset) => { @@ -125,15 +107,6 @@ class GizmoTranslate extends GizmoTransform { offset.scale(this.snapIncrement); } this._setNodePositions(offset); - this._checkForPlaneFlip(); - }); - - this.on('coordSpace:set', () => { - this._checkForPlaneFlip(); - }); - - this.on('nodes:attach', () => { - this._checkForPlaneFlip(); }); this.on('nodes:detach', () => { @@ -210,16 +183,6 @@ class GizmoTranslate extends GizmoTransform { this._shapes.xy[prop] = value; } - _checkForPlaneFlip() { - const gizmoPos = this.gizmo.getPosition(); - const cameraPos = this.camera.entity.getPosition(); - tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); - - for (let i = 0; i < this._planes.length; i++) { - this._planes[i].checkForFlip(tmpV1); - } - } - _storeNodePositions() { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; From b94a0c9161d88d0724365ef4ab0058ffa92eb013 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 10:19:17 +0000 Subject: [PATCH 094/178] fixed accidental axis color switch --- extras/gizmo/gizmo-transform.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index e71450308ed..51c194064f7 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -23,8 +23,8 @@ const pointDelta = new Vec3(); const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; const RED_COLOR = new Color(1, 0.3, 0.3); -const BLUE_COLOR = new Color(0.3, 1, 0.3); -const GREEN_COLOR = new Color(0.3, 0.3, 1); +const GREEN_COLOR = new Color(0.3, 1, 0.3); +const BLUE_COLOR = new Color(0.3, 0.3, 1); const YELLOW_COLOR = new Color(1, 1, 0.3); const SEMI_YELLOW_COLOR = new Color(1, 1, 0.3, 0.5); const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.5); From 98edd5d1e9fd5fe1976fbe8edeeaa17768ddbb78 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 10:23:20 +0000 Subject: [PATCH 095/178] renamed cull materials --- extras/gizmo/gizmo-rotate.js | 6 ++-- extras/gizmo/gizmo-scale.js | 12 ++++---- extras/gizmo/gizmo-transform.js | 54 ++++++++++++++++----------------- extras/gizmo/gizmo-translate.js | 12 ++++---- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index b0a939c408b..2fcfa8776ac 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -23,7 +23,7 @@ class GizmoRotate extends GizmoTransform { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z.culled, + defaultColor: this._materials.axis.z.cullBack, hoverColor: this._materials.hover }), x: new AxisDisk({ @@ -31,7 +31,7 @@ class GizmoRotate extends GizmoTransform { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x.culled, + defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover }), y: new AxisDisk({ @@ -39,7 +39,7 @@ class GizmoRotate extends GizmoTransform { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y.culled, + defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover }), face: new AxisDisk({ diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index b5080bd21e1..30d0116b778 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -26,7 +26,7 @@ class GizmoScale extends GizmoTransform { flipAxis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x.noCull, + defaultColor: this._materials.axis.x.cullNone, hoverColor: this._materials.hover }), xz: new AxisPlane({ @@ -34,7 +34,7 @@ class GizmoScale extends GizmoTransform { flipAxis: 'z', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y.noCull, + defaultColor: this._materials.axis.y.cullNone, hoverColor: this._materials.hover }), xy: new AxisPlane({ @@ -42,28 +42,28 @@ class GizmoScale extends GizmoTransform { flipAxis: 'x', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z.noCull, + defaultColor: this._materials.axis.z.cullNone, hoverColor: this._materials.hover }), x: new AxisBoxLine({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x.culled, + defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover }), y: new AxisBoxLine({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y.culled, + defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover }), z: new AxisBoxLine({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z.culled, + defaultColor: this._materials.axis.z.cullBack, hoverColor: this._materials.hover }) }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 51c194064f7..395eda08943 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -41,14 +41,14 @@ class GizmoTransform extends Gizmo { * @typedef MaterialsObject * @property {Object} axis - The object containing axis materials. * @property {Object} axis.x - The object containing the X axis materials. - * @property {StandardMaterial} axis.x.culled - The X axis material with culling. - * @property {StandardMaterial} axis.x.noCull - The X axis material without culling. + * @property {StandardMaterial} axis.x.cullBack - The X axis material with front culling. + * @property {StandardMaterial} axis.x.cullNone - The X axis material without culling. * @property {Object} axis.y - The object containing the Y axis materials. - * @property {StandardMaterial} axis.y.culled - The Y axis material with culling. - * @property {StandardMaterial} axis.y.noCull - The Y axis material without culling. + * @property {StandardMaterial} axis.y.cullBack - The Y axis material with front culling. + * @property {StandardMaterial} axis.y.cullNone - The Y axis material without culling. * @property {Object} axis.z - The object containing the Z axis materials. - * @property {StandardMaterial} axis.z.culled - The Z axis material with culling. - * @property {StandardMaterial} axis.z.noCull - The Z axis material without culling. + * @property {StandardMaterial} axis.z.cullBack - The Z axis material with front culling. + * @property {StandardMaterial} axis.z.cullNone - The Z axis material without culling. * @property {StandardMaterial} axis.face - The camera facing (face) axis material. Only for rotate * @property {StandardMaterial} hover - The hover material. * @property {StandardMaterial} center - The center shape material. Only for scale. @@ -61,16 +61,16 @@ class GizmoTransform extends Gizmo { _materials = { axis: { x: { - culled: this._createMaterial(RED_COLOR), - noCull: this._createMaterial(RED_COLOR, true) + cullBack: this._createMaterial(RED_COLOR), + cullNone: this._createMaterial(RED_COLOR, true) }, y: { - culled: this._createMaterial(GREEN_COLOR), - noCull: this._createMaterial(GREEN_COLOR, true) + cullBack: this._createMaterial(GREEN_COLOR), + cullNone: this._createMaterial(GREEN_COLOR, true) }, z: { - culled: this._createMaterial(BLUE_COLOR), - noCull: this._createMaterial(BLUE_COLOR, true) + cullBack: this._createMaterial(BLUE_COLOR), + cullNone: this._createMaterial(BLUE_COLOR, true) }, face: this._createMaterial(SEMI_YELLOW_COLOR) }, @@ -280,36 +280,36 @@ class GizmoTransform extends Gizmo { } set xAxisColor(value) { - this._materials.axis.x.culled.emissive.copy(value); - this._materials.axis.x.noCull.emissive.copy(value); - this._materials.axis.x.culled.update(); - this._materials.axis.x.noCull.update(); + this._materials.axis.x.cullBack.emissive.copy(value); + this._materials.axis.x.cullNone.emissive.copy(value); + this._materials.axis.x.cullBack.update(); + this._materials.axis.x.cullNone.update(); } get xAxisColor() { - return this._materials.axis.x.culled.emissive; + return this._materials.axis.x.cullBack.emissive; } set yAxisColor(value) { - this._materials.axis.y.culled.emissive.copy(value); - this._materials.axis.y.noCull.emissive.copy(value); - this._materials.axis.y.culled.update(); - this._materials.axis.y.noCull.update(); + this._materials.axis.y.cullBack.emissive.copy(value); + this._materials.axis.y.cullNone.emissive.copy(value); + this._materials.axis.y.cullBack.update(); + this._materials.axis.y.cullNone.update(); } get yAxisColor() { - return this._materials.axis.y.culled.emissive; + return this._materials.axis.y.cullBack.emissive; } set zAxisColor(value) { - this._materials.axis.z.culled.emissive.copy(value); - this._materials.axis.z.noCull.emissive.copy(value); - this._materials.axis.z.culled.update(); - this._materials.axis.z.noCull.update(); + this._materials.axis.z.cullBack.emissive.copy(value); + this._materials.axis.z.cullNone.emissive.copy(value); + this._materials.axis.z.cullBack.update(); + this._materials.axis.z.cullNone.update(); } get zAxisColor() { - return this._materials.axis.z.culled.emissive; + return this._materials.axis.z.cullBack.emissive; } set faceAxisColor(value) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index b4263488256..ddf87e0bbf3 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -23,7 +23,7 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x.noCull, + defaultColor: this._materials.axis.x.cullNone, hoverColor: this._materials.hover }), xz: new AxisPlane({ @@ -31,7 +31,7 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'z', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y.noCull, + defaultColor: this._materials.axis.y.cullNone, hoverColor: this._materials.hover }), xy: new AxisPlane({ @@ -39,28 +39,28 @@ class GizmoTranslate extends GizmoTransform { flipAxis: 'x', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z.noCull, + defaultColor: this._materials.axis.z.cullNone, hoverColor: this._materials.hover }), x: new AxisArrow({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), - defaultColor: this._materials.axis.x.culled, + defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover }), y: new AxisArrow({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), - defaultColor: this._materials.axis.y.culled, + defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover }), z: new AxisArrow({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), - defaultColor: this._materials.axis.z.culled, + defaultColor: this._materials.axis.z.cullBack, hoverColor: this._materials.hover }) }; From 33bdd9aadad7fe037dc2d4e8c2bc43c8963d22eb Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 10:27:34 +0000 Subject: [PATCH 096/178] jsdoc update for materials --- extras/gizmo/gizmo-transform.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 395eda08943..081b7c68161 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -41,13 +41,13 @@ class GizmoTransform extends Gizmo { * @typedef MaterialsObject * @property {Object} axis - The object containing axis materials. * @property {Object} axis.x - The object containing the X axis materials. - * @property {StandardMaterial} axis.x.cullBack - The X axis material with front culling. + * @property {StandardMaterial} axis.x.cullBack - The X axis material with back culling. * @property {StandardMaterial} axis.x.cullNone - The X axis material without culling. * @property {Object} axis.y - The object containing the Y axis materials. - * @property {StandardMaterial} axis.y.cullBack - The Y axis material with front culling. + * @property {StandardMaterial} axis.y.cullBack - The Y axis material with back culling. * @property {StandardMaterial} axis.y.cullNone - The Y axis material without culling. * @property {Object} axis.z - The object containing the Z axis materials. - * @property {StandardMaterial} axis.z.cullBack - The Z axis material with front culling. + * @property {StandardMaterial} axis.z.cullBack - The Z axis material with back culling. * @property {StandardMaterial} axis.z.cullNone - The Z axis material without culling. * @property {StandardMaterial} axis.face - The camera facing (face) axis material. Only for rotate * @property {StandardMaterial} hover - The hover material. From 35a8419c3ae3812ece5e7c1ceaa6b6f3cc2699e2 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 10:28:26 +0000 Subject: [PATCH 097/178] removed unused vector in gizmo scale --- extras/gizmo/gizmo-scale.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 30d0116b778..da778b70c9d 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -5,9 +5,6 @@ import { import { AxisBoxCenter, AxisBoxLine, AxisPlane } from './axis-shapes.js'; import { GizmoTransform } from "./gizmo-transform.js"; -// temporary variables -const tmpV1 = new Vec3(); - /** * Scaling gizmo. * From 4706dfb7997ccc8d3f815b1e141861fa8d35062b Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 11:24:10 +0000 Subject: [PATCH 098/178] add uniform scaling on planes --- extras/gizmo/gizmo-rotate.js | 2 +- extras/gizmo/gizmo-scale.js | 28 +++++++++++++++++++++++++++- extras/gizmo/gizmo-transform.js | 6 +++--- extras/gizmo/gizmo-translate.js | 2 +- extras/gizmo/gizmo.js | 2 +- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 2fcfa8776ac..0d7fe0fa763 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -106,7 +106,7 @@ class GizmoRotate extends GizmoTransform { this._storeNodeRotations(); }); - this.on('transform:move', (axis, offset, angle) => { + this.on('transform:move', (axis, isPlane, offset, angle) => { if (this.snap) { angle = Math.round(angle / this.snapIncrement) * this.snapIncrement; } diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index da778b70c9d..6d2e66a425d 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -5,6 +5,9 @@ import { import { AxisBoxCenter, AxisBoxLine, AxisPlane } from './axis-shapes.js'; import { GizmoTransform } from "./gizmo-transform.js"; +// temporary variables +const tmpV1 = new Vec3(); + /** * Scaling gizmo. * @@ -75,6 +78,13 @@ class GizmoScale extends GizmoTransform { */ _nodeScales = new Map(); + /** + * State for if uniform scaling is enabled for planes. Defaults to true. + * + * @type {boolean} + */ + uniform = true; + snapIncrement = 1; /** @@ -90,17 +100,33 @@ class GizmoScale extends GizmoTransform { this._createTransform(); + this.on('key:down', (key, shiftKey, ctrlKey) => { + this.uniform = !ctrlKey; + }); + + this.on('key:up', () => { + this.uniform = true; + }); + this.on('transform:start', (start) => { start.sub(Vec3.ONE); this._storeNodeScales(); }); - this.on('transform:move', (axis, offset) => { + this.on('transform:move', (axis, isPlane, offset) => { if (this.snap) { offset.scale(1 / this.snapIncrement); offset.round(); offset.scale(this.snapIncrement); } + if (this.uniform && isPlane) { + tmpV1.set(Math.abs(offset.x), Math.abs(offset.y), Math.abs(offset.z)); + tmpV1[axis] = 0; + const v = Math.max(tmpV1.x, tmpV1.y, tmpV1.z); + tmpV1.set(v * Math.sign(offset.x), v * Math.sign(offset.y), v * Math.sign(offset.z)); + tmpV1[axis] = 1; + offset.copy(tmpV1); + } this._setNodeScales(offset); }); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 081b7c68161..a87776edf42 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -184,14 +184,14 @@ class GizmoTransform extends Gizmo { _dragging = false; /** - * State for if snapping is enabled. Defaults to false + * State for if snapping is enabled. Defaults to false. * * @type {boolean} */ snap = false; /** - * Snapping increment. Defaults to 1 + * Snapping increment. Defaults to 1. * * @type {number} */ @@ -243,7 +243,7 @@ class GizmoTransform extends Gizmo { const pointInfo = this._calcPoint(x, y); pointDelta.copy(pointInfo.point).sub(this._pointStart); const angleDelta = pointInfo.angle - this._angleStart; - this.fire('transform:move', this._selectedAxis, pointDelta, angleDelta); + this.fire('transform:move', this._selectedAxis, this._selectedIsPlane, pointDelta, angleDelta); this._hoverAxis = ''; this._hoverIsPlane = false; } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index ddf87e0bbf3..4c6c0f0c272 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -100,7 +100,7 @@ class GizmoTranslate extends GizmoTransform { this._storeNodePositions(); }); - this.on('transform:move', (axis, offset) => { + this.on('transform:move', (axis, isPlane, offset) => { if (this.snap) { offset.scale(1 / this.snapIncrement); offset.round(); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 2ede0703762..6aa723eb8ad 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -122,7 +122,7 @@ class Gizmo extends EventHandler { if (!this.gizmo.enabled) { return; } - this.fire('key:down', e.key, e.shiftKey, e.metaKey); + this.fire('key:down', e.key, e.shiftKey, e.ctrlKey, e.metaKey); }; this._onKeyUp = (e) => { if (!this.gizmo.enabled) { From fa6b43be117ae7b5b54cb1ebceb5dd39a5cc2ca3 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 11:58:35 +0000 Subject: [PATCH 099/178] updated unform scaling to use length --- extras/gizmo/gizmo-scale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 6d2e66a425d..1917c94e5c5 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -122,7 +122,7 @@ class GizmoScale extends GizmoTransform { if (this.uniform && isPlane) { tmpV1.set(Math.abs(offset.x), Math.abs(offset.y), Math.abs(offset.z)); tmpV1[axis] = 0; - const v = Math.max(tmpV1.x, tmpV1.y, tmpV1.z); + const v = tmpV1.length(); tmpV1.set(v * Math.sign(offset.x), v * Math.sign(offset.y), v * Math.sign(offset.z)); tmpV1[axis] = 1; offset.copy(tmpV1); From 6643e2d4cafc0c933a6b52db68a6f3a580c562cd Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 12:45:29 +0000 Subject: [PATCH 100/178] added fly controls; added update scale to app update --- examples/src/examples/misc/gizmos.mjs | 35 ++++++++++++++++++++++----- extras/gizmo/gizmo.js | 29 +++++++++++----------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 728f4aa4ece..148077f132e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -229,7 +229,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { * @param {import('../../options.mjs').ExampleOptions} options - The example options. * @returns {Promise} The example application. */ -async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { +async function example({ canvas, deviceType, data, glslangPath, twgslPath, scriptsPath }) { class GizmoHandler { _type = 'translate'; @@ -282,7 +282,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } } - const gfxOptions = { deviceTypes: [deviceType], glslangUrl: glslangPath + 'glslang.js', @@ -292,17 +291,22 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { const device = await pc.createGraphicsDevice(canvas, gfxOptions); const createOptions = new pc.AppOptions(); createOptions.graphicsDevice = device; + createOptions.mouse = new pc.Mouse(document.body); + createOptions.keyboard = new pc.Keyboard(window); createOptions.componentSystems = [ pc.RenderComponentSystem, pc.CameraComponentSystem, - pc.LightComponentSystem + pc.LightComponentSystem, + pc.ScriptComponentSystem ]; createOptions.resourceHandlers = [ // @ts-ignore pc.TextureHandler, // @ts-ignore - pc.ContainerHandler + pc.ContainerHandler, + // @ts-ignore + pc.ScriptHandler ]; const app = new pc.AppBase(canvas); @@ -341,6 +345,24 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { } }); + // assets + const assets = { + script: new pc.Asset('script', 'script', { url: scriptsPath + 'camera/fly-camera.js' }) + }; + + /** + * @param {pc.Asset[] | number[]} assetList - The asset list. + * @param {pc.AssetRegistry} assetRegistry - The asset registry. + * @returns {Promise} The promise. + */ + function loadAssets(assetList, assetRegistry) { + return new Promise((resolve) => { + const assetListLoader = new pc.AssetListLoader(assetList, assetRegistry); + assetListLoader.load(resolve); + }); + } + await loadAssets(Object.values(assets), app.assets); + app.start(); // create box entities @@ -368,9 +390,11 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { camera.addComponent('camera', { clearColor: new pc.Color(0.5, 0.6, 0.9) }); - app.root.addChild(camera); + camera.addComponent("script"); + camera.script.create("flyCamera"); camera.setPosition(5, 3, 5); camera.lookAt(0, 0, 0); + app.root.addChild(camera); // create directional light entity const light = new pc.Entity('light'); @@ -402,7 +426,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath }) { camera.camera.orthoHeight = value; break; } - gizmoHandler.gizmo.updateScale(); return; case 'gizmo': if (gizmoHandler.skipSetFire) { diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 6aa723eb8ad..c50ee43a261 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -96,24 +96,30 @@ class Gizmo extends EventHandler { this._createLayer(); this._createGizmo(); - this.updateScale(); + this._updateScale(); this._onPointerMove = (e) => { - if (!this.gizmo.enabled) { + if (!this.gizmo.enabled || document.pointerLockElement) { return; } const selection = this._getSelection(e.clientX, e.clientY); + if (selection[0]) { + e.preventDefault(); + } this.fire('pointer:move', e.clientX, e.clientY, selection[0]); }; this._onPointerDown = (e) => { - if (!this.gizmo.enabled) { + if (!this.gizmo.enabled || document.pointerLockElement) { return; } const selection = this._getSelection(e.clientX, e.clientY); + if (selection[0]) { + e.preventDefault(); + } this.fire('pointer:down', e.clientX, e.clientY, selection[0]); }; this._onPointerUp = (e) => { - if (!this.gizmo.enabled) { + if (!this.gizmo.enabled || document.pointerLockElement) { return; } this.fire('pointer:up'); @@ -131,6 +137,8 @@ class Gizmo extends EventHandler { this.fire('key:up'); }; + app.on('update', () => this._updateScale()); + app.on('destroy', () => this.detach()); } @@ -146,7 +154,7 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; - this.updateScale(); + this._updateScale(); this.fire('size:set', this._size); } @@ -215,15 +223,7 @@ class Gizmo extends EventHandler { this.fire('eulerAngles:set', tmpV1); } - /** - * Updates the scale of the gizmo based on projection - * and the distance from the camera. - * - * @example - * const gizmo = new pcx.Gizmo(app, camera); - * gizmo.updateScale(); - */ - updateScale() { + _updateScale() { let scale = 1; if (this.camera.projection === PROJECTION_PERSPECTIVE) { scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; @@ -235,7 +235,6 @@ class Gizmo extends EventHandler { this.gizmo.setLocalScale(tmpV1); this.fire('scale:set', tmpV1); - } _getSelection(x, y) { From 63ed2edba68be2b7b815625f5de0b609cd3fe73d Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 13:42:25 +0000 Subject: [PATCH 101/178] added cull none material for hovering --- extras/gizmo/gizmo-rotate.js | 8 ++++---- extras/gizmo/gizmo-scale.js | 14 +++++++------- extras/gizmo/gizmo-transform.js | 24 +++++++++++++----------- extras/gizmo/gizmo-translate.js | 12 ++++++------ 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 0d7fe0fa763..fbaad3f5ac6 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -24,7 +24,7 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), x: new AxisDisk({ device: this.app.graphicsDevice, @@ -32,7 +32,7 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), y: new AxisDisk({ device: this.app.graphicsDevice, @@ -40,14 +40,14 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), face: new AxisDisk({ device: this.app.graphicsDevice, axis: 'face', layers: [this.layer.id], defaultColor: this._materials.axis.face, - hoverColor: this._materials.hover, + hoverColor: this._materials.hover.cullBack, ringRadius: 0.8 }) }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 1917c94e5c5..9652838f787 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -19,7 +19,7 @@ class GizmoScale extends GizmoTransform { axis: 'xyz', layers: [this.layer.id], defaultColor: this._materials.center, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), yz: new AxisPlane({ axis: 'x', @@ -27,7 +27,7 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), xz: new AxisPlane({ axis: 'y', @@ -35,7 +35,7 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), xy: new AxisPlane({ axis: 'z', @@ -43,28 +43,28 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), x: new AxisBoxLine({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), y: new AxisBoxLine({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), z: new AxisBoxLine({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }) }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index a87776edf42..56e37e0923a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -1,6 +1,7 @@ import { math, CULLFACE_NONE, + CULLFACE_BACK, PROJECTION_PERSPECTIVE, BLEND_NORMAL, Color, @@ -62,19 +63,22 @@ class GizmoTransform extends Gizmo { axis: { x: { cullBack: this._createMaterial(RED_COLOR), - cullNone: this._createMaterial(RED_COLOR, true) + cullNone: this._createMaterial(RED_COLOR, CULLFACE_NONE) }, y: { cullBack: this._createMaterial(GREEN_COLOR), - cullNone: this._createMaterial(GREEN_COLOR, true) + cullNone: this._createMaterial(GREEN_COLOR, CULLFACE_NONE) }, z: { cullBack: this._createMaterial(BLUE_COLOR), - cullNone: this._createMaterial(BLUE_COLOR, true) + cullNone: this._createMaterial(BLUE_COLOR, CULLFACE_NONE) }, face: this._createMaterial(SEMI_YELLOW_COLOR) }, - hover: this._createMaterial(YELLOW_COLOR), + hover: { + cullBack: this._createMaterial(YELLOW_COLOR), + cullNone: this._createMaterial(YELLOW_COLOR, CULLFACE_NONE) + }, center: this._createMaterial(SEMI_WHITE_COLOR), guide: this._createMaterial(SEMI_WHITE_COLOR) }; @@ -322,12 +326,12 @@ class GizmoTransform extends Gizmo { } set hoverColor(value) { - this._materials.hover.emissive.copy(value); - this._materials.hover.update(); + this._materials.hover.cullBack.emissive.copy(value); + this._materials.hover.cullBack.update(); } get hoverColor() { - return this._materials.hover.emissive; + return this._materials.hover.cullBack.emissive; } set guideLineColor(value) { @@ -475,12 +479,10 @@ class GizmoTransform extends Gizmo { this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true); } - _createMaterial(color, disableCulling = false) { + _createMaterial(color, cull = CULLFACE_BACK) { const material = new StandardMaterial(); material.emissive = color; - if (disableCulling) { - material.cull = CULLFACE_NONE; - } + material.cull = cull; if (color.a !== 1) { material.opacity = color.a; material.blendType = BLEND_NORMAL; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 4c6c0f0c272..831f062cd73 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -24,7 +24,7 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), xz: new AxisPlane({ axis: 'y', @@ -32,7 +32,7 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), xy: new AxisPlane({ axis: 'z', @@ -40,28 +40,28 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullNone, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullNone }), x: new AxisArrow({ axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), y: new AxisArrow({ axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }), z: new AxisArrow({ axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover + hoverColor: this._materials.hover.cullBack }) }; From 74f4976abe39a09ef762c2d01a9ec9876b903574 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 13:49:20 +0000 Subject: [PATCH 102/178] removed guidline disk and facing disk set on app update --- extras/gizmo/gizmo-rotate.js | 40 ++++-------------------------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index fbaad3f5ac6..bbea4144c04 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -54,14 +54,6 @@ class GizmoRotate extends GizmoTransform { _isRotation = true; - /** - * Internal axis shape for guide ring. - * - * @type {AxisDisk} - * @private - */ - _guideRingShape; - /** * Internal mapping from each attached node to their starting rotation (euler angles) in local space. * @@ -102,7 +94,6 @@ class GizmoRotate extends GizmoTransform { this._createTransform(); this.on('transform:start', () => { - this._setFacingDisks(); this._storeNodeRotations(); }); @@ -110,23 +101,18 @@ class GizmoRotate extends GizmoTransform { if (this.snap) { angle = Math.round(angle / this.snapIncrement) * this.snapIncrement; } - this._setFacingDisks(); this._setNodeRotations(axis, angle); }); - this.on('coordSpace:set', () => { - this._setFacingDisks(); - }); - - this.on('nodes:attach', () => { - this._setFacingDisks(); - }); - this.on('nodes:detach', () => { this._nodeLocalRotations.clear(); this._nodeRotations.clear(); this._nodeOffsets.clear(); }); + + this.app.on('update', () => { + this._faceDiskToCamera(this._shapes.face.entity); + }); } set xyzTubeRadius(value) { @@ -165,12 +151,6 @@ class GizmoRotate extends GizmoTransform { this._shapes.x[prop] = value; this._shapes.y[prop] = value; this._shapes.z[prop] = value; - this._guideRingShape[prop] = value; - } - - _setFacingDisks() { - this._faceDiskToCamera(this._guideRingShape.entity); - this._faceDiskToCamera(this._shapes.face.entity); } _faceDiskToCamera(entity) { @@ -178,18 +158,6 @@ class GizmoRotate extends GizmoTransform { entity.rotateLocal(90, 0, 0); } - _createTransform() { - super._createTransform(); - - // guide ring - this._guideRingShape = new AxisDisk({ - device: this.app.graphicsDevice, - layers: [this.layer.id], - defaultColor: this._materials.guide - }); - this._meshRoot.addChild(this._guideRingShape.entity); - } - _storeNodeRotations() { const gizmoPos = this.gizmo.getPosition(); for (let i = 0; i < this.nodes.length; i++) { From 9025b002c19a18d78082f2ce75bd0a0ee438e7d5 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 14:07:56 +0000 Subject: [PATCH 103/178] fixed local rotation stuttering issue --- extras/gizmo/gizmo-transform.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 56e37e0923a..2a8e6f5492d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -107,6 +107,14 @@ class GizmoTransform extends Gizmo { */ _shapeMap = new Map(); + /** + * Internal gizmo starting rotation in world space. + * + * @type {Quat} + * @private + */ + _gizmoRotationStart = new Quat(); + /** * Internal currently hovered shape. * @@ -260,6 +268,7 @@ class GizmoTransform extends Gizmo { if (meshInstance) { this._selectedAxis = this._getAxis(meshInstance); this._selectedIsPlane = this._getIsPlane(meshInstance); + this._gizmoRotationStart.copy(this.gizmo.getRotation()); const pointInfo = this._calcPoint(x, y); this._pointStart.copy(pointInfo.point); this._angleStart = pointInfo.angle; @@ -404,7 +413,7 @@ class GizmoTransform extends Gizmo { planeNormal[axis] = 1; // rotate plane normal by gizmo rotation - tmpQ1.copy(this.gizmo.getRotation()).transformVector(planeNormal, planeNormal); + tmpQ1.copy(this._gizmoRotationStart).transformVector(planeNormal, planeNormal); if (!isPlane && !isRotation) { tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); @@ -466,6 +475,7 @@ class GizmoTransform extends Gizmo { break; } } + return { point, angle }; } From 1a6b93e402572c6b60eb0c3910602f2d0674c15d Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 14:19:38 +0000 Subject: [PATCH 104/178] fixed facing axis rotation --- extras/gizmo/gizmo-transform.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 2a8e6f5492d..7c510c9b299 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -161,7 +161,7 @@ class GizmoTransform extends Gizmo { * @type {Vec3} * @private */ - _pointStart = new Vec3(); + _selectionStartPosition = new Vec3(); /** * Internal selection starting angle in world space. @@ -169,7 +169,7 @@ class GizmoTransform extends Gizmo { * @type {number} * @private */ - _angleStart = 0; + _selectionStartAngle = 0; /** * Internal state if transform is a rotation. @@ -253,8 +253,8 @@ class GizmoTransform extends Gizmo { if (this._dragging) { const pointInfo = this._calcPoint(x, y); - pointDelta.copy(pointInfo.point).sub(this._pointStart); - const angleDelta = pointInfo.angle - this._angleStart; + pointDelta.copy(pointInfo.point).sub(this._selectionStartPosition); + const angleDelta = pointInfo.angle - this._selectionStartAngle; this.fire('transform:move', this._selectedAxis, this._selectedIsPlane, pointDelta, angleDelta); this._hoverAxis = ''; this._hoverIsPlane = false; @@ -270,9 +270,9 @@ class GizmoTransform extends Gizmo { this._selectedIsPlane = this._getIsPlane(meshInstance); this._gizmoRotationStart.copy(this.gizmo.getRotation()); const pointInfo = this._calcPoint(x, y); - this._pointStart.copy(pointInfo.point); - this._angleStart = pointInfo.angle; - this.fire('transform:start', this._pointStart); + this._selectionStartPosition.copy(pointInfo.point); + this._selectionStartAngle = pointInfo.angle; + this.fire('transform:start', this._selectionStartPosition); this._dragging = true; } }); @@ -459,6 +459,12 @@ class GizmoTransform extends Gizmo { } } + if (isFacing) { + // translate the point to the same orientation as the camera + tmpQ1.copy(this.camera.entity.getRotation()); + tmpQ1.invert().transformVector(point, point); + } + // calculate angle based on axis let angle = 0; if (isRotation) { From 01498b31146a5a3e13dd640c33e4442ea30133ba Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 15:19:45 +0000 Subject: [PATCH 105/178] added rotate angle guide line; hover cleanup on detach --- extras/gizmo/gizmo-rotate.js | 18 ++++++++++++++++-- extras/gizmo/gizmo-transform.js | 21 ++++++++++++++------- extras/gizmo/gizmo.js | 17 ++++++++++++----- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index bbea4144c04..cbe3b5723b2 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -8,6 +8,7 @@ import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); const tmpQ2 = new Quat(); @@ -93,11 +94,14 @@ class GizmoRotate extends GizmoTransform { this._createTransform(); - this.on('transform:start', () => { + const guideAngleLine = new Vec3(); + this.on('transform:start', (point) => { + guideAngleLine.copy(point).normalize().scale(this.xyzRingRadius); this._storeNodeRotations(); }); - this.on('transform:move', (axis, isPlane, offset, angle) => { + this.on('transform:move', (axis, isPlane, offset, angle, point) => { + guideAngleLine.copy(point).normalize().scale(this.xyzRingRadius); if (this.snap) { angle = Math.round(angle / this.snapIncrement) * this.snapIncrement; } @@ -112,6 +116,9 @@ class GizmoRotate extends GizmoTransform { this.app.on('update', () => { this._faceDiskToCamera(this._shapes.face.entity); + if (this._dragging) { + this._drawGuideAngleLine(guideAngleLine); + } }); } @@ -153,6 +160,13 @@ class GizmoRotate extends GizmoTransform { this._shapes.z[prop] = value; } + _drawGuideAngleLine(point) { + const gizmoPos = this.gizmo.getPosition(); + tmpV1.set(0, 0, 0); + tmpV2.copy(point).scale(this._scale); + this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this.guideLineColor, false, this.layer); + } + _faceDiskToCamera(entity) { entity.lookAt(this.camera.entity.getPosition()); entity.rotateLocal(90, 0, 0); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 7c510c9b299..71161dafa66 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -87,7 +87,7 @@ class GizmoTransform extends Gizmo { * Internal version of the guide line color. * * @type {Color} - * @private + * @protected */ _guideLineColor = new Color(1, 1, 1, 0.8); @@ -161,7 +161,7 @@ class GizmoTransform extends Gizmo { * @type {Vec3} * @private */ - _selectionStartPosition = new Vec3(); + _selectionStartPoint = new Vec3(); /** * Internal selection starting angle in world space. @@ -191,7 +191,7 @@ class GizmoTransform extends Gizmo { * Internal state for if the gizmo is being dragged. * * @type {boolean} - * @private + * @protected */ _dragging = false; @@ -253,9 +253,9 @@ class GizmoTransform extends Gizmo { if (this._dragging) { const pointInfo = this._calcPoint(x, y); - pointDelta.copy(pointInfo.point).sub(this._selectionStartPosition); + pointDelta.copy(pointInfo.point).sub(this._selectionStartPoint); const angleDelta = pointInfo.angle - this._selectionStartAngle; - this.fire('transform:move', this._selectedAxis, this._selectedIsPlane, pointDelta, angleDelta); + this.fire('transform:move', this._selectedAxis, this._selectedIsPlane, pointDelta, angleDelta, pointInfo.point, pointInfo.angle); this._hoverAxis = ''; this._hoverIsPlane = false; } @@ -270,9 +270,9 @@ class GizmoTransform extends Gizmo { this._selectedIsPlane = this._getIsPlane(meshInstance); this._gizmoRotationStart.copy(this.gizmo.getRotation()); const pointInfo = this._calcPoint(x, y); - this._selectionStartPosition.copy(pointInfo.point); + this._selectionStartPoint.copy(pointInfo.point); this._selectionStartAngle = pointInfo.angle; - this.fire('transform:start', this._selectionStartPosition); + this.fire('transform:start', this._selectionStartPoint, this._selectionStartAngle); this._dragging = true; } }); @@ -290,6 +290,13 @@ class GizmoTransform extends Gizmo { this.on('key:up', () => { this.snap = false; }); + + this.on('nodes:detach', () => { + this._hoverAxis = ''; + this._hoverIsPlane = false; + this._hover(null); + this.fire('pointer:up'); + }); } set xAxisColor(value) { diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c50ee43a261..c698e1e3622 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -42,6 +42,14 @@ class Gizmo extends EventHandler { */ _size = 1; + /** + * Internal version of the gizmo scale. + * + * @type {number} + * @protected + */ + _scale = 1; + /** * Internal version of coordinate space. * @@ -224,14 +232,13 @@ class Gizmo extends EventHandler { } _updateScale() { - let scale = 1; if (this.camera.projection === PROJECTION_PERSPECTIVE) { - scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; + this._scale = this._getProjFrustumWidth() * PERS_SCALE_RATIO; } else { - scale = this.camera.orthoHeight * ORTHO_SCALE_RATIO; + this._scale = this.camera.orthoHeight * ORTHO_SCALE_RATIO; } - scale = Math.max(scale * this._size, MIN_GIZMO_SCALE); - tmpV1.set(scale, scale, scale); + this._scale = Math.max(this._scale * this._size, MIN_GIZMO_SCALE); + tmpV1.set(this._scale, this._scale, this._scale); this.gizmo.setLocalScale(tmpV1); this.fire('scale:set', tmpV1); From f17da1eac9be7053343b18ee93cd40e808192703 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 15:36:49 +0000 Subject: [PATCH 106/178] rename of offset to delta --- extras/gizmo/gizmo-rotate.js | 10 +++++----- extras/gizmo/gizmo-scale.js | 20 ++++++++++---------- extras/gizmo/gizmo-translate.js | 16 ++++++++-------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index cbe3b5723b2..c7e66d68435 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -100,12 +100,12 @@ class GizmoRotate extends GizmoTransform { this._storeNodeRotations(); }); - this.on('transform:move', (axis, isPlane, offset, angle, point) => { + this.on('transform:move', (axis, isPlane, pointDelta, angleDelta, point) => { guideAngleLine.copy(point).normalize().scale(this.xyzRingRadius); if (this.snap) { - angle = Math.round(angle / this.snapIncrement) * this.snapIncrement; + angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } - this._setNodeRotations(axis, angle); + this._setNodeRotations(axis, angleDelta); }); this.on('nodes:detach', () => { @@ -182,7 +182,7 @@ class GizmoRotate extends GizmoTransform { } } - _setNodeRotations(axis, angle) { + _setNodeRotations(axis, angleDelta) { const gizmoPos = this.gizmo.getPosition(); const cameraPos = this.camera.entity.getPosition(); const isFacing = axis === 'face'; @@ -196,7 +196,7 @@ class GizmoRotate extends GizmoTransform { tmpV1[axis] = 1; } - tmpQ1.setFromAxisAngle(tmpV1, angle); + tmpQ1.setFromAxisAngle(tmpV1, angleDelta); if (!isFacing && this._coordSpace === 'local') { tmpQ2.copy(this._nodeLocalRotations.get(node)).mul(tmpQ1); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 9652838f787..ecbae49cd7b 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -113,21 +113,21 @@ class GizmoScale extends GizmoTransform { this._storeNodeScales(); }); - this.on('transform:move', (axis, isPlane, offset) => { + this.on('transform:move', (axis, isPlane, pointDelta) => { if (this.snap) { - offset.scale(1 / this.snapIncrement); - offset.round(); - offset.scale(this.snapIncrement); + pointDelta.scale(1 / this.snapIncrement); + pointDelta.round(); + pointDelta.scale(this.snapIncrement); } if (this.uniform && isPlane) { - tmpV1.set(Math.abs(offset.x), Math.abs(offset.y), Math.abs(offset.z)); + tmpV1.set(Math.abs(pointDelta.x), Math.abs(pointDelta.y), Math.abs(pointDelta.z)); tmpV1[axis] = 0; const v = tmpV1.length(); - tmpV1.set(v * Math.sign(offset.x), v * Math.sign(offset.y), v * Math.sign(offset.z)); + tmpV1.set(v * Math.sign(pointDelta.x), v * Math.sign(pointDelta.y), v * Math.sign(pointDelta.z)); tmpV1[axis] = 1; - offset.copy(tmpV1); + pointDelta.copy(tmpV1); } - this._setNodeScales(offset); + this._setNodeScales(pointDelta); }); this.on('nodes:detach', () => { @@ -218,10 +218,10 @@ class GizmoScale extends GizmoTransform { } } - _setNodeScales(point) { + _setNodeScales(pointDelta) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - node.setLocalScale(this._nodeScales.get(node).clone().mul(point)); + node.setLocalScale(this._nodeScales.get(node).clone().mul(pointDelta)); } } } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 831f062cd73..ffb66767e88 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -100,13 +100,13 @@ class GizmoTranslate extends GizmoTransform { this._storeNodePositions(); }); - this.on('transform:move', (axis, isPlane, offset) => { + this.on('transform:move', (axis, isPlane, pointDelta) => { if (this.snap) { - offset.scale(1 / this.snapIncrement); - offset.round(); - offset.scale(this.snapIncrement); + pointDelta.scale(1 / this.snapIncrement); + pointDelta.round(); + pointDelta.scale(this.snapIncrement); } - this._setNodePositions(offset); + this._setNodePositions(pointDelta); }); this.on('nodes:detach', () => { @@ -191,11 +191,11 @@ class GizmoTranslate extends GizmoTransform { } } - _setNodePositions(point) { + _setNodePositions(pointDelta) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; if (this._coordSpace === 'local') { - tmpV1.copy(point); + tmpV1.copy(pointDelta); node.parent.getWorldTransform().getScale(tmpV2); tmpV2.x = 1 / tmpV2.x; tmpV2.y = 1 / tmpV2.y; @@ -204,7 +204,7 @@ class GizmoTranslate extends GizmoTransform { tmpV1.mul(tmpV2); node.setLocalPosition(this._nodeLocalPositions.get(node).clone().add(tmpV1)); } else { - node.setPosition(this._nodePositions.get(node).clone().add(point)); + node.setPosition(this._nodePositions.get(node).clone().add(pointDelta)); } } From 43f35c1bb93c29e98e41f89a6452010529122e77 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 16:06:03 +0000 Subject: [PATCH 107/178] transform event cleanup; angle guide line now match axis colors --- extras/gizmo/gizmo-rotate.js | 13 +++++++------ extras/gizmo/gizmo-scale.js | 8 +++++--- extras/gizmo/gizmo-transform.js | 12 ++++++------ extras/gizmo/gizmo-translate.js | 2 +- extras/gizmo/gizmo.js | 14 ++++---------- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index c7e66d68435..8a4651b0ba1 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -95,17 +95,17 @@ class GizmoRotate extends GizmoTransform { this._createTransform(); const guideAngleLine = new Vec3(); - this.on('transform:start', (point) => { - guideAngleLine.copy(point).normalize().scale(this.xyzRingRadius); + this.on('transform:start', () => { + guideAngleLine.copy(this._selectionStartPoint).normalize().scale(this.xyzRingRadius); this._storeNodeRotations(); }); - this.on('transform:move', (axis, isPlane, pointDelta, angleDelta, point) => { - guideAngleLine.copy(point).normalize().scale(this.xyzRingRadius); + this.on('transform:move', (pointDelta, angleDelta, pointLast) => { + guideAngleLine.copy(pointLast).normalize().scale(this.xyzRingRadius); if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } - this._setNodeRotations(axis, angleDelta); + this._setNodeRotations(this._selectedAxis, angleDelta); }); this.on('nodes:detach', () => { @@ -161,10 +161,11 @@ class GizmoRotate extends GizmoTransform { } _drawGuideAngleLine(point) { + const axis = this._selectedAxis; const gizmoPos = this.gizmo.getPosition(); tmpV1.set(0, 0, 0); tmpV2.copy(point).scale(this._scale); - this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this.guideLineColor, false, this.layer); + this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this._materials.axis[axis].cullBack.emissive, false, this.layer); } _faceDiskToCamera(entity) { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index ecbae49cd7b..d66b720b5bf 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -108,12 +108,14 @@ class GizmoScale extends GizmoTransform { this.uniform = true; }); - this.on('transform:start', (start) => { - start.sub(Vec3.ONE); + this.on('transform:start', () => { + this._selectionStartPoint.sub(Vec3.ONE); this._storeNodeScales(); }); - this.on('transform:move', (axis, isPlane, pointDelta) => { + this.on('transform:move', (pointDelta) => { + const axis = this._selectedAxis; + const isPlane = this._selectedIsPlane; if (this.snap) { pointDelta.scale(1 / this.snapIncrement); pointDelta.round(); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 71161dafa66..8f2038f43aa 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -143,7 +143,7 @@ class GizmoTransform extends Gizmo { * Internal currently selected axis. * * @type {string} - * @private + * @protected */ _selectedAxis = ''; @@ -151,7 +151,7 @@ class GizmoTransform extends Gizmo { * Internal state of if currently selected shape is a plane. * * @type {boolean} - * @private + * @protected */ _selectedIsPlane = false; @@ -159,7 +159,7 @@ class GizmoTransform extends Gizmo { * Internal selection starting coordinates in world space. * * @type {Vec3} - * @private + * @protected */ _selectionStartPoint = new Vec3(); @@ -167,7 +167,7 @@ class GizmoTransform extends Gizmo { * Internal selection starting angle in world space. * * @type {number} - * @private + * @protected */ _selectionStartAngle = 0; @@ -255,7 +255,7 @@ class GizmoTransform extends Gizmo { const pointInfo = this._calcPoint(x, y); pointDelta.copy(pointInfo.point).sub(this._selectionStartPoint); const angleDelta = pointInfo.angle - this._selectionStartAngle; - this.fire('transform:move', this._selectedAxis, this._selectedIsPlane, pointDelta, angleDelta, pointInfo.point, pointInfo.angle); + this.fire('transform:move', pointDelta, angleDelta, pointInfo.point, pointInfo.angle); this._hoverAxis = ''; this._hoverIsPlane = false; } @@ -272,7 +272,7 @@ class GizmoTransform extends Gizmo { const pointInfo = this._calcPoint(x, y); this._selectionStartPoint.copy(pointInfo.point); this._selectionStartAngle = pointInfo.angle; - this.fire('transform:start', this._selectionStartPoint, this._selectionStartAngle); + this.fire('transform:start'); this._dragging = true; } }); diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index ffb66767e88..d8abf1f52bf 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -100,7 +100,7 @@ class GizmoTranslate extends GizmoTransform { this._storeNodePositions(); }); - this.on('transform:move', (axis, isPlane, pointDelta) => { + this.on('transform:move', (pointDelta) => { if (this.snap) { pointDelta.scale(1 / this.snapIncrement); pointDelta.round(); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c698e1e3622..d06130212d8 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -153,7 +153,6 @@ class Gizmo extends EventHandler { set coordSpace(value) { this._coordSpace = value ?? 'world'; this._updateRotation(); - this.fire('coordSpace:set', this._coordSpace); } get coordSpace() { @@ -163,7 +162,6 @@ class Gizmo extends EventHandler { set size(value) { this._size = value; this._updateScale(); - this.fire('size:set', this._size); } get size() { @@ -215,18 +213,15 @@ class Gizmo extends EventHandler { } _updateRotation() { + tmpV1.set(0, 0, 0); if (this._coordSpace === 'local') { - tmpV1.set(0, 0, 0); for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; tmpV1.add(node.getEulerAngles()); } tmpV1.scale(1.0 / (this.nodes.length || 1)); - this.gizmo.setEulerAngles(tmpV1); - } else { - tmpV1.set(0, 0, 0); - this.gizmo.setEulerAngles(tmpV1); } + this.gizmo.setEulerAngles(tmpV1); this.fire('eulerAngles:set', tmpV1); } @@ -238,10 +233,9 @@ class Gizmo extends EventHandler { this._scale = this.camera.orthoHeight * ORTHO_SCALE_RATIO; } this._scale = Math.max(this._scale * this._size, MIN_GIZMO_SCALE); - tmpV1.set(this._scale, this._scale, this._scale); - this.gizmo.setLocalScale(tmpV1); + this.gizmo.setLocalScale(this._scale, this._scale, this._scale); - this.fire('scale:set', tmpV1); + this.fire('scale:set', this._scale); } _getSelection(x, y) { From cbfdb22449be09a0dfc77a8c7101e562b859435f Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 16:10:37 +0000 Subject: [PATCH 108/178] fixed guide line angle colors --- extras/gizmo/gizmo-rotate.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 8a4651b0ba1..08551e6928d 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -163,9 +163,10 @@ class GizmoRotate extends GizmoTransform { _drawGuideAngleLine(point) { const axis = this._selectedAxis; const gizmoPos = this.gizmo.getPosition(); + const color = axis === 'face' ? this._materials.hover.cullBack.emissive : this._materials.axis[axis].cullBack.emissive; tmpV1.set(0, 0, 0); tmpV2.copy(point).scale(this._scale); - this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this._materials.axis[axis].cullBack.emissive, false, this.layer); + this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), color, false, this.layer); } _faceDiskToCamera(entity) { From c2d744693c997e9e3f6a61cfb1b900454fc101ef Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 16:11:29 +0000 Subject: [PATCH 109/178] set guide line angle color to black --- extras/gizmo/gizmo-rotate.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 08551e6928d..bacdd2cf5a9 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,4 +1,5 @@ import { + Color, Quat, Vec3 } from 'playcanvas'; @@ -161,12 +162,10 @@ class GizmoRotate extends GizmoTransform { } _drawGuideAngleLine(point) { - const axis = this._selectedAxis; const gizmoPos = this.gizmo.getPosition(); - const color = axis === 'face' ? this._materials.hover.cullBack.emissive : this._materials.axis[axis].cullBack.emissive; tmpV1.set(0, 0, 0); tmpV2.copy(point).scale(this._scale); - this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), color, false, this.layer); + this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), Color.BLACK, false, this.layer); } _faceDiskToCamera(entity) { From 88670361fc058caec3d1eb8ac80000c0b4e29a04 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 17:37:36 +0000 Subject: [PATCH 110/178] fixed face rotation and added its guide line for angle --- extras/gizmo/gizmo-rotate.js | 8 ++++++-- extras/gizmo/gizmo-transform.js | 11 ++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index bacdd2cf5a9..dee37ade2fc 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -97,12 +97,12 @@ class GizmoRotate extends GizmoTransform { const guideAngleLine = new Vec3(); this.on('transform:start', () => { - guideAngleLine.copy(this._selectionStartPoint).normalize().scale(this.xyzRingRadius); + guideAngleLine.copy(this._selectionStartPoint).normalize().scale(this._getGuideAngleScale()); this._storeNodeRotations(); }); this.on('transform:move', (pointDelta, angleDelta, pointLast) => { - guideAngleLine.copy(pointLast).normalize().scale(this.xyzRingRadius); + guideAngleLine.copy(pointLast).normalize().scale(this._getGuideAngleScale()); if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } @@ -161,6 +161,10 @@ class GizmoRotate extends GizmoTransform { this._shapes.z[prop] = value; } + _getGuideAngleScale() { + return this._selectedAxis === 'face' ? this.faceRingRadius : this.xyzRingRadius; + } + _drawGuideAngleLine(point) { const gizmoPos = this.gizmo.getPosition(); tmpV1.set(0, 0, 0); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 8f2038f43aa..cc3b9c9df33 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -466,12 +466,6 @@ class GizmoTransform extends Gizmo { } } - if (isFacing) { - // translate the point to the same orientation as the camera - tmpQ1.copy(this.camera.entity.getRotation()); - tmpQ1.invert().transformVector(point, point); - } - // calculate angle based on axis let angle = 0; if (isRotation) { @@ -483,9 +477,12 @@ class GizmoTransform extends Gizmo { angle = Math.atan2(point.x, point.z) * math.RAD_TO_DEG; break; case 'z': - case 'face': angle = Math.atan2(point.y, point.x) * math.RAD_TO_DEG; break; + case 'face': + this.camera.entity.getRotation().invert().transformVector(point, tmpV1); + angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG; + break; } } From 8ae4dae1d6acced91431ac4771f0fc5a3c7e46bd Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 17:42:05 +0000 Subject: [PATCH 111/178] refactored camera rotation quaternion --- extras/gizmo/gizmo-transform.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index cc3b9c9df33..0dff224da3a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -395,6 +395,7 @@ class GizmoTransform extends Gizmo { _calcPoint(x, y) { const gizmoPos = this.gizmo.getPosition(); const mouseWPos = this.camera.screenToWorld(x, y, 1); + const cameraRot = this.camera.getRotation(); const rayOrigin = this.camera.entity.getPosition(); const rayDir = new Vec3(); const planeNormal = new Vec3(); @@ -480,7 +481,7 @@ class GizmoTransform extends Gizmo { angle = Math.atan2(point.y, point.x) * math.RAD_TO_DEG; break; case 'face': - this.camera.entity.getRotation().invert().transformVector(point, tmpV1); + cameraRot.invert().transformVector(point, tmpV1); angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG; break; } From a2c2299da84f149a9b4f9da1e5222bbdf652131c Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 9 Jan 2024 17:44:26 +0000 Subject: [PATCH 112/178] entity fix for camera rotation --- extras/gizmo/gizmo-transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 0dff224da3a..8a058fc8852 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -395,7 +395,7 @@ class GizmoTransform extends Gizmo { _calcPoint(x, y) { const gizmoPos = this.gizmo.getPosition(); const mouseWPos = this.camera.screenToWorld(x, y, 1); - const cameraRot = this.camera.getRotation(); + const cameraRot = this.camera.entity.getRotation(); const rayOrigin = this.camera.entity.getPosition(); const rayDir = new Vec3(); const planeNormal = new Vec3(); From e9de0769dd7d4a713f607e8ebf92aa4363c70dcd Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 14:26:29 +0000 Subject: [PATCH 113/178] added lighting to meshes; hoisted ray intersection function --- extras/gizmo/axis-shapes.js | 201 ++++++++++++++++++++++---------- extras/gizmo/gizmo-rotate.js | 13 +-- extras/gizmo/gizmo-scale.js | 14 +-- extras/gizmo/gizmo-transform.js | 1 + extras/gizmo/gizmo-translate.js | 12 +- extras/gizmo/gizmo.js | 67 ++++++----- 6 files changed, 190 insertions(+), 118 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index d44d5806b1d..071cfc620b0 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -1,5 +1,10 @@ import { + createBox, + createCone, + createCylinder, + createPlane, createTorus, + createMesh, Color, MeshInstance, Entity, @@ -8,6 +13,65 @@ import { // constants const TORUS_SEGMENTS = 80; +const LIGHT_DIR = new Vec3(1, 2, 3); +const MESH_TEMPLATES = { + box: createBox, + cone: createCone, + cylinder: createCylinder, + plane: createPlane, + torus: createTorus +}; + +// temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); + +function createShadowMesh(device, entity, type, templateOpts = {}) { + const createTemplate = MESH_TEMPLATES[type]; + if (!createTemplate) { + throw new Error('Invalid primitive type.'); + } + + const meshT = createTemplate(device, templateOpts); + + const options = { + positions: [], + normals: [], + uvs: [], + indices: [], + colors: [] + }; + + meshT.getPositions(options.positions); + meshT.getNormals(options.normals); + meshT.getIndices(options.indices); + meshT.getUvs(0, options.uvs); + + const wtm = entity.getWorldTransform().clone().invert(); + wtm.transformVector(templateOpts.lightDir ?? LIGHT_DIR, tmpV1); + tmpV1.normalize(); + const numVertices = meshT.vertexBuffer.numVertices; + calculateShadowColors(tmpV1, numVertices, options.normals, options.colors); + + const mesh = createMesh(device, options.positions, options); + + return mesh; +} + +function calculateShadowColors(lightDir, numVertices, normals, colors = []) { + for (let i = 0; i < numVertices; i++) { + const x = normals[i * 3]; + const y = normals[i * 3 + 1]; + const z = normals[i * 3 + 2]; + tmpV2.set(x, y, z); + + const dot = lightDir.dot(tmpV2); + const shadow = dot * 0.25 + 0.75; + colors.push(shadow * 255, shadow * 255, shadow * 255, 1); + } + + return colors; +} class AxisShape { _position; @@ -16,22 +80,29 @@ class AxisShape { _scale; + _layers = []; + _defaultColor; _hoverColor; + device; + axis; entity; meshInstances = []; - constructor(options) { + constructor(device, options) { + this.device = device; this.axis = options.axis ?? 'x'; this._position = options.position ?? new Vec3(); this._rotation = options.rotation ?? new Vec3(); this._scale = options.scale ?? new Vec3(1, 1, 1); + this._layers = options.layers ?? this._layers; + this._defaultColor = options.defaultColor ?? Color.BLACK; this._hoverColor = options.hoverColor ?? Color.WHITE; } @@ -55,10 +126,10 @@ class AxisArrow extends AxisShape { _arrowLength = 0.2; - constructor(options = {}) { - super(options); + constructor(device, options = {}) { + super(device, options); - this._createArrow(options.layers ?? []); + this._createArrow(); } set gap(value) { @@ -109,33 +180,36 @@ class AxisArrow extends AxisShape { return this._arrowLength; } - _createArrow(layers) { + _createArrow() { this.entity = new Entity('axis_' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); this._line = new Entity('line_' + this.axis); + let mesh = createShadowMesh(this.device, this.entity, 'cylinder'); + let meshInstance = new MeshInstance(mesh, this._defaultColor); this._line.addComponent('render', { - type: 'cylinder', - layers: layers, - material: this._defaultColor, + meshInstances: [meshInstance], + layers: this._layers, castShadows: false }); this._updateLine(); this.entity.addChild(this._line); - this.meshInstances.push(...this._line.render.meshInstances); + this.meshInstances.push(meshInstance); this._arrow = new Entity('arrow_' + this.axis); + mesh = createShadowMesh(this.device, this.entity, 'cone'); + meshInstance = new MeshInstance(mesh, this._defaultColor); this._arrow.addComponent('render', { - type: 'cone', - layers: layers, - material: this._defaultColor, + meshInstances: [meshInstance], + layers: this._layers, castShadows: false }); this._updateArrow(); this.entity.addChild(this._arrow); - this.meshInstances.push(...this._arrow.render.meshInstances); + this.meshInstances.push(meshInstance); + } _updateLine() { @@ -152,24 +226,25 @@ class AxisArrow extends AxisShape { class AxisBoxCenter extends AxisShape { _size = 0.14; - constructor(options = {}) { - super(options); + constructor(device, options = {}) { + super(device, options); - this._createCenter(options.layers ?? []); + this._createCenter(); } - _createCenter(layers) { + _createCenter() { this.entity = new Entity('center_' + this.axis); - this.entity.addComponent('render', { - type: 'box', - layers: layers, - material: this._defaultColor, - castShadows: false - }); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); + const mesh = createShadowMesh(this.device, this.entity, 'box'); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + this.entity.addComponent('render', { + meshInstances: [meshInstance], + layers: this._layers, + castShadows: false + }); + this.meshInstances.push(meshInstance); } set size(value) { @@ -191,10 +266,10 @@ class AxisBoxLine extends AxisShape { _boxSize = 0.14; - constructor(options = {}) { - super(options); + constructor(device, options = {}) { + super(device, options); - this._createBoxLine(options.layers ?? []); + this._createBoxLine(); } set gap(value) { @@ -236,33 +311,35 @@ class AxisBoxLine extends AxisShape { return this._boxSize; } - _createBoxLine(layers) { + _createBoxLine() { this.entity = new Entity('axis_' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); this._line = new Entity('line_' + this.axis); + let mesh = createShadowMesh(this.device, this.entity, 'cylinder'); + let meshInstance = new MeshInstance(mesh, this._defaultColor); this._line.addComponent('render', { - type: 'cylinder', - layers: layers, - material: this._defaultColor, + meshInstances: [meshInstance], + layers: this._layers, castShadows: false }); this._updateLine(); this.entity.addChild(this._line); - this.meshInstances.push(...this._line.render.meshInstances); + this.meshInstances.push(meshInstance); this._box = new Entity('box_' + this.axis); + mesh = createShadowMesh(this.device, this.entity, 'box'); + meshInstance = new MeshInstance(mesh, this._defaultColor); this._box.addComponent('render', { - type: 'box', - layers: layers, - material: this._defaultColor, + meshInstances: [meshInstance], + layers: this._layers, castShadows: false }); this._updateBox(); this.entity.addChild(this._box); - this.meshInstances.push(...this._box.render.meshInstances); + this.meshInstances.push(meshInstance); } _updateLine() { @@ -277,40 +354,39 @@ class AxisBoxLine extends AxisShape { } class AxisDisk extends AxisShape { - _device; - _tubeRadius = 0.02; _ringRadius = 0.55; - constructor(options = {}) { - super(options); + _lightDir = LIGHT_DIR; - this._device = options.device; + constructor(device, options = {}) { + super(device, options); this._tubeRadius = options.tubeRadius ?? this._tubeRadius; this._ringRadius = options.ringRadius ?? this._ringRadius; + this._lightDir = options.lightDir ?? this._lightDir; - this._createDisk(options.layers ?? []); + this._createDisk(); } - _createDisk(layers) { - const meshInstance = new MeshInstance(this._createTorusMesh(), this._defaultColor); - + _createDisk() { this.entity = new Entity('disk_' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + const meshInstance = new MeshInstance(this._createTorusMesh(), this._defaultColor); this.entity.addComponent('render', { meshInstances: [meshInstance], - layers: layers, + layers: this._layers, castShadows: false }); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); this.meshInstances.push(meshInstance); } _createTorusMesh() { - return createTorus(this._device, { + return createShadowMesh(this.device, this.entity, 'torus', { + lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, segments: TORUS_SEGMENTS @@ -341,12 +417,10 @@ class AxisPlane extends AxisShape { _gap = 0.1; - constructor(options) { - super(options); - - this._flipAxis = options.flipAxis ?? 'x'; + constructor(device, options = {}) { + super(device, options); - this._createPlane(options.layers ?? []); + this._createPlane(); } _getPosition() { @@ -356,18 +430,19 @@ class AxisPlane extends AxisShape { return position; } - _createPlane(layers) { + _createPlane() { this.entity = new Entity('plane_' + this.axis); - this.entity.addComponent('render', { - type: 'plane', - layers: layers, - material: this._defaultColor, - castShadows: false - }); this.entity.setLocalPosition(this._getPosition()); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); - this.meshInstances.push(...this.entity.render.meshInstances); + const mesh = createShadowMesh(this.device, this.entity, 'plane'); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + this.entity.addComponent('render', { + meshInstances: [meshInstance], + layers: this._layers, + castShadows: false + }); + this.meshInstances.push(meshInstance); } set size(value) { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index dee37ade2fc..f232bded68a 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -20,36 +20,33 @@ const tmpQ2 = new Quat(); */ class GizmoRotate extends GizmoTransform { _shapes = { - z: new AxisDisk({ - device: this.app.graphicsDevice, + z: new AxisDisk(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, hoverColor: this._materials.hover.cullBack }), - x: new AxisDisk({ - device: this.app.graphicsDevice, + x: new AxisDisk(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover.cullBack }), - y: new AxisDisk({ - device: this.app.graphicsDevice, + y: new AxisDisk(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover.cullBack }), - face: new AxisDisk({ - device: this.app.graphicsDevice, + face: new AxisDisk(this.app.graphicsDevice, { axis: 'face', layers: [this.layer.id], defaultColor: this._materials.axis.face, hoverColor: this._materials.hover.cullBack, + lightDir: this.camera.entity.forward, ringRadius: 0.8 }) }; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index d66b720b5bf..f15ed9944d2 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -15,13 +15,13 @@ const tmpV1 = new Vec3(); */ class GizmoScale extends GizmoTransform { _shapes = { - xyz: new AxisBoxCenter({ + xyz: new AxisBoxCenter(this.app.graphicsDevice, { axis: 'xyz', layers: [this.layer.id], defaultColor: this._materials.center, hoverColor: this._materials.hover.cullBack }), - yz: new AxisPlane({ + yz: new AxisPlane(this.app.graphicsDevice, { axis: 'x', flipAxis: 'y', layers: [this.layer.id], @@ -29,7 +29,7 @@ class GizmoScale extends GizmoTransform { defaultColor: this._materials.axis.x.cullNone, hoverColor: this._materials.hover.cullNone }), - xz: new AxisPlane({ + xz: new AxisPlane(this.app.graphicsDevice, { axis: 'y', flipAxis: 'z', layers: [this.layer.id], @@ -37,7 +37,7 @@ class GizmoScale extends GizmoTransform { defaultColor: this._materials.axis.y.cullNone, hoverColor: this._materials.hover.cullNone }), - xy: new AxisPlane({ + xy: new AxisPlane(this.app.graphicsDevice, { axis: 'z', flipAxis: 'x', layers: [this.layer.id], @@ -45,21 +45,21 @@ class GizmoScale extends GizmoTransform { defaultColor: this._materials.axis.z.cullNone, hoverColor: this._materials.hover.cullNone }), - x: new AxisBoxLine({ + x: new AxisBoxLine(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover.cullBack }), - y: new AxisBoxLine({ + y: new AxisBoxLine(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover.cullBack }), - z: new AxisBoxLine({ + z: new AxisBoxLine(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 8a058fc8852..b868801c082 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -503,6 +503,7 @@ class GizmoTransform extends Gizmo { _createMaterial(color, cull = CULLFACE_BACK) { const material = new StandardMaterial(); material.emissive = color; + material.emissiveVertexColor = true; material.cull = cull; if (color.a !== 1) { material.opacity = color.a; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index d8abf1f52bf..ff97e30ca69 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -18,7 +18,7 @@ const tmpQ1 = new Quat(); */ class GizmoTranslate extends GizmoTransform { _shapes = { - yz: new AxisPlane({ + yz: new AxisPlane(this.app.graphicsDevice, { axis: 'x', flipAxis: 'y', layers: [this.layer.id], @@ -26,7 +26,7 @@ class GizmoTranslate extends GizmoTransform { defaultColor: this._materials.axis.x.cullNone, hoverColor: this._materials.hover.cullNone }), - xz: new AxisPlane({ + xz: new AxisPlane(this.app.graphicsDevice, { axis: 'y', flipAxis: 'z', layers: [this.layer.id], @@ -34,7 +34,7 @@ class GizmoTranslate extends GizmoTransform { defaultColor: this._materials.axis.y.cullNone, hoverColor: this._materials.hover.cullNone }), - xy: new AxisPlane({ + xy: new AxisPlane(this.app.graphicsDevice, { axis: 'z', flipAxis: 'x', layers: [this.layer.id], @@ -42,21 +42,21 @@ class GizmoTranslate extends GizmoTransform { defaultColor: this._materials.axis.z.cullNone, hoverColor: this._materials.hover.cullNone }), - x: new AxisArrow({ + x: new AxisArrow(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover.cullBack }), - y: new AxisArrow({ + y: new AxisArrow(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover.cullBack }), - z: new AxisArrow({ + z: new AxisArrow(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index d06130212d8..60494ce05db 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -30,6 +30,37 @@ const EPSILON = 1e-6; const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; +function rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { + e1.sub2(v1, v0); + e2.sub2(v2, v0); + h.cross(dir, e2); + const a = e1.dot(h); + if (a > -EPSILON && a < EPSILON) { + return false; + } + + const f = 1 / a; + s.sub2(origin, v0); + const u = f * s.dot(h); + if (u < 0 || u > 1) { + return false; + } + + q.cross(s, e1); + const v = f * dir.dot(q); + if (v < 0 || u + v > 1) { + return false; + } + + const t = f * e2.dot(q); + if (t > EPSILON) { + out.copy(dir).scale(t).add(origin); + return true; + } + + return false; +} + /** * The base class for all gizmos. */ @@ -250,8 +281,7 @@ class Gizmo extends EventHandler { for (let j = 0; j < meshInstances.length; j++) { const meshInstance = meshInstances[j]; const mesh = meshInstance.mesh; - const wtm = meshInstance.node.getWorldTransform().clone(); - wtm.invert(); + const wtm = meshInstance.node.getWorldTransform().clone().invert(); wtm.transformPoint(start, xstart); wtm.transformVector(dir, xdir); @@ -271,7 +301,7 @@ class Gizmo extends EventHandler { tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - if (this._rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { + if (rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { selection.push(meshInstance); } } @@ -281,37 +311,6 @@ class Gizmo extends EventHandler { return selection; } - _rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { - e1.sub2(v1, v0); - e2.sub2(v2, v0); - h.cross(dir, e2); - const a = e1.dot(h); - if (a > -EPSILON && a < EPSILON) { - return false; - } - - const f = 1 / a; - s.sub2(origin, v0); - const u = f * s.dot(h); - if (u < 0 || u > 1) { - return false; - } - - q.cross(s, e1); - const v = f * dir.dot(q); - if (v < 0 || u + v > 1) { - return false; - } - - const t = f * e2.dot(q); - if (t > EPSILON) { - out.copy(dir).scale(t).add(origin); - return true; - } - - return false; - } - /** * Attach an array of graph nodes to the gizmo. * From 01b1c5c747d9d5f57eed404624636f43b9b3a67f Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 14:29:43 +0000 Subject: [PATCH 114/178] changed rotate guide line to hover color --- extras/gizmo/gizmo-rotate.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index f232bded68a..6d6dca57f01 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,5 +1,4 @@ import { - Color, Quat, Vec3 } from 'playcanvas'; @@ -166,7 +165,7 @@ class GizmoRotate extends GizmoTransform { const gizmoPos = this.gizmo.getPosition(); tmpV1.set(0, 0, 0); tmpV2.copy(point).scale(this._scale); - this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), Color.BLACK, false, this.layer); + this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this.hoverColor, false, this.layer); } _faceDiskToCamera(entity) { From f480584ccfb7b51cba35972143922dd21a8751af Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 14:34:32 +0000 Subject: [PATCH 115/178] made axes colors semi transparent --- extras/gizmo/gizmo-transform.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index b868801c082..cfcce521b10 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -23,9 +23,9 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; -const RED_COLOR = new Color(1, 0.3, 0.3); -const GREEN_COLOR = new Color(0.3, 1, 0.3); -const BLUE_COLOR = new Color(0.3, 0.3, 1); +const RED_COLOR = new Color(1, 0.3, 0.3, 0.75); +const GREEN_COLOR = new Color(0.3, 1, 0.3, 0.75); +const BLUE_COLOR = new Color(0.3, 0.3, 1, 0.75); const YELLOW_COLOR = new Color(1, 1, 0.3); const SEMI_YELLOW_COLOR = new Color(1, 1, 0.3, 0.5); const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.5); From 7d547a43bec2aadbb9dbeb62f4a4f568b42f444f Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 14:39:14 +0000 Subject: [PATCH 116/178] refactoring of unused variable in createShadowMesh --- extras/gizmo/axis-shapes.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 071cfc620b0..140c573bd0a 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -32,7 +32,7 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { throw new Error('Invalid primitive type.'); } - const meshT = createTemplate(device, templateOpts); + const mesh = createTemplate(device, templateOpts); const options = { positions: [], @@ -42,20 +42,18 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { colors: [] }; - meshT.getPositions(options.positions); - meshT.getNormals(options.normals); - meshT.getIndices(options.indices); - meshT.getUvs(0, options.uvs); + mesh.getPositions(options.positions); + mesh.getNormals(options.normals); + mesh.getIndices(options.indices); + mesh.getUvs(0, options.uvs); const wtm = entity.getWorldTransform().clone().invert(); wtm.transformVector(templateOpts.lightDir ?? LIGHT_DIR, tmpV1); tmpV1.normalize(); - const numVertices = meshT.vertexBuffer.numVertices; + const numVertices = mesh.vertexBuffer.numVertices; calculateShadowColors(tmpV1, numVertices, options.normals, options.colors); - const mesh = createMesh(device, options.positions, options); - - return mesh; + return createMesh(device, options.positions, options); } function calculateShadowColors(lightDir, numVertices, normals, colors = []) { From 0de825ebcecabbfed3f72dd53433f56a31a88b7d Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 14:57:04 +0000 Subject: [PATCH 117/178] fixed entity transform for shadow to use correct entity --- extras/gizmo/axis-shapes.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 140c573bd0a..d0738f43448 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -185,7 +185,8 @@ class AxisArrow extends AxisShape { this.entity.setLocalScale(this._scale); this._line = new Entity('line_' + this.axis); - let mesh = createShadowMesh(this.device, this.entity, 'cylinder'); + this.entity.addChild(this._line); + let mesh = createShadowMesh(this.device, this._line, 'cylinder'); let meshInstance = new MeshInstance(mesh, this._defaultColor); this._line.addComponent('render', { meshInstances: [meshInstance], @@ -193,11 +194,11 @@ class AxisArrow extends AxisShape { castShadows: false }); this._updateLine(); - this.entity.addChild(this._line); this.meshInstances.push(meshInstance); this._arrow = new Entity('arrow_' + this.axis); - mesh = createShadowMesh(this.device, this.entity, 'cone'); + this.entity.addChild(this._arrow); + mesh = createShadowMesh(this.device, this._arrow, 'cone'); meshInstance = new MeshInstance(mesh, this._defaultColor); this._arrow.addComponent('render', { meshInstances: [meshInstance], @@ -205,7 +206,6 @@ class AxisArrow extends AxisShape { castShadows: false }); this._updateArrow(); - this.entity.addChild(this._arrow); this.meshInstances.push(meshInstance); } @@ -316,7 +316,8 @@ class AxisBoxLine extends AxisShape { this.entity.setLocalScale(this._scale); this._line = new Entity('line_' + this.axis); - let mesh = createShadowMesh(this.device, this.entity, 'cylinder'); + this.entity.addChild(this._line); + let mesh = createShadowMesh(this.device, this._line, 'cylinder'); let meshInstance = new MeshInstance(mesh, this._defaultColor); this._line.addComponent('render', { meshInstances: [meshInstance], @@ -324,11 +325,11 @@ class AxisBoxLine extends AxisShape { castShadows: false }); this._updateLine(); - this.entity.addChild(this._line); this.meshInstances.push(meshInstance); this._box = new Entity('box_' + this.axis); - mesh = createShadowMesh(this.device, this.entity, 'box'); + this.entity.addChild(this._box); + mesh = createShadowMesh(this.device, this._box, 'box'); meshInstance = new MeshInstance(mesh, this._defaultColor); this._box.addComponent('render', { meshInstances: [meshInstance], @@ -336,7 +337,6 @@ class AxisBoxLine extends AxisShape { castShadows: false }); this._updateBox(); - this.entity.addChild(this._box); this.meshInstances.push(meshInstance); } From 7e860459d2104f51e2387074172cd57c332c9408 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 10 Jan 2024 15:00:02 +0000 Subject: [PATCH 118/178] color constants rename --- extras/gizmo/gizmo-transform.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index cfcce521b10..066caedfe67 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -23,9 +23,9 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); const GUIDELINE_SIZE = 1e3; -const RED_COLOR = new Color(1, 0.3, 0.3, 0.75); -const GREEN_COLOR = new Color(0.3, 1, 0.3, 0.75); -const BLUE_COLOR = new Color(0.3, 0.3, 1, 0.75); +const SEMI_RED_COLOR = new Color(1, 0.3, 0.3, 0.75); +const SEMI_GREEN_COLOR = new Color(0.3, 1, 0.3, 0.75); +const SEMI_BLUE_COLOR = new Color(0.3, 0.3, 1, 0.75); const YELLOW_COLOR = new Color(1, 1, 0.3); const SEMI_YELLOW_COLOR = new Color(1, 1, 0.3, 0.5); const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.5); @@ -62,16 +62,16 @@ class GizmoTransform extends Gizmo { _materials = { axis: { x: { - cullBack: this._createMaterial(RED_COLOR), - cullNone: this._createMaterial(RED_COLOR, CULLFACE_NONE) + cullBack: this._createMaterial(SEMI_RED_COLOR), + cullNone: this._createMaterial(SEMI_RED_COLOR, CULLFACE_NONE) }, y: { - cullBack: this._createMaterial(GREEN_COLOR), - cullNone: this._createMaterial(GREEN_COLOR, CULLFACE_NONE) + cullBack: this._createMaterial(SEMI_GREEN_COLOR), + cullNone: this._createMaterial(SEMI_GREEN_COLOR, CULLFACE_NONE) }, z: { - cullBack: this._createMaterial(BLUE_COLOR), - cullNone: this._createMaterial(BLUE_COLOR, CULLFACE_NONE) + cullBack: this._createMaterial(SEMI_BLUE_COLOR), + cullNone: this._createMaterial(SEMI_BLUE_COLOR, CULLFACE_NONE) }, face: this._createMaterial(SEMI_YELLOW_COLOR) }, From 88a973d645a06ef8df30c44a089b30e05d050755 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 10:27:09 +0000 Subject: [PATCH 119/178] updated rotation rings to be arcs for rotation --- extras/gizmo/axis-shapes.js | 60 +++++++++++++++++++++++++++++++-- extras/gizmo/gizmo-rotate.js | 27 ++++++++++++--- extras/gizmo/gizmo-transform.js | 2 +- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index d0738f43448..b66f72c871a 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -3,7 +3,6 @@ import { createCone, createCylinder, createPlane, - createTorus, createMesh, Color, MeshInstance, @@ -26,6 +25,59 @@ const MESH_TEMPLATES = { const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); +function createTorus(device, opts = {}) { + // Check the supplied options and provide defaults for unspecified ones + const rc = opts.tubeRadius ?? 0.2; + const rt = opts.ringRadius ?? 0.3; + const segments = opts.segments ?? 30; + const sides = opts.sides ?? 20; + const sectorAngle = opts.sectorAngle ?? 2 * Math.PI; + + // Variable declarations + const positions = []; + const normals = []; + const uvs = []; + const indices = []; + + for (let i = 0; i <= sides; i++) { + for (let j = 0; j <= segments; j++) { + const x = Math.cos(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const y = Math.sin(2 * Math.PI * i / sides) * rc; + const z = Math.sin(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + + const nx = Math.cos(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); + const ny = Math.sin(2 * Math.PI * i / sides); + const nz = Math.sin(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); + + const u = i / sides; + const v = 1 - j / segments; + + positions.push(x, y, z); + normals.push(nx, ny, nz); + uvs.push(u, 1.0 - v); + + if ((i < sides) && (j < segments)) { + const first = ((i)) * (segments + 1) + ((j)); + const second = ((i + 1)) * (segments + 1) + ((j)); + const third = ((i)) * (segments + 1) + ((j + 1)); + const fourth = ((i + 1)) * (segments + 1) + ((j + 1)); + + indices.push(first, second, third); + indices.push(second, fourth, third); + } + } + } + + const options = { + normals: normals, + uvs: uvs, + uvs1: uvs, + indices: indices + }; + + return createMesh(device, positions, options); +} + function createShadowMesh(device, entity, type, templateOpts = {}) { const createTemplate = MESH_TEMPLATES[type]; if (!createTemplate) { @@ -356,13 +408,16 @@ class AxisDisk extends AxisShape { _ringRadius = 0.55; - _lightDir = LIGHT_DIR; + _sectorAngle; + + _lightDir; constructor(device, options = {}) { super(device, options); this._tubeRadius = options.tubeRadius ?? this._tubeRadius; this._ringRadius = options.ringRadius ?? this._ringRadius; + this._sectorAngle = options.sectorAngle ?? this._sectorAngle; this._lightDir = options.lightDir ?? this._lightDir; this._createDisk(); @@ -387,6 +442,7 @@ class AxisDisk extends AxisShape { lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, segments: TORUS_SEGMENTS }); } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 6d6dca57f01..9d7d3704ec1 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,4 +1,5 @@ import { + math, Quat, Vec3 } from 'playcanvas'; @@ -22,23 +23,26 @@ class GizmoRotate extends GizmoTransform { z: new AxisDisk(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], - rotation: new Vec3(90, 0, 0), + rotation: new Vec3(90, 0, 90), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.cullBack, + sectorAngle: Math.PI }), x: new AxisDisk(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.cullBack, + sectorAngle: Math.PI }), y: new AxisDisk(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.cullBack, + sectorAngle: Math.PI }), face: new AxisDisk(this.app.graphicsDevice, { axis: 'face', @@ -46,7 +50,7 @@ class GizmoRotate extends GizmoTransform { defaultColor: this._materials.axis.face, hoverColor: this._materials.hover.cullBack, lightDir: this.camera.entity.forward, - ringRadius: 0.8 + ringRadius: 0.65 }) }; @@ -113,6 +117,8 @@ class GizmoRotate extends GizmoTransform { this.app.on('update', () => { this._faceDiskToCamera(this._shapes.face.entity); + this._faceRingsToCamera(); + if (this._dragging) { this._drawGuideAngleLine(guideAngleLine); } @@ -173,6 +179,17 @@ class GizmoRotate extends GizmoTransform { entity.rotateLocal(90, 0, 0); } + _faceRingsToCamera() { + tmpV1.copy(this.camera.entity.getPosition()).sub(this.gizmo.getPosition()); + tmpQ1.copy(this.gizmo.getRotation()).invert().transformVector(tmpV1, tmpV1); + let angle = Math.atan2(tmpV1.z, tmpV1.y) * math.RAD_TO_DEG; + this._shapes.x.entity.setLocalEulerAngles(0, angle - 90, -90); + angle = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG; + this._shapes.y.entity.setLocalEulerAngles(0, angle, 0); + angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG; + this._shapes.z.entity.setLocalEulerAngles(90, 0, angle + 90); + } + _storeNodeRotations() { const gizmoPos = this.gizmo.getPosition(); for (let i = 0; i < this.nodes.length; i++) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 066caedfe67..1cc76a0556d 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -73,7 +73,7 @@ class GizmoTransform extends Gizmo { cullBack: this._createMaterial(SEMI_BLUE_COLOR), cullNone: this._createMaterial(SEMI_BLUE_COLOR, CULLFACE_NONE) }, - face: this._createMaterial(SEMI_YELLOW_COLOR) + face: this._createMaterial(SEMI_WHITE_COLOR) }, hover: { cullBack: this._createMaterial(YELLOW_COLOR), From 9a87c732851c015d7289a6cf37d872ed474344a7 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 10:33:07 +0000 Subject: [PATCH 120/178] extracted camera position for look at functions --- extras/gizmo/gizmo-rotate.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 9d7d3704ec1..e61289771ba 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -116,8 +116,9 @@ class GizmoRotate extends GizmoTransform { }); this.app.on('update', () => { - this._faceDiskToCamera(this._shapes.face.entity); - this._faceRingsToCamera(); + const cameraPos = this.camera.entity.getPosition(); + this._faceAxisLookAt(cameraPos); + this._xyzAxisLookAt(cameraPos); if (this._dragging) { this._drawGuideAngleLine(guideAngleLine); @@ -174,13 +175,13 @@ class GizmoRotate extends GizmoTransform { this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this.hoverColor, false, this.layer); } - _faceDiskToCamera(entity) { - entity.lookAt(this.camera.entity.getPosition()); - entity.rotateLocal(90, 0, 0); + _faceAxisLookAt(position) { + this._shapes.face.entity.lookAt(position); + this._shapes.face.entity.rotateLocal(90, 0, 0); } - _faceRingsToCamera() { - tmpV1.copy(this.camera.entity.getPosition()).sub(this.gizmo.getPosition()); + _xyzAxisLookAt(position) { + tmpV1.copy(position).sub(this.gizmo.getPosition()); tmpQ1.copy(this.gizmo.getRotation()).invert().transformVector(tmpV1, tmpV1); let angle = Math.atan2(tmpV1.z, tmpV1.y) * math.RAD_TO_DEG; this._shapes.x.entity.setLocalEulerAngles(0, angle - 90, -90); From d1198fd4b632e1d278c3254505a4c8f9d13402c2 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 10:34:25 +0000 Subject: [PATCH 121/178] updated face disk size --- extras/gizmo/gizmo-rotate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index e61289771ba..e0cf9402495 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -50,7 +50,7 @@ class GizmoRotate extends GizmoTransform { defaultColor: this._materials.axis.face, hoverColor: this._materials.hover.cullBack, lightDir: this.camera.entity.forward, - ringRadius: 0.65 + ringRadius: 0.63 }) }; From ba233bae327fc759fb7aebe09e8a5112c7080ee2 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 11:22:38 +0000 Subject: [PATCH 122/178] added full circle when dragging and hiding of other axes --- extras/gizmo/axis-shapes.js | 31 +++++++++++++++++++++++++------ extras/gizmo/gizmo-rotate.js | 22 ++++++++++++++++++++-- extras/gizmo/gizmo-transform.js | 6 ++++-- extras/gizmo/gizmo.js | 3 +++ 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index b66f72c871a..f48f0bd8b7c 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -428,21 +428,25 @@ class AxisDisk extends AxisShape { this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); - const meshInstance = new MeshInstance(this._createTorusMesh(), this._defaultColor); + const meshInstances = [ + new MeshInstance(this._createTorusMesh(this.entity, this._sectorAngle), this._defaultColor), + new MeshInstance(this._createTorusMesh(this.entity, 2 * Math.PI), this._defaultColor) + ]; + meshInstances[1].visible = false; this.entity.addComponent('render', { - meshInstances: [meshInstance], + meshInstances: meshInstances, layers: this._layers, castShadows: false }); - this.meshInstances.push(meshInstance); + this.meshInstances.push(...meshInstances); } - _createTorusMesh() { - return createShadowMesh(this.device, this.entity, 'torus', { + _createTorusMesh(entity, sectorAngle) { + return createShadowMesh(this.device, entity, 'torus', { lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, + sectorAngle: sectorAngle, segments: TORUS_SEGMENTS }); } @@ -464,6 +468,21 @@ class AxisDisk extends AxisShape { get ringRadius() { return this._ringRadius; } + + drag(state) { + this.meshInstances[0].visible = !state; + this.meshInstances[1].visible = state; + } + + hide(state) { + if (state) { + this.meshInstances[0].visible = false; + this.meshInstances[1].visible = false; + return; + } + + this.drag(false); + } } class AxisPlane extends AxisShape { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index e0cf9402495..76c3dcb663d 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -97,18 +97,25 @@ class GizmoRotate extends GizmoTransform { const guideAngleLine = new Vec3(); this.on('transform:start', () => { - guideAngleLine.copy(this._selectionStartPoint).normalize().scale(this._getGuideAngleScale()); + guideAngleLine.copy(this._selectionStartPoint).normalize(); + guideAngleLine.scale(this._getGuideAngleScale()); this._storeNodeRotations(); + this._drag(true); }); this.on('transform:move', (pointDelta, angleDelta, pointLast) => { - guideAngleLine.copy(pointLast).normalize().scale(this._getGuideAngleScale()); + guideAngleLine.copy(pointLast).normalize(); + guideAngleLine.scale(this._getGuideAngleScale()); if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } this._setNodeRotations(this._selectedAxis, angleDelta); }); + this.on('transform:end', () => { + this._drag(false); + }); + this.on('nodes:detach', () => { this._nodeLocalRotations.clear(); this._nodeRotations.clear(); @@ -191,6 +198,17 @@ class GizmoRotate extends GizmoTransform { this._shapes.z.entity.setLocalEulerAngles(90, 0, angle + 90); } + _drag(state) { + for (const axis in this._shapes) { + const shape = this._shapes[axis]; + if (axis === this._selectedAxis) { + shape.drag(state); + } else { + shape.hide(state); + } + } + } + _storeNodeRotations() { const gizmoPos = this.gizmo.getPosition(); for (let i = 0; i < this.nodes.length; i++) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 1cc76a0556d..38052fa62cd 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -265,6 +265,7 @@ class GizmoTransform extends Gizmo { if (this._dragging) { return; } + if (meshInstance) { this._selectedAxis = this._getAxis(meshInstance); this._selectedIsPlane = this._getIsPlane(meshInstance); @@ -272,13 +273,15 @@ class GizmoTransform extends Gizmo { const pointInfo = this._calcPoint(x, y); this._selectionStartPoint.copy(pointInfo.point); this._selectionStartAngle = pointInfo.angle; - this.fire('transform:start'); this._dragging = true; + this.fire('transform:start'); } }); this.on('pointer:up', () => { this._dragging = false; + this.fire('transform:end'); + this._selectedAxis = ''; this._selectedIsPlane = false; }); @@ -377,7 +380,6 @@ class GizmoTransform extends Gizmo { return; } this._hoverAxis = this._getAxis(meshInstance); - this._hoverIsPlane = this._getIsPlane(meshInstance); const shape = this._shapeMap.get(meshInstance); if (shape === this._hoverShape) { return; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 60494ce05db..9fdb9b5d405 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -280,6 +280,9 @@ class Gizmo extends EventHandler { const meshInstances = renders[i].meshInstances; for (let j = 0; j < meshInstances.length; j++) { const meshInstance = meshInstances[j]; + if (!meshInstance.visible) { + continue; + } const mesh = meshInstance.mesh; const wtm = meshInstance.node.getWorldTransform().clone().invert(); From 5d183afd5e0ebabd7dff41ecb9554a75b20c7598 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 11:35:06 +0000 Subject: [PATCH 123/178] fixed guide angle rotation for local space --- extras/gizmo/gizmo-rotate.js | 2 ++ extras/gizmo/gizmo-transform.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 76c3dcb663d..66e5edfeba3 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -99,6 +99,7 @@ class GizmoRotate extends GizmoTransform { this.on('transform:start', () => { guideAngleLine.copy(this._selectionStartPoint).normalize(); guideAngleLine.scale(this._getGuideAngleScale()); + this._gizmoRotationStart.transformVector(guideAngleLine, guideAngleLine); this._storeNodeRotations(); this._drag(true); }); @@ -106,6 +107,7 @@ class GizmoRotate extends GizmoTransform { this.on('transform:move', (pointDelta, angleDelta, pointLast) => { guideAngleLine.copy(pointLast).normalize(); guideAngleLine.scale(this._getGuideAngleScale()); + this._gizmoRotationStart.transformVector(guideAngleLine, guideAngleLine); if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 38052fa62cd..a256ba0893b 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -111,7 +111,7 @@ class GizmoTransform extends Gizmo { * Internal gizmo starting rotation in world space. * * @type {Quat} - * @private + * @protected */ _gizmoRotationStart = new Quat(); From f16f9425edea669ccc7b2a16dd089f27a0fd588c Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 12:12:18 +0000 Subject: [PATCH 124/178] refactored all colors; removed excess color settings --- examples/src/examples/misc/gizmos.mjs | 25 ----- extras/gizmo/gizmo-rotate.js | 8 +- extras/gizmo/gizmo-scale.js | 16 +-- extras/gizmo/gizmo-transform.js | 139 +++++++++++--------------- extras/gizmo/gizmo-translate.js | 12 +-- 5 files changed, 75 insertions(+), 125 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 148077f132e..1c867bbb63c 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -71,25 +71,6 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { binding: new BindingTwoWay(), link: { observer, path: 'gizmo.zAxisColor' } }) - ), - type === 'rotate' && - jsx(LabelGroup, { text: 'Face Axis' }, - jsx(ColorPicker, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.faceAxisColor' } - }) - ), - jsx(LabelGroup, { text: 'Hover' }, - jsx(ColorPicker, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.hoverColor' } - }) - ), - jsx(LabelGroup, { text: 'Guide Line' }, - jsx(ColorPicker, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.guideLineColor' } - }) ) ), jsx(Panel, { headerText: 'Mesh' }, @@ -260,9 +241,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip xAxisColor: Object.values(gizmo.xAxisColor), yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), - faceAxisColor: Object.values(gizmo.faceAxisColor), - hoverColor: Object.values(gizmo.hoverColor), - guideLineColor: Object.values(gizmo.guideLineColor), coordSpace: gizmo.coordSpace, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, @@ -438,9 +416,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip case 'xAxisColor': case 'yAxisColor': case 'zAxisColor': - case 'faceAxisColor': - case 'hoverColor': - case 'guideLineColor': tmpC.set(...value); gizmoHandler.gizmo[pathArray[1]] = tmpC; break; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 66e5edfeba3..a9032eb16f7 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -25,7 +25,7 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 90), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover.cullBack, + hoverColor: this._materials.hover.z.cullBack, sectorAngle: Math.PI }), x: new AxisDisk(this.app.graphicsDevice, { @@ -33,7 +33,7 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover.cullBack, + hoverColor: this._materials.hover.x.cullBack, sectorAngle: Math.PI }), y: new AxisDisk(this.app.graphicsDevice, { @@ -41,14 +41,14 @@ class GizmoRotate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover.cullBack, + hoverColor: this._materials.hover.y.cullBack, sectorAngle: Math.PI }), face: new AxisDisk(this.app.graphicsDevice, { axis: 'face', layers: [this.layer.id], defaultColor: this._materials.axis.face, - hoverColor: this._materials.hover.cullBack, + hoverColor: this._materials.hover.face, lightDir: this.camera.entity.forward, ringRadius: 0.63 }) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index f15ed9944d2..4107282cf11 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -18,8 +18,8 @@ class GizmoScale extends GizmoTransform { xyz: new AxisBoxCenter(this.app.graphicsDevice, { axis: 'xyz', layers: [this.layer.id], - defaultColor: this._materials.center, - hoverColor: this._materials.hover.cullBack + defaultColor: this._materials.axis.xyz, + hoverColor: this._materials.hover.xyz }), yz: new AxisPlane(this.app.graphicsDevice, { axis: 'x', @@ -27,7 +27,7 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.x.cullNone }), xz: new AxisPlane(this.app.graphicsDevice, { axis: 'y', @@ -35,7 +35,7 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.y.cullNone }), xy: new AxisPlane(this.app.graphicsDevice, { axis: 'z', @@ -43,28 +43,28 @@ class GizmoScale extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.z.cullNone }), x: new AxisBoxLine(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.x.cullBack }), y: new AxisBoxLine(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.y.cullBack }), z: new AxisBoxLine(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.z.cullBack }) }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index a256ba0893b..4b5df5d26f8 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -22,13 +22,17 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); -const GUIDELINE_SIZE = 1e3; -const SEMI_RED_COLOR = new Color(1, 0.3, 0.3, 0.75); -const SEMI_GREEN_COLOR = new Color(0.3, 1, 0.3, 0.75); -const SEMI_BLUE_COLOR = new Color(0.3, 0.3, 1, 0.75); -const YELLOW_COLOR = new Color(1, 1, 0.3); -const SEMI_YELLOW_COLOR = new Color(1, 1, 0.3, 0.5); -const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.5); +const SPANLINE_SIZE = 1e3; + +const RED_COLOR = new Color(1, 0.3, 0.3); +const SEMI_RED_COLOR = new Color(1, 0.3, 0.3, 0.6); +const GREEN_COLOR = new Color(0.3, 1, 0.3); +const SEMI_GREEN_COLOR = new Color(0.3, 1, 0.3, 0.6); +const BLUE_COLOR = new Color(0.3, 0.3, 1); +const SEMI_BLUE_COLOR = new Color(0.3, 0.3, 1, 0.6); +const WHITE_COLOR = new Color(1, 1, 1); +const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.6); + /** * The base class for all transform gizmos. @@ -39,24 +43,7 @@ class GizmoTransform extends Gizmo { /** * Internal material objects for mesh instances. * - * @typedef MaterialsObject - * @property {Object} axis - The object containing axis materials. - * @property {Object} axis.x - The object containing the X axis materials. - * @property {StandardMaterial} axis.x.cullBack - The X axis material with back culling. - * @property {StandardMaterial} axis.x.cullNone - The X axis material without culling. - * @property {Object} axis.y - The object containing the Y axis materials. - * @property {StandardMaterial} axis.y.cullBack - The Y axis material with back culling. - * @property {StandardMaterial} axis.y.cullNone - The Y axis material without culling. - * @property {Object} axis.z - The object containing the Z axis materials. - * @property {StandardMaterial} axis.z.cullBack - The Z axis material with back culling. - * @property {StandardMaterial} axis.z.cullNone - The Z axis material without culling. - * @property {StandardMaterial} axis.face - The camera facing (face) axis material. Only for rotate - * @property {StandardMaterial} hover - The hover material. - * @property {StandardMaterial} center - The center shape material. Only for scale. - * @property {StandardMaterial} guide - The guide ring axis material. Only for rotate. - */ - /** - * @type {MaterialsObject} + * @type {Object} * @protected */ _materials = { @@ -73,14 +60,25 @@ class GizmoTransform extends Gizmo { cullBack: this._createMaterial(SEMI_BLUE_COLOR), cullNone: this._createMaterial(SEMI_BLUE_COLOR, CULLFACE_NONE) }, - face: this._createMaterial(SEMI_WHITE_COLOR) + face: this._createMaterial(SEMI_WHITE_COLOR), + xyz: this._createMaterial(SEMI_WHITE_COLOR) }, hover: { - cullBack: this._createMaterial(YELLOW_COLOR), - cullNone: this._createMaterial(YELLOW_COLOR, CULLFACE_NONE) - }, - center: this._createMaterial(SEMI_WHITE_COLOR), - guide: this._createMaterial(SEMI_WHITE_COLOR) + x: { + cullBack: this._createMaterial(RED_COLOR), + cullNone: this._createMaterial(RED_COLOR, CULLFACE_NONE) + }, + y: { + cullBack: this._createMaterial(GREEN_COLOR), + cullNone: this._createMaterial(GREEN_COLOR, CULLFACE_NONE) + }, + z: { + cullBack: this._createMaterial(BLUE_COLOR), + cullNone: this._createMaterial(BLUE_COLOR, CULLFACE_NONE) + }, + face: this._createMaterial(WHITE_COLOR), + xyz: this._createMaterial(WHITE_COLOR) + } }; /** @@ -224,28 +222,7 @@ class GizmoTransform extends Gizmo { if (!this.gizmo.enabled) { return; } - - // draw guidelines - const gizmoPos = this.gizmo.getPosition(); - tmpQ1.copy(this.gizmo.getRotation()); - const checkAxis = this._hoverAxis || this._selectedAxis; - const checkIsPlane = this._hoverIsPlane || this._selectedIsPlane; - for (let i = 0; i < VEC3_AXES.length; i++) { - const axis = VEC3_AXES[i]; - if (checkAxis === 'xyz') { - this._drawGuideLine(gizmoPos, axis); - continue; - } - if (checkIsPlane) { - if (axis !== checkAxis) { - this._drawGuideLine(gizmoPos, axis); - } - } else { - if (axis === checkAxis) { - this._drawGuideLine(gizmoPos, axis); - } - } - } + this._drawGuideLines(); }); this.on('pointer:move', (x, y, meshInstance) => { @@ -335,32 +312,6 @@ class GizmoTransform extends Gizmo { return this._materials.axis.z.cullBack.emissive; } - set faceAxisColor(value) { - this._materials.axis.face.emissive.copy(value); - this._materials.axis.face.update(); - } - - get faceAxisColor() { - return this._materials.axis.face.emissive; - } - - set hoverColor(value) { - this._materials.hover.cullBack.emissive.copy(value); - this._materials.hover.cullBack.update(); - } - - get hoverColor() { - return this._materials.hover.cullBack.emissive; - } - - set guideLineColor(value) { - this._guideLineColor.copy(value); - } - - get guideLineColor() { - return this._guideLineColor; - } - _getAxis(meshInstance) { if (!meshInstance) { return ''; @@ -492,14 +443,38 @@ class GizmoTransform extends Gizmo { return { point, angle }; } - _drawGuideLine(pos, axis) { + _drawGuideLines() { + const gizmoPos = this.gizmo.getPosition(); + tmpQ1.copy(this.gizmo.getRotation()); + const checkAxis = this._hoverAxis || this._selectedAxis; + const checkIsPlane = this._hoverIsPlane || this._selectedIsPlane; + for (let i = 0; i < VEC3_AXES.length; i++) { + const axis = VEC3_AXES[i]; + const color = this._materials.axis[axis].cullBack.emissive; + if (checkAxis === 'xyz') { + this._drawSpanLine(gizmoPos, axis, color); + continue; + } + if (checkIsPlane) { + if (axis !== checkAxis) { + this._drawSpanLine(gizmoPos, axis, color); + } + } else { + if (axis === checkAxis) { + this._drawSpanLine(gizmoPos, axis, color); + } + } + } + } + + _drawSpanLine(pos, axis, color) { tmpV1.set(0, 0, 0); tmpV1[axis] = 1; - tmpV1.scale(GUIDELINE_SIZE); + tmpV1.scale(SPANLINE_SIZE); tmpV2.copy(tmpV1).scale(-1); tmpQ1.transformVector(tmpV1, tmpV1); tmpQ1.transformVector(tmpV2, tmpV2); - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideLineColor, true); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, true); } _createMaterial(color, cull = CULLFACE_BACK) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index ff97e30ca69..eb5b6555e44 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -24,7 +24,7 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.x.cullNone }), xz: new AxisPlane(this.app.graphicsDevice, { axis: 'y', @@ -32,7 +32,7 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.y.cullNone }), xy: new AxisPlane(this.app.graphicsDevice, { axis: 'z', @@ -40,28 +40,28 @@ class GizmoTranslate extends GizmoTransform { layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullNone, - hoverColor: this._materials.hover.cullNone + hoverColor: this._materials.hover.z.cullNone }), x: new AxisArrow(this.app.graphicsDevice, { axis: 'x', layers: [this.layer.id], rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.x.cullBack }), y: new AxisArrow(this.app.graphicsDevice, { axis: 'y', layers: [this.layer.id], rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.y.cullBack }), z: new AxisArrow(this.app.graphicsDevice, { axis: 'z', layers: [this.layer.id], rotation: new Vec3(90, 0, 0), defaultColor: this._materials.axis.z.cullBack, - hoverColor: this._materials.hover.cullBack + hoverColor: this._materials.hover.z.cullBack }) }; From fbb6af6f19fc87d6e2d8928bcb645fdff49786f8 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 12:14:37 +0000 Subject: [PATCH 125/178] added alias for gizmoRot in draw guide lines --- extras/gizmo/gizmo-transform.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 4b5df5d26f8..049f118b994 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -33,7 +33,6 @@ const SEMI_BLUE_COLOR = new Color(0.3, 0.3, 1, 0.6); const WHITE_COLOR = new Color(1, 1, 1); const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.6); - /** * The base class for all transform gizmos. * @@ -445,35 +444,35 @@ class GizmoTransform extends Gizmo { _drawGuideLines() { const gizmoPos = this.gizmo.getPosition(); - tmpQ1.copy(this.gizmo.getRotation()); + const gizmoRot = tmpQ1.copy(this.gizmo.getRotation()); const checkAxis = this._hoverAxis || this._selectedAxis; const checkIsPlane = this._hoverIsPlane || this._selectedIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { const axis = VEC3_AXES[i]; const color = this._materials.axis[axis].cullBack.emissive; if (checkAxis === 'xyz') { - this._drawSpanLine(gizmoPos, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis, color); continue; } if (checkIsPlane) { if (axis !== checkAxis) { - this._drawSpanLine(gizmoPos, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis, color); } } else { if (axis === checkAxis) { - this._drawSpanLine(gizmoPos, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis, color); } } } } - _drawSpanLine(pos, axis, color) { + _drawSpanLine(pos, rot, axis, color) { tmpV1.set(0, 0, 0); tmpV1[axis] = 1; tmpV1.scale(SPANLINE_SIZE); tmpV2.copy(tmpV1).scale(-1); - tmpQ1.transformVector(tmpV1, tmpV1); - tmpQ1.transformVector(tmpV2, tmpV2); + rot.transformVector(tmpV1, tmpV1); + rot.transformVector(tmpV2, tmpV2); this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, true); } From 7433615092e00f4fc17150752bec79d0c0a4357e Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 12:15:21 +0000 Subject: [PATCH 126/178] inverted uniform keybind --- extras/gizmo/gizmo-scale.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 4107282cf11..a37d601edf7 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -101,11 +101,11 @@ class GizmoScale extends GizmoTransform { this._createTransform(); this.on('key:down', (key, shiftKey, ctrlKey) => { - this.uniform = !ctrlKey; + this.uniform = ctrlKey; }); this.on('key:up', () => { - this.uniform = true; + this.uniform = false; }); this.on('transform:start', () => { From 9e022091ef1da901044b30a875a0f98f7ab27160 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 12:45:20 +0000 Subject: [PATCH 127/178] hoisted guideAngleEnd --- extras/gizmo/gizmo-rotate.js | 37 ++++++++++++-------- extras/gizmo/gizmo-transform.js | 60 ++++++++++++++++----------------- extras/gizmo/gizmo.js | 12 +++---- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index a9032eb16f7..47f237053e0 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -80,6 +80,14 @@ class GizmoRotate extends GizmoTransform { */ _nodeOffsets = new Map(); + /** + * Internal vector for the end point of the guide line angle. + * + * @type {Vec3} + * @private + */ + _guideAngleEnd = new Vec3(); + snapIncrement = 5; /** @@ -95,23 +103,18 @@ class GizmoRotate extends GizmoTransform { this._createTransform(); - const guideAngleLine = new Vec3(); this.on('transform:start', () => { - guideAngleLine.copy(this._selectionStartPoint).normalize(); - guideAngleLine.scale(this._getGuideAngleScale()); - this._gizmoRotationStart.transformVector(guideAngleLine, guideAngleLine); this._storeNodeRotations(); + this._updateGuideAngleEnd(this._selectionStartPoint); this._drag(true); }); this.on('transform:move', (pointDelta, angleDelta, pointLast) => { - guideAngleLine.copy(pointLast).normalize(); - guideAngleLine.scale(this._getGuideAngleScale()); - this._gizmoRotationStart.transformVector(guideAngleLine, guideAngleLine); if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } this._setNodeRotations(this._selectedAxis, angleDelta); + this._updateGuideAngleEnd(pointLast); }); this.on('transform:end', () => { @@ -130,7 +133,8 @@ class GizmoRotate extends GizmoTransform { this._xyzAxisLookAt(cameraPos); if (this._dragging) { - this._drawGuideAngleLine(guideAngleLine); + const gizmoPos = this.gizmo.getPosition(); + this._drawGuideAngleLine(gizmoPos, this._selectedAxis); } }); } @@ -173,15 +177,20 @@ class GizmoRotate extends GizmoTransform { this._shapes.z[prop] = value; } - _getGuideAngleScale() { - return this._selectedAxis === 'face' ? this.faceRingRadius : this.xyzRingRadius; + _updateGuideAngleEnd(point) { + const axis = this._selectedAxis; + const scale = axis === 'face' ? this.faceRingRadius : this.xyzRingRadius; + this._guideAngleEnd.copy(point).normalize(); + this._guideAngleEnd.scale(scale); + this._gizmoRotationStart.transformVector(this._guideAngleEnd, this._guideAngleEnd); } - _drawGuideAngleLine(point) { - const gizmoPos = this.gizmo.getPosition(); + _drawGuideAngleLine(pos, axis) { tmpV1.set(0, 0, 0); - tmpV2.copy(point).scale(this._scale); - this.app.drawLine(tmpV1.add(gizmoPos), tmpV2.add(gizmoPos), this.hoverColor, false, this.layer); + tmpV2.copy(this._guideAngleEnd).scale(this._scale); + const color = axis === 'face' ? this._materials.hover.face.emissive : + this._materials.hover[axis].cullBack.emissive; + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, false, this.layer); } _faceAxisLookAt(position) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 049f118b994..d21cd92620e 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -88,6 +88,14 @@ class GizmoTransform extends Gizmo { */ _guideLineColor = new Color(1, 1, 1, 0.8); + /** + * Internal gizmo starting rotation in world space. + * + * @type {Quat} + * @protected + */ + _gizmoRotationStart = new Quat(); + /** * Internal object containing the axis shapes to render. * @@ -97,20 +105,12 @@ class GizmoTransform extends Gizmo { _shapes = {}; /** - * Internal mapping of mesh instances to axis shapes. + * Internal mapping of mesh instances to axis shapes for hovering. * * @type {Map} * @private */ - _shapeMap = new Map(); - - /** - * Internal gizmo starting rotation in world space. - * - * @type {Quat} - * @protected - */ - _gizmoRotationStart = new Quat(); + _hoverShapeMap = new Map(); /** * Internal currently hovered shape. @@ -224,19 +224,6 @@ class GizmoTransform extends Gizmo { this._drawGuideLines(); }); - this.on('pointer:move', (x, y, meshInstance) => { - this._hover(meshInstance); - - if (this._dragging) { - const pointInfo = this._calcPoint(x, y); - pointDelta.copy(pointInfo.point).sub(this._selectionStartPoint); - const angleDelta = pointInfo.angle - this._selectionStartAngle; - this.fire('transform:move', pointDelta, angleDelta, pointInfo.point, pointInfo.angle); - this._hoverAxis = ''; - this._hoverIsPlane = false; - } - }); - this.on('pointer:down', (x, y, meshInstance) => { if (this._dragging) { return; @@ -254,6 +241,19 @@ class GizmoTransform extends Gizmo { } }); + this.on('pointer:move', (x, y, meshInstance) => { + this._hover(meshInstance); + + if (this._dragging) { + const pointInfo = this._calcPoint(x, y); + pointDelta.copy(pointInfo.point).sub(this._selectionStartPoint); + const angleDelta = pointInfo.angle - this._selectionStartAngle; + this.fire('transform:move', pointDelta, angleDelta, pointInfo.point, pointInfo.angle); + this._hoverAxis = ''; + this._hoverIsPlane = false; + } + }); + this.on('pointer:up', () => { this._dragging = false; this.fire('transform:end'); @@ -330,7 +330,7 @@ class GizmoTransform extends Gizmo { return; } this._hoverAxis = this._getAxis(meshInstance); - const shape = this._shapeMap.get(meshInstance); + const shape = this._hoverShapeMap.get(meshInstance); if (shape === this._hoverShape) { return; } @@ -449,30 +449,30 @@ class GizmoTransform extends Gizmo { const checkIsPlane = this._hoverIsPlane || this._selectedIsPlane; for (let i = 0; i < VEC3_AXES.length; i++) { const axis = VEC3_AXES[i]; - const color = this._materials.axis[axis].cullBack.emissive; if (checkAxis === 'xyz') { - this._drawSpanLine(gizmoPos, gizmoRot, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis); continue; } if (checkIsPlane) { if (axis !== checkAxis) { - this._drawSpanLine(gizmoPos, gizmoRot, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis); } } else { if (axis === checkAxis) { - this._drawSpanLine(gizmoPos, gizmoRot, axis, color); + this._drawSpanLine(gizmoPos, gizmoRot, axis); } } } } - _drawSpanLine(pos, rot, axis, color) { + _drawSpanLine(pos, rot, axis) { tmpV1.set(0, 0, 0); tmpV1[axis] = 1; tmpV1.scale(SPANLINE_SIZE); tmpV2.copy(tmpV1).scale(-1); rot.transformVector(tmpV1, tmpV1); rot.transformVector(tmpV2, tmpV2); + const color = this._materials.hover[axis].cullBack.emissive; this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, true); } @@ -498,7 +498,7 @@ class GizmoTransform extends Gizmo { const shape = this._shapes[key]; this._meshRoot.addChild(shape.entity); for (let i = 0; i < shape.meshInstances.length; i++) { - this._shapeMap.set(shape.meshInstances[i], shape); + this._hoverShapeMap.set(shape.meshInstances[i], shape); } } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 9fdb9b5d405..e5041f6766b 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -137,7 +137,7 @@ class Gizmo extends EventHandler { this._updateScale(); - this._onPointerMove = (e) => { + this._onPointerDown = (e) => { if (!this.gizmo.enabled || document.pointerLockElement) { return; } @@ -145,9 +145,9 @@ class Gizmo extends EventHandler { if (selection[0]) { e.preventDefault(); } - this.fire('pointer:move', e.clientX, e.clientY, selection[0]); + this.fire('pointer:down', e.clientX, e.clientY, selection[0]); }; - this._onPointerDown = (e) => { + this._onPointerMove = (e) => { if (!this.gizmo.enabled || document.pointerLockElement) { return; } @@ -155,7 +155,7 @@ class Gizmo extends EventHandler { if (selection[0]) { e.preventDefault(); } - this.fire('pointer:down', e.clientX, e.clientY, selection[0]); + this.fire('pointer:move', e.clientX, e.clientY, selection[0]); }; this._onPointerUp = (e) => { if (!this.gizmo.enabled || document.pointerLockElement) { @@ -327,8 +327,8 @@ class Gizmo extends EventHandler { this._updatePosition(); this._updateRotation(); - window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerup', this._onPointerUp); window.addEventListener('keydown', this._onKeyDown); window.addEventListener('keyup', this._onKeyUp); @@ -353,8 +353,8 @@ class Gizmo extends EventHandler { this.nodes = []; - window.removeEventListener('pointermove', this._onPointerMove); window.removeEventListener('pointerdown', this._onPointerDown); + window.removeEventListener('pointermove', this._onPointerMove); window.removeEventListener('pointerup', this._onPointerUp); window.removeEventListener('keydown', this._onKeyDown); window.removeEventListener('keyup', this._onKeyUp); From 377558ed9375545494a40c89209ee638e9baadb0 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 13:32:14 +0000 Subject: [PATCH 128/178] added guide colors object --- extras/gizmo/gizmo-rotate.js | 4 +--- extras/gizmo/gizmo-transform.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 47f237053e0..4839bfdc1f6 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -188,9 +188,7 @@ class GizmoRotate extends GizmoTransform { _drawGuideAngleLine(pos, axis) { tmpV1.set(0, 0, 0); tmpV2.copy(this._guideAngleEnd).scale(this._scale); - const color = axis === 'face' ? this._materials.hover.face.emissive : - this._materials.hover[axis].cullBack.emissive; - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, false, this.layer); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideColors[axis], false, this.layer); } _faceAxisLookAt(position) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index d21cd92620e..5605ad00425 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -83,10 +83,15 @@ class GizmoTransform extends Gizmo { /** * Internal version of the guide line color. * - * @type {Color} + * @type {Object} * @protected */ - _guideLineColor = new Color(1, 1, 1, 0.8); + _guideColors = { + x: RED_COLOR, + y: GREEN_COLOR, + z: BLUE_COLOR, + face: WHITE_COLOR + }; /** * Internal gizmo starting rotation in world space. @@ -472,8 +477,7 @@ class GizmoTransform extends Gizmo { tmpV2.copy(tmpV1).scale(-1); rot.transformVector(tmpV1, tmpV1); rot.transformVector(tmpV2, tmpV2); - const color = this._materials.hover[axis].cullBack.emissive; - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, true); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideColors[axis], true); } _createMaterial(color, cull = CULLFACE_BACK) { From 04403f96e88a3d1c7d2e754d41fb34e0dbb0216a Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 16:24:13 +0000 Subject: [PATCH 129/178] added separation between intersection and render meshes --- extras/gizmo/axis-shapes.js | 235 +++++++++++++++++++++++--------- extras/gizmo/gizmo-transform.js | 10 +- extras/gizmo/gizmo.js | 65 +++++---- 3 files changed, 206 insertions(+), 104 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index f48f0bd8b7c..7e26fb3019e 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -11,7 +11,8 @@ import { } from 'playcanvas'; // constants -const TORUS_SEGMENTS = 80; +const TORUS_RENDER_SEGMENTS = 80; +const TORUS_INTERSECT_SEGMENTS = 20; const LIGHT_DIR = new Vec3(1, 2, 3); const MESH_TEMPLATES = { box: createBox, @@ -142,7 +143,7 @@ class AxisShape { entity; - meshInstances = []; + miData = []; constructor(device, options) { this.device = device; @@ -159,8 +160,8 @@ class AxisShape { hover(state) { const material = state ? this._hoverColor : this._defaultColor; - for (let i = 0; i < this.meshInstances.length; i++) { - this.meshInstances[i].material = material; + for (let i = 0; i < this.miData.length; i++) { + this.miData[i].meshInstance.material = material; } } } @@ -176,6 +177,8 @@ class AxisArrow extends AxisShape { _arrowLength = 0.2; + _intersectTolerance = 0.1; + constructor(device, options = {}) { super(device, options); @@ -230,46 +233,67 @@ class AxisArrow extends AxisShape { return this._arrowLength; } - _createArrow() { - this.entity = new Entity('axis_' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - - this._line = new Entity('line_' + this.axis); - this.entity.addChild(this._line); - let mesh = createShadowMesh(this.device, this._line, 'cylinder'); + _createLine() { + this._lineRender = new Entity('lineRender:' + this.axis); + this.entity.addChild(this._lineRender); + let mesh = createShadowMesh(this.device, this._lineRender, 'cylinder'); let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._line.addComponent('render', { + this._lineRender.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); - this._updateLine(); - this.meshInstances.push(meshInstance); + this.miData.push({ meshInstance, intersect: false }); - this._arrow = new Entity('arrow_' + this.axis); - this.entity.addChild(this._arrow); - mesh = createShadowMesh(this.device, this._arrow, 'cone'); + this._lineIntersect = new Entity('lineIntersect:' + this.axis); + this.entity.addChild(this._lineIntersect); + mesh = createCylinder(this.device); meshInstance = new MeshInstance(mesh, this._defaultColor); - this._arrow.addComponent('render', { + meshInstance.visible = false; + this._lineIntersect.addComponent('render', { + meshInstances: [meshInstance], + layers: [], + castShadows: false + }); + this.miData.push({ meshInstance, intersect: true }); + + this._updateLine(); + } + + _createHead() { + this._head = new Entity('head:' + this.axis); + this.entity.addChild(this._head); + const mesh = createShadowMesh(this.device, this._head, 'cone'); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + this._head.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); this._updateArrow(); - this.meshInstances.push(meshInstance); + this.miData.push({ meshInstance, intersect: true }); + } + _createArrow() { + this.entity = new Entity('arrow:' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._createLine(); + this._createHead(); } _updateLine() { - this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); - this._line.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); + this._lineRender.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._lineRender.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); + this._lineIntersect.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._lineIntersect.setLocalScale(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); } _updateArrow() { - this._arrow.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); - this._arrow.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); + this._head.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); + this._head.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); } } @@ -283,7 +307,7 @@ class AxisBoxCenter extends AxisShape { } _createCenter() { - this.entity = new Entity('center_' + this.axis); + this.entity = new Entity('center:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); @@ -294,7 +318,7 @@ class AxisBoxCenter extends AxisShape { layers: this._layers, castShadows: false }); - this.meshInstances.push(meshInstance); + this.miData.push({ meshInstance, intersect: true }); } set size(value) { @@ -316,6 +340,8 @@ class AxisBoxLine extends AxisShape { _boxSize = 0.14; + _intersectTolerance = 0.1; + constructor(device, options = {}) { super(device, options); @@ -361,40 +387,62 @@ class AxisBoxLine extends AxisShape { return this._boxSize; } - _createBoxLine() { - this.entity = new Entity('axis_' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - - this._line = new Entity('line_' + this.axis); - this.entity.addChild(this._line); - let mesh = createShadowMesh(this.device, this._line, 'cylinder'); + _createLine() { + this._lineRender = new Entity('lineRender:' + this.axis); + this.entity.addChild(this._lineRender); + let mesh = createShadowMesh(this.device, this._lineRender, 'cylinder'); let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._line.addComponent('render', { + this._lineRender.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); + this.miData.push({ meshInstance, intersect: false }); + + this._lineIntersect = new Entity('lineIntersect:' + this.axis); + this.entity.addChild(this._lineIntersect); + mesh = createCylinder(this.device); + meshInstance = new MeshInstance(mesh, this._defaultColor); + meshInstance.visible = false; + this._lineIntersect.addComponent('render', { + meshInstances: [meshInstance], + layers: [], + castShadows: false + }); + this.miData.push({ meshInstance, intersect: true }); + this._updateLine(); - this.meshInstances.push(meshInstance); + } - this._box = new Entity('box_' + this.axis); + _createBox() { + this._box = new Entity('box:' + this.axis); this.entity.addChild(this._box); - mesh = createShadowMesh(this.device, this._box, 'box'); - meshInstance = new MeshInstance(mesh, this._defaultColor); + const mesh = createShadowMesh(this.device, this._box, 'box'); + const meshInstance = new MeshInstance(mesh, this._defaultColor); this._box.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); this._updateBox(); - this.meshInstances.push(meshInstance); + this.miData.push({ meshInstance, intersect: true }); + } + + _createBoxLine() { + this.entity = new Entity('axis:' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._createLine(); + this._createBox(); } _updateLine() { - this._line.setLocalPosition(new Vec3(0, this._gap + this._lineLength * 0.5, 0)); - this._line.setLocalScale(new Vec3(this._lineThickness, this._lineLength, this._lineThickness)); + this._lineRender.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._lineRender.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); + this._lineIntersect.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._lineIntersect.setLocalScale(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); } _updateBox() { @@ -412,6 +460,8 @@ class AxisDisk extends AxisShape { _lightDir; + _intersectTolerance = 0.1; + constructor(device, options = {}) { super(device, options); @@ -424,36 +474,59 @@ class AxisDisk extends AxisShape { } _createDisk() { - this.entity = new Entity('disk_' + this.axis); + this.entity = new Entity('diskRoot:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); - const meshInstances = [ - new MeshInstance(this._createTorusMesh(this.entity, this._sectorAngle), this._defaultColor), - new MeshInstance(this._createTorusMesh(this.entity, 2 * Math.PI), this._defaultColor) - ]; - meshInstances[1].visible = false; - this.entity.addComponent('render', { - meshInstances: meshInstances, + + this._diskRender = new Entity('diskRender:' + this.axis); + this.entity.addChild(this._diskRender); + const arcMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + lightDir: this._lightDir, + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, + segments: TORUS_RENDER_SEGMENTS + }); + const circleMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + lightDir: this._lightDir, + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius, + sectorAngle: 2 * Math.PI, + segments: TORUS_RENDER_SEGMENTS + }); + const arcMeshInstance = new MeshInstance(arcMesh, this._defaultColor); + const circleMeshInstance = new MeshInstance(circleMesh, this._defaultColor); + circleMeshInstance.visible = false; + this._diskRender.addComponent('render', { + meshInstances: [arcMeshInstance, circleMeshInstance], layers: this._layers, castShadows: false }); - this.meshInstances.push(...meshInstances); - } + this.miData.push({ meshInstance: arcMeshInstance, intersect: false }); + this.miData.push({ meshInstance: circleMeshInstance, intersect: false }); - _createTorusMesh(entity, sectorAngle) { - return createShadowMesh(this.device, entity, 'torus', { - lightDir: this._lightDir, - tubeRadius: this._tubeRadius, + this._diskIntersect = new Entity('diskIntersect:' + this.axis); + this.entity.addChild(this._diskIntersect); + const mesh = createTorus(this.device, { + tubeRadius: this._tubeRadius + this._intersectTolerance, ringRadius: this._ringRadius, - sectorAngle: sectorAngle, - segments: TORUS_SEGMENTS + sectorAngle: this._sectorAngle, + segments: TORUS_INTERSECT_SEGMENTS }); + const meshInstance = new MeshInstance(mesh, this._defaultColor); + meshInstance.visible = false; + this._diskIntersect.addComponent('render', { + meshInstances: [meshInstance], + layers: [], + castShadows: false + }); + this.miData.push({ meshInstance, intersect: true }); } set tubeRadius(value) { this._tubeRadius = value ?? 0.1; - this.meshInstances[0].mesh = this._createTorusMesh(); + this._updateDisk(); } get tubeRadius() { @@ -462,22 +535,48 @@ class AxisDisk extends AxisShape { set ringRadius(value) { this._ringRadius = value ?? 0.1; - this.meshInstances[0].mesh = this._createTorusMesh(); + this._updateDisk(); } get ringRadius() { return this._ringRadius; } + _updateDisk() { + const arcMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + lightDir: this._lightDir, + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, + segments: TORUS_RENDER_SEGMENTS + }); + const circleMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + lightDir: this._lightDir, + tubeRadius: this._tubeRadius, + ringRadius: this._ringRadius, + sectorAngle: 2 * Math.PI, + segments: TORUS_RENDER_SEGMENTS + }); + const mesh = createTorus(this.device, { + tubeRadius: this._tubeRadius + this._intersectTolerance, + ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, + segments: TORUS_INTERSECT_SEGMENTS + }); + this.miData[0].meshInstance.mesh = arcMesh; + this.miData[1].meshInstance.mesh = circleMesh; + this.miData[2].meshInstance.mesh = mesh; + } + drag(state) { - this.meshInstances[0].visible = !state; - this.meshInstances[1].visible = state; + this.miData[0].meshInstance.visible = !state; + this.miData[1].meshInstance.visible = state; } hide(state) { if (state) { - this.meshInstances[0].visible = false; - this.meshInstances[1].visible = false; + this.miData[0].meshInstance.visible = false; + this.miData[1].meshInstance.visible = false; return; } @@ -504,7 +603,7 @@ class AxisPlane extends AxisShape { } _createPlane() { - this.entity = new Entity('plane_' + this.axis); + this.entity = new Entity('plane:' + this.axis); this.entity.setLocalPosition(this._getPosition()); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); @@ -515,7 +614,7 @@ class AxisPlane extends AxisShape { layers: this._layers, castShadows: false }); - this.meshInstances.push(meshInstance); + this.miData.push({ meshInstance, intersect: true }); } set size(value) { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 5605ad00425..ce9811d5a8e 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -320,7 +320,7 @@ class GizmoTransform extends Gizmo { if (!meshInstance) { return ''; } - return meshInstance.node.name.split("_")[1]; + return meshInstance.node.name.split(":")[1]; } _getIsPlane(meshInstance) { @@ -501,8 +501,12 @@ class GizmoTransform extends Gizmo { for (const key in this._shapes) { const shape = this._shapes[key]; this._meshRoot.addChild(shape.entity); - for (let i = 0; i < shape.meshInstances.length; i++) { - this._hoverShapeMap.set(shape.meshInstances[i], shape); + for (let i = 0; i < shape.miData.length; i++) { + const { meshInstance, intersect } = shape.miData[i]; + this._hoverShapeMap.set(meshInstance, shape); + if (intersect) { + this.intersectMeshInstances.push(meshInstance); + } } } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index e5041f6766b..98a2e0d95d7 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -117,6 +117,12 @@ class Gizmo extends EventHandler { */ gizmo; + /** + * The mesh instances to check for intersections + * + * @type {import('playcanvas').MeshInstance[]} + */ + intersectMeshInstances = []; /** * Creates a new Gizmo object. @@ -275,41 +281,34 @@ class Gizmo extends EventHandler { const dir = end.clone().sub(start).normalize(); const selection = []; - const renders = this.gizmo.findComponents('render'); - for (let i = 0; i < renders.length; i++) { - const meshInstances = renders[i].meshInstances; - for (let j = 0; j < meshInstances.length; j++) { - const meshInstance = meshInstances[j]; - if (!meshInstance.visible) { - continue; + for (let j = 0; j < this.intersectMeshInstances.length; j++) { + const meshInstance = this.intersectMeshInstances[j]; + const mesh = meshInstance.mesh; + const wtm = meshInstance.node.getWorldTransform().clone().invert(); + + wtm.transformPoint(start, xstart); + wtm.transformVector(dir, xdir); + xdir.normalize(); + + const pos = []; + const idx = []; + mesh.getPositions(pos); + mesh.getIndices(idx); + + for (let k = 0; k < idx.length; k += 3) { + const i1 = idx[k]; + const i2 = idx[k + 1]; + const i3 = idx[k + 2]; + + tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + + if (rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { + selection.push(meshInstance); } - const mesh = meshInstance.mesh; - const wtm = meshInstance.node.getWorldTransform().clone().invert(); - - wtm.transformPoint(start, xstart); - wtm.transformVector(dir, xdir); - xdir.normalize(); - - const pos = []; - const idx = []; - mesh.getPositions(pos); - mesh.getIndices(idx); - - for (let k = 0; k < idx.length; k += 3) { - const i1 = idx[k]; - const i2 = idx[k + 1]; - const i3 = idx[k + 2]; - - tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); - tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); - tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - - if (rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { - selection.push(meshInstance); - } - } - } + } return selection; } From 7054a3e056c8fbab47599879f062958913df05ec Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 11 Jan 2024 16:26:47 +0000 Subject: [PATCH 130/178] intersect meshInstance using tmp material --- extras/gizmo/axis-shapes.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 7e26fb3019e..8bc6e8c9732 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -4,6 +4,7 @@ import { createCylinder, createPlane, createMesh, + Material, Color, MeshInstance, Entity, @@ -25,6 +26,7 @@ const MESH_TEMPLATES = { // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); +const tmpMat = new Material(); function createTorus(device, opts = {}) { // Check the supplied options and provide defaults for unspecified ones @@ -248,7 +250,7 @@ class AxisArrow extends AxisShape { this._lineIntersect = new Entity('lineIntersect:' + this.axis); this.entity.addChild(this._lineIntersect); mesh = createCylinder(this.device); - meshInstance = new MeshInstance(mesh, this._defaultColor); + meshInstance = new MeshInstance(mesh, tmpMat); meshInstance.visible = false; this._lineIntersect.addComponent('render', { meshInstances: [meshInstance], @@ -402,7 +404,7 @@ class AxisBoxLine extends AxisShape { this._lineIntersect = new Entity('lineIntersect:' + this.axis); this.entity.addChild(this._lineIntersect); mesh = createCylinder(this.device); - meshInstance = new MeshInstance(mesh, this._defaultColor); + meshInstance = new MeshInstance(mesh, tmpMat); meshInstance.visible = false; this._lineIntersect.addComponent('render', { meshInstances: [meshInstance], @@ -514,7 +516,7 @@ class AxisDisk extends AxisShape { sectorAngle: this._sectorAngle, segments: TORUS_INTERSECT_SEGMENTS }); - const meshInstance = new MeshInstance(mesh, this._defaultColor); + const meshInstance = new MeshInstance(mesh, tmpMat); meshInstance.visible = false; this._diskIntersect.addComponent('render', { meshInstances: [meshInstance], From 3d6d02a221157dae701da8d79f3710f5ef4b8ec1 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 13:36:31 +0000 Subject: [PATCH 131/178] fixed lighting issue on outer disk; prepping for triangle intersection --- extras/gizmo/axis-shapes.js | 8 +++--- extras/gizmo/gizmo-rotate.js | 13 ++++++++- extras/gizmo/gizmo-transform.js | 48 +++++++++++++++------------------ extras/gizmo/gizmo.js | 12 ++++----- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 8bc6e8c9732..b3938a4cce0 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -88,7 +88,6 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { } const mesh = createTemplate(device, templateOpts); - const options = { positions: [], normals: [], @@ -102,8 +101,10 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { mesh.getIndices(options.indices); mesh.getUvs(0, options.uvs); + // TODO: face disk entity hasnt been added to world yet so world transform is before lookAt const wtm = entity.getWorldTransform().clone().invert(); - wtm.transformVector(templateOpts.lightDir ?? LIGHT_DIR, tmpV1); + tmpV1.copy(LIGHT_DIR); + wtm.transformVector(tmpV1, tmpV1); tmpV1.normalize(); const numVertices = mesh.vertexBuffer.numVertices; calculateShadowColors(tmpV1, numVertices, options.normals, options.colors); @@ -470,7 +471,6 @@ class AxisDisk extends AxisShape { this._tubeRadius = options.tubeRadius ?? this._tubeRadius; this._ringRadius = options.ringRadius ?? this._ringRadius; this._sectorAngle = options.sectorAngle ?? this._sectorAngle; - this._lightDir = options.lightDir ?? this._lightDir; this._createDisk(); } @@ -484,14 +484,12 @@ class AxisDisk extends AxisShape { this._diskRender = new Entity('diskRender:' + this.axis); this.entity.addChild(this._diskRender); const arcMesh = createShadowMesh(this.device, this._diskRender, 'torus', { - lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, segments: TORUS_RENDER_SEGMENTS }); const circleMesh = createShadowMesh(this.device, this._diskRender, 'torus', { - lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: 2 * Math.PI, diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 4839bfdc1f6..52c9b638719 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,6 +1,7 @@ import { math, Quat, + Mat4, Vec3 } from 'playcanvas'; @@ -10,6 +11,7 @@ import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); +const tmpM1 = new Mat4(); const tmpQ1 = new Quat(); const tmpQ2 = new Quat(); @@ -47,9 +49,9 @@ class GizmoRotate extends GizmoTransform { face: new AxisDisk(this.app.graphicsDevice, { axis: 'face', layers: [this.layer.id], + rotation: this._getLookAtEulerAngles(this.camera.entity.getPosition()), defaultColor: this._materials.axis.face, hoverColor: this._materials.hover.face, - lightDir: this.camera.entity.forward, ringRadius: 0.63 }) }; @@ -191,6 +193,15 @@ class GizmoRotate extends GizmoTransform { this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideColors[axis], false, this.layer); } + _getLookAtEulerAngles(position) { + tmpV1.set(0, 0, 0); + tmpM1.setLookAt(tmpV1, position, Vec3.UP); + tmpQ1.setFromMat4(tmpM1); + tmpQ1.getEulerAngles(tmpV1); + tmpV1.x += 90; + return tmpV1; + } + _faceAxisLookAt(position) { this._shapes.face.entity.lookAt(position); this._shapes.face.entity.rotateLocal(90, 0, 0); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index ce9811d5a8e..4c402eafbd1 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -30,6 +30,7 @@ const GREEN_COLOR = new Color(0.3, 1, 0.3); const SEMI_GREEN_COLOR = new Color(0.3, 1, 0.3, 0.6); const BLUE_COLOR = new Color(0.3, 0.3, 1); const SEMI_BLUE_COLOR = new Color(0.3, 0.3, 1, 0.6); +const YELLOW_COLOR = new Color(1, 1, 0.5); const WHITE_COLOR = new Color(1, 1, 1); const SEMI_WHITE_COLOR = new Color(1, 1, 1, 0.6); @@ -75,7 +76,7 @@ class GizmoTransform extends Gizmo { cullBack: this._createMaterial(BLUE_COLOR), cullNone: this._createMaterial(BLUE_COLOR, CULLFACE_NONE) }, - face: this._createMaterial(WHITE_COLOR), + face: this._createMaterial(YELLOW_COLOR), xyz: this._createMaterial(WHITE_COLOR) } }; @@ -90,7 +91,7 @@ class GizmoTransform extends Gizmo { x: RED_COLOR, y: GREEN_COLOR, z: BLUE_COLOR, - face: WHITE_COLOR + face: YELLOW_COLOR }; /** @@ -181,14 +182,6 @@ class GizmoTransform extends Gizmo { */ _isRotation = false; - /** - * Internal entity for mesh root. - * - * @type {Entity} - * @protected - */ - _meshRoot; - /** * Internal state for if the gizmo is being dragged. * @@ -284,10 +277,7 @@ class GizmoTransform extends Gizmo { } set xAxisColor(value) { - this._materials.axis.x.cullBack.emissive.copy(value); - this._materials.axis.x.cullNone.emissive.copy(value); - this._materials.axis.x.cullBack.update(); - this._materials.axis.x.cullNone.update(); + this._updateAxisColor('x', value); } get xAxisColor() { @@ -295,10 +285,7 @@ class GizmoTransform extends Gizmo { } set yAxisColor(value) { - this._materials.axis.y.cullBack.emissive.copy(value); - this._materials.axis.y.cullNone.emissive.copy(value); - this._materials.axis.y.cullBack.update(); - this._materials.axis.y.cullNone.update(); + this._updateAxisColor('y', value); } get yAxisColor() { @@ -306,16 +293,27 @@ class GizmoTransform extends Gizmo { } set zAxisColor(value) { - this._materials.axis.z.cullBack.emissive.copy(value); - this._materials.axis.z.cullNone.emissive.copy(value); - this._materials.axis.z.cullBack.update(); - this._materials.axis.z.cullNone.update(); + this._updateAxisColor('z', value); } get zAxisColor() { return this._materials.axis.z.cullBack.emissive; } + _updateAxisColor(axis, value) { + this._guideColors[axis].copy(value); + + this._materials.axis[axis].cullBack.emissive.copy(value); + this._materials.axis[axis].cullNone.emissive.copy(value); + this._materials.hover[axis].cullBack.emissive.copy(value); + this._materials.hover[axis].cullNone.emissive.copy(value); + + this._materials.axis[axis].cullBack.update(); + this._materials.axis[axis].cullNone.update(); + this._materials.hover[axis].cullBack.update(); + this._materials.hover[axis].cullNone.update(); + } + _getAxis(meshInstance) { if (!meshInstance) { return ''; @@ -493,14 +491,10 @@ class GizmoTransform extends Gizmo { } _createTransform() { - // mesh root - this._meshRoot = new Entity('meshRoot'); - this.gizmo.addChild(this._meshRoot); - // shapes for (const key in this._shapes) { const shape = this._shapes[key]; - this._meshRoot.addChild(shape.entity); + this.gizmo.addChild(shape.entity); for (let i = 0; i < shape.miData.length; i++) { const { meshInstance, intersect } = shape.miData[i]; this._hoverShapeMap.set(meshInstance, shape); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 98a2e0d95d7..4701467f5ca 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -291,14 +291,14 @@ class Gizmo extends EventHandler { xdir.normalize(); const pos = []; - const idx = []; + const indices = []; mesh.getPositions(pos); - mesh.getIndices(idx); + mesh.getIndices(indices); - for (let k = 0; k < idx.length; k += 3) { - const i1 = idx[k]; - const i2 = idx[k + 1]; - const i3 = idx[k + 2]; + for (let k = 0; k < indices.length; k += 3) { + const i1 = indices[k]; + const i2 = indices[k + 1]; + const i3 = indices[k + 2]; tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); From 5aa03811cff19f5802c509ec0aa444639957bf31 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 13:41:24 +0000 Subject: [PATCH 132/178] extracted ray intersection code into tri class --- extras/gizmo/triangle.js | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 extras/gizmo/triangle.js diff --git a/extras/gizmo/triangle.js b/extras/gizmo/triangle.js new file mode 100644 index 00000000000..709b090052d --- /dev/null +++ b/extras/gizmo/triangle.js @@ -0,0 +1,66 @@ +import { + Vec3 +} from 'playcanvas'; + +// temporary variables +const e1 = new Vec3(); +const e2 = new Vec3(); +const h = new Vec3(); +const s = new Vec3(); +const q = new Vec3(); + +// constants +const EPSILON = 1e-6; + +class Triangle { + v0; + + v1; + + v2; + + constructor(v0 = new Vec3(), v1 = new Vec3(), v2 = new Vec3()) { + if (v0.length === 3) { + this.v0 = v0[0]; + this.v1 = v0[1]; + this.v2 = v0[2]; + } else { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + } + } + + intersectRay(origin, dir, out = new Vec3(), epsilon = EPSILON) { + e1.sub2(this.v1, this.v0); + e2.sub2(this.v2, this.v0); + h.cross(dir, e2); + const a = e1.dot(h); + if (a > -epsilon && a < epsilon) { + return false; + } + + const f = 1 / a; + s.sub2(origin, this.v0); + const u = f * s.dot(h); + if (u < 0 || u > 1) { + return false; + } + + q.cross(s, e1); + const v = f * dir.dot(q); + if (v < 0 || u + v > 1) { + return false; + } + + const t = f * e2.dot(q); + if (t > epsilon) { + out.copy(dir).scale(t).add(origin); + return true; + } + + return false; + } +} + +export { Triangle }; From 0b1dcd5fa6dfa33685b8ec5e96801ae00375cce1 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 13:41:25 +0000 Subject: [PATCH 133/178] tri class for ray intersection --- extras/gizmo/gizmo.js | 47 +++++----------------------- extras/gizmo/{triangle.js => tri.js} | 12 +++++-- 2 files changed, 17 insertions(+), 42 deletions(-) rename extras/gizmo/{triangle.js => tri.js} (89%) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 4701467f5ca..cc05343b09c 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -8,59 +8,25 @@ import { Vec3 } from 'playcanvas'; +import { + Tri +} from './tri.js'; + // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpV3 = new Vec3(); -const tmpV4 = new Vec3(); +const tmpT1 = new Tri(); const xstart = new Vec3(); const xdir = new Vec3(); -const e1 = new Vec3(); -const e2 = new Vec3(); -const h = new Vec3(); -const s = new Vec3(); -const q = new Vec3(); - // constants const GIZMO_LAYER_ID = 1e5; const MIN_GIZMO_SCALE = 1e-4; -const EPSILON = 1e-6; const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; -function rayIntersectsTriangle(origin, dir, v0, v1, v2, out) { - e1.sub2(v1, v0); - e2.sub2(v2, v0); - h.cross(dir, e2); - const a = e1.dot(h); - if (a > -EPSILON && a < EPSILON) { - return false; - } - - const f = 1 / a; - s.sub2(origin, v0); - const u = f * s.dot(h); - if (u < 0 || u > 1) { - return false; - } - - q.cross(s, e1); - const v = f * dir.dot(q); - if (v < 0 || u + v > 1) { - return false; - } - - const t = f * e2.dot(q); - if (t > EPSILON) { - out.copy(dir).scale(t).add(origin); - return true; - } - - return false; -} - /** * The base class for all gizmos. */ @@ -303,8 +269,9 @@ class Gizmo extends EventHandler { tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + tmpT1.set(tmpV1, tmpV2, tmpV3); - if (rayIntersectsTriangle(xstart, xdir, tmpV1, tmpV2, tmpV3, tmpV4)) { + if (tmpT1.intersectRay(xstart, xdir)) { selection.push(meshInstance); } } diff --git a/extras/gizmo/triangle.js b/extras/gizmo/tri.js similarity index 89% rename from extras/gizmo/triangle.js rename to extras/gizmo/tri.js index 709b090052d..27988902e72 100644 --- a/extras/gizmo/triangle.js +++ b/extras/gizmo/tri.js @@ -12,7 +12,7 @@ const q = new Vec3(); // constants const EPSILON = 1e-6; -class Triangle { +class Tri { v0; v1; @@ -31,6 +31,14 @@ class Triangle { } } + set(v0, v1, v2) { + this.v0.copy(v0); + this.v1.copy(v1); + this.v2.copy(v2); + + return this; + } + intersectRay(origin, dir, out = new Vec3(), epsilon = EPSILON) { e1.sub2(this.v1, this.v0); e2.sub2(this.v2, this.v0); @@ -63,4 +71,4 @@ class Triangle { } } -export { Triangle }; +export { Tri }; From d7f5fd2432e9d1bc3e9b0a73a4ac536ec7c94a99 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 16:58:20 +0000 Subject: [PATCH 134/178] modified intersection code to use triangles --- extras/gizmo/axis-shapes.js | 320 +++++++++++++++++--------------- extras/gizmo/gizmo-transform.js | 14 +- extras/gizmo/gizmo.js | 74 ++++---- extras/gizmo/tri.js | 43 ++++- 4 files changed, 249 insertions(+), 202 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index b3938a4cce0..6c83f7f9995 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -4,13 +4,16 @@ import { createCylinder, createPlane, createMesh, - Material, Color, MeshInstance, Entity, + Mat4, + Quat, Vec3 } from 'playcanvas'; +import { Tri } from './tri'; + // constants const TORUS_RENDER_SEGMENTS = 80; const TORUS_INTERSECT_SEGMENTS = 20; @@ -26,7 +29,7 @@ const MESH_TEMPLATES = { // temporary variables const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); -const tmpMat = new Material(); +const tmpQ1 = new Quat(); function createTorus(device, opts = {}) { // Check the supplied options and provide defaults for unspecified ones @@ -146,7 +149,9 @@ class AxisShape { entity; - miData = []; + triData = []; + + meshInstances = []; constructor(device, options) { this.device = device; @@ -163,8 +168,8 @@ class AxisShape { hover(state) { const material = state ? this._hoverColor : this._defaultColor; - for (let i = 0; i < this.miData.length; i++) { - this.miData[i].meshInstance.material = material; + for (let i = 0; i < this.meshInstances.length; i++) { + this.meshInstances[i].material = material; } } } @@ -190,8 +195,8 @@ class AxisArrow extends AxisShape { set gap(value) { this._gap = value ?? 0; + this._updateHead(); this._updateLine(); - this._updateArrow(); } get gap() { @@ -200,8 +205,8 @@ class AxisArrow extends AxisShape { set lineThickness(value) { this._lineThickness = value ?? 1; + this._updateHead(); this._updateLine(); - this._updateArrow(); } get lineThickness() { @@ -210,8 +215,8 @@ class AxisArrow extends AxisShape { set lineLength(value) { this._lineLength = value ?? 1; + this._updateHead(); this._updateLine(); - this._updateArrow(); } get lineLength() { @@ -220,7 +225,7 @@ class AxisArrow extends AxisShape { set arrowThickness(value) { this._arrowThickness = value ?? 1; - this._updateArrow(); + this._updateHead(); } get arrowThickness() { @@ -229,74 +234,76 @@ class AxisArrow extends AxisShape { set arrowLength(value) { this._arrowLength = value ?? 1; - this._updateArrow(); + this._updateHead(); } get arrowLength() { return this._arrowLength; } - _createLine() { - this._lineRender = new Entity('lineRender:' + this.axis); - this.entity.addChild(this._lineRender); - let mesh = createShadowMesh(this.device, this._lineRender, 'cylinder'); + _createArrow() { + // intersect + this.triData.push({ + tris: Tri.trisFromMesh(createCone(this.device)), + ptm: new Mat4() + }, { + tris: Tri.trisFromMesh(createCylinder(this.device)), + ptm: new Mat4() + }); + + this.entity = new Entity('arrow:' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + // head + this._head = new Entity('head:' + this.axis); + this.entity.addChild(this._head); + this._updateHead(); + let mesh = createShadowMesh(this.device, this._head, 'cone'); let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._lineRender.addComponent('render', { + this._head.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); - this.miData.push({ meshInstance, intersect: false }); - - this._lineIntersect = new Entity('lineIntersect:' + this.axis); - this.entity.addChild(this._lineIntersect); - mesh = createCylinder(this.device); - meshInstance = new MeshInstance(mesh, tmpMat); - meshInstance.visible = false; - this._lineIntersect.addComponent('render', { - meshInstances: [meshInstance], - layers: [], - castShadows: false - }); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); + // line + this._line = new Entity('line:' + this.axis); + this.entity.addChild(this._line); this._updateLine(); - } - - _createHead() { - this._head = new Entity('head:' + this.axis); - this.entity.addChild(this._head); - const mesh = createShadowMesh(this.device, this._head, 'cone'); - const meshInstance = new MeshInstance(mesh, this._defaultColor); - this._head.addComponent('render', { + mesh = createShadowMesh(this.device, this._line, 'cylinder'); + meshInstance = new MeshInstance(mesh, this._defaultColor); + this._line.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); - this._updateArrow(); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); } - _createArrow() { - this.entity = new Entity('arrow:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); + _updateHead() { + // intersect + tmpV1.set(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); + tmpQ1.set(0, 0, 0, 1); + tmpV2.set(this._arrowThickness, this._arrowLength, this._arrowThickness); + this.triData[0].ptm.setTRS(tmpV1, tmpQ1, tmpV2); - this._createLine(); - this._createHead(); + this._head.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); + this._head.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); } _updateLine() { - this._lineRender.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); - this._lineRender.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); - this._lineIntersect.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); - this._lineIntersect.setLocalScale(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); - } + // intersect + tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); + tmpQ1.set(0, 0, 0, 1); + tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); + this.triData[1].ptm.setTRS(tmpV1, tmpQ1, tmpV2); - _updateArrow() { - this._head.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); - this._head.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); + // render + this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._line.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); } } @@ -310,7 +317,14 @@ class AxisBoxCenter extends AxisShape { } _createCenter() { - this.entity = new Entity('center:' + this.axis); + // intersect + this.triData.push({ + tris: Tri.trisFromMesh(createBox(this.device)), + ptm: new Mat4() + }); + + // render + this.entity = new Entity('boxCenter:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); @@ -321,17 +335,22 @@ class AxisBoxCenter extends AxisShape { layers: this._layers, castShadows: false }); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); } set size(value) { this._size = value ?? 1; - this.entity.setLocalScale(this._size, this._size, this._size); + this._updateTransform(); } get size() { return this._size; } + + _updateTransform() { + // intersect/render + this.entity.setLocalScale(this._size, this._size, this._size); + } } class AxisBoxLine extends AxisShape { @@ -390,67 +409,69 @@ class AxisBoxLine extends AxisShape { return this._boxSize; } - _createLine() { - this._lineRender = new Entity('lineRender:' + this.axis); - this.entity.addChild(this._lineRender); - let mesh = createShadowMesh(this.device, this._lineRender, 'cylinder'); + _createBoxLine() { + // intersect + this.triData.push({ + tris: Tri.trisFromMesh(createBox(this.device)), + ptm: new Mat4() + }, { + tris: Tri.trisFromMesh(createCylinder(this.device)), + ptm: new Mat4() + }); + + // render + this.entity = new Entity('boxLine:' + this.axis); + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + + this._box = new Entity('box:' + this.axis); + this.entity.addChild(this._box); + this._updateBox(); + let mesh = createShadowMesh(this.device, this._box, 'box'); let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._lineRender.addComponent('render', { + this._box.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); - this.miData.push({ meshInstance, intersect: false }); - - this._lineIntersect = new Entity('lineIntersect:' + this.axis); - this.entity.addChild(this._lineIntersect); - mesh = createCylinder(this.device); - meshInstance = new MeshInstance(mesh, tmpMat); - meshInstance.visible = false; - this._lineIntersect.addComponent('render', { - meshInstances: [meshInstance], - layers: [], - castShadows: false - }); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); + this._line = new Entity('line:' + this.axis); + this.entity.addChild(this._line); this._updateLine(); - } - - _createBox() { - this._box = new Entity('box:' + this.axis); - this.entity.addChild(this._box); - const mesh = createShadowMesh(this.device, this._box, 'box'); - const meshInstance = new MeshInstance(mesh, this._defaultColor); - this._box.addComponent('render', { + mesh = createShadowMesh(this.device, this._line, 'cylinder'); + meshInstance = new MeshInstance(mesh, this._defaultColor); + this._line.addComponent('render', { meshInstances: [meshInstance], layers: this._layers, castShadows: false }); - this._updateBox(); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); } - _createBoxLine() { - this.entity = new Entity('axis:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); + _updateBox() { + // intersect + tmpV1.set(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0); + tmpQ1.set(0, 0, 0, 1); + tmpV2.set(this._boxSize, this._boxSize, this._boxSize); + this.triData[0].ptm.setTRS(tmpV1, tmpQ1, tmpV2); - this._createLine(); - this._createBox(); + // render + this._box.setLocalPosition(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0); + this._box.setLocalScale(this._boxSize, this._boxSize, this._boxSize); } _updateLine() { - this._lineRender.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); - this._lineRender.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); - this._lineIntersect.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); - this._lineIntersect.setLocalScale(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); - } + // intersect + tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); + tmpQ1.set(0, 0, 0, 1); + tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); + this.triData[1].ptm.setTRS(tmpV1, tmpQ1, tmpV2); - _updateBox() { - this._box.setLocalPosition(new Vec3(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0)); - this._box.setLocalScale(new Vec3(this._boxSize, this._boxSize, this._boxSize)); + // render + this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); + this._line.setLocalScale(this._lineThickness, this._lineLength, this._lineThickness); } } @@ -476,20 +497,29 @@ class AxisDisk extends AxisShape { } _createDisk() { - this.entity = new Entity('diskRoot:' + this.axis); + // intersect + this.triData.push({ + tris: Tri.trisFromMesh(createTorus(this.device, { + tubeRadius: this._tubeRadius + this._intersectTolerance, + ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, + segments: TORUS_INTERSECT_SEGMENTS + })), + ptm: new Mat4() + }); + + // render + this.entity = new Entity('disk:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); - - this._diskRender = new Entity('diskRender:' + this.axis); - this.entity.addChild(this._diskRender); - const arcMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + const arcMesh = createShadowMesh(this.device, this.entity, 'torus', { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, segments: TORUS_RENDER_SEGMENTS }); - const circleMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + const circleMesh = createShadowMesh(this.device, this.entity, 'torus', { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: 2 * Math.PI, @@ -498,35 +528,17 @@ class AxisDisk extends AxisShape { const arcMeshInstance = new MeshInstance(arcMesh, this._defaultColor); const circleMeshInstance = new MeshInstance(circleMesh, this._defaultColor); circleMeshInstance.visible = false; - this._diskRender.addComponent('render', { + this.entity.addComponent('render', { meshInstances: [arcMeshInstance, circleMeshInstance], layers: this._layers, castShadows: false }); - this.miData.push({ meshInstance: arcMeshInstance, intersect: false }); - this.miData.push({ meshInstance: circleMeshInstance, intersect: false }); - - this._diskIntersect = new Entity('diskIntersect:' + this.axis); - this.entity.addChild(this._diskIntersect); - const mesh = createTorus(this.device, { - tubeRadius: this._tubeRadius + this._intersectTolerance, - ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, - segments: TORUS_INTERSECT_SEGMENTS - }); - const meshInstance = new MeshInstance(mesh, tmpMat); - meshInstance.visible = false; - this._diskIntersect.addComponent('render', { - meshInstances: [meshInstance], - layers: [], - castShadows: false - }); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(arcMeshInstance, circleMeshInstance); } set tubeRadius(value) { this._tubeRadius = value ?? 0.1; - this._updateDisk(); + this._updateTransform(); } get tubeRadius() { @@ -535,48 +547,50 @@ class AxisDisk extends AxisShape { set ringRadius(value) { this._ringRadius = value ?? 0.1; - this._updateDisk(); + this._updateTransform(); } get ringRadius() { return this._ringRadius; } - _updateDisk() { - const arcMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + _updateTransform() { + // intersect + this.triData[0].tris = Tri.trisFromMesh(createTorus(this.device, { + tubeRadius: this._tubeRadius + this._intersectTolerance, + ringRadius: this._ringRadius, + sectorAngle: this._sectorAngle, + segments: TORUS_INTERSECT_SEGMENTS + })); + + // render + const arcMesh = createShadowMesh(this.device, this.entity, 'torus', { lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, segments: TORUS_RENDER_SEGMENTS }); - const circleMesh = createShadowMesh(this.device, this._diskRender, 'torus', { + const circleMesh = createShadowMesh(this.device, this.entity, 'torus', { lightDir: this._lightDir, tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, sectorAngle: 2 * Math.PI, segments: TORUS_RENDER_SEGMENTS }); - const mesh = createTorus(this.device, { - tubeRadius: this._tubeRadius + this._intersectTolerance, - ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, - segments: TORUS_INTERSECT_SEGMENTS - }); - this.miData[0].meshInstance.mesh = arcMesh; - this.miData[1].meshInstance.mesh = circleMesh; - this.miData[2].meshInstance.mesh = mesh; + this.meshInstances[0].meshInstance.mesh = arcMesh; + this.meshInstances[1].meshInstance.mesh = circleMesh; } drag(state) { - this.miData[0].meshInstance.visible = !state; - this.miData[1].meshInstance.visible = state; + this.meshInstances[0].visible = !state; + this.meshInstances[1].visible = state; } hide(state) { if (state) { - this.miData[0].meshInstance.visible = false; - this.miData[1].meshInstance.visible = false; + this.meshInstances[0].visible = false; + this.meshInstances[1].visible = false; return; } @@ -603,6 +617,13 @@ class AxisPlane extends AxisShape { } _createPlane() { + // intersect + this.triData.push({ + tris: Tri.trisFromMesh(createPlane(this.device)), + ptm: new Mat4() + }); + + // render this.entity = new Entity('plane:' + this.axis); this.entity.setLocalPosition(this._getPosition()); this.entity.setLocalEulerAngles(this._rotation); @@ -614,13 +635,12 @@ class AxisPlane extends AxisShape { layers: this._layers, castShadows: false }); - this.miData.push({ meshInstance, intersect: true }); + this.meshInstances.push(meshInstance); } set size(value) { this._size = value ?? 1; - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalScale(this._size, this._size, this._size); + this._updateTransform(); } get size() { @@ -629,12 +649,18 @@ class AxisPlane extends AxisShape { set gap(value) { this._gap = value ?? 0; - this.entity.setLocalPosition(this._getPosition()); + this._updateTransform(); } get gap() { return this._gap; } + + _updateTransform() { + // intersect/render + this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalScale(this._size, this._size, this._size); + } } export { AxisShape, AxisArrow, AxisBoxCenter, AxisBoxLine, AxisDisk, AxisPlane }; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 4c402eafbd1..c4318f8505c 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -6,7 +6,6 @@ import { BLEND_NORMAL, Color, StandardMaterial, - Entity, Vec3, Quat } from 'playcanvas'; @@ -495,12 +494,13 @@ class GizmoTransform extends Gizmo { for (const key in this._shapes) { const shape = this._shapes[key]; this.gizmo.addChild(shape.entity); - for (let i = 0; i < shape.miData.length; i++) { - const { meshInstance, intersect } = shape.miData[i]; - this._hoverShapeMap.set(meshInstance, shape); - if (intersect) { - this.intersectMeshInstances.push(meshInstance); - } + this.intersectData.push({ + triData: shape.triData, + parent: shape.entity, + meshInstances: shape.meshInstances + }); + for (let i = 0; i < shape.meshInstances.length; i++) { + this._hoverShapeMap.set(shape.meshInstances[i], shape); } } } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index cc05343b09c..9caa98811b3 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -5,18 +5,13 @@ import { EventHandler, Layer, Entity, + Mat4, Vec3 } from 'playcanvas'; -import { - Tri -} from './tri.js'; - // temporary variables const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpV3 = new Vec3(); -const tmpT1 = new Tri(); +const tmpM1 = new Mat4(); const xstart = new Vec3(); const xdir = new Vec3(); @@ -84,11 +79,24 @@ class Gizmo extends EventHandler { gizmo; /** - * The mesh instances to check for intersections + * @typedef TriData + * @property {import('./tri.js').Tri[]} tris - The array of triangles for + * the mesh. + * @property {Mat4} ptm - The parent transform matrix for the mesh. + */ + /** + * @typedef IntersectData + * @property {TriData[]} triData - The array of {@link TriData} + * @property {import('playcanvas').GraphNode} parent - The mesh parent node. + * @property {import('playcanvas').MeshInstance[]} meshInstances - + * array of mesh instances for rendering + */ + /** + * The intersection data object. * - * @type {import('playcanvas').MeshInstance[]} + * @type {IntersectData[]} */ - intersectMeshInstances = []; + intersectData = []; /** * Creates a new Gizmo object. @@ -246,38 +254,26 @@ class Gizmo extends EventHandler { const end = this.camera.screenToWorld(x, y, this.camera.farClip); const dir = end.clone().sub(start).normalize(); - const selection = []; - for (let j = 0; j < this.intersectMeshInstances.length; j++) { - const meshInstance = this.intersectMeshInstances[j]; - const mesh = meshInstance.mesh; - const wtm = meshInstance.node.getWorldTransform().clone().invert(); - - wtm.transformPoint(start, xstart); - wtm.transformVector(dir, xdir); - xdir.normalize(); - - const pos = []; - const indices = []; - mesh.getPositions(pos); - mesh.getIndices(indices); - - for (let k = 0; k < indices.length; k += 3) { - const i1 = indices[k]; - const i2 = indices[k + 1]; - const i3 = indices[k + 2]; - - tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); - tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); - tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - tmpT1.set(tmpV1, tmpV2, tmpV3); - - if (tmpT1.intersectRay(xstart, xdir)) { - selection.push(meshInstance); + for (let i = 0; i < this.intersectData.length; i++) { + const { triData, parent, meshInstances } = this.intersectData[i]; + const wtm = parent.getWorldTransform().clone(); + for (let j = 0; j < triData.length; j++) { + const { tris, ptm } = triData[j]; + tmpM1.copy(wtm).mul(ptm); + tmpM1.invert(); + tmpM1.transformPoint(start, xstart); + tmpM1.transformVector(dir, xdir); + xdir.normalize(); + + for (let k = 0; k < tris.length; k++) { + if (tris[k].intersectRay(xstart, xdir)) { + return meshInstances; + } } } - } - return selection; + + return []; } /** diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index 27988902e72..bea565c09c2 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -3,6 +3,10 @@ import { } from 'playcanvas'; // temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); + const e1 = new Vec3(); const e2 = new Vec3(); const h = new Vec3(); @@ -13,21 +17,42 @@ const q = new Vec3(); const EPSILON = 1e-6; class Tri { - v0; + v0 = new Vec3(); + + v1 = new Vec3(); + + v2 = new Vec3(); - v1; + static trisFromMesh(mesh) { + const tris = []; + const pos = []; + const indices = []; + mesh.getPositions(pos); + mesh.getIndices(indices); - v2; + for (let k = 0; k < indices.length; k += 3) { + const i1 = indices[k]; + const i2 = indices[k + 1]; + const i3 = indices[k + 2]; + + tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + const tri = new Tri(tmpV1, tmpV2, tmpV3); + tris.push(tri); + } + return tris; + } constructor(v0 = new Vec3(), v1 = new Vec3(), v2 = new Vec3()) { if (v0.length === 3) { - this.v0 = v0[0]; - this.v1 = v0[1]; - this.v2 = v0[2]; + this.v0.copy(v0[0]); + this.v1.copy(v0[1]); + this.v2.copy(v0[2]); } else { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + this.v0.copy(v0); + this.v1.copy(v1); + this.v2.copy(v2); } } From 394319dd87fa712984abbe09d880558c576baad2 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 17:05:18 +0000 Subject: [PATCH 135/178] fixed linting issue --- extras/gizmo/axis-shapes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 6c83f7f9995..2c726c77506 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -12,7 +12,7 @@ import { Vec3 } from 'playcanvas'; -import { Tri } from './tri'; +import { Tri } from './tri.js'; // constants const TORUS_RENDER_SEGMENTS = 80; From 785811e48456c3c3ff170b4251353dc7bc5cd485 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 12 Jan 2024 17:11:01 +0000 Subject: [PATCH 136/178] added check for window for event listeners --- extras/gizmo/gizmo.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 9caa98811b3..a9b95153b54 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -289,11 +289,13 @@ class Gizmo extends EventHandler { this._updatePosition(); this._updateRotation(); - window.addEventListener('pointerdown', this._onPointerDown); - window.addEventListener('pointermove', this._onPointerMove); - window.addEventListener('pointerup', this._onPointerUp); - window.addEventListener('keydown', this._onKeyDown); - window.addEventListener('keyup', this._onKeyUp); + if (window) { + window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointermove', this._onPointerMove); + window.addEventListener('pointerup', this._onPointerUp); + window.addEventListener('keydown', this._onKeyDown); + window.addEventListener('keyup', this._onKeyUp); + } this.fire('nodes:attach'); @@ -315,11 +317,13 @@ class Gizmo extends EventHandler { this.nodes = []; - window.removeEventListener('pointerdown', this._onPointerDown); - window.removeEventListener('pointermove', this._onPointerMove); - window.removeEventListener('pointerup', this._onPointerUp); - window.removeEventListener('keydown', this._onKeyDown); - window.removeEventListener('keyup', this._onKeyUp); + if (window) { + window.removeEventListener('pointerdown', this._onPointerDown); + window.removeEventListener('pointermove', this._onPointerMove); + window.removeEventListener('pointerup', this._onPointerUp); + window.removeEventListener('keydown', this._onKeyDown); + window.removeEventListener('keyup', this._onKeyUp); + } } } From 0763f63486e05fb13d38e44145085c05f51bdec8 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 14 Jan 2024 19:38:06 +0000 Subject: [PATCH 137/178] removed todo --- extras/gizmo/axis-shapes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 2c726c77506..6b04d45ab4b 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -104,7 +104,6 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { mesh.getIndices(options.indices); mesh.getUvs(0, options.uvs); - // TODO: face disk entity hasnt been added to world yet so world transform is before lookAt const wtm = entity.getWorldTransform().clone().invert(); tmpV1.copy(LIGHT_DIR); wtm.transformVector(tmpV1, tmpV1); From 3421da72365fd548ef639b4313f4c3600ab36639 Mon Sep 17 00:00:00 2001 From: kpal81xd Date: Sun, 14 Jan 2024 19:39:17 +0000 Subject: [PATCH 138/178] created variables for magic numbers --- extras/gizmo/axis-shapes.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 6b04d45ab4b..fe3eee80521 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -15,6 +15,8 @@ import { import { Tri } from './tri.js'; // constants +const SHADOW_DAMP_SCALE = 0.25; +const SHADOW_DAMP_OFFSET = 0.75; const TORUS_RENDER_SEGMENTS = 80; const TORUS_INTERSECT_SEGMENTS = 20; const LIGHT_DIR = new Vec3(1, 2, 3); @@ -122,7 +124,7 @@ function calculateShadowColors(lightDir, numVertices, normals, colors = []) { tmpV2.set(x, y, z); const dot = lightDir.dot(tmpV2); - const shadow = dot * 0.25 + 0.75; + const shadow = dot * SHADOW_DAMP_SCALE + SHADOW_DAMP_OFFSET; colors.push(shadow * 255, shadow * 255, shadow * 255, 1); } From 4726b44134cbbb9dd065b9ee022637b813d0e332 Mon Sep 17 00:00:00 2001 From: kpal Date: Mon, 15 Jan 2024 12:33:51 +0000 Subject: [PATCH 139/178] extracted mesh tri data into new class --- extras/gizmo/axis-shapes.js | 91 ++++++++++++++------------------- extras/gizmo/gizmo-transform.js | 2 +- extras/gizmo/gizmo.js | 15 ++---- extras/gizmo/mesh-tri-data.js | 74 +++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 65 deletions(-) create mode 100644 extras/gizmo/mesh-tri-data.js diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index fe3eee80521..e2bc220b99a 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -7,12 +7,11 @@ import { Color, MeshInstance, Entity, - Mat4, Quat, Vec3 } from 'playcanvas'; -import { Tri } from './tri.js'; +import { MeshTriData } from './mesh-tri-data.js'; // constants const SHADOW_DAMP_SCALE = 0.25; @@ -150,7 +149,7 @@ class AxisShape { entity; - triData = []; + meshTriDataList = []; meshInstances = []; @@ -191,6 +190,11 @@ class AxisArrow extends AxisShape { constructor(device, options = {}) { super(device, options); + this.meshTriDataList = [ + new MeshTriData(createCone(this.device)), + new MeshTriData(createCylinder(this.device)) + ]; + this._createArrow(); } @@ -243,15 +247,6 @@ class AxisArrow extends AxisShape { } _createArrow() { - // intersect - this.triData.push({ - tris: Tri.trisFromMesh(createCone(this.device)), - ptm: new Mat4() - }, { - tris: Tri.trisFromMesh(createCylinder(this.device)), - ptm: new Mat4() - }); - this.entity = new Entity('arrow:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); @@ -289,7 +284,7 @@ class AxisArrow extends AxisShape { tmpV1.set(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); tmpQ1.set(0, 0, 0, 1); tmpV2.set(this._arrowThickness, this._arrowLength, this._arrowThickness); - this.triData[0].ptm.setTRS(tmpV1, tmpQ1, tmpV2); + this.meshTriDataList[0].setTransform(tmpV1, tmpQ1, tmpV2); this._head.setLocalPosition(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0); this._head.setLocalScale(this._arrowThickness, this._arrowLength, this._arrowThickness); @@ -300,7 +295,7 @@ class AxisArrow extends AxisShape { tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); tmpQ1.set(0, 0, 0, 1); tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); - this.triData[1].ptm.setTRS(tmpV1, tmpQ1, tmpV2); + this.meshTriDataList[1].setTransform(tmpV1, tmpQ1, tmpV2); // render this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); @@ -314,17 +309,14 @@ class AxisBoxCenter extends AxisShape { constructor(device, options = {}) { super(device, options); + this.meshTriDataList = [ + new MeshTriData(createBox(this.device)) + ]; + this._createCenter(); } _createCenter() { - // intersect - this.triData.push({ - tris: Tri.trisFromMesh(createBox(this.device)), - ptm: new Mat4() - }); - - // render this.entity = new Entity('boxCenter:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); @@ -368,6 +360,11 @@ class AxisBoxLine extends AxisShape { constructor(device, options = {}) { super(device, options); + this.meshTriDataList = [ + new MeshTriData(createBox(this.device)), + new MeshTriData(createCylinder(this.device)) + ]; + this._createBoxLine(); } @@ -411,21 +408,12 @@ class AxisBoxLine extends AxisShape { } _createBoxLine() { - // intersect - this.triData.push({ - tris: Tri.trisFromMesh(createBox(this.device)), - ptm: new Mat4() - }, { - tris: Tri.trisFromMesh(createCylinder(this.device)), - ptm: new Mat4() - }); - - // render this.entity = new Entity('boxLine:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._scale); + // box this._box = new Entity('box:' + this.axis); this.entity.addChild(this._box); this._updateBox(); @@ -438,6 +426,7 @@ class AxisBoxLine extends AxisShape { }); this.meshInstances.push(meshInstance); + // line this._line = new Entity('line:' + this.axis); this.entity.addChild(this._line); this._updateLine(); @@ -456,7 +445,7 @@ class AxisBoxLine extends AxisShape { tmpV1.set(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0); tmpQ1.set(0, 0, 0, 1); tmpV2.set(this._boxSize, this._boxSize, this._boxSize); - this.triData[0].ptm.setTRS(tmpV1, tmpQ1, tmpV2); + this.meshTriDataList[0].setTransform(tmpV1, tmpQ1, tmpV2); // render this._box.setLocalPosition(0, this._gap + this._boxSize * 0.5 + this._lineLength, 0); @@ -468,7 +457,7 @@ class AxisBoxLine extends AxisShape { tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); tmpQ1.set(0, 0, 0, 1); tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); - this.triData[1].ptm.setTRS(tmpV1, tmpQ1, tmpV2); + this.meshTriDataList[1].setTransform(tmpV1, tmpQ1, tmpV2); // render this._line.setLocalPosition(0, this._gap + this._lineLength * 0.5, 0); @@ -494,22 +483,19 @@ class AxisDisk extends AxisShape { this._ringRadius = options.ringRadius ?? this._ringRadius; this._sectorAngle = options.sectorAngle ?? this._sectorAngle; - this._createDisk(); - } - - _createDisk() { - // intersect - this.triData.push({ - tris: Tri.trisFromMesh(createTorus(this.device, { + this.meshTriDataList = [ + new MeshTriData(createTorus(this.device, { tubeRadius: this._tubeRadius + this._intersectTolerance, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, segments: TORUS_INTERSECT_SEGMENTS - })), - ptm: new Mat4() - }); + })) + ]; - // render + this._createDisk(); + } + + _createDisk() { this.entity = new Entity('disk:' + this.axis); this.entity.setLocalPosition(this._position); this.entity.setLocalEulerAngles(this._rotation); @@ -557,7 +543,7 @@ class AxisDisk extends AxisShape { _updateTransform() { // intersect - this.triData[0].tris = Tri.trisFromMesh(createTorus(this.device, { + this.meshTriDataList[0].setTris(createTorus(this.device, { tubeRadius: this._tubeRadius + this._intersectTolerance, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, @@ -579,8 +565,8 @@ class AxisDisk extends AxisShape { sectorAngle: 2 * Math.PI, segments: TORUS_RENDER_SEGMENTS }); - this.meshInstances[0].meshInstance.mesh = arcMesh; - this.meshInstances[1].meshInstance.mesh = circleMesh; + this.meshInstances[0].mesh = arcMesh; + this.meshInstances[1].mesh = circleMesh; } drag(state) { @@ -607,6 +593,10 @@ class AxisPlane extends AxisShape { constructor(device, options = {}) { super(device, options); + this.meshTriDataList = [ + new MeshTriData(createPlane(this.device)) + ]; + this._createPlane(); } @@ -618,13 +608,6 @@ class AxisPlane extends AxisShape { } _createPlane() { - // intersect - this.triData.push({ - tris: Tri.trisFromMesh(createPlane(this.device)), - ptm: new Mat4() - }); - - // render this.entity = new Entity('plane:' + this.axis); this.entity.setLocalPosition(this._getPosition()); this.entity.setLocalEulerAngles(this._rotation); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index c4318f8505c..80958cd4346 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -495,7 +495,7 @@ class GizmoTransform extends Gizmo { const shape = this._shapes[key]; this.gizmo.addChild(shape.entity); this.intersectData.push({ - triData: shape.triData, + meshTriDataList: shape.meshTriDataList, parent: shape.entity, meshInstances: shape.meshInstances }); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index a9b95153b54..e3b7ba7573a 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -78,15 +78,10 @@ class Gizmo extends EventHandler { */ gizmo; - /** - * @typedef TriData - * @property {import('./tri.js').Tri[]} tris - The array of triangles for - * the mesh. - * @property {Mat4} ptm - The parent transform matrix for the mesh. - */ /** * @typedef IntersectData - * @property {TriData[]} triData - The array of {@link TriData} + * @property {import('./mesh-tri-data.js').MeshTriData[]} meshTriDataList - + * The array of {@link MeshTriData} * @property {import('playcanvas').GraphNode} parent - The mesh parent node. * @property {import('playcanvas').MeshInstance[]} meshInstances - * array of mesh instances for rendering @@ -255,10 +250,10 @@ class Gizmo extends EventHandler { const dir = end.clone().sub(start).normalize(); for (let i = 0; i < this.intersectData.length; i++) { - const { triData, parent, meshInstances } = this.intersectData[i]; + const { meshTriDataList, parent, meshInstances } = this.intersectData[i]; const wtm = parent.getWorldTransform().clone(); - for (let j = 0; j < triData.length; j++) { - const { tris, ptm } = triData[j]; + for (let j = 0; j < meshTriDataList.length; j++) { + const { tris, ptm } = meshTriDataList[j]; tmpM1.copy(wtm).mul(ptm); tmpM1.invert(); tmpM1.transformPoint(start, xstart); diff --git a/extras/gizmo/mesh-tri-data.js b/extras/gizmo/mesh-tri-data.js new file mode 100644 index 00000000000..6d51f77a589 --- /dev/null +++ b/extras/gizmo/mesh-tri-data.js @@ -0,0 +1,74 @@ +import { + Mesh, + Mat4, + Quat, + Vec3 +} from 'playcanvas'; + +import { Tri } from './tri.js'; + +// temporary variables +const tmpV1 = new Vec3(); +const tmpV2 = new Vec3(); +const tmpV3 = new Vec3(); + +/** + * The class for holding mesh triangle data. + */ +class MeshTriData { + /** + * The local to parent transform matrix. + * + * @type {Mat4} + */ + _ptm = new Mat4(); + + /** + * The array of triangles for the mesh. + * + * @type {import('./tri.js').Tri[]} + */ + tris; + + constructor(mesh) { + this.setTris(mesh); + } + + get ptm() { + return this._ptm; + } + + _trisFromMesh(mesh) { + const tris = []; + const pos = []; + const indices = []; + mesh.getPositions(pos); + mesh.getIndices(indices); + + for (let k = 0; k < indices.length; k += 3) { + const i1 = indices[k]; + const i2 = indices[k + 1]; + const i3 = indices[k + 2]; + + tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); + tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); + tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); + const tri = new Tri(tmpV1, tmpV2, tmpV3); + tris.push(tri); + } + return tris; + } + + setTransform(pos = new Vec3(), rot = new Quat(), scale = new Vec3()) { + this.ptm.setTRS(pos, rot, scale); + } + + setTris(mesh) { + if (!mesh || !(mesh instanceof Mesh)) { + throw new Error('No mesh provided.'); + } + this.tris = this._trisFromMesh(mesh); + } +} + +export { MeshTriData }; From f412fdfccdb2a1c66f97ca5f25fb339889e125f0 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 10:25:06 +0000 Subject: [PATCH 140/178] Removed unused code in tri class --- extras/gizmo/tri.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index bea565c09c2..e3690afbe6a 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -23,27 +23,6 @@ class Tri { v2 = new Vec3(); - static trisFromMesh(mesh) { - const tris = []; - const pos = []; - const indices = []; - mesh.getPositions(pos); - mesh.getIndices(indices); - - for (let k = 0; k < indices.length; k += 3) { - const i1 = indices[k]; - const i2 = indices[k + 1]; - const i3 = indices[k + 2]; - - tmpV1.set(pos[i1 * 3], pos[i1 * 3 + 1], pos[i1 * 3 + 2]); - tmpV2.set(pos[i2 * 3], pos[i2 * 3 + 1], pos[i2 * 3 + 2]); - tmpV3.set(pos[i3 * 3], pos[i3 * 3 + 1], pos[i3 * 3 + 2]); - const tri = new Tri(tmpV1, tmpV2, tmpV3); - tris.push(tri); - } - return tris; - } - constructor(v0 = new Vec3(), v1 = new Vec3(), v2 = new Vec3()) { if (v0.length === 3) { this.v0.copy(v0[0]); From cc6edfbb4727f927ad2a233e9c9f9ab97dd33f67 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 11:18:49 +0000 Subject: [PATCH 141/178] removed unused vectors in tri class --- extras/gizmo/tri.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index e3690afbe6a..0f3e6ef3279 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -2,11 +2,6 @@ import { Vec3 } from 'playcanvas'; -// temporary variables -const tmpV1 = new Vec3(); -const tmpV2 = new Vec3(); -const tmpV3 = new Vec3(); - const e1 = new Vec3(); const e2 = new Vec3(); const h = new Vec3(); From fc7fe030020637f739cd57322501e0e2169e0d36 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 13:31:35 +0000 Subject: [PATCH 142/178] removed bloat vec3 and array ctor override for tri class --- extras/gizmo/tri.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index 0f3e6ef3279..ccc3f115a3b 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -12,22 +12,14 @@ const q = new Vec3(); const EPSILON = 1e-6; class Tri { - v0 = new Vec3(); + v0; - v1 = new Vec3(); + v1; - v2 = new Vec3(); + v2; - constructor(v0 = new Vec3(), v1 = new Vec3(), v2 = new Vec3()) { - if (v0.length === 3) { - this.v0.copy(v0[0]); - this.v1.copy(v0[1]); - this.v2.copy(v0[2]); - } else { - this.v0.copy(v0); - this.v1.copy(v1); - this.v2.copy(v2); - } + constructor(v0, v1, v2) { + this.set(v0, v1, v2); } set(v0, v1, v2) { From 781610d5991c8d6eedb85961a0cbc60dc792a6a2 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 13:34:41 +0000 Subject: [PATCH 143/178] removed vec3 creation in intersectRay --- extras/gizmo/tri.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index ccc3f115a3b..d57b0aaae42 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -30,7 +30,7 @@ class Tri { return this; } - intersectRay(origin, dir, out = new Vec3(), epsilon = EPSILON) { + intersectRay(origin, dir, out, epsilon = EPSILON) { e1.sub2(this.v1, this.v0); e2.sub2(this.v2, this.v0); h.cross(dir, e2); @@ -54,7 +54,9 @@ class Tri { const t = f * e2.dot(q); if (t > epsilon) { - out.copy(dir).scale(t).add(origin); + if (out instanceof Vec3) { + out.copy(dir).scale(t).add(origin); + } return true; } From e6a22e76056207c1f25fbc96c08b5ddffdfde763 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 13:38:46 +0000 Subject: [PATCH 144/178] updated jsdoc for ptm --- extras/gizmo/mesh-tri-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/mesh-tri-data.js b/extras/gizmo/mesh-tri-data.js index 6d51f77a589..a7134e48ece 100644 --- a/extras/gizmo/mesh-tri-data.js +++ b/extras/gizmo/mesh-tri-data.js @@ -17,7 +17,7 @@ const tmpV3 = new Vec3(); */ class MeshTriData { /** - * The local to parent transform matrix. + * The transform of the mesh. * * @type {Mat4} */ From 3791fa75b7c1d1d06cad77c2458d4609d24f3bb5 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 14:08:59 +0000 Subject: [PATCH 145/178] extracted layer outside gizmo class; fixed tri set issue --- examples/src/examples/misc/gizmos.mjs | 15 ++++++++++++--- extras/gizmo/gizmo-rotate.js | 7 ++++--- extras/gizmo/gizmo-scale.js | 7 ++++--- extras/gizmo/gizmo-transform.js | 7 ++++--- extras/gizmo/gizmo-translate.js | 7 ++++--- extras/gizmo/gizmo.js | 24 +++--------------------- extras/gizmo/tri.js | 6 +++--- 7 files changed, 34 insertions(+), 39 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 1c867bbb63c..23f1e60f81a 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -217,10 +217,19 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip skipSetFire = false; constructor(app, camera) { + const layer = new pc.Layer({ + name: 'Gizmo', + clearDepthBuffer: true, + opaqueSortMode: pc.SORTMODE_NONE, + transparentSortMode: pc.SORTMODE_NONE + }); + app.scene.layers.push(layer); + camera.layers = camera.layers.concat(layer.id); + this.gizmos = { - translate: new pcx.GizmoTranslate(app, camera), - rotate: new pcx.GizmoRotate(app, camera), - scale: new pcx.GizmoScale(app, camera) + translate: new pcx.GizmoTranslate(app, camera, layer), + rotate: new pcx.GizmoRotate(app, camera, layer), + scale: new pcx.GizmoScale(app, camera, layer) }; } diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 52c9b638719..fa2e09d476d 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -97,11 +97,12 @@ class GizmoRotate extends GizmoTransform { * * @param {import('playcanvas').AppBase} app - The application instance. * @param {import('playcanvas').CameraComponent} camera - The camera component. + * @param {import('playcanvas').Layer} layer - The render layer. * @example - * const gizmo = new pcx.GizmoRotate(app, camera); + * const gizmo = new pcx.GizmoRotate(app, camera, layer); */ - constructor(app, camera) { - super(app, camera); + constructor(app, camera, layer) { + super(app, camera, layer); this._createTransform(); diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index a37d601edf7..e7148111257 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -92,11 +92,12 @@ class GizmoScale extends GizmoTransform { * * @param {import('playcanvas').AppBase} app - The application instance. * @param {import('playcanvas').CameraComponent} camera - The camera component. + * @param {import('playcanvas').Layer} layer - The render layer. * @example - * const gizmo = new pcx.GizmoScale(app, camera); + * const gizmo = new pcx.GizmoScale(app, camera, layer); */ - constructor(app, camera) { - super(app, camera); + constructor(app, camera, layer) { + super(app, camera, layer); this._createTransform(); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 80958cd4346..386306fc9c8 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -208,11 +208,12 @@ class GizmoTransform extends Gizmo { * * @param {import('playcanvas').AppBase} app - The application instance. * @param {import('playcanvas').CameraComponent} camera - The camera component. + * @param {import('playcanvas').Layer} layer - The render layer. * @example - * const gizmo = new pcx.GizmoTransform(app, camera); + * const gizmo = new pcx.GizmoTransform(app, camera, layer); */ - constructor(app, camera) { - super(app, camera); + constructor(app, camera, layer) { + super(app, camera, layer); this.app.on('update', () => { if (!this.gizmo.enabled) { diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index eb5b6555e44..4b5a2fc6185 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -88,11 +88,12 @@ class GizmoTranslate extends GizmoTransform { * * @param {import('playcanvas').AppBase} app - The application instance. * @param {import('playcanvas').CameraComponent} camera - The camera component. + * @param {import('playcanvas').Layer} layer - The render layer. * @example - * const gizmo = new pcx.GizmoTranslate(app, camera); + * const gizmo = new pcx.GizmoTranslate(app, camera, layer); */ - constructor(app, camera) { - super(app, camera); + constructor(app, camera, layer) { + super(app, camera, layer); this._createTransform(); diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index e3b7ba7573a..e42d7ecd5e6 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -98,16 +98,17 @@ class Gizmo extends EventHandler { * * @param {import('playcanvas').AppBase} app - The application instance. * @param {import('playcanvas').CameraComponent} camera - The camera component. + * @param {import('playcanvas').Layer} layer - The render layer. * @example * const gizmo = new pcx.Gizmo(app, camera); */ - constructor(app, camera) { + constructor(app, camera, layer) { super(); this.app = app; this.camera = camera; + this.layer = layer; - this._createLayer(); this._createGizmo(); this._updateScale(); @@ -181,25 +182,6 @@ class Gizmo extends EventHandler { return dist * Math.tan(this.camera.fov * math.DEG_TO_RAD / 2); } - _createLayer() { - const layerMap = this.app.scene.layers.layerIdMap; - if (layerMap.has(GIZMO_LAYER_ID)) { - this.layer = layerMap.get(GIZMO_LAYER_ID); - } else { - this.layer = new Layer({ - id: GIZMO_LAYER_ID, - name: 'Gizmo', - clearDepthBuffer: true, - opaqueSortMode: SORTMODE_NONE, - transparentSortMode: SORTMODE_NONE - }); - this.app.scene.layers.push(this.layer); - } - if (this.camera.layers.indexOf(this.layer.id) === -1) { - this.camera.layers = this.camera.layers.concat(this.layer.id); - } - } - _createGizmo() { this.gizmo = new Entity('gizmo'); this.app.root.addChild(this.gizmo); diff --git a/extras/gizmo/tri.js b/extras/gizmo/tri.js index d57b0aaae42..52cc78fc4aa 100644 --- a/extras/gizmo/tri.js +++ b/extras/gizmo/tri.js @@ -12,11 +12,11 @@ const q = new Vec3(); const EPSILON = 1e-6; class Tri { - v0; + v0 = new Vec3(); - v1; + v1 = new Vec3(); - v2; + v2 = new Vec3(); constructor(v0, v1, v2) { this.set(v0, v1, v2); From 3c58750dc8a269510c73516496fdd8a36bbb1285 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 14:09:53 +0000 Subject: [PATCH 146/178] removed unused variables in gizmo class --- extras/gizmo/gizmo.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index e42d7ecd5e6..13ccfb4c104 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -1,9 +1,7 @@ import { math, PROJECTION_PERSPECTIVE, - SORTMODE_NONE, EventHandler, - Layer, Entity, Mat4, Vec3 @@ -17,7 +15,6 @@ const xstart = new Vec3(); const xdir = new Vec3(); // constants -const GIZMO_LAYER_ID = 1e5; const MIN_GIZMO_SCALE = 1e-4; const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; From fd8781551f5532347a557a796c652d46364fcbfd Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 14:18:23 +0000 Subject: [PATCH 147/178] used constants for world and local space --- extras/gizmo/gizmo-rotate.js | 5 +++-- extras/gizmo/gizmo-scale.js | 3 ++- extras/gizmo/gizmo-translate.js | 3 ++- extras/gizmo/gizmo.js | 22 ++++++++++++++++++---- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index fa2e09d476d..9954774bcd9 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -6,6 +6,7 @@ import { } from 'playcanvas'; import { AxisDisk } from './axis-shapes.js'; +import { LOCAL_COORD_SPACE } from './gizmo.js'; import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables @@ -256,7 +257,7 @@ class GizmoRotate extends GizmoTransform { tmpQ1.setFromAxisAngle(tmpV1, angleDelta); - if (!isFacing && this._coordSpace === 'local') { + if (!isFacing && this._coordSpace === LOCAL_COORD_SPACE) { tmpQ2.copy(this._nodeLocalRotations.get(node)).mul(tmpQ1); node.setLocalRotation(tmpQ2); } else { @@ -268,7 +269,7 @@ class GizmoRotate extends GizmoTransform { } } - if (this._coordSpace === 'local') { + if (this._coordSpace === LOCAL_COORD_SPACE) { this._updateRotation(); } } diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index e7148111257..a1ca90dacfd 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -3,6 +3,7 @@ import { } from 'playcanvas'; import { AxisBoxCenter, AxisBoxLine, AxisPlane } from './axis-shapes.js'; +import { LOCAL_COORD_SPACE } from './gizmo.js'; import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables @@ -68,7 +69,7 @@ class GizmoScale extends GizmoTransform { }) }; - _coordSpace = 'local'; + _coordSpace = LOCAL_COORD_SPACE; /** * Internal mapping from each attached node to their starting scale. diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 4b5a2fc6185..250d22d4885 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -4,6 +4,7 @@ import { } from 'playcanvas'; import { AxisArrow, AxisPlane } from './axis-shapes.js'; +import { LOCAL_COORD_SPACE } from './gizmo.js'; import { GizmoTransform } from "./gizmo-transform.js"; // temporary variables @@ -195,7 +196,7 @@ class GizmoTranslate extends GizmoTransform { _setNodePositions(pointDelta) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; - if (this._coordSpace === 'local') { + if (this._coordSpace === LOCAL_COORD_SPACE) { tmpV1.copy(pointDelta); node.parent.getWorldTransform().getScale(tmpV2); tmpV2.x = 1 / tmpV2.x; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 13ccfb4c104..6ccb7bb3139 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -19,6 +19,20 @@ const MIN_GIZMO_SCALE = 1e-4; const PERS_SCALE_RATIO = 0.3; const ORTHO_SCALE_RATIO = 0.32; +/** + * Local coordinate space. + * + * @type {string} + */ +export const LOCAL_COORD_SPACE = 'local'; + +/** + * World coordinate space. + * + * @type {string} + */ +export const WORLD_COORD_SPACE = 'world'; + /** * The base class for all gizmos. */ @@ -40,12 +54,12 @@ class Gizmo extends EventHandler { _scale = 1; /** - * Internal version of coordinate space. + * Internal version of coordinate space. Defaults to {@link WORLD_COORD_SPACE}. * * @type {string} * @protected */ - _coordSpace = 'world'; + _coordSpace = WORLD_COORD_SPACE; /** * The application instance containing the gizmo. @@ -155,7 +169,7 @@ class Gizmo extends EventHandler { } set coordSpace(value) { - this._coordSpace = value ?? 'world'; + this._coordSpace = value ?? WORLD_COORD_SPACE; this._updateRotation(); } @@ -199,7 +213,7 @@ class Gizmo extends EventHandler { _updateRotation() { tmpV1.set(0, 0, 0); - if (this._coordSpace === 'local') { + if (this._coordSpace === LOCAL_COORD_SPACE) { for (let i = 0; i < this.nodes.length; i++) { const node = this.nodes[i]; tmpV1.add(node.getEulerAngles()); From 307812ba42cfb55a7d989ec33fbcb32195ca1199 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 14:25:44 +0000 Subject: [PATCH 148/178] moved event handlers to ctor and added dtor for event handler and gizmo entity cleanup --- extras/gizmo/gizmo.js | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 6ccb7bb3139..c88e5c9c77c 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -163,9 +163,17 @@ class Gizmo extends EventHandler { this.fire('key:up'); }; + if (window) { + window.addEventListener('pointerdown', this._onPointerDown); + window.addEventListener('pointermove', this._onPointerMove); + window.addEventListener('pointerup', this._onPointerUp); + window.addEventListener('keydown', this._onKeyDown); + window.addEventListener('keyup', this._onKeyUp); + } + app.on('update', () => this._updateScale()); - app.on('destroy', () => this.detach()); + app.on('destroy', () => this.destroy()); } set coordSpace(value) { @@ -277,14 +285,6 @@ class Gizmo extends EventHandler { this._updatePosition(); this._updateRotation(); - if (window) { - window.addEventListener('pointerdown', this._onPointerDown); - window.addEventListener('pointermove', this._onPointerMove); - window.addEventListener('pointerup', this._onPointerUp); - window.addEventListener('keydown', this._onKeyDown); - window.addEventListener('keyup', this._onKeyUp); - } - this.fire('nodes:attach'); this.gizmo.enabled = true; @@ -304,6 +304,19 @@ class Gizmo extends EventHandler { this.fire('nodes:detach'); this.nodes = []; + } + + /** + * Destroys the gizmo instance; detaches + * all graph nodes. + * + * @example + * const gizmo = new pcx.Gizmo(); + * gizmo.attach([boxA, boxB]); + * gizmo.destroy(); + */ + destroy() { + this.detach(); if (window) { window.removeEventListener('pointerdown', this._onPointerDown); @@ -312,6 +325,8 @@ class Gizmo extends EventHandler { window.removeEventListener('keydown', this._onKeyDown); window.removeEventListener('keyup', this._onKeyUp); } + + this.gizmo.destroy(); } } From d3061c486844e14c361c90cad5e99f5482bdf9c3 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 14:27:11 +0000 Subject: [PATCH 149/178] removed get/set uvs from shadow mesh creation --- extras/gizmo/axis-shapes.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index e2bc220b99a..9f50dca2763 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -95,7 +95,6 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { const options = { positions: [], normals: [], - uvs: [], indices: [], colors: [] }; @@ -103,7 +102,6 @@ function createShadowMesh(device, entity, type, templateOpts = {}) { mesh.getPositions(options.positions); mesh.getNormals(options.normals); mesh.getIndices(options.indices); - mesh.getUvs(0, options.uvs); const wtm = entity.getWorldTransform().clone().invert(); tmpV1.copy(LIGHT_DIR); From cfb58e3b0fdf1bba5adf624e29aedb01629d4c83 Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 16:53:01 +0000 Subject: [PATCH 150/178] moved repeated methods to base class in axis-shapes --- extras/gizmo/axis-shapes.js | 183 ++++++++++++++---------------------- 1 file changed, 72 insertions(+), 111 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 9f50dca2763..63b8226eca1 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -164,6 +164,36 @@ class AxisShape { this._hoverColor = options.hoverColor ?? Color.WHITE; } + _createRoot(name) { + this.entity = new Entity(name + ':' + this.axis); + this._updateRootTransform(); + } + + _updateRootTransform() { + this.entity.setLocalPosition(this._position); + this.entity.setLocalEulerAngles(this._rotation); + this.entity.setLocalScale(this._scale); + } + + _addRenderMeshes(entity, meshes) { + const meshInstances = []; + for (let i = 0; i < meshes.length; i++) { + const mi = new MeshInstance(meshes[i], this._defaultColor); + meshInstances.push(mi); + this.meshInstances.push(mi); + } + entity.addComponent('render', { + meshInstances: meshInstances, + layers: this._layers, + castShadows: false + }); + } + + _addRenderShadowMesh(entity, type) { + const mesh = createShadowMesh(this.device, entity, type); + this._addRenderMeshes(entity, [mesh]); + } + hover(state) { const material = state ? this._hoverColor : this._defaultColor; for (let i = 0; i < this.meshInstances.length; i++) { @@ -245,36 +275,19 @@ class AxisArrow extends AxisShape { } _createArrow() { - this.entity = new Entity('arrow:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); + this._createRoot('arrow'); // head this._head = new Entity('head:' + this.axis); this.entity.addChild(this._head); this._updateHead(); - let mesh = createShadowMesh(this.device, this._head, 'cone'); - let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._head.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._addRenderShadowMesh(this._head, 'cone'); // line this._line = new Entity('line:' + this.axis); this.entity.addChild(this._line); this._updateLine(); - mesh = createShadowMesh(this.device, this._line, 'cylinder'); - meshInstance = new MeshInstance(mesh, this._defaultColor); - this._line.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._addRenderShadowMesh(this._line, 'cylinder'); } _updateHead() { @@ -315,18 +328,11 @@ class AxisBoxCenter extends AxisShape { } _createCenter() { - this.entity = new Entity('boxCenter:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - const mesh = createShadowMesh(this.device, this.entity, 'box'); - const meshInstance = new MeshInstance(mesh, this._defaultColor); - this.entity.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._createRoot('boxCenter'); + this._updateTransform(); + + // box + this._addRenderShadowMesh(this.entity, 'box'); } set size(value) { @@ -406,36 +412,20 @@ class AxisBoxLine extends AxisShape { } _createBoxLine() { - this.entity = new Entity('boxLine:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); + this._createRoot('boxLine'); // box this._box = new Entity('box:' + this.axis); this.entity.addChild(this._box); this._updateBox(); - let mesh = createShadowMesh(this.device, this._box, 'box'); - let meshInstance = new MeshInstance(mesh, this._defaultColor); - this._box.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._addRenderShadowMesh(this._box, 'box'); // line this._line = new Entity('line:' + this.axis); this.entity.addChild(this._line); this._updateLine(); - mesh = createShadowMesh(this.device, this._line, 'cylinder'); - meshInstance = new MeshInstance(mesh, this._defaultColor); - this._line.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._addRenderShadowMesh(this._line, 'cylinder'); + } _updateBox() { @@ -482,43 +472,39 @@ class AxisDisk extends AxisShape { this._sectorAngle = options.sectorAngle ?? this._sectorAngle; this.meshTriDataList = [ - new MeshTriData(createTorus(this.device, { - tubeRadius: this._tubeRadius + this._intersectTolerance, - ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, - segments: TORUS_INTERSECT_SEGMENTS - })) + new MeshTriData(this._createIntersectTorus()) ]; this._createDisk(); } - _createDisk() { - this.entity = new Entity('disk:' + this.axis); - this.entity.setLocalPosition(this._position); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._scale); - const arcMesh = createShadowMesh(this.device, this.entity, 'torus', { - tubeRadius: this._tubeRadius, + _createIntersectTorus() { + return createTorus(this.device, { + tubeRadius: this._tubeRadius + this._intersectTolerance, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, - segments: TORUS_RENDER_SEGMENTS + segments: TORUS_INTERSECT_SEGMENTS }); - const circleMesh = createShadowMesh(this.device, this.entity, 'torus', { + } + + _createRenderTorus(sectorAngle) { + return createShadowMesh(this.device, this.entity, 'torus', { tubeRadius: this._tubeRadius, ringRadius: this._ringRadius, - sectorAngle: 2 * Math.PI, + sectorAngle: sectorAngle, segments: TORUS_RENDER_SEGMENTS }); - const arcMeshInstance = new MeshInstance(arcMesh, this._defaultColor); - const circleMeshInstance = new MeshInstance(circleMesh, this._defaultColor); - circleMeshInstance.visible = false; - this.entity.addComponent('render', { - meshInstances: [arcMeshInstance, circleMeshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(arcMeshInstance, circleMeshInstance); + } + + _createDisk() { + this._createRoot('disk'); + + // arc/circle + this._addRenderMeshes(this.entity, [ + this._createRenderTorus(this._sectorAngle), + this._createRenderTorus(2 * Math.PI) + ]); + this.drag(false); } set tubeRadius(value) { @@ -541,30 +527,11 @@ class AxisDisk extends AxisShape { _updateTransform() { // intersect - this.meshTriDataList[0].setTris(createTorus(this.device, { - tubeRadius: this._tubeRadius + this._intersectTolerance, - ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, - segments: TORUS_INTERSECT_SEGMENTS - })); + this.meshTriDataList[0].setTris(this._createIntersectTorus()); // render - const arcMesh = createShadowMesh(this.device, this.entity, 'torus', { - lightDir: this._lightDir, - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius, - sectorAngle: this._sectorAngle, - segments: TORUS_RENDER_SEGMENTS - }); - const circleMesh = createShadowMesh(this.device, this.entity, 'torus', { - lightDir: this._lightDir, - tubeRadius: this._tubeRadius, - ringRadius: this._ringRadius, - sectorAngle: 2 * Math.PI, - segments: TORUS_RENDER_SEGMENTS - }); - this.meshInstances[0].mesh = arcMesh; - this.meshInstances[1].mesh = circleMesh; + this.meshInstances[0].mesh = this._createRenderTorus(this._sectorAngle); + this.meshInstances[1].mesh = this._createRenderTorus(2 * Math.PI); } drag(state) { @@ -606,18 +573,11 @@ class AxisPlane extends AxisShape { } _createPlane() { - this.entity = new Entity('plane:' + this.axis); - this.entity.setLocalPosition(this._getPosition()); - this.entity.setLocalEulerAngles(this._rotation); - this.entity.setLocalScale(this._size, this._size, this._size); - const mesh = createShadowMesh(this.device, this.entity, 'plane'); - const meshInstance = new MeshInstance(mesh, this._defaultColor); - this.entity.addComponent('render', { - meshInstances: [meshInstance], - layers: this._layers, - castShadows: false - }); - this.meshInstances.push(meshInstance); + this._createRoot('plane'); + this._updateTransform(); + + // plane + this._addRenderShadowMesh(this.entity, 'plane'); } set size(value) { @@ -641,6 +601,7 @@ class AxisPlane extends AxisShape { _updateTransform() { // intersect/render this.entity.setLocalPosition(this._getPosition()); + this.entity.setLocalEulerAngles(this._rotation); this.entity.setLocalScale(this._size, this._size, this._size); } } From 377e267a6e39a78ea50ac60de20fc44d55a37cef Mon Sep 17 00:00:00 2001 From: kpal Date: Tue, 16 Jan 2024 17:34:33 +0000 Subject: [PATCH 151/178] destroy handler for tri mesh and shapes --- extras/gizmo/axis-shapes.js | 4 ++++ extras/gizmo/gizmo-transform.js | 8 ++++++++ extras/gizmo/mesh-tri-data.js | 5 ++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 63b8226eca1..10788185be8 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -200,6 +200,10 @@ class AxisShape { this.meshInstances[i].material = material; } } + + destroy() { + this.entity.destroy(); + } } class AxisArrow extends AxisShape { diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 386306fc9c8..170224ba366 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -505,6 +505,14 @@ class GizmoTransform extends Gizmo { } } } + + destroy() { + for (const key in this._shapes) { + this._shapes[key].destroy(); + } + + super.destroy(); + } } export { GizmoTransform }; diff --git a/extras/gizmo/mesh-tri-data.js b/extras/gizmo/mesh-tri-data.js index a7134e48ece..d851285166e 100644 --- a/extras/gizmo/mesh-tri-data.js +++ b/extras/gizmo/mesh-tri-data.js @@ -38,12 +38,15 @@ class MeshTriData { return this._ptm; } - _trisFromMesh(mesh) { + _trisFromMesh(mesh, destroy = true) { const tris = []; const pos = []; const indices = []; mesh.getPositions(pos); mesh.getIndices(indices); + if (destroy) { + mesh.destroy(); + } for (let k = 0; k < indices.length; k += 3) { const i1 = indices[k]; From b3646dedc30a62c6ed489b2f362760dfe7c09419 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:29:23 +0000 Subject: [PATCH 152/178] hover plane now shows plane axis over facing axis --- extras/gizmo/gizmo-transform.js | 1 + 1 file changed, 1 insertion(+) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 170224ba366..6eb601aac9a 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -333,6 +333,7 @@ class GizmoTransform extends Gizmo { return; } this._hoverAxis = this._getAxis(meshInstance); + this._hoverIsPlane = this._getIsPlane(meshInstance); const shape = this._hoverShapeMap.get(meshInstance); if (shape === this._hoverShape) { return; From 9952eea0f2e4a37d1ba17c08861442325fb70f6c Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:40:56 +0000 Subject: [PATCH 153/178] replaced fly camera with orbit; removed distance control --- examples/src/examples/misc/gizmos.mjs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 23f1e60f81a..a8e35016124 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -178,14 +178,6 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { link: { observer, path: 'camera.proj' } }) ), - jsx(LabelGroup, { text: 'Distance' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.dist' }, - min: 1, - max: 10 - }) - ), jsx(LabelGroup, { text: 'FOV' }, jsx(SliderInput, { binding: new BindingTwoWay(), @@ -334,7 +326,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // assets const assets = { - script: new pc.Asset('script', 'script', { url: scriptsPath + 'camera/fly-camera.js' }) + script: new pc.Asset('script', 'script', { url: scriptsPath + 'camera/orbit-camera.js' }) }; /** @@ -378,9 +370,10 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip clearColor: new pc.Color(0.5, 0.6, 0.9) }); camera.addComponent("script"); - camera.script.create("flyCamera"); - camera.setPosition(5, 3, 5); - camera.lookAt(0, 0, 0); + camera.script.create("orbitCamera"); + camera.script.create("orbitCameraInputMouse"); + camera.script.create("orbitCameraInputTouch"); + camera.rotate(-20, 45, 0); app.root.addChild(camera); // create directional light entity @@ -403,9 +396,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip case 'proj': camera.camera.projection = value - 1; break; - case 'dist': - camera.setPosition(5 * value, 3 * value, 5 * value); - break; case 'fov': camera.camera.fov = value; break; From 68a6586f188482ac316cb26a9630ced22d61684d Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:54:42 +0000 Subject: [PATCH 154/178] Fixed rotate lighting and added starting guide angle line --- examples/src/examples/misc/gizmos.mjs | 2 +- extras/gizmo/gizmo-rotate.js | 42 ++++++++++++++++++++------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index a8e35016124..f8df4b00dbd 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -384,7 +384,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // create gizmo const gizmoHandler = new GizmoHandler(app, camera.camera); - gizmoHandler.switch('translate', [boxA, boxB]); + gizmoHandler.switch('rotate', [boxA, boxB]); const tmpC = new pc.Color(); data.on('*:set', (/** @type {string} */ path, value) => { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 9954774bcd9..622c8876b8f 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -1,5 +1,6 @@ import { math, + Color, Quat, Mat4, Vec3 @@ -83,6 +84,22 @@ class GizmoRotate extends GizmoTransform { */ _nodeOffsets = new Map(); + /** + * Internal color for guide angle starting line. + * + * @type {Vec3} + * @private + */ + _guideAngleStartColor = Color.WHITE; + + /** + * Internal vector for the start point of the guide line angle. + * + * @type {Vec3} + * @private + */ + _guideAngleStart = new Vec3(); + /** * Internal vector for the end point of the guide line angle. * @@ -109,7 +126,8 @@ class GizmoRotate extends GizmoTransform { this.on('transform:start', () => { this._storeNodeRotations(); - this._updateGuideAngleEnd(this._selectionStartPoint); + this._updateGuideAngle(this._selectionStartPoint, this._guideAngleStart); + this._updateGuideAngle(this._selectionStartPoint, this._guideAngleEnd); this._drag(true); }); @@ -118,7 +136,7 @@ class GizmoRotate extends GizmoTransform { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } this._setNodeRotations(this._selectedAxis, angleDelta); - this._updateGuideAngleEnd(pointLast); + this._updateGuideAngle(pointLast, this._guideAngleEnd); }); this.on('transform:end', () => { @@ -138,7 +156,9 @@ class GizmoRotate extends GizmoTransform { if (this._dragging) { const gizmoPos = this.gizmo.getPosition(); - this._drawGuideAngleLine(gizmoPos, this._selectedAxis); + this._drawGuideAngleLine(gizmoPos, this._selectedAxis, + this._guideAngleStart, this._guideAngleStartColor); + this._drawGuideAngleLine(gizmoPos, this._selectedAxis, this._guideAngleEnd); } }); } @@ -181,18 +201,18 @@ class GizmoRotate extends GizmoTransform { this._shapes.z[prop] = value; } - _updateGuideAngleEnd(point) { + _updateGuideAngle(point, out) { const axis = this._selectedAxis; const scale = axis === 'face' ? this.faceRingRadius : this.xyzRingRadius; - this._guideAngleEnd.copy(point).normalize(); - this._guideAngleEnd.scale(scale); - this._gizmoRotationStart.transformVector(this._guideAngleEnd, this._guideAngleEnd); + out.copy(point).normalize(); + out.scale(scale); + this._gizmoRotationStart.transformVector(out, out); } - _drawGuideAngleLine(pos, axis) { + _drawGuideAngleLine(pos, axis, point, color = this._guideColors[axis]) { tmpV1.set(0, 0, 0); - tmpV2.copy(this._guideAngleEnd).scale(this._scale); - this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), this._guideColors[axis], false, this.layer); + tmpV2.copy(point).scale(this._scale); + this.app.drawLine(tmpV1.add(pos), tmpV2.add(pos), color, false, this.layer); } _getLookAtEulerAngles(position) { @@ -200,7 +220,7 @@ class GizmoRotate extends GizmoTransform { tmpM1.setLookAt(tmpV1, position, Vec3.UP); tmpQ1.setFromMat4(tmpM1); tmpQ1.getEulerAngles(tmpV1); - tmpV1.x += 90; + tmpV1.x -= 90; return tmpV1; } From 7d3c020341cd949c6e732e2a94eeadaeeb626391 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:56:07 +0000 Subject: [PATCH 155/178] Updated guide angle start color and jsdoc type --- extras/gizmo/gizmo-rotate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 622c8876b8f..4a6f47cbfdb 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -90,7 +90,7 @@ class GizmoRotate extends GizmoTransform { * @type {Vec3} * @private */ - _guideAngleStartColor = Color.WHITE; + _guideAngleStartColor = new Color(0, 0, 0, 0.3); /** * Internal vector for the start point of the guide line angle. From 439cbd04257c2299e0abc4ad3e0bdc70e9a01d99 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:56:26 +0000 Subject: [PATCH 156/178] Fixed jsdoc type for guide angle start color --- extras/gizmo/gizmo-rotate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 4a6f47cbfdb..72968063cf1 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -87,7 +87,7 @@ class GizmoRotate extends GizmoTransform { /** * Internal color for guide angle starting line. * - * @type {Vec3} + * @type {Color} * @private */ _guideAngleStartColor = new Color(0, 0, 0, 0.3); From 1623cab531fcc453e8a55e241b5b0ea9eb5be98c Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 10:59:11 +0000 Subject: [PATCH 157/178] reverted default transform in example to translate --- examples/src/examples/misc/gizmos.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index f8df4b00dbd..a8e35016124 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -384,7 +384,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // create gizmo const gizmoHandler = new GizmoHandler(app, camera.camera); - gizmoHandler.switch('rotate', [boxA, boxB]); + gizmoHandler.switch('translate', [boxA, boxB]); const tmpC = new pc.Color(); data.on('*:set', (/** @type {string} */ path, value) => { From 2e69cf0974bc314ccaea40521d4bd68f65b8f1a9 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 11:10:45 +0000 Subject: [PATCH 158/178] modified selection to sort by camera distance --- extras/gizmo/gizmo.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index c88e5c9c77c..ca54a54994d 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -250,6 +250,7 @@ class Gizmo extends EventHandler { const end = this.camera.screenToWorld(x, y, this.camera.farClip); const dir = end.clone().sub(start).normalize(); + const selection = []; for (let i = 0; i < this.intersectData.length; i++) { const { meshTriDataList, parent, meshInstances } = this.intersectData[i]; const wtm = parent.getWorldTransform().clone(); @@ -262,13 +263,21 @@ class Gizmo extends EventHandler { xdir.normalize(); for (let k = 0; k < tris.length; k++) { - if (tris[k].intersectRay(xstart, xdir)) { - return meshInstances; + if (tris[k].intersectRay(xstart, xdir, tmpV1)) { + selection.push({ + dist: tmpV1.sub(xstart).length(), + meshInstances: meshInstances + }); } } } } + if (selection.length) { + selection.sort((s0, s1) => s0.dist - s1.dist); + return selection[0].meshInstances; + } + return []; } From b995fd388e326f1c3e13fa9b3f27efe97fdcfb22 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 11:26:50 +0000 Subject: [PATCH 159/178] added intersection tolerance as control setting --- examples/src/examples/misc/gizmos.mjs | 14 ++++++++++++- extras/gizmo/axis-shapes.js | 29 ++++++++++++++++++++++++++- extras/gizmo/gizmo-rotate.js | 9 +++++++++ extras/gizmo/gizmo-scale.js | 8 ++++++++ extras/gizmo/gizmo-translate.js | 8 ++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index a8e35016124..4be6be22dc8 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -73,7 +73,18 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { }) ) ), - jsx(Panel, { headerText: 'Mesh' }, + jsx(Panel, { headerText: 'Intersection' }, + jsx(LabelGroup, { text: 'Tolerance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.intersectionTolerance' }, + min: 0, + max: 0.5, + precision: 2 + }) + ) + ), + jsx(Panel, { headerText: 'Render' }, (type === 'translate' || type === 'scale') && jsx(LabelGroup, { text: 'Gap' }, jsx(SliderInput, { @@ -243,6 +254,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), coordSpace: gizmo.coordSpace, + intersectionTolerance: gizmo.intersectionTolerance, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, axisLineLength: gizmo.axisLineLength, diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 10788185be8..0eabd837b77 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -278,6 +278,15 @@ class AxisArrow extends AxisShape { return this._arrowLength; } + set intersectionTolerance(value) { + this._intersectTolerance = value; + this._updateLine(); + } + + get intersectionTolerance() { + return this._intersectTolerance; + } + _createArrow() { this._createRoot('arrow'); @@ -415,6 +424,15 @@ class AxisBoxLine extends AxisShape { return this._boxSize; } + set intersectionTolerance(value) { + this._intersectTolerance = value; + this._updateLine(); + } + + get intersectionTolerance() { + return this._intersectTolerance; + } + _createBoxLine() { this._createRoot('boxLine'); @@ -466,7 +484,7 @@ class AxisDisk extends AxisShape { _lightDir; - _intersectTolerance = 0.1; + _intersectTolerance = 0.05; constructor(device, options = {}) { super(device, options); @@ -529,6 +547,15 @@ class AxisDisk extends AxisShape { return this._ringRadius; } + set intersectionTolerance(value) { + this._intersectTolerance = value; + this._updateTransform(); + } + + get intersectionTolerance() { + return this._intersectTolerance; + } + _updateTransform() { // intersect this.meshTriDataList[0].setTris(this._createIntersectTorus()); diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 72968063cf1..1e49d6b5f28 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -195,6 +195,15 @@ class GizmoRotate extends GizmoTransform { return this._shapes.face.ringRadius; } + set intersectionTolerance(value) { + this._setDiskProp('intersectionTolerance', value); + this._shapes.face.intersectionTolerance = value; + } + + get intersectionTolerance() { + return this._shapes.x.intersectionTolerance; + } + _setDiskProp(prop, value) { this._shapes.x[prop] = value; this._shapes.y[prop] = value; diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index a1ca90dacfd..923a97cd845 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -203,6 +203,14 @@ class GizmoScale extends GizmoTransform { return this._shapes.xyz.size; } + set intersectionTolerance(value) { + this._setArrowProp('intersectionTolerance', value); + } + + get intersectionTolerance() { + return this._shapes.x.intersectionTolerance; + } + _setArrowProp(prop, value) { this._shapes.x[prop] = value; this._shapes.y[prop] = value; diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 250d22d4885..2feaf4b6517 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -173,6 +173,14 @@ class GizmoTranslate extends GizmoTransform { return this._shapes.yz.gap; } + set intersectionTolerance(value) { + this._setArrowProp('intersectionTolerance', value); + } + + get intersectionTolerance() { + return this._shapes.x.intersectionTolerance; + } + _setArrowProp(prop, value) { this._shapes.x[prop] = value; this._shapes.y[prop] = value; From 84cbaa63f1326e84f8d4461ea914715d43f88197 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 11:54:21 +0000 Subject: [PATCH 160/178] added center tolerance; fixed center material overlap --- examples/src/examples/misc/gizmos.mjs | 43 +++++++++++++++----- extras/gizmo/axis-shapes.js | 56 ++++++++++++++++++--------- extras/gizmo/gizmo-rotate.js | 10 ++--- extras/gizmo/gizmo-scale.js | 17 ++++++-- extras/gizmo/gizmo-transform.js | 2 +- extras/gizmo/gizmo-translate.js | 16 ++++---- 6 files changed, 97 insertions(+), 47 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 4be6be22dc8..67b5d39cd93 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -74,15 +74,36 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { ) ), jsx(Panel, { headerText: 'Intersection' }, - jsx(LabelGroup, { text: 'Tolerance' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.intersectionTolerance' }, - min: 0, - max: 0.5, - precision: 2 - }) - ) + (type === 'translate' || type === 'scale') && + jsx(LabelGroup, { text: 'Line Tolerance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisLineTolerance' }, + min: 0, + max: 0.5, + precision: 2 + }) + ), + type === 'scale' && + jsx(LabelGroup, { text: 'Center Tolerance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.axisCenterTolerance' }, + min: 0, + max: 0.5, + precision: 2 + }) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Ring Tolerance' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.ringTolerance' }, + min: 0, + max: 0.5, + precision: 2 + }) + ) ), jsx(Panel, { headerText: 'Render' }, (type === 'translate' || type === 'scale') && @@ -254,7 +275,9 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), coordSpace: gizmo.coordSpace, - intersectionTolerance: gizmo.intersectionTolerance, + axisLineTolerance: gizmo.axisLineTolerance, + axisCenterTolerance: gizmo.axisCenterTolerance, + ringTolerance: gizmo.ringTolerance, axisGap: gizmo.axisGap, axisLineThickness: gizmo.axisLineThickness, axisLineLength: gizmo.axisLineLength, diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 0eabd837b77..71ae8a6ea1a 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -217,7 +217,7 @@ class AxisArrow extends AxisShape { _arrowLength = 0.2; - _intersectTolerance = 0.1; + _tolerance = 0.1; constructor(device, options = {}) { super(device, options); @@ -278,13 +278,13 @@ class AxisArrow extends AxisShape { return this._arrowLength; } - set intersectionTolerance(value) { - this._intersectTolerance = value; + set tolerance(value) { + this._tolerance = value; this._updateLine(); } - get intersectionTolerance() { - return this._intersectTolerance; + get tolerance() { + return this._tolerance; } _createArrow() { @@ -318,7 +318,7 @@ class AxisArrow extends AxisShape { // intersect tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); tmpQ1.set(0, 0, 0, 1); - tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); + tmpV2.set(this._lineThickness + this._tolerance, this._lineLength, this._lineThickness + this._tolerance); this.meshTriDataList[1].setTransform(tmpV1, tmpQ1, tmpV2); // render @@ -330,6 +330,8 @@ class AxisArrow extends AxisShape { class AxisBoxCenter extends AxisShape { _size = 0.14; + _tolerance = 0.05; + constructor(device, options = {}) { super(device, options); @@ -357,8 +359,24 @@ class AxisBoxCenter extends AxisShape { return this._size; } + set tolerance(value) { + this._tolerance = value; + this._updateTransform(); + } + + get tolerance() { + return this._tolerance; + } + _updateTransform() { - // intersect/render + // intersect + const iSize = (this._size + this._tolerance) / this._size; + tmpV1.set(0, 0, 0); + tmpQ1.set(0, 0, 0, 1); + tmpV2.set(iSize, iSize, iSize); + this.meshTriDataList[0].setTransform(tmpV1, tmpQ1, tmpV2); + + // render this.entity.setLocalScale(this._size, this._size, this._size); } } @@ -372,7 +390,7 @@ class AxisBoxLine extends AxisShape { _boxSize = 0.14; - _intersectTolerance = 0.1; + _tolerance = 0.1; constructor(device, options = {}) { super(device, options); @@ -424,13 +442,13 @@ class AxisBoxLine extends AxisShape { return this._boxSize; } - set intersectionTolerance(value) { - this._intersectTolerance = value; + set tolerance(value) { + this._tolerance = value; this._updateLine(); } - get intersectionTolerance() { - return this._intersectTolerance; + get tolerance() { + return this._tolerance; } _createBoxLine() { @@ -466,7 +484,7 @@ class AxisBoxLine extends AxisShape { // intersect tmpV1.set(0, this._gap + this._lineLength * 0.5, 0); tmpQ1.set(0, 0, 0, 1); - tmpV2.set(this._lineThickness + this._intersectTolerance, this._lineLength, this._lineThickness + this._intersectTolerance); + tmpV2.set(this._lineThickness + this._tolerance, this._lineLength, this._lineThickness + this._tolerance); this.meshTriDataList[1].setTransform(tmpV1, tmpQ1, tmpV2); // render @@ -484,7 +502,7 @@ class AxisDisk extends AxisShape { _lightDir; - _intersectTolerance = 0.05; + _tolerance = 0.05; constructor(device, options = {}) { super(device, options); @@ -502,7 +520,7 @@ class AxisDisk extends AxisShape { _createIntersectTorus() { return createTorus(this.device, { - tubeRadius: this._tubeRadius + this._intersectTolerance, + tubeRadius: this._tubeRadius + this._tolerance, ringRadius: this._ringRadius, sectorAngle: this._sectorAngle, segments: TORUS_INTERSECT_SEGMENTS @@ -547,13 +565,13 @@ class AxisDisk extends AxisShape { return this._ringRadius; } - set intersectionTolerance(value) { - this._intersectTolerance = value; + set tolerance(value) { + this._tolerance = value; this._updateTransform(); } - get intersectionTolerance() { - return this._intersectTolerance; + get tolerance() { + return this._tolerance; } _updateTransform() { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 1e49d6b5f28..e847fe6d3db 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -195,13 +195,13 @@ class GizmoRotate extends GizmoTransform { return this._shapes.face.ringRadius; } - set intersectionTolerance(value) { - this._setDiskProp('intersectionTolerance', value); - this._shapes.face.intersectionTolerance = value; + set ringTolerance(value) { + this._setDiskProp('tolerance', value); + this._shapes.face.tolerance = value; } - get intersectionTolerance() { - return this._shapes.x.intersectionTolerance; + get ringTolerance() { + return this._shapes.x.tolerance; } _setDiskProp(prop, value) { diff --git a/extras/gizmo/gizmo-scale.js b/extras/gizmo/gizmo-scale.js index 923a97cd845..3e77d690acf 100644 --- a/extras/gizmo/gizmo-scale.js +++ b/extras/gizmo/gizmo-scale.js @@ -171,6 +171,14 @@ class GizmoScale extends GizmoTransform { return this._shapes.x.lineLength; } + set axisLineTolerance(value) { + this._setArrowProp('tolerance', value); + } + + get axisLineTolerance() { + return this._shapes.x.tolerance; + } + set axisBoxSize(value) { this._setArrowProp('boxSize', value); } @@ -203,14 +211,15 @@ class GizmoScale extends GizmoTransform { return this._shapes.xyz.size; } - set intersectionTolerance(value) { - this._setArrowProp('intersectionTolerance', value); + set axisCenterTolerance(value) { + this._shapes.xyz.tolerance = value; } - get intersectionTolerance() { - return this._shapes.x.intersectionTolerance; + get axisCenterTolerance() { + return this._shapes.xyz.tolerance; } + _setArrowProp(prop, value) { this._shapes.x[prop] = value; this._shapes.y[prop] = value; diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 6eb601aac9a..4ccf2de8677 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -484,9 +484,9 @@ class GizmoTransform extends Gizmo { material.emissive = color; material.emissiveVertexColor = true; material.cull = cull; + material.blendType = BLEND_NORMAL; if (color.a !== 1) { material.opacity = color.a; - material.blendType = BLEND_NORMAL; } return material; } diff --git a/extras/gizmo/gizmo-translate.js b/extras/gizmo/gizmo-translate.js index 2feaf4b6517..2e02da1ebd8 100644 --- a/extras/gizmo/gizmo-translate.js +++ b/extras/gizmo/gizmo-translate.js @@ -141,6 +141,14 @@ class GizmoTranslate extends GizmoTransform { return this._shapes.x.lineLength; } + set axisLineTolerance(value) { + this._setArrowProp('tolerance', value); + } + + get axisLineTolerance() { + return this._shapes.x.tolerance; + } + set axisArrowThickness(value) { this._setArrowProp('arrowThickness', value); } @@ -173,14 +181,6 @@ class GizmoTranslate extends GizmoTransform { return this._shapes.yz.gap; } - set intersectionTolerance(value) { - this._setArrowProp('intersectionTolerance', value); - } - - get intersectionTolerance() { - return this._shapes.x.intersectionTolerance; - } - _setArrowProp(prop, value) { this._shapes.x[prop] = value; this._shapes.y[prop] = value; From 3ef00de02a7c0b6e1d11e0e2c0ef018989d84605 Mon Sep 17 00:00:00 2001 From: kpal Date: Wed, 17 Jan 2024 17:31:41 +0000 Subject: [PATCH 161/178] added new rotation logic; modified guide line angle calculation --- examples/src/examples/misc/gizmos.mjs | 14 ++++++-- extras/gizmo/gizmo-rotate.js | 43 +++++++++++++++++-------- extras/gizmo/gizmo-transform.js | 46 +++++++++++++++++---------- 3 files changed, 72 insertions(+), 31 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 67b5d39cd93..bd51dace863 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -5,7 +5,7 @@ import * as pc from 'playcanvas'; * @returns {JSX.Element} The returned JSX Element. */ function controls({ observer, ReactPCUI, React, jsx, fragment }) { - const { BindingTwoWay, LabelGroup, Panel, ColorPicker, SliderInput, SelectInput } = ReactPCUI; + const { BindingTwoWay, LabelGroup, Panel, BooleanInput, ColorPicker, SliderInput, SelectInput } = ReactPCUI; const [type, setType] = React.useState('translate'); @@ -51,7 +51,15 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { max: 10, precision: 0 }) - ) + ), + type === 'rotate' && + jsx(LabelGroup, { text: 'Legacy Rotation' }, + jsx(BooleanInput, { + type: 'toggle', + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.legacyRotation' } + }) + ) ), jsx(Panel, { headerText: 'Color' }, jsx(LabelGroup, { text: 'X Axis' }, @@ -271,6 +279,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip type: type, size: gizmo.size, snapIncrement: gizmo.snapIncrement, + legacyRotation: gizmo.legacyRotation, xAxisColor: Object.values(gizmo.xAxisColor), yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), @@ -386,6 +395,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip }); boxA.setPosition(0.5, 0, -0.5); app.root.addChild(boxA); + const boxB = new pc.Entity('cubeB'); boxB.addComponent('render', { type: 'box' diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index e847fe6d3db..3606d8cc7ab 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -125,18 +125,43 @@ class GizmoRotate extends GizmoTransform { this._createTransform(); this.on('transform:start', () => { + const axis = this._selectedAxis; + const isFacing = axis === 'face'; + const scale = isFacing ? this.faceRingRadius : this.xyzRingRadius; + this._storeNodeRotations(); - this._updateGuideAngle(this._selectionStartPoint, this._guideAngleStart); - this._updateGuideAngle(this._selectionStartPoint, this._guideAngleEnd); + + // guide angle line start + this._guideAngleStart.copy(this._selectionStartPoint).normalize(); + this._guideAngleStart.scale(scale); + this._gizmoRotationStart.transformVector(this._guideAngleStart, this._guideAngleStart); + this._guideAngleEnd.copy(this._guideAngleStart); + + // drag handle for disk (arc <-> circle) this._drag(true); }); - this.on('transform:move', (pointDelta, angleDelta, pointLast) => { + this.on('transform:move', (pointDelta, angleDelta) => { + const gizmoPos = this.gizmo.getPosition(); + const cameraPos = this.camera.entity.getPosition(); + const axis = this._selectedAxis; + const isFacing = axis === 'face'; + if (this.snap) { angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement; } - this._setNodeRotations(this._selectedAxis, angleDelta); - this._updateGuideAngle(pointLast, this._guideAngleEnd); + this._setNodeRotations(axis, angleDelta); + + // guide angle line update rotation + tmpV1.set(0, 0, 0); + if (isFacing) { + tmpV1.copy(cameraPos).sub(gizmoPos).normalize(); + } else { + tmpV1[axis] = 1; + } + this._gizmoRotationStart.transformVector(tmpV1, tmpV1); + tmpQ1.setFromAxisAngle(tmpV1, angleDelta); + tmpQ1.transformVector(this._guideAngleStart, this._guideAngleEnd); }); this.on('transform:end', () => { @@ -210,14 +235,6 @@ class GizmoRotate extends GizmoTransform { this._shapes.z[prop] = value; } - _updateGuideAngle(point, out) { - const axis = this._selectedAxis; - const scale = axis === 'face' ? this.faceRingRadius : this.xyzRingRadius; - out.copy(point).normalize(); - out.scale(scale); - this._gizmoRotationStart.transformVector(out, out); - } - _drawGuideAngleLine(pos, axis, point, color = this._guideColors[axis]) { tmpV1.set(0, 0, 0); tmpV2.copy(point).scale(this._scale); diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 4ccf2de8677..658f6709d79 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -22,6 +22,7 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); const SPANLINE_SIZE = 1e3; +const ROTATE_SCALE = 900; const RED_COLOR = new Color(1, 0.3, 0.3); const SEMI_RED_COLOR = new Color(1, 0.3, 0.3, 0.6); @@ -203,6 +204,13 @@ class GizmoTransform extends Gizmo { */ snapIncrement = 1; + /** + * Use legacy rotation calculation. Defaults to false. + * + * @type {boolean} + */ + legacyRotation = false; + /** * Creates a new GizmoTransform object. * @@ -246,7 +254,7 @@ class GizmoTransform extends Gizmo { const pointInfo = this._calcPoint(x, y); pointDelta.copy(pointInfo.point).sub(this._selectionStartPoint); const angleDelta = pointInfo.angle - this._selectionStartAngle; - this.fire('transform:move', pointDelta, angleDelta, pointInfo.point, pointInfo.angle); + this.fire('transform:move', pointDelta, angleDelta); this._hoverAxis = ''; this._hoverIsPlane = false; } @@ -423,23 +431,29 @@ class GizmoTransform extends Gizmo { } } - // calculate angle based on axis + // calculate angle let angle = 0; if (isRotation) { - switch (axis) { - case 'x': - angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; - break; - case 'y': - angle = Math.atan2(point.x, point.z) * math.RAD_TO_DEG; - break; - case 'z': - angle = Math.atan2(point.y, point.x) * math.RAD_TO_DEG; - break; - case 'face': - cameraRot.invert().transformVector(point, tmpV1); - angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG; - break; + if (this.legacyRotation || isFacing) { + switch (axis) { + case 'x': + angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; + break; + case 'y': + angle = Math.atan2(point.x, point.z) * math.RAD_TO_DEG; + break; + case 'z': + angle = Math.atan2(point.y, point.x) * math.RAD_TO_DEG; + break; + case 'face': + cameraRot.invert().transformVector(point, tmpV1); + angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG; + break; + } + } else { + tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); + tmpV2.cross(planeNormal, tmpV1).normalize(); + angle = mouseWPos.dot(tmpV2) * ROTATE_SCALE; } } From c09416e18f75135a6102c1b643d882109d374fd6 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 11:50:43 +0000 Subject: [PATCH 162/178] cleaned up example and prepared for picker --- examples/src/examples/misc/gizmos.mjs | 164 ++++++++++++++++++-------- 1 file changed, 117 insertions(+), 47 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index bd51dace863..c779c14be53 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -9,7 +9,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const [type, setType] = React.useState('translate'); - this.setType = (value) => setType(value); + window.setType = (value) => setType(value); return fragment( jsx(Panel, { headerText: 'Transform' }, @@ -243,22 +243,16 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { * @returns {Promise} The example application. */ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scriptsPath }) { + // Class for handling gizmos class GizmoHandler { _type = 'translate'; - skipSetFire = false; + _nodes = []; - constructor(app, camera) { - const layer = new pc.Layer({ - name: 'Gizmo', - clearDepthBuffer: true, - opaqueSortMode: pc.SORTMODE_NONE, - transparentSortMode: pc.SORTMODE_NONE - }); - app.scene.layers.push(layer); - camera.layers = camera.layers.concat(layer.id); + skipSetFire = false; - this.gizmos = { + constructor(app, camera, layer) { + this._gizmos = { translate: new pcx.GizmoTranslate(app, camera, layer), rotate: new pcx.GizmoRotate(app, camera, layer), scale: new pcx.GizmoScale(app, camera, layer) @@ -266,14 +260,11 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip } get gizmo() { - return this.gizmos[this._type]; + return this._gizmos[this._type]; } - switch(type, nodes) { - this.gizmo.detach(); - this._type = type ?? 'translate'; + _updateData(type) { const gizmo = this.gizmo; - gizmo.attach(nodes); this.skipSetFire = true; data.set('gizmo', { type: type, @@ -303,6 +294,32 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip }); this.skipSetFire = false; } + + attach(nodes) { + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if (this._nodes.indexOf(node) === -1) { + this._nodes.push(node); + } + } + } + + detach() { + this._nodes.length = 0; + } + + switch(type) { + this.gizmo.detach(); + this._type = type ?? 'translate'; + this.gizmo.attach(this._nodes); + this._updateData(type); + } + + destroy() { + for (const type in this._gizmos) { + this._gizmos[type].destroy(); + } + } } const gfxOptions = { @@ -342,37 +359,11 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // Ensure canvas is resized when window changes size const resize = () => app.resizeCanvas(); window.addEventListener('resize', resize); - app.on('destroy', () => { - window.removeEventListener('resize', resize); - }); - - // control keybinds - const setType = (value) => { - data.set('gizmo.type', value); - this.top.setType(value); - }; - window.addEventListener('keypress', (e) => { - switch (e.key) { - case 'x': - data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); - break; - case '1': - setType('translate'); - break; - case '2': - setType('rotate'); - break; - case '3': - setType('scale'); - break; - } - }); - // assets + // load assets const assets = { script: new pc.Asset('script', 'script', { url: scriptsPath + 'camera/orbit-camera.js' }) }; - /** * @param {pc.Asset[] | number[]} assetList - The asset list. * @param {pc.AssetRegistry} assetRegistry - The asset registry. @@ -427,10 +418,47 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip app.root.addChild(light); light.setEulerAngles(45, 20, 0); + // create gizmoLayer + const gizmoLayer = new pc.Layer({ + name: 'Gizmo', + clearDepthBuffer: true, + opaqueSortMode: pc.SORTMODE_NONE, + transparentSortMode: pc.SORTMODE_NONE + }); + app.scene.layers.push(gizmoLayer); + camera.camera.layers = camera.camera.layers.concat(gizmoLayer.id); + // create gizmo - const gizmoHandler = new GizmoHandler(app, camera.camera); - gizmoHandler.switch('translate', [boxA, boxB]); + const gizmoHandler = new GizmoHandler(app, camera.camera, gizmoLayer); + gizmoHandler.attach([boxA, boxB]); + gizmoHandler.switch('translate'); + + // Change gizmo mode keybinds + const setType = (value) => { + data.set('gizmo.type', value); + // call method from top context (same as controls) + window.top.setType(value); + }; + const keypress = (e) => { + switch (e.key) { + case 'x': + data.set('gizmo.coordSpace', data.get('gizmo.coordSpace') === 'world' ? 'local' : 'world'); + break; + case '1': + setType('translate'); + break; + case '2': + setType('rotate'); + break; + case '3': + setType('scale'); + break; + } + }; + window.addEventListener('keypress', keypress); + + // Gizmo and camera set handler const tmpC = new pc.Color(); data.on('*:set', (/** @type {string} */ path, value) => { const pathArray = path.split('.'); @@ -455,7 +483,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip } switch (pathArray[1]) { case 'type': - gizmoHandler.switch(value, [boxA, boxB]); + gizmoHandler.switch(value); break; case 'xAxisColor': case 'yAxisColor': @@ -471,6 +499,48 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip } }); + // Picker + // const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight); + // const worldLayer = app.scene.layers.getLayerByName("World"); + // const pickerLayers = [worldLayer, gizmoLayer]; + + // const onPointerDown = (e) => { + // if (picker) { + // picker.prepare(camera.camera, app.scene, pickerLayers); + // } + + // const selection = picker.getSelection(e.clientX - 1, e.clientY - 1, 2, 2); + // console.log(selection[0]?.node.name); + + // // // skip adding selection if selected gizmo + // // if (selection[0] && + // // selection[0].node.render.layers.indexOf(gizmoLayer.id) !== -1 + // // ) { + // // return; + // // } + + // // // reset gizmo nodes if not multi select + // // if (!e.ctrlKey && !e.metaKey) { + // // gizmoNodes.length = 0; + // // } + + // // if (selection[0] && gizmoNodes.indexOf(selection[0].node) === -1) { + // // gizmoNodes.push(selection[0].node); + // // } + + // // gizmoHandler.switch('translate', gizmoNodes); + // }; + + // window.addEventListener('pointerdown', onPointerDown); + + app.on('destroy', () => { + this.gizmoHandler.destroy(); + + window.removeEventListener('resize', resize); + window.removeEventListener('keypress', keypress); + // window.removeEventListener('pointerdown', onPointerDown); + }); + return app; } From 1f4dfa2d18509abe074b057213ac52c95bc50776 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 13:52:14 +0000 Subject: [PATCH 163/178] renamed legacyRotation to useLegacyRotation --- examples/src/examples/misc/gizmos.mjs | 4 ++-- extras/gizmo/gizmo-transform.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index c779c14be53..c6a3e1c20f4 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -57,7 +57,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { jsx(BooleanInput, { type: 'toggle', binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.legacyRotation' } + link: { observer, path: 'gizmo.useLegacyRotation' } }) ) ), @@ -270,7 +270,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip type: type, size: gizmo.size, snapIncrement: gizmo.snapIncrement, - legacyRotation: gizmo.legacyRotation, + useLegacyRotation: gizmo.useLegacyRotation, xAxisColor: Object.values(gizmo.xAxisColor), yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 658f6709d79..b8b2af1f3f1 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -209,7 +209,7 @@ class GizmoTransform extends Gizmo { * * @type {boolean} */ - legacyRotation = false; + useLegacyRotation = false; /** * Creates a new GizmoTransform object. @@ -434,7 +434,7 @@ class GizmoTransform extends Gizmo { // calculate angle let angle = 0; if (isRotation) { - if (this.legacyRotation || isFacing) { + if (this.useLegacyRotation || isFacing) { switch (axis) { case 'x': angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; From bbee0a42af6ae448c038f19e1a484d7236309648 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 14:03:23 +0000 Subject: [PATCH 164/178] use legacy rotation when axis almost parallel to camera forward axis --- extras/gizmo/gizmo-transform.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index b8b2af1f3f1..81aef2f147e 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -21,6 +21,7 @@ const pointDelta = new Vec3(); // constants const VEC3_AXES = Object.keys(tmpV1); +const FACING_EPSILON = 0.2; const SPANLINE_SIZE = 1e3; const ROTATE_SCALE = 900; @@ -434,7 +435,14 @@ class GizmoTransform extends Gizmo { // calculate angle let angle = 0; if (isRotation) { - if (this.useLegacyRotation || isFacing) { + let isAxisFacing = isFacing; + if (!this.useLegacyRotation) { + tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); + tmpV2.cross(planeNormal, tmpV1); + isAxisFacing ||= tmpV2.length() < FACING_EPSILON; + } + + if (this.useLegacyRotation || isAxisFacing) { switch (axis) { case 'x': angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; @@ -451,9 +459,7 @@ class GizmoTransform extends Gizmo { break; } } else { - tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); - tmpV2.cross(planeNormal, tmpV1).normalize(); - angle = mouseWPos.dot(tmpV2) * ROTATE_SCALE; + angle = mouseWPos.dot(tmpV2.normalize()) * ROTATE_SCALE; } } From dd6ee7abe198ee426449be064ee45afadcb66c7e Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 15:33:07 +0000 Subject: [PATCH 165/178] added picker to example and make design like editor viewport --- examples/src/examples/misc/gizmos.mjs | 174 +++++++++++++++++--------- extras/gizmo/gizmo.js | 4 + 2 files changed, 121 insertions(+), 57 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index c6a3e1c20f4..3af182bbcbc 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -249,20 +249,37 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip _nodes = []; + _ignorePicker = false; + skipSetFire = false; + constructor(app, camera, layer) { this._gizmos = { translate: new pcx.GizmoTranslate(app, camera, layer), rotate: new pcx.GizmoRotate(app, camera, layer), scale: new pcx.GizmoScale(app, camera, layer) }; + + for (const type in this._gizmos) { + const gizmo = this._gizmos[type]; + gizmo.on('pointer:down', (x, y, selection) => { + this._ignorePicker = !!selection; + }); + gizmo.on('pointer:up', () => { + this._ignorePicker = false; + }); + } } get gizmo() { return this._gizmos[this._type]; } + get ignorePicker() { + return this._ignorePicker; + } + _updateData(type) { const gizmo = this.gizmo; this.skipSetFire = true; @@ -295,17 +312,19 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip this.skipSetFire = false; } - attach(nodes) { - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (this._nodes.indexOf(node) === -1) { - this._nodes.push(node); - } + add(node, clear = false) { + if (clear) { + this._nodes.length = 0; } + if (this._nodes.indexOf(node) === -1) { + this._nodes.push(node); + } + this.gizmo.attach(this._nodes); } - detach() { + clear() { this._nodes.length = 0; + this.gizmo.detach(); } switch(type) { @@ -379,20 +398,44 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip app.start(); - // create box entities - const boxA = new pc.Entity('cubeA'); - boxA.addComponent('render', { - type: 'box' + // create entities + function createMaterial(color) { + const material = new pc.StandardMaterial(); + material.diffuse = color; + return material; + } + const box = new pc.Entity('box'); + box.addComponent('render', { + type: 'box', + material: createMaterial(new pc.Color(0.8, 1, 1)) }); - boxA.setPosition(0.5, 0, -0.5); - app.root.addChild(boxA); + box.setPosition(1, 0, 1); + app.root.addChild(box); - const boxB = new pc.Entity('cubeB'); - boxB.addComponent('render', { - type: 'box' + const sphere = new pc.Entity('sphere'); + sphere.addComponent('render', { + type: 'sphere', + material: createMaterial(new pc.Color(1, 0.8, 1)) + }); + sphere.setPosition(-1, 0, 1); + app.root.addChild(sphere); + + const cone = new pc.Entity('cone'); + cone.addComponent('render', { + type: 'cone', + material: createMaterial(new pc.Color(1, 1, 0.8)) + }); + cone.setPosition(-1, 0, -1); + cone.setLocalScale(1.5, 2.25, 1.5); + app.root.addChild(cone); + + const capsule = new pc.Entity('capsule'); + capsule.addComponent('render', { + type: 'capsule', + material: createMaterial(new pc.Color(0.8, 0.8, 1)) }); - boxB.setPosition(-0.5, 0, 0.5); - app.root.addChild(boxB); + capsule.setPosition(1, 0, -1); + app.root.addChild(capsule); // create camera entity data.set('camera', { @@ -403,20 +446,37 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip }); const camera = new pc.Entity('camera'); camera.addComponent('camera', { - clearColor: new pc.Color(0.5, 0.6, 0.9) + clearColor: new pc.Color(0.1, 0.1, 0.1) }); camera.addComponent("script"); - camera.script.create("orbitCamera"); + const orbitCamera = camera.script.create("orbitCamera"); camera.script.create("orbitCameraInputMouse"); camera.script.create("orbitCameraInputTouch"); - camera.rotate(-20, 45, 0); + camera.setPosition(1, 1, 1); app.root.addChild(camera); + orbitCamera.distance = 14; - // create directional light entity - const light = new pc.Entity('light'); - light.addComponent('light'); - app.root.addChild(light); - light.setEulerAngles(45, 20, 0); + // create 3-point lighting + const backLight = new pc.Entity('light'); + backLight.addComponent('light', { + intensity: 0.5 + }); + app.root.addChild(backLight); + backLight.setEulerAngles(-60, 0, 90); + + const fillLight = new pc.Entity('light'); + fillLight.addComponent('light', { + intensity: 0.5 + }); + app.root.addChild(fillLight); + fillLight.setEulerAngles(45, 0, 0); + + const keyLight = new pc.Entity('light'); + keyLight.addComponent('light', { + intensity: 1 + }); + app.root.addChild(keyLight); + keyLight.setEulerAngles(0, 0, -60); // create gizmoLayer const gizmoLayer = new pc.Layer({ @@ -430,7 +490,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // create gizmo const gizmoHandler = new GizmoHandler(app, camera.camera, gizmoLayer); - gizmoHandler.attach([boxA, boxB]); gizmoHandler.switch('translate'); // Change gizmo mode keybinds @@ -500,45 +559,46 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip }); // Picker - // const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight); - // const worldLayer = app.scene.layers.getLayerByName("World"); - // const pickerLayers = [worldLayer, gizmoLayer]; - - // const onPointerDown = (e) => { - // if (picker) { - // picker.prepare(camera.camera, app.scene, pickerLayers); - // } - - // const selection = picker.getSelection(e.clientX - 1, e.clientY - 1, 2, 2); - // console.log(selection[0]?.node.name); + const picker = new pc.Picker(app, canvas.clientWidth, canvas.clientHeight); + const worldLayer = app.scene.layers.getLayerByName("World"); + const pickerLayers = [worldLayer]; - // // // skip adding selection if selected gizmo - // // if (selection[0] && - // // selection[0].node.render.layers.indexOf(gizmoLayer.id) !== -1 - // // ) { - // // return; - // // } - - // // // reset gizmo nodes if not multi select - // // if (!e.ctrlKey && !e.metaKey) { - // // gizmoNodes.length = 0; - // // } + const onPointerDown = (e) => { + if (gizmoHandler.ignorePicker) { + return; + } - // // if (selection[0] && gizmoNodes.indexOf(selection[0].node) === -1) { - // // gizmoNodes.push(selection[0].node); - // // } + if (picker) { + picker.prepare(camera.camera, app.scene, pickerLayers); + } - // // gizmoHandler.switch('translate', gizmoNodes); - // }; + const selection = picker.getSelection(e.clientX - 1, e.clientY - 1, 2, 2); + if (!selection[0]) { + gizmoHandler.clear(); + return; + } - // window.addEventListener('pointerdown', onPointerDown); + gizmoHandler.add(selection[0].node, !e.ctrlKey && !e.metaKey); + }; + window.addEventListener('pointerdown', onPointerDown); + + const gridColor = new pc.Color(1, 1, 1, 0.5); + const gridHalfSize = 4; + 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); + }); app.on('destroy', () => { - this.gizmoHandler.destroy(); + gizmoHandler.destroy(); window.removeEventListener('resize', resize); window.removeEventListener('keypress', keypress); - // window.removeEventListener('pointerdown', onPointerDown); + window.removeEventListener('pointerdown', onPointerDown); }); return app; diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index ca54a54994d..0ed4b7ea805 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -290,6 +290,10 @@ class Gizmo extends EventHandler { * gizmo.attach([boxA, boxB]); */ attach(nodes = []) { + if (nodes.length === 0) { + return; + } + this.nodes = nodes; this._updatePosition(); this._updateRotation(); From dafcc83697c998612db5f1593a70ab07162638da Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 16:17:48 +0000 Subject: [PATCH 166/178] Fixed lighting; changed local rotation of multiple nodes to follow last node --- extras/gizmo/gizmo-rotate.js | 2 +- extras/gizmo/gizmo.js | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 3606d8cc7ab..18cfd2b8c0e 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -246,7 +246,7 @@ class GizmoRotate extends GizmoTransform { tmpM1.setLookAt(tmpV1, position, Vec3.UP); tmpQ1.setFromMat4(tmpM1); tmpQ1.getEulerAngles(tmpV1); - tmpV1.x -= 90; + tmpV1.x += 90; return tmpV1; } diff --git a/extras/gizmo/gizmo.js b/extras/gizmo/gizmo.js index 0ed4b7ea805..53d498e042c 100644 --- a/extras/gizmo/gizmo.js +++ b/extras/gizmo/gizmo.js @@ -222,15 +222,11 @@ class Gizmo extends EventHandler { _updateRotation() { tmpV1.set(0, 0, 0); if (this._coordSpace === LOCAL_COORD_SPACE) { - for (let i = 0; i < this.nodes.length; i++) { - const node = this.nodes[i]; - tmpV1.add(node.getEulerAngles()); - } - tmpV1.scale(1.0 / (this.nodes.length || 1)); + tmpV1.copy(this.nodes[this.nodes.length - 1].getEulerAngles()); } this.gizmo.setEulerAngles(tmpV1); - this.fire('eulerAngles:set', tmpV1); + this.fire('rotation:set', tmpV1); } _updateScale() { From 4b640c2189a022e400ff25579f80c9bfc3a25686 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 16:50:23 +0000 Subject: [PATCH 167/178] removed legacy rotation --- examples/src/examples/misc/gizmos.mjs | 11 +---------- extras/gizmo/gizmo-transform.js | 17 ++++------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 3af182bbcbc..e8bed7fb11e 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -51,15 +51,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { max: 10, precision: 0 }) - ), - type === 'rotate' && - jsx(LabelGroup, { text: 'Legacy Rotation' }, - jsx(BooleanInput, { - type: 'toggle', - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.useLegacyRotation' } - }) - ) + ) ), jsx(Panel, { headerText: 'Color' }, jsx(LabelGroup, { text: 'X Axis' }, @@ -287,7 +279,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip type: type, size: gizmo.size, snapIncrement: gizmo.snapIncrement, - useLegacyRotation: gizmo.useLegacyRotation, xAxisColor: Object.values(gizmo.xAxisColor), yAxisColor: Object.values(gizmo.yAxisColor), zAxisColor: Object.values(gizmo.zAxisColor), diff --git a/extras/gizmo/gizmo-transform.js b/extras/gizmo/gizmo-transform.js index 81aef2f147e..7b2cbd76275 100644 --- a/extras/gizmo/gizmo-transform.js +++ b/extras/gizmo/gizmo-transform.js @@ -205,13 +205,6 @@ class GizmoTransform extends Gizmo { */ snapIncrement = 1; - /** - * Use legacy rotation calculation. Defaults to false. - * - * @type {boolean} - */ - useLegacyRotation = false; - /** * Creates a new GizmoTransform object. * @@ -436,13 +429,11 @@ class GizmoTransform extends Gizmo { let angle = 0; if (isRotation) { let isAxisFacing = isFacing; - if (!this.useLegacyRotation) { - tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); - tmpV2.cross(planeNormal, tmpV1); - isAxisFacing ||= tmpV2.length() < FACING_EPSILON; - } + tmpV1.copy(rayOrigin).sub(gizmoPos).normalize(); + tmpV2.cross(planeNormal, tmpV1); + isAxisFacing ||= tmpV2.length() < FACING_EPSILON; - if (this.useLegacyRotation || isAxisFacing) { + if (isAxisFacing) { switch (axis) { case 'x': angle = Math.atan2(point.z, point.y) * math.RAD_TO_DEG; From 450aa17c691438674917ea69bbf138bac39d6e6c Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 17:15:24 +0000 Subject: [PATCH 168/178] updated parameters for createTorus in core engine --- extras/gizmo/axis-shapes.js | 54 +------------------------------------ src/scene/procedural.js | 11 +++++--- 2 files changed, 8 insertions(+), 57 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 71ae8a6ea1a..3206b88398b 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -4,6 +4,7 @@ import { createCylinder, createPlane, createMesh, + createTorus, Color, MeshInstance, Entity, @@ -32,59 +33,6 @@ const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); const tmpQ1 = new Quat(); -function createTorus(device, opts = {}) { - // Check the supplied options and provide defaults for unspecified ones - const rc = opts.tubeRadius ?? 0.2; - const rt = opts.ringRadius ?? 0.3; - const segments = opts.segments ?? 30; - const sides = opts.sides ?? 20; - const sectorAngle = opts.sectorAngle ?? 2 * Math.PI; - - // Variable declarations - const positions = []; - const normals = []; - const uvs = []; - const indices = []; - - for (let i = 0; i <= sides; i++) { - for (let j = 0; j <= segments; j++) { - const x = Math.cos(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); - const y = Math.sin(2 * Math.PI * i / sides) * rc; - const z = Math.sin(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); - - const nx = Math.cos(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); - const ny = Math.sin(2 * Math.PI * i / sides); - const nz = Math.sin(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); - - const u = i / sides; - const v = 1 - j / segments; - - positions.push(x, y, z); - normals.push(nx, ny, nz); - uvs.push(u, 1.0 - v); - - if ((i < sides) && (j < segments)) { - const first = ((i)) * (segments + 1) + ((j)); - const second = ((i + 1)) * (segments + 1) + ((j)); - const third = ((i)) * (segments + 1) + ((j + 1)); - const fourth = ((i + 1)) * (segments + 1) + ((j + 1)); - - indices.push(first, second, third); - indices.push(second, fourth, third); - } - } - } - - const options = { - normals: normals, - uvs: uvs, - uvs1: uvs, - indices: indices - }; - - return createMesh(device, positions, options); -} - function createShadowMesh(device, entity, type, templateOpts = {}) { const createTemplate = MESH_TEMPLATES[type]; if (!createTemplate) { diff --git a/src/scene/procedural.js b/src/scene/procedural.js index b70805628c4..e1746c856f9 100644 --- a/src/scene/procedural.js +++ b/src/scene/procedural.js @@ -298,6 +298,8 @@ function createMesh(device, positions, opts) { * (defaults to 0.2). * @param {number} [opts.ringRadius] - The radius from the centre of the torus to the centre of the * tube (defaults to 0.3). + * @param {number} [opts.sectorAngle] - The sector angle in radians of the ring of the torus + * (defaults to 2 * Math.PI). * @param {number} [opts.segments] - The number of radial divisions forming cross-sections of the * torus ring (defaults to 20). * @param {number} [opts.sides] - The number of divisions around the tubular body of the torus ring @@ -309,6 +311,7 @@ function createTorus(device, opts = {}) { // Check the supplied options and provide defaults for unspecified ones const rc = opts.tubeRadius ?? 0.2; const rt = opts.ringRadius ?? 0.3; + const sectorAngle = opts.sectorAngle ?? 2 * Math.PI; const segments = opts.segments ?? 30; const sides = opts.sides ?? 20; const calcTangents = opts.calculateTangents ?? false; @@ -321,13 +324,13 @@ function createTorus(device, opts = {}) { for (let i = 0; i <= sides; i++) { for (let j = 0; j <= segments; j++) { - const x = Math.cos(2 * Math.PI * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const x = Math.cos(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); const y = Math.sin(2 * Math.PI * i / sides) * rc; - const z = Math.sin(2 * Math.PI * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const z = Math.sin(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); - const nx = Math.cos(2 * Math.PI * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nx = Math.cos(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); const ny = Math.sin(2 * Math.PI * i / sides); - const nz = Math.sin(2 * Math.PI * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nz = Math.sin(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); const u = i / sides; const v = 1 - j / segments; From da96b049b715c281af9743a43302986924e35fd2 Mon Sep 17 00:00:00 2001 From: kpal Date: Thu, 18 Jan 2024 17:57:40 +0000 Subject: [PATCH 169/178] updated jsdoc --- extras/gizmo/gizmo-rotate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 18cfd2b8c0e..2f507560eb1 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -61,17 +61,17 @@ class GizmoRotate extends GizmoTransform { _isRotation = true; /** - * Internal mapping from each attached node to their starting rotation (euler angles) in local space. + * Internal mapping from each attached node to their starting rotation in local space. * - * @type {Map} + * @type {Map} * @private */ _nodeLocalRotations = new Map(); /** - * Internal mapping from each attached node to their starting rotation (euler angles) in world space. + * Internal mapping from each attached node to their starting rotation in world space. * - * @type {Map} + * @type {Map} * @private */ _nodeRotations = new Map(); From de63511592d9c1686ba79e1254de3ad7b8572d5b Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:10:38 +0000 Subject: [PATCH 170/178] fixed scale warping on global rotation of inverted scale objects. --- examples/src/examples/misc/gizmos.mjs | 1 + extras/gizmo/axis-shapes.js | 16 ++++++++-------- extras/gizmo/gizmo-rotate.js | 6 ++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index e8bed7fb11e..f560702b2bb 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -482,6 +482,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip // create gizmo const gizmoHandler = new GizmoHandler(app, camera.camera, gizmoLayer); gizmoHandler.switch('translate'); + gizmoHandler.add(box); // Change gizmo mode keybinds const setType = (value) => { diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index 3206b88398b..ab451a33efb 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -157,13 +157,13 @@ class AxisShape { class AxisArrow extends AxisShape { _gap = 0; - _lineThickness = 0.04; + _lineThickness = 0.02; _lineLength = 0.5; - _arrowThickness = 0.15; + _arrowThickness = 0.12; - _arrowLength = 0.2; + _arrowLength = 0.18; _tolerance = 0.1; @@ -276,7 +276,7 @@ class AxisArrow extends AxisShape { } class AxisBoxCenter extends AxisShape { - _size = 0.14; + _size = 0.12; _tolerance = 0.05; @@ -332,11 +332,11 @@ class AxisBoxCenter extends AxisShape { class AxisBoxLine extends AxisShape { _gap = 0; - _lineThickness = 0.04; + _lineThickness = 0.02; _lineLength = 0.5; - _boxSize = 0.14; + _boxSize = 0.12; _tolerance = 0.1; @@ -442,9 +442,9 @@ class AxisBoxLine extends AxisShape { } class AxisDisk extends AxisShape { - _tubeRadius = 0.02; + _tubeRadius = 0.01; - _ringRadius = 0.55; + _ringRadius = 0.5; _sectorAngle; diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index 2f507560eb1..afe634b6568 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -54,7 +54,7 @@ class GizmoRotate extends GizmoTransform { rotation: this._getLookAtEulerAngles(this.camera.entity.getPosition()), defaultColor: this._materials.axis.face, hoverColor: this._materials.hover.face, - ringRadius: 0.63 + ringRadius: 0.55 }) }; @@ -310,7 +310,9 @@ class GizmoRotate extends GizmoTransform { tmpV1.copy(this._nodeOffsets.get(node)); tmpQ1.transformVector(tmpV1, tmpV1); tmpQ2.copy(tmpQ1).mul(this._nodeRotations.get(node)); - node.setRotation(tmpQ2); + + // Fix: Rotation via quaternion causes scale warping? + node.setEulerAngles(tmpQ2.getEulerAngles()); node.setPosition(tmpV1.add(gizmoPos)); } } From e3ea8976d23b7370d691ddedb3924a4b32792e2b Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:21:28 +0000 Subject: [PATCH 171/178] changed torus sectorAngle to be degrees --- extras/gizmo/axis-shapes.js | 4 ++-- extras/gizmo/gizmo-rotate.js | 8 ++++---- src/scene/procedural.js | 11 ++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/extras/gizmo/axis-shapes.js b/extras/gizmo/axis-shapes.js index ab451a33efb..ae568c54543 100644 --- a/extras/gizmo/axis-shapes.js +++ b/extras/gizmo/axis-shapes.js @@ -490,7 +490,7 @@ class AxisDisk extends AxisShape { // arc/circle this._addRenderMeshes(this.entity, [ this._createRenderTorus(this._sectorAngle), - this._createRenderTorus(2 * Math.PI) + this._createRenderTorus(360) ]); this.drag(false); } @@ -528,7 +528,7 @@ class AxisDisk extends AxisShape { // render this.meshInstances[0].mesh = this._createRenderTorus(this._sectorAngle); - this.meshInstances[1].mesh = this._createRenderTorus(2 * Math.PI); + this.meshInstances[1].mesh = this._createRenderTorus(360); } drag(state) { diff --git a/extras/gizmo/gizmo-rotate.js b/extras/gizmo/gizmo-rotate.js index afe634b6568..975fdb6c884 100644 --- a/extras/gizmo/gizmo-rotate.js +++ b/extras/gizmo/gizmo-rotate.js @@ -30,7 +30,7 @@ class GizmoRotate extends GizmoTransform { rotation: new Vec3(90, 0, 90), defaultColor: this._materials.axis.z.cullBack, hoverColor: this._materials.hover.z.cullBack, - sectorAngle: Math.PI + sectorAngle: 180 }), x: new AxisDisk(this.app.graphicsDevice, { axis: 'x', @@ -38,7 +38,7 @@ class GizmoRotate extends GizmoTransform { rotation: new Vec3(0, 0, -90), defaultColor: this._materials.axis.x.cullBack, hoverColor: this._materials.hover.x.cullBack, - sectorAngle: Math.PI + sectorAngle: 180 }), y: new AxisDisk(this.app.graphicsDevice, { axis: 'y', @@ -46,7 +46,7 @@ class GizmoRotate extends GizmoTransform { rotation: new Vec3(0, 0, 0), defaultColor: this._materials.axis.y.cullBack, hoverColor: this._materials.hover.y.cullBack, - sectorAngle: Math.PI + sectorAngle: 180 }), face: new AxisDisk(this.app.graphicsDevice, { axis: 'face', @@ -311,7 +311,7 @@ class GizmoRotate extends GizmoTransform { tmpQ1.transformVector(tmpV1, tmpV1); tmpQ2.copy(tmpQ1).mul(this._nodeRotations.get(node)); - // Fix: Rotation via quaternion causes scale warping? + // Fix: Rotation via quaternion when scale inverted causes scale warping? node.setEulerAngles(tmpQ2.getEulerAngles()); node.setPosition(tmpV1.add(gizmoPos)); } diff --git a/src/scene/procedural.js b/src/scene/procedural.js index e1746c856f9..23618aa6390 100644 --- a/src/scene/procedural.js +++ b/src/scene/procedural.js @@ -1,3 +1,4 @@ +import { math } from '../core/math/math.js'; import { Vec2 } from '../core/math/vec2.js'; import { Vec3 } from '../core/math/vec3.js'; @@ -311,7 +312,7 @@ function createTorus(device, opts = {}) { // Check the supplied options and provide defaults for unspecified ones const rc = opts.tubeRadius ?? 0.2; const rt = opts.ringRadius ?? 0.3; - const sectorAngle = opts.sectorAngle ?? 2 * Math.PI; + const sectorAngle = opts.sectorAngle ?? 360; const segments = opts.segments ?? 30; const sides = opts.sides ?? 20; const calcTangents = opts.calculateTangents ?? false; @@ -324,13 +325,13 @@ function createTorus(device, opts = {}) { for (let i = 0; i <= sides; i++) { for (let j = 0; j <= segments; j++) { - const x = Math.cos(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const x = Math.cos(sectorAngle * math.DEG_TO_RAD * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); const y = Math.sin(2 * Math.PI * i / sides) * rc; - const z = Math.sin(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const z = Math.sin(sectorAngle * math.DEG_TO_RAD * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); - const nx = Math.cos(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nx = Math.cos(sectorAngle * math.DEG_TO_RAD * j / segments) * Math.cos(2 * Math.PI * i / sides); const ny = Math.sin(2 * Math.PI * i / sides); - const nz = Math.sin(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nz = Math.sin(sectorAngle * math.DEG_TO_RAD * j / segments) * Math.cos(2 * Math.PI * i / sides); const u = i / sides; const v = 1 - j / segments; From f14a420a168dc25a903c350576d3d48fc4999773 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:22:45 +0000 Subject: [PATCH 172/178] updated jsdoc for createTorus --- src/scene/procedural.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene/procedural.js b/src/scene/procedural.js index 23618aa6390..612113ae59e 100644 --- a/src/scene/procedural.js +++ b/src/scene/procedural.js @@ -299,7 +299,7 @@ function createMesh(device, positions, opts) { * (defaults to 0.2). * @param {number} [opts.ringRadius] - The radius from the centre of the torus to the centre of the * tube (defaults to 0.3). - * @param {number} [opts.sectorAngle] - The sector angle in radians of the ring of the torus + * @param {number} [opts.sectorAngle] - The sector angle in degrees of the ring of the torus * (defaults to 2 * Math.PI). * @param {number} [opts.segments] - The number of radial divisions forming cross-sections of the * torus ring (defaults to 20). From cce21d1b6fd27a86e03fe3d6eedfe60008636eba Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:29:24 +0000 Subject: [PATCH 173/178] moved deg to rad conversion to variable initializer --- src/scene/procedural.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scene/procedural.js b/src/scene/procedural.js index 612113ae59e..28de5aea2ed 100644 --- a/src/scene/procedural.js +++ b/src/scene/procedural.js @@ -312,7 +312,7 @@ function createTorus(device, opts = {}) { // Check the supplied options and provide defaults for unspecified ones const rc = opts.tubeRadius ?? 0.2; const rt = opts.ringRadius ?? 0.3; - const sectorAngle = opts.sectorAngle ?? 360; + const sectorAngle = (opts.sectorAngle ?? 360) * math.DEG_TO_RAD; const segments = opts.segments ?? 30; const sides = opts.sides ?? 20; const calcTangents = opts.calculateTangents ?? false; @@ -325,13 +325,13 @@ function createTorus(device, opts = {}) { for (let i = 0; i <= sides; i++) { for (let j = 0; j <= segments; j++) { - const x = Math.cos(sectorAngle * math.DEG_TO_RAD * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const x = Math.cos(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); const y = Math.sin(2 * Math.PI * i / sides) * rc; - const z = Math.sin(sectorAngle * math.DEG_TO_RAD * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); + const z = Math.sin(sectorAngle * j / segments) * (rt + rc * Math.cos(2 * Math.PI * i / sides)); - const nx = Math.cos(sectorAngle * math.DEG_TO_RAD * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nx = Math.cos(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); const ny = Math.sin(2 * Math.PI * i / sides); - const nz = Math.sin(sectorAngle * math.DEG_TO_RAD * j / segments) * Math.cos(2 * Math.PI * i / sides); + const nz = Math.sin(sectorAngle * j / segments) * Math.cos(2 * Math.PI * i / sides); const u = i / sides; const v = 1 - j / segments; From 0bf79746eaa733d1c5f1b044a1535dab2d6eba51 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:56:32 +0000 Subject: [PATCH 174/178] snapping on load fix --- examples/src/examples/misc/gizmos.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index f560702b2bb..eace067f4be 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -483,6 +483,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip const gizmoHandler = new GizmoHandler(app, camera.camera, gizmoLayer); gizmoHandler.switch('translate'); gizmoHandler.add(box); + this.focus(); // Change gizmo mode keybinds const setType = (value) => { From 5fe43f7b7866be52ba49025de6c2daec7718642e Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 14:59:14 +0000 Subject: [PATCH 175/178] hidden coordspace for scaling --- examples/src/examples/misc/gizmos.mjs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index eace067f4be..64a80f83308 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -25,16 +25,17 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { onSelect: this.setType }) ), - jsx(LabelGroup, { text: 'Coord Space' }, - jsx(SelectInput, { - options: [ - { v: 'world', t: 'World' }, - { v: 'local', t: 'Local' } - ], - binding: new BindingTwoWay(), - link: { observer, path: 'gizmo.coordSpace' } - }) - ), + (type === 'translate' || type === 'rotate') && + jsx(LabelGroup, { text: 'Coord Space' }, + jsx(SelectInput, { + options: [ + { v: 'world', t: 'World' }, + { v: 'local', t: 'Local' } + ], + binding: new BindingTwoWay(), + link: { observer, path: 'gizmo.coordSpace' } + }) + ), jsx(LabelGroup, { text: 'Size' }, jsx(SliderInput, { binding: new BindingTwoWay(), From 5b446423341863bbc57a1a1d9150ad45bbbd3084 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 15:02:45 +0000 Subject: [PATCH 176/178] disabled webgpu for example --- examples/src/examples/misc/gizmos.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index 64a80f83308..bdfdb3e56e2 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -600,7 +600,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip class GizmosExample { static CATEGORY = 'Misc'; - static WEBGPU_ENABLED = true; + static WEBGPU_ENABLED = false; static controls = controls; static example = example; } From 6f629635792058be9e268808b9693f0118f4a727 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 15:10:29 +0000 Subject: [PATCH 177/178] Fixed picker resizing selection issue --- examples/src/examples/misc/gizmos.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index bdfdb3e56e2..bf8f19424bd 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -563,6 +563,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip } if (picker) { + picker.resize(canvas.clientWidth, canvas.clientHeight); picker.prepare(camera.camera, app.scene, pickerLayers); } From 9f9cfc1b705ee7ab637b1e294ce28673e84be077 Mon Sep 17 00:00:00 2001 From: kpal Date: Fri, 19 Jan 2024 16:14:56 +0000 Subject: [PATCH 178/178] removed orthoHeight setting; set in orbit camera --- examples/src/examples/misc/gizmos.mjs | 34 ++++++++++----------------- scripts/camera/orbit-camera.js | 18 +++++++++++++- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/examples/src/examples/misc/gizmos.mjs b/examples/src/examples/misc/gizmos.mjs index bf8f19424bd..b36d51596ec 100644 --- a/examples/src/examples/misc/gizmos.mjs +++ b/examples/src/examples/misc/gizmos.mjs @@ -8,6 +8,7 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { const { BindingTwoWay, LabelGroup, Panel, BooleanInput, ColorPicker, SliderInput, SelectInput } = ReactPCUI; const [type, setType] = React.useState('translate'); + const [proj, setProj] = React.useState(pc.PROJECTION_PERSPECTIVE); window.setType = (value) => setType(value); @@ -208,25 +209,19 @@ function controls({ observer, ReactPCUI, React, jsx, fragment }) { { v: pc.PROJECTION_ORTHOGRAPHIC + 1, t: 'Orthographic' } ], binding: new BindingTwoWay(), - link: { observer, path: 'camera.proj' } + link: { observer, path: 'camera.proj' }, + onSelect: value => setProj(value - 1) }) ), - jsx(LabelGroup, { text: 'FOV' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.fov' }, - min: 30, - max: 100 - }) - ), - jsx(LabelGroup, { text: 'Ortho Height' }, - jsx(SliderInput, { - binding: new BindingTwoWay(), - link: { observer, path: 'camera.orthoHeight' }, - min: 1, - max: 20 - }) - ) + (proj === pc.PROJECTION_PERSPECTIVE) && + jsx(LabelGroup, { text: 'FOV' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'camera.fov' }, + min: 30, + max: 100 + }) + ) ) ); } @@ -442,7 +437,7 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip }); camera.addComponent("script"); const orbitCamera = camera.script.create("orbitCamera"); - camera.script.create("orbitCameraInputMouse"); + const orbitCameraInputMouse = camera.script.create("orbitCameraInputMouse"); camera.script.create("orbitCameraInputTouch"); camera.setPosition(1, 1, 1); app.root.addChild(camera); @@ -525,9 +520,6 @@ async function example({ canvas, deviceType, data, glslangPath, twgslPath, scrip case 'fov': camera.camera.fov = value; break; - case 'orthoHeight': - camera.camera.orthoHeight = value; - break; } return; case 'gizmo': diff --git a/scripts/camera/orbit-camera.js b/scripts/camera/orbit-camera.js index d46c0604d4a..d53cf072ab6 100644 --- a/scripts/camera/orbit-camera.js +++ b/scripts/camera/orbit-camera.js @@ -41,6 +41,18 @@ Object.defineProperty(OrbitCamera.prototype, "distance", { } }); +// Property to get and set the camera orthoHeight +// Clamped above 0 +Object.defineProperty(OrbitCamera.prototype, "orthoHeight", { + get: function () { + return this.entity.camera.orthoHeight; + }, + + set: function (value) { + this.entity.camera.orthoHeight = Math.max(0, value); + } +}); + // Property to get and set the pitch of the camera around the pivot point (degrees) // Clamped between this.pitchAngleMin and this.pitchAngleMax @@ -472,7 +484,11 @@ OrbitCameraInputMouse.prototype.onMouseMove = function (event) { OrbitCameraInputMouse.prototype.onMouseWheel = function (event) { - this.orbitCamera.distance -= event.wheel * this.distanceSensitivity * (this.orbitCamera.distance * 0.1); + if (this.entity.camera.projection === pc.PROJECTION_PERSPECTIVE) { + this.orbitCamera.distance -= event.wheel * this.distanceSensitivity * (this.orbitCamera.distance * 0.1); + } else { + this.orbitCamera.orthoHeight -= event.wheel * this.distanceSensitivity; + } event.event.preventDefault(); };