diff --git a/package-lock.json b/package-lock.json index a4db9c8e3..9b23b7caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "monaco-themes": "^0.4.2", "nodemon": "3.1.7", "ot-text": "github:playcanvas/ot-text", - "playcanvas": "^2.10.6", + "playcanvas": "^2.11.2", "postcss": "8.4.48", "rollup": "4.25.0", "rollup-plugin-copy": "3.5.0", @@ -56,7 +56,7 @@ "typescript": "5.6.3" }, "engines": { - "node": ">=18.20.0" + "node": ">=18.0.0" } }, "node_modules/@babel/code-frame": { @@ -6110,9 +6110,9 @@ } }, "node_modules/playcanvas": { - "version": "2.10.6", - "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-2.10.6.tgz", - "integrity": "sha512-u/mHmG0Vk6Nr7qmMNCiAs1N2uQM/xjRHZ2fDcGJTpSCDU1dN3p1Nb3eGriQmJmJbpH1Y7D560JSWRnqavqUm7w==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-2.11.2.tgz", + "integrity": "sha512-hmfKzxfgoxt3CmJIi7Gu3nWn+2wLWwIRwnEebj/VVUo9oCPQ4ekqvhl0pZXNMk8b2CZKSwtGY/88JTfPowuEzQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 5d996f4e3..f80635300 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "monaco-themes": "^0.4.2", "nodemon": "3.1.7", "ot-text": "github:playcanvas/ot-text", - "playcanvas": "^2.10.6", + "playcanvas": "^2.11.2", "postcss": "8.4.48", "rollup": "4.25.0", "rollup-plugin-copy": "3.5.0", diff --git a/src/core/constants.ts b/src/core/constants.ts index 19c5ec1bf..faf4d3a7c 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -4,6 +4,9 @@ export const ENGINE_VERSION = typeof pc !== 'undefined' ? `${pc.version}` : '0.0 // Gizmo mask export const GIZMO_MASK = 8; +// Picker force pick tag +export const FORCE_PICK_TAG = 'force-pick'; + // Layer ids export const LAYERID_WORLD = 0; export const LAYERID_DEPTH = 1; diff --git a/src/editor/entities/entities-gizmo-rotate.ts b/src/editor/entities/entities-gizmo-rotate.ts deleted file mode 100644 index a51c052c5..000000000 --- a/src/editor/entities/entities-gizmo-rotate.ts +++ /dev/null @@ -1,397 +0,0 @@ -editor.once('load', () => { - let events = []; - let items = []; - const quat = new pc.Quat(); - const quatB = new pc.Quat(); - const vecA = new pc.Vec3(); - let timeoutUpdatePosition, timeoutUpdateRotation; - let coordSystem = 'world'; - const gizmoPos = new pc.Vec3(); - let gizmoMoving = false; - let gizmoAxis; - - // get position of gizmo based on selected entities - const getGizmoPosition = function () { - if (!items.length) { - return; - } - - if (items.length === 1) { - vecA.copy(items[0].obj.entity.getPosition()); - } else if (coordSystem === 'local') { - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - vecA.copy(reference.obj.entity.getPosition()); - } else { - const selection = editor.call('selection:aabb'); - if (!selection) { - return; - } - vecA.copy(selection.center); - } - - return vecA; - }; - - const getGizmoRotation = function () { - if (!items.length) { - return; - } - - if (coordSystem === 'local') { - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - const rot = reference.obj.entity.getEulerAngles(); - - return [rot.x, rot.y, rot.z]; - } - return [0, 0, 0]; - - }; - - editor.on('gizmo:coordSystem', (system) => { - if (coordSystem === system) { - return; - } - - coordSystem = system; - - const rot = getGizmoRotation(); - if (rot) { - editor.call('gizmo:rotate:rotation', rot[0], rot[1], rot[2]); - } - - const vec = getGizmoPosition(); - if (vec) { - editor.call('gizmo:rotate:position', vec.x, vec.y, vec.z); - } - - editor.call('viewport:render'); - }); - - // update gizmo position - const updateGizmoPosition = function () { - if (!items.length || timeoutUpdatePosition || gizmoMoving) { - return; - } - - timeoutUpdatePosition = true; - - setTimeout(() => { - timeoutUpdatePosition = false; - - const vec = getGizmoPosition(); - if (vec) { - editor.call('gizmo:rotate:position', vec.x, vec.y, vec.z); - } - }); - }; - - // update gizmo position - const updateGizmoRotation = function () { - if (!gizmoMoving) { - updateGizmoPosition(); - } - - if (!items.length || timeoutUpdateRotation) { - return; - } - - timeoutUpdateRotation = true; - - setTimeout(() => { - timeoutUpdateRotation = false; - - const vec = getGizmoRotation(); - if (vec) { - editor.call('gizmo:rotate:rotation', vec[0], vec[1], vec[2]); - } - }); - }; - - // start translating - const onGizmoStart = function (axis) { - gizmoAxis = axis; - gizmoMoving = true; - - gizmoPos.copy(editor.call('gizmo:rotate:position')); - - for (let i = 0; i < items.length; i++) { - let rot = items[i].obj.entity.getEulerAngles(); - items[i].start[0] = rot.x; - items[i].start[1] = rot.y; - items[i].start[2] = rot.z; - items[i].pos = items[i].start.slice(0); - - const posLocal = items[i].obj.entity.getLocalPosition(); - - items[i].startPosLocal[0] = posLocal.x; - items[i].startPosLocal[1] = posLocal.y; - items[i].startPosLocal[2] = posLocal.z; - - const pos = items[i].obj.entity.getPosition(); - - items[i].offset[0] = pos.x - gizmoPos.x; - items[i].offset[1] = pos.y - gizmoPos.y; - items[i].offset[2] = pos.z - gizmoPos.z; - - rot = items[i].obj.get('rotation'); - items[i].startLocal[0] = rot[0]; - items[i].startLocal[1] = rot[1]; - items[i].startLocal[2] = rot[2]; - - items[i].startLocalQuat.copy(items[i].obj.entity.getLocalRotation()); - items[i].startQuat.copy(items[i].obj.entity.getRotation()); - - items[i].obj.history.enabled = false; - } - }; - - // end translating - const onGizmoEnd = function () { - gizmoMoving = false; - const records = []; - - for (let i = 0; i < items.length; i++) { - items[i].obj.history.enabled = true; - - records.push({ - item: items[i].obj, - valueRotOld: items[i].startLocal.slice(0), - valueRot: items[i].obj.get('rotation'), - valuePosOld: items[i].startPosLocal.slice(0), - valuePos: items[i].obj.get('position') - }); - } - - editor.api.globals.history.add({ - name: 'entities.rotate', - combine: false, - undo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('position', records[i].valuePosOld); - item.set('rotation', records[i].valueRotOld); - item.history.enabled = true; - } - }, - redo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('position', records[i].valuePos); - item.set('rotation', records[i].valueRot); - item.history.enabled = true; - } - } - }); - - const pos = getGizmoPosition(); - editor.call('gizmo:rotate:position', pos.x, pos.y, pos.z); - }; - - // translated - const onGizmoOffset = function (angle, point) { - timeoutUpdateRotation = true; - - for (let i = 0; i < items.length; i++) { - if (items[i].child) { - continue; - } - - // skip 2D screens - if (items[i].obj.get('components.screen.screenSpace')) { - continue; - } - - vecA.set(0, 0, 0); - vecA[gizmoAxis] = 1; - - quat.setFromAxisAngle(vecA, angle); - - if (coordSystem === 'local') { - quatB.copy(items[i].startLocalQuat).mul(quat); - items[i].obj.entity.setLocalRotation(quatB); - } else if (items.length === 1) { - quatB.copy(quat).mul(items[i].startQuat); - items[i].obj.entity.setRotation(quatB); - } else { - vecA.set(items[i].offset[0], items[i].offset[1], items[i].offset[2]); - quat.transformVector(vecA, vecA); - quatB.copy(quat).mul(items[i].startQuat); - items[i].obj.entity.setRotation(quatB); - items[i].obj.entity.setPosition(vecA.add(gizmoPos)); - - const pos = items[i].obj.entity.getLocalPosition(); - items[i].obj.set('position', [pos.x, pos.y, pos.z]); - } - - const angles = items[i].obj.entity.getLocalEulerAngles(); - items[i].obj.set('rotation', [angles.x, angles.y, angles.z]); - } - - timeoutUpdateRotation = false; - - if (items.length > 1 || coordSystem === 'local') { - const rot = getGizmoRotation(); - editor.call('gizmo:rotate:rotation', rot[0], rot[1], rot[2]); - } - }; - - const onRender = function () { - if (!gizmoMoving && items.length) { - let dirty = false; - for (let i = 0; i < items.length; i++) { - if (!items[i].obj.entity) { - continue; - } - - const pos = items[i].obj.entity.getPosition(); - if (pos.x !== items[i].pos[0] || pos.y !== items[i].pos[1] || pos.z !== items[i].pos[2]) { - dirty = true; - items[i].pos[0] = pos.x; - items[i].pos[1] = pos.y; - items[i].pos[2] = pos.z; - } - } - - if (dirty) { - const pos = getGizmoPosition(); - editor.call('gizmo:translate:position', pos.x, pos.y, pos.z); - } - } - - if (items.length > 1 && !coordSystem === 'world') { - const rot = getGizmoRotation(); - editor.call('gizmo:rotate:rotation', rot[0], rot[1], rot[2]); - } - }; - - const updateChildRelation = function () { - const itemIds = { }; - for (let i = 0; i < items.length; i++) { - itemIds[items[i].obj.get('resource_id')] = items[i]; - } - - for (let i = 0; i < items.length; i++) { - let child = false; - let parent = items[i].obj.entity._parent; - let id = ''; - while (!child && parent) { - id = parent.getGuid(); - if (itemIds[id]) { - parent = itemIds[id]; - child = true; - break; - } - parent = parent._parent; - } - items[i].child = child; - items[i].parent = child ? parent : null; - } - - updateGizmoPosition(); - }; - - const updateGizmo = function () { - if (!editor.call('permissions:write')) { - return; - } - - const objects = editor.call('selector:items'); - - for (let i = 0; i < events.length; i++) { - events[i].unbind(); - } - events = []; - items = []; - - if (editor.call('selector:type') === 'entity' && editor.call('gizmo:type') === 'rotate') { - for (let i = 0; i < objects.length; i++) { - if (!objects[i].entity) { - continue; - } - - const pos = objects[i].entity.getPosition(); - - items.push({ - obj: objects[i], - startLocalQuat: objects[i].entity.getLocalRotation().clone(), - startQuat: objects[i].entity.getRotation().clone(), - pos: [pos.x, pos.y, pos.z], - offset: [0, 0, 0], - start: [0, 0, 0], - startLocal: [0, 0, 0], - startPosLocal: [0, 0, 0] - }); - - // position - events.push(objects[i].on('position:set', updateGizmoPosition)); - // position.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`position.${n}:set`, updateGizmoPosition)); - } - - // rotation - events.push(objects[i].on('rotation:set', updateGizmoRotation)); - // rotation.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`rotation.${n}:set`, updateGizmoRotation)); - } - - events.push(objects[i].on('parent:set', updateChildRelation)); - } - - if (!items.length) { - return; - } - - updateChildRelation(); - - // gizmo start - events.push(editor.on('gizmo:rotate:start', onGizmoStart)); - // gizmo end - events.push(editor.on('gizmo:rotate:end', onGizmoEnd)); - // gizmo offset - events.push(editor.on('gizmo:rotate:offset', onGizmoOffset)); - - // rotation gizmo - const rot = getGizmoRotation(); - editor.call('gizmo:rotate:rotation', rot[0], rot[1], rot[2]); - // position gizmo - const pos = getGizmoPosition(); - editor.call('gizmo:rotate:position', pos.x, pos.y, pos.z); - // show gizmo - editor.call('gizmo:rotate:toggle', true); - // on render - events.push(editor.on('gizmo:rotate:render', onRender)); - // render - editor.call('viewport:render'); - } else { - // hide gizmo - editor.call('gizmo:rotate:toggle', false); - // render - editor.call('viewport:render'); - } - }; - - editor.on('gizmo:type', updateGizmo); - editor.on('selector:change', updateGizmo); -}); diff --git a/src/editor/entities/entities-gizmo-scale.ts b/src/editor/entities/entities-gizmo-scale.ts deleted file mode 100644 index 9b26beb0d..000000000 --- a/src/editor/entities/entities-gizmo-scale.ts +++ /dev/null @@ -1,334 +0,0 @@ -editor.once('load', () => { - let events = []; - let items = []; - const quat = new pc.Quat(); - const vecA = new pc.Vec3(); - const vecB = new pc.Vec3(); - const vecC = new pc.Vec3(); - let timeoutUpdatePosition, timeoutUpdateRotation; - let app; - let gizmoMoving = false; - let gizmoAxis, gizmoMiddle; - const linesColorActive = new pc.Color(1, 1, 1, 1); - const linesColorBehind = new pc.Color(1, 1, 1, 0.05); - - // get position of gizmo based on selected entities - const getGizmoPosition = function () { - if (!items.length) { - return; - } - - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - vecA.copy(reference.obj.entity.getPosition()); - - return vecA; - }; - - const getGizmoRotation = function () { - if (!items.length) { - return; - } - - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - const rot = reference.obj.entity.getEulerAngles(); - - return [rot.x, rot.y, rot.z]; - }; - - // update gizmo position - const updateGizmoPosition = function () { - if (!items.length || timeoutUpdatePosition) { - return; - } - - timeoutUpdatePosition = true; - - setTimeout(() => { - timeoutUpdatePosition = false; - - const vec = getGizmoPosition(); - if (vec) { - editor.call('gizmo:scale:position', vec.x, vec.y, vec.z); - } - }); - }; - - // update gizmo position - const updateGizmoRotation = function () { - if (!items.length || timeoutUpdateRotation) { - return; - } - - timeoutUpdateRotation = true; - - setTimeout(() => { - timeoutUpdateRotation = false; - - const vec = getGizmoRotation(); - if (vec) { - editor.call('gizmo:scale:rotation', vec[0], vec[1], vec[2]); - } - }); - }; - - // start translating - const onGizmoStart = function (axis, middle) { - gizmoAxis = axis; - gizmoMiddle = middle; - gizmoMoving = true; - - for (let i = 0; i < items.length; i++) { - const scale = items[i].obj.get('scale'); - items[i].start[0] = scale[0]; - items[i].start[1] = scale[1]; - items[i].start[2] = scale[2]; - items[i].pos = items[i].start.slice(0); - items[i].obj.history.enabled = false; - } - }; - - // end translating - const onGizmoEnd = function () { - gizmoMoving = false; - const records = []; - - for (let i = 0; i < items.length; i++) { - items[i].obj.history.enabled = true; - - records.push({ - item: items[i].obj, - valueOld: items[i].start.slice(0), - value: items[i].obj.get('scale') - }); - } - - editor.api.globals.history.add({ - name: 'entities.scale', - combine: false, - undo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('scale', records[i].valueOld); - item.history.enabled = true; - } - }, - redo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('scale', records[i].value); - item.history.enabled = true; - } - } - }); - }; - - // scaled - const onGizmoOffset = function (x, y, z) { - for (let i = 0; i < items.length; i++) { - if (items[i].child) { - continue; - } - - // skip 2D screens - if (items[i].obj.get('components.screen.screenSpace')) { - continue; - } - - items[i].obj.set('scale', [items[i].start[0] * x, items[i].start[1] * y, items[i].start[2] * z]); - } - }; - - const onRender = function () { - if (!app) { - return; - } // webgl not available - - if (gizmoMoving) { - const camera = editor.call('camera:current'); - const immediateLayer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); - const brightLayer = editor.call('gizmo:layers', 'Bright Gizmo'); - - for (let i = 0; i < items.length; i++) { - if (items[i].child) { - continue; - } - - vecA.copy(items[i].obj.entity.getPosition()); - quat.copy(items[i].obj.entity.getRotation()); - - if (gizmoAxis === 'x' || gizmoMiddle) { - vecB.set(camera.camera.farClip * 2, 0, 0); - quat.transformVector(vecB, vecB).add(vecA); - vecC.set(camera.camera.farClip * -2, 0, 0); - quat.transformVector(vecC, vecC).add(vecA); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } - if (gizmoAxis === 'y' || gizmoMiddle) { - vecB.set(0, camera.camera.farClip * 2, 0); - quat.transformVector(vecB, vecB).add(vecA); - vecC.set(0, camera.camera.farClip * -2, 0); - quat.transformVector(vecC, vecC).add(vecA); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } - if (gizmoAxis === 'z' || gizmoMiddle) { - vecB.set(0, 0, camera.camera.farClip * 2); - quat.transformVector(vecB, vecB).add(vecA); - vecC.set(0, 0, camera.camera.farClip * -2); - quat.transformVector(vecC, vecC).add(vecA); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } - } - } else { - let dirty = false; - for (let i = 0; i < items.length; i++) { - if (!items[i].obj.entity) { - continue; - } - - const pos = items[i].obj.entity.getPosition(); - if (pos.x !== items[i].pos[0] || pos.y !== items[i].pos[1] || pos.z !== items[i].pos[2]) { - dirty = true; - items[i].pos[0] = pos.x; - items[i].pos[1] = pos.y; - items[i].pos[2] = pos.z; - } - } - - if (dirty) { - const pos = getGizmoPosition(); - editor.call('gizmo:scale:position', pos.x, pos.y, pos.z); - } - } - }; - - editor.once('viewport:load', (application) => { - app = application; - }); - - const updateChildRelation = function () { - const itemIds = { }; - for (let i = 0; i < items.length; i++) { - itemIds[items[i].obj.get('resource_id')] = items[i]; - } - - for (let i = 0; i < items.length; i++) { - let child = false; - let parent = items[i].obj.entity._parent; - let id = ''; - while (!child && parent) { - id = parent.getGuid(); - if (itemIds[id]) { - parent = itemIds[id]; - child = true; - break; - } - parent = parent._parent; - } - items[i].child = child; - items[i].parent = child ? parent : null; - } - }; - - const updateGizmo = function () { - if (!editor.call('permissions:write')) { - return; - } - - const objects = editor.call('selector:items'); - - for (let i = 0; i < events.length; i++) { - events[i].unbind(); - } - events = []; - items = []; - - if (editor.call('selector:type') === 'entity' && editor.call('gizmo:type') === 'scale') { - for (let i = 0; i < objects.length; i++) { - if (!objects[i].entity) { - continue; - } - - const pos = objects[i].entity.getPosition(); - - items.push({ - obj: objects[i], - pos: [pos.x, pos.y, pos.z], - start: [1, 1, 1] - }); - - // position - events.push(objects[i].on('position:set', updateGizmoPosition)); - // position.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`position.${n}:set`, updateGizmoPosition)); - } - - // rotation - events.push(objects[i].on('rotation:set', updateGizmoRotation)); - // rotation.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`rotation.${n}:set`, updateGizmoRotation)); - } - - events.push(objects[i].on('parent:set', updateChildRelation)); - } - - if (!items.length) { - return; - } - - updateChildRelation(); - - const rot = getGizmoRotation(); - editor.call('gizmo:scale:rotation', rot[0], rot[1], rot[2]); - - // gizmo start - events.push(editor.on('gizmo:scale:start', onGizmoStart)); - // gizmo end - events.push(editor.on('gizmo:scale:end', onGizmoEnd)); - // gizmo offset - events.push(editor.on('gizmo:scale:offset', onGizmoOffset)); - - // position gizmo - const pos = getGizmoPosition(); - editor.call('gizmo:scale:position', pos.x, pos.y, pos.z); - // show gizmo - editor.call('gizmo:scale:toggle', true); - // on render - events.push(editor.on('gizmo:scale:render', onRender)); - // render - editor.call('viewport:render'); - } else { - // hide gizmo - editor.call('gizmo:scale:toggle', false); - // render - editor.call('viewport:render'); - } - }; - - editor.on('gizmo:type', updateGizmo); - editor.on('selector:change', updateGizmo); -}); diff --git a/src/editor/entities/entities-gizmo-translate.ts b/src/editor/entities/entities-gizmo-translate.ts deleted file mode 100644 index 564831fc8..000000000 --- a/src/editor/entities/entities-gizmo-translate.ts +++ /dev/null @@ -1,430 +0,0 @@ -editor.once('load', () => { - let events = []; - let items = []; - const quat = new pc.Quat(); - const vecA = new pc.Vec3(); - const vecB = new pc.Vec3(); - const vecC = new pc.Vec3(); - let timeoutUpdatePosition, timeoutUpdateRotation; - let coordSystem = 'world'; - let app; - let gizmoMoving = false; - let gizmoAxis, gizmoPlane; - const movingStart = new pc.Vec3(); - const linesColorActive = new pc.Color(1, 1, 1, 1); - const linesColor = new pc.Color(1, 1, 1, 0.2); - const linesColorBehind = new pc.Color(1, 1, 1, 0.05); - - // get position of gizmo based on selected entities - const getGizmoPosition = function () { - if (!items.length) { - return; - } - - if (items.length === 1) { - if (items[0].obj.entity) { - vecA.copy(items[0].obj.entity.getPosition()); - } else { - return null; - } - } else if (coordSystem === 'local') { - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - vecA.copy(reference.obj.entity.getPosition()); - } else { - const selection = editor.call('selection:aabb'); - if (!selection) { - return; - } - vecA.copy(selection.center); - } - - return vecA; - }; - - const getGizmoRotation = function () { - if (!items.length) { - return; - } - - if (coordSystem === 'local') { - let reference = items[items.length - 1]; - let parent = reference.parent; - while (parent) { - reference = parent; - parent = parent.parent; - } - const rot = reference.obj.entity.getEulerAngles(); - return [rot.x, rot.y, rot.z]; - } - return [0, 0, 0]; - - }; - - editor.on('gizmo:coordSystem', (system) => { - if (coordSystem === system) { - return; - } - - coordSystem = system; - - const pos = getGizmoPosition(); - if (pos) { - editor.call('gizmo:translate:position', pos.x, pos.y, pos.z); - } - - const rot = getGizmoRotation(); - if (rot) { - editor.call('gizmo:translate:rotation', rot[0], rot[1], rot[2]); - } - - editor.call('viewport:render'); - }); - - // update gizmo position - const updateGizmoPosition = function () { - if (!items.length || timeoutUpdatePosition || gizmoMoving) { - return; - } - - timeoutUpdatePosition = true; - - setTimeout(() => { - timeoutUpdatePosition = false; - - const vec = getGizmoPosition(); - if (vec) { - editor.call('gizmo:translate:position', vec.x, vec.y, vec.z); - } - }); - }; - - // update gizmo position - const updateGizmoRotation = function () { - if (!items.length || timeoutUpdateRotation) { - return; - } - - timeoutUpdateRotation = true; - - setTimeout(() => { - timeoutUpdateRotation = false; - - const vec = getGizmoRotation(); - if (vec) { - editor.call('gizmo:translate:rotation', vec[0], vec[1], vec[2]); - } - }); - }; - - // start translating - const onGizmoStart = function (axis, plane) { - gizmoAxis = axis; - gizmoPlane = plane; - gizmoMoving = true; - - movingStart.copy(getGizmoPosition()); - - for (let i = 0; i < items.length; i++) { - let pos = items[i].obj.entity.getPosition(); - items[i].start[0] = pos.x; - items[i].start[1] = pos.y; - items[i].start[2] = pos.z; - items[i].pos = items[i].start.slice(0); - - pos = items[i].obj.get('position'); - items[i].startLocal[0] = pos[0]; - items[i].startLocal[1] = pos[1]; - items[i].startLocal[2] = pos[2]; - - items[i].obj.history.enabled = false; - } - }; - - // end translating - const onGizmoEnd = function () { - gizmoMoving = false; - const records = []; - - for (let i = 0; i < items.length; i++) { - items[i].obj.history.enabled = true; - - const data = { - item: items[i].obj, - valueOld: items[i].startLocal.slice(0), - value: items[i].obj.get('position') - }; - - records.push(data); - } - - editor.api.globals.history.add({ - name: 'entities.translate', - combine: false, - undo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('position', records[i].valueOld); - item.history.enabled = true; - } - }, - redo: function () { - for (let i = 0; i < records.length; i++) { - const item = records[i].item.latest(); - if (!item) { - continue; - } - - item.history.enabled = false; - item.set('position', records[i].value); - item.history.enabled = true; - } - } - }); - }; - - // translated - const onGizmoOffset = function (x, y, z) { - timeoutUpdateRotation = true; - - for (let i = 0; i < items.length; i++) { - if (items[i].child) { - continue; - } - - const entity = items[i].obj.entity; - - if (coordSystem === 'local') { - vecA.set(x, y, z); - - // scale by inverse world scale to ensure correct movement - entity.parent.getWorldTransform().getScale(vecB); - vecB.x = 1 / vecB.x; - vecB.y = 1 / vecB.y; - vecB.z = 1 / vecB.z; - - quat.copy(entity.getLocalRotation()).transformVector(vecA, vecA); - vecA.mul(vecB); - entity.setLocalPosition(items[i].startLocal[0] + vecA.x, items[i].startLocal[1] + vecA.y, items[i].startLocal[2] + vecA.z); - } else { - entity.setPosition(items[i].start[0] + x, items[i].start[1] + y, items[i].start[2] + z); - } - - const pos = entity.getLocalPosition(); - items[i].obj.set('position', [pos.x, pos.y, pos.z]); - } - - timeoutUpdateRotation = false; - - const pos = getGizmoPosition(); - editor.call('gizmo:translate:position', pos.x, pos.y, pos.z); - }; - - const onRender = function () { - if (!app) { - return; - } // webgl not available - - if (!gizmoMoving && items.length) { - let dirty = false; - for (let i = 0; i < items.length; i++) { - if (!items[i].obj.entity) { - continue; - } - - const pos = items[i].obj.entity.getPosition(); - if (pos.x !== items[i].pos[0] || pos.y !== items[i].pos[1] || pos.z !== items[i].pos[2]) { - dirty = true; - items[i].pos[0] = pos.x; - items[i].pos[1] = pos.y; - items[i].pos[2] = pos.z; - } - } - - if (dirty) { - const pos = getGizmoPosition(); - if (pos) { - editor.call('gizmo:translate:position', pos.x, pos.y, pos.z); - } - } - } - - if (gizmoMoving && items.length) { - const camera = editor.call('camera:current'); - let pos; - - const immediateLayer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); - const brightLayer = editor.call('gizmo:layers', 'Bright Gizmo'); - - const len = coordSystem === 'local' ? items.length : 1; - for (let i = 0; i < len; i++) { - if (items[i].child) { - continue; - } - - if (coordSystem === 'local') { - pos = items[i].obj.entity.getPosition(); - quat.copy(items[i].obj.entity.getRotation()); - } else { - pos = editor.call('gizmo:translate:position'); - quat.setFromEulerAngles(0, 0, 0); - } - - // x - vecB.set(camera.camera.farClip * 2, 0, 0); - quat.transformVector(vecB, vecB).add(pos); - vecC.set(camera.camera.farClip * -2, 0, 0); - quat.transformVector(vecC, vecC).add(pos); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - if ((gizmoAxis === 'x' && !gizmoPlane) || (gizmoPlane && (gizmoAxis === 'y' || gizmoAxis === 'z'))) { - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } else { - app.drawLine(vecB, vecC, linesColor, true, brightLayer); - } - - // y - vecB.set(0, camera.camera.farClip * 2, 0); - quat.transformVector(vecB, vecB).add(pos); - vecC.set(0, camera.camera.farClip * -2, 0); - quat.transformVector(vecC, vecC).add(pos); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - if ((gizmoAxis === 'y' && !gizmoPlane) || (gizmoPlane && (gizmoAxis === 'x' || gizmoAxis === 'z'))) { - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } else { - app.drawLine(vecB, vecC, linesColor, true, brightLayer); - } - - // z - vecB.set(0, 0, camera.camera.farClip * 2); - quat.transformVector(vecB, vecB).add(pos); - vecC.set(0, 0, camera.camera.farClip * -2); - quat.transformVector(vecC, vecC).add(pos); - app.drawLine(vecB, vecC, linesColorBehind, true, immediateLayer); - if ((gizmoAxis === 'z' && !gizmoPlane) || (gizmoPlane && (gizmoAxis === 'x' || gizmoAxis === 'y'))) { - app.drawLine(vecB, vecC, linesColorActive, true, brightLayer); - } else { - app.drawLine(vecB, vecC, linesColor, true, brightLayer); - } - } - } - }; - - editor.once('viewport:load', (application) => { - app = application; - }); - - const updateChildRelation = function () { - const itemIds = { }; - for (let i = 0; i < items.length; i++) { - itemIds[items[i].obj.get('resource_id')] = items[i]; - } - - for (let i = 0; i < items.length; i++) { - let child = false; - let parent = items[i].obj.entity._parent; - let id = ''; - while (!child && parent) { - id = parent.getGuid(); - if (itemIds[id]) { - parent = itemIds[id]; - child = true; - break; - } - parent = parent._parent; - } - items[i].child = child; - items[i].parent = child ? parent : null; - } - }; - - const updateGizmo = function () { - if (!editor.call('permissions:write')) { - return; - } - - const objects = editor.call('selector:items'); - - for (let i = 0; i < events.length; i++) { - events[i].unbind(); - } - events = []; - items = []; - - if (editor.call('selector:type') === 'entity' && editor.call('gizmo:type') === 'translate') { - for (let i = 0; i < objects.length; i++) { - if (!objects[i].entity) { - continue; - } - - const pos = objects[i].entity.getPosition(); - - items.push({ - obj: objects[i], - pos: [pos.x, pos.y, pos.z], - start: [0, 0, 0], - startLocal: [0, 0, 0] - }); - - // position - events.push(objects[i].on('position:set', updateGizmoPosition)); - // position.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`position.${n}:set`, updateGizmoPosition)); - } - - // rotation - events.push(objects[i].on('rotation:set', updateGizmoRotation)); - // rotation.* - for (let n = 0; n < 3; n++) { - events.push(objects[i].on(`rotation.${n}:set`, updateGizmoRotation)); - } - - events.push(objects[i].on('parent:set', updateChildRelation)); - } - - if (!items.length) { - return; - } - - updateChildRelation(); - - const rot = getGizmoRotation(); - editor.call('gizmo:translate:rotation', rot[0], rot[1], rot[2]); - - // gizmo start - events.push(editor.on('gizmo:translate:start', onGizmoStart)); - // gizmo end - events.push(editor.on('gizmo:translate:end', onGizmoEnd)); - // gizmo offset - events.push(editor.on('gizmo:translate:offset', onGizmoOffset)); - - // position gizmo - const pos = getGizmoPosition(); - editor.call('gizmo:translate:position', pos.x, pos.y, pos.z); - // show gizmo - editor.call('gizmo:translate:toggle', true); - // on render - events.push(editor.on('gizmo:translate:render', onRender)); - // render - editor.call('viewport:render'); - } else { - // hide gizmo - editor.call('gizmo:translate:toggle', false); - // render - editor.call('viewport:render'); - } - }; - - editor.on('gizmo:type', updateGizmo); - editor.on('selector:change', updateGizmo); - editor.on('gizmo:translate:sync', updateGizmo); -}); diff --git a/src/editor/index.ts b/src/editor/index.ts index b54c3093b..cbce1ff07 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -111,17 +111,12 @@ import './entities/entities-components-menu.ts'; import './entities/entities-template-menu.ts'; import './entities/entities-pick.ts'; import './entities/entities-icons.ts'; -import './entities/entities-gizmo-translate.ts'; -import './entities/entities-gizmo-scale.ts'; -import './entities/entities-gizmo-rotate.ts'; // gizmo import './viewport/gizmo/gizmo.ts'; import './viewport/gizmo/gizmo-layers.ts'; import './viewport/gizmo/gizmo-point.ts'; -import './viewport/gizmo/gizmo-translate.ts'; -import './viewport/gizmo/gizmo-scale.ts'; -import './viewport/gizmo/gizmo-rotate.ts'; +import './viewport/gizmo/gizmo-transform.ts'; import './viewport/gizmo/gizmo-camera.ts'; import './viewport/gizmo/gizmo-light.ts'; import './viewport/gizmo/gizmo-collision.ts'; diff --git a/src/editor/viewport/gizmo/gizmo-element-anchor.ts b/src/editor/viewport/gizmo/gizmo-element-anchor.ts index b6933994b..da4fd7d07 100644 --- a/src/editor/viewport/gizmo/gizmo-element-anchor.ts +++ b/src/editor/viewport/gizmo/gizmo-element-anchor.ts @@ -1,4 +1,4 @@ -import { GIZMO_MASK } from '../../../core/constants.ts'; +import { FORCE_PICK_TAG, GIZMO_MASK } from '../../../core/constants.ts'; import { createColorMaterial } from '../viewport-color-material.ts'; editor.once('load', () => { @@ -23,6 +23,12 @@ editor.once('load', () => { let offset = new pc.Vec3(); let visible = true; + const createEntity = () => { + const entity = new pc.Entity(); + entity.tags.add(FORCE_PICK_TAG); + return entity; + }; + const createAnchorGizmo = function () { const obj = { root: null, @@ -38,7 +44,7 @@ editor.once('load', () => { handle: null }; - obj.root = new pc.Entity(); + obj.root = createEntity(); obj.root.enabled = false; const c = 0.8; @@ -48,11 +54,11 @@ editor.once('load', () => { const layer = editor.call('gizmo:layers', 'Axis Gizmo'); const createCone = function (angle) { - const result = new pc.Entity(); + const result = createEntity(); result.setLocalEulerAngles(0, 0, angle); obj.root.addChild(result); - const cone = new pc.Entity(); + const cone = createEntity(); cone.addComponent('model', { type: 'cone', layers: [layer.id] @@ -76,8 +82,8 @@ editor.once('load', () => { obj.handles.bl = createCone(130 + 180); obj.handles.br = createCone(230 + 180); - // obj.handles.center = new pc.Entity(); - // var sphere = new pc.Entity(); + // obj.handles.center = createEntity(); + // var sphere = createEntity(); // obj.handles.center.addChild(sphere); // sphere.addComponent('model', {type: 'sphere'}); // sphere.model.castShadows = false; @@ -291,6 +297,7 @@ editor.once('load', () => { if (!node || !node.handle) { if (gizmoAnchor.handle) { gizmoAnchor.handle = null; + editor.emit('gizmo:transform:visible', true); for (const key in gizmoAnchor.handles) { setModelMaterial(gizmoAnchor.handles[key].handleModel, gizmoAnchor.matInactive); @@ -304,6 +311,7 @@ editor.once('load', () => { } else { if (!gizmoAnchor.handle || gizmoAnchor.handle !== node.handle) { gizmoAnchor.handle = node.handle; + editor.emit('gizmo:transform:visible', false); for (const key in gizmoAnchor.handles) { setModelMaterial(gizmoAnchor.handles[key].handleModel, gizmoAnchor.handles[key] === gizmoAnchor.handle ? gizmoAnchor.matActive : gizmoAnchor.matInactive); @@ -337,10 +345,6 @@ editor.once('load', () => { anchorStart = selectedEntity.get('components.element.anchor').slice(0); } - - editor.call('gizmo:translate:visible', false); - editor.call('gizmo:rotate:visible', false); - editor.call('gizmo:scale:visible', false); }; const onTapMove = function (tap) { @@ -366,9 +370,6 @@ editor.once('load', () => { moving = false; mouseTap = tap; - editor.call('gizmo:translate:visible', true); - editor.call('gizmo:rotate:visible', true); - editor.call('gizmo:scale:visible', true); editor.call('viewport:pick:state', true); // update entity anchor diff --git a/src/editor/viewport/gizmo/gizmo-element-size.ts b/src/editor/viewport/gizmo/gizmo-element-size.ts index 90681a761..b645ca8f6 100644 --- a/src/editor/viewport/gizmo/gizmo-element-size.ts +++ b/src/editor/viewport/gizmo/gizmo-element-size.ts @@ -261,9 +261,7 @@ editor.once('load', () => { pickStart.copy(pickPlane(tap.x, tap.y)); } - editor.call('gizmo:translate:visible', false); - editor.call('gizmo:rotate:visible', false); - editor.call('gizmo:scale:visible', false); + editor.emit('gizmo:transform:visible', false); }; const onTapMove = function (tap) { @@ -289,9 +287,7 @@ editor.once('load', () => { moving = false; mouseTap = tap; - editor.call('gizmo:translate:visible', true); - editor.call('gizmo:rotate:visible', true); - editor.call('gizmo:scale:visible', true); + editor.emit('gizmo:transform:visible', true); editor.call('viewport:pick:state', true); if (selectedEntity) { diff --git a/src/editor/viewport/gizmo/gizmo-rotate.ts b/src/editor/viewport/gizmo/gizmo-rotate.ts deleted file mode 100644 index cecf88386..000000000 --- a/src/editor/viewport/gizmo/gizmo-rotate.ts +++ /dev/null @@ -1,572 +0,0 @@ -import { GIZMO_MASK } from '../../../core/constants.ts'; -import { createColorMaterial } from '../viewport-color-material.ts'; - -editor.once('load', () => { - let gizmo = null; - let visible = true; - let moving = false; - let mouseTap = null; - let mouseTapMoved = false; - const posCameraLast = new pc.Vec3(); - let enabled = false; - let hover = false; - let hoverAxis = ''; - const gizmoSize = 0.4; - const vecA = new pc.Vec3(); - const vecB = new pc.Vec3(); - const vecC = new pc.Vec3(); - const vecD = new pc.Vec3(); - const quat = new pc.Quat(); - let evtTapStart; - let angleStart = 0; - const startRotation = new pc.Quat(); - - const createMaterial = function (color) { - const mat = createColorMaterial(); - mat.color = color; - if (color.a !== 1) { - mat.blendState = new pc.BlendState(true, pc.BLENDEQUATION_ADD, pc.BLENDMODE_SRC_ALPHA, pc.BLENDMODE_ONE_MINUS_SRC_ALPHA); - } - mat.update(); - return mat; - }; - - const createMeshInstance = function (node, mesh, material) { - const mi = new pc.MeshInstance(mesh, material, node); - mi.cull = false; - return mi; - }; - - const createLinesModel = function (app) { - // Create the rotate gizmo geometry - const device = app.graphicsDevice; - const axisSegments = 50; - const numVerts = (axisSegments + 1); - let angle = 0.0; - let iterator; - let sinAngle, cosAngle; - const scale = 2; - - const vertexFormat = new pc.VertexFormat(app.graphicsDevice, [ - { semantic: pc.SEMANTIC_POSITION, components: 3, type: pc.TYPE_FLOAT32 } - ]); - - const vertexBuffers = []; - for (let axis = 0; axis < 3; axis++) { - // Create a vertex buffer - vertexBuffers.push(new pc.VertexBuffer(device, vertexFormat, numVerts)); - - // Fill the vertex buffer - iterator = new pc.VertexIterator(vertexBuffers[axis]); - for (let seg = 0; seg <= axisSegments; seg++) { - angle = 2 * Math.PI * (seg / axisSegments); - sinAngle = Math.sin(angle); - cosAngle = Math.cos(angle); - if (axis === 0) { - iterator.element[pc.SEMANTIC_POSITION].set(0, sinAngle * scale, cosAngle * scale); - } else if (axis === 1) { - iterator.element[pc.SEMANTIC_POSITION].set(sinAngle * scale, 0, cosAngle * scale); - } else if (axis === 2) { - iterator.element[pc.SEMANTIC_POSITION].set(sinAngle * scale, cosAngle * scale, 0); - } - iterator.next(); - } - iterator.end(); - } - - const node = new pc.GraphNode(); - let mesh, meshInstance; - - const meshInstances = []; - const materials = [ - createMaterial(new pc.Color(1, 0, 0, 1.1)), - createMaterial(new pc.Color(0, 1, 0, 1.1)), - createMaterial(new pc.Color(0, 0, 1, 1.1)) - ]; - - // create 3 rings of lines (the visible portion of the gizmo) - for (let i = 0; i < 3; i++) { - mesh = new pc.Mesh(device); - mesh.vertexBuffer = vertexBuffers[i]; - mesh.indexBuffer[0] = null; - mesh.primitive[0].type = pc.PRIMITIVE_LINESTRIP; - mesh.primitive[0].base = 0; - mesh.primitive[0].count = vertexBuffers[i].getNumVertices(); - mesh.primitive[0].indexed = false; - - meshInstance = createMeshInstance(node, mesh, materials[i]); - meshInstance.mask = GIZMO_MASK; - meshInstance.mat = materials[i]; - meshInstances.push(meshInstance); - } - - // create a sphere which is used to render in the center and cull the rings (via depth buffer) - mesh = pc.Mesh.fromGeometry(device, new pc.SphereGeometry({ - segments: 75, - radius: 1.95 - })); - const material = createMaterial(new pc.Color(1, 1, 1, 0.5)); - material.redWrite = false; - material.greenWrite = false; - material.blueWrite = false; - material.alphaWrite = false; - material.update(); - meshInstance = createMeshInstance(node, mesh, material); - meshInstance.mask = GIZMO_MASK; - meshInstances.push(meshInstance); - - return meshInstances; - }; - - const createEntity = function (app) { - const obj = { - root: null, - sphere: null, - plane: { - x: null, - y: null, - z: null - }, - line: { - x: null, - y: null, - z: null, - cull: null - }, - hoverable: [], - matActive: null, - matBehind: null, - matBehindHover: { }, - matBehindActive: null - }; - - // materials - obj.matBehind = createMaterial(new pc.Color(1, 1, 1, 0.1)); - obj.matBehind.depthTest = false; - obj.matBehindHover.x = createMaterial(new pc.Color(1, 0, 0, 0.2)); - obj.matBehindHover.y = createMaterial(new pc.Color(0, 1, 0, 0.2)); - obj.matBehindHover.z = createMaterial(new pc.Color(0, 0, 1, 0.2)); - obj.matBehindHover.x.depthTest = false; - obj.matBehindHover.y.depthTest = false; - obj.matBehindHover.z.depthTest = false; - obj.matBehindActive = createMaterial(new pc.Color(1, 1, 1, 1.1)); - obj.matBehindActive.depthTest = false; - obj.colorActive = new pc.Color(1, 1, 1, 1); - - const gizmoLayer = editor.call('gizmo:layers', 'Axis Gizmo').id; - - // root entity - const entity = obj.root = new pc.Entity(); - - // plane x - const planeX = obj.plane.x = new pc.Entity(); - obj.hoverable.push(planeX); - planeX.axis = 'x'; - planeX.plane = true; - planeX.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - entity.addChild(planeX); - planeX.setLocalEulerAngles(90, -90, 0); - planeX.setLocalScale(4.1, 0.3, 4.1); - planeX.mat = planeX.model.material = createMaterial(new pc.Color(1, 0, 0, 0)); - - // plane y - const planeY = obj.plane.y = new pc.Entity(); - obj.hoverable.push(planeY); - planeY.axis = 'y'; - planeY.plane = true; - planeY.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - entity.addChild(planeY); - planeY.setLocalEulerAngles(0, 0, 0); - planeY.setLocalScale(4.2, 0.3, 4.2); - planeY.mat = planeY.model.material = createMaterial(new pc.Color(0, 1, 0, 0)); - - // plane z - const planeZ = obj.plane.z = new pc.Entity(); - obj.hoverable.push(planeZ); - planeZ.axis = 'z'; - planeZ.plane = true; - planeZ.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - entity.addChild(planeZ); - planeZ.setLocalEulerAngles(90, 0, 0); - planeZ.setLocalScale(4, 0.3, 4); - planeZ.mat = planeZ.model.material = createMaterial(new pc.Color(0, 0, 1, 0)); - - // sphere - const sphere = obj.sphere = new pc.Entity(); - sphere.addComponent('model', { - type: 'sphere', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - entity.addChild(sphere); - sphere.setLocalScale(3, 3, 3); - sphere.mat = sphere.model.material = createMaterial(new pc.Color(1, 1, 1, 0)); - - obj.matActive = createMaterial(new pc.Color(1, 1, 1, 1.1)); - - const lines = createLinesModel(app); - obj.line.x = lines[0]; - obj.line.y = lines[1]; - obj.line.z = lines[2]; - obj.line.cull = lines[3]; - - return obj; - }; - - const pickPlane = function (x, y) { - const camera = editor.call('camera:current'); - - const mouseWPos = camera.camera.screenToWorld(x, y, 1); - const posGizmo = gizmo.root.getPosition(); - const rayOrigin = vecA.copy(camera.getPosition()); - const rayDirection = vecB; - const planeNormal = vecC.set(0, 0, 0); - planeNormal[hoverAxis] = 1; - - // rotate plane to local space - quat.copy(startRotation).transformVector(planeNormal, planeNormal); - - // ray from camera - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - rayDirection.copy(mouseWPos).sub(rayOrigin).normalize(); - } else { - rayOrigin.copy(mouseWPos); - camera.getWorldTransform().transformVector(vecD.set(0, 0, -1), rayDirection); - } - - // pick the plane - const rayPlaneDot = planeNormal.dot(rayDirection); - const planeDist = posGizmo.dot(planeNormal); - const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; - const pickedPos = rayDirection.scale(-pointPlaneDist).add(rayOrigin).sub(posGizmo); - - // rotate vector to world space - quat.invert().transformVector(pickedPos, pickedPos); - - let angle = 0; - if (hoverAxis === 'x') { - angle = Math.atan2(pickedPos.z, pickedPos.y) / (Math.PI / 180); - } else if (hoverAxis === 'y') { - angle = Math.atan2(pickedPos.x, pickedPos.z) / (Math.PI / 180); - } else if (hoverAxis === 'z') { - angle = Math.atan2(pickedPos.y, pickedPos.x) / (Math.PI / 180); - } - - return { - angle: angle, - point: pickedPos - }; - }; - - const onTapStart = function (tap) { - if (moving || tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', false); - - moving = true; - mouseTap = tap; - mouseTapMoved = true; - - if (gizmo.root.enabled) { - startRotation.copy(gizmo.root.getRotation()); - const data = pickPlane(tap.x, tap.y); - angleStart = data.angle; - } - - editor.emit('gizmo:rotate:start', hoverAxis); - editor.call('viewport:pick:state', false); - }; - - const onTapMove = function (tap) { - if (!moving) { - return; - } - - mouseTap = tap; - mouseTapMoved = true; - }; - - const onTapEnd = function (tap) { - if (tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', true); - - if (!moving) { - return; - } - - moving = false; - mouseTap = tap; - - editor.emit('gizmo:rotate:end'); - editor.call('viewport:pick:state', true); - }; - - let snap = false; - let snapIncrement = 5; - editor.on('gizmo:snap', (state, increment) => { - snap = state; - snapIncrement = increment * 5; - }); - - // enable/disable gizmo - editor.method('gizmo:rotate:toggle', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = state && editor.call('permissions:write'); - enabled = state; - - visible = true; - - editor.call('viewport:render'); - }); - - editor.on('permissions:writeState', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = enabled && state; - editor.call('viewport:render'); - }); - - // show/hide gizmo - editor.method('gizmo:rotate:visible', (state) => { - if (!gizmo) { - return; - } - - visible = state; - - for (let i = 0; i < gizmo.hoverable.length; i++) { - if (!gizmo.hoverable[i].model) { - continue; - } - - gizmo.hoverable[i].model.enabled = state; - } - - editor.call('viewport:render'); - }); - - // position gizmo - editor.method('gizmo:rotate:position', (x, y, z) => { - if (x === undefined) { - return gizmo.root.getPosition(); - } - - gizmo.root.setPosition(x, y, z); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // rotate gizmo - editor.method('gizmo:rotate:rotation', (pitch, yaw, roll) => { - gizmo.root.setEulerAngles(pitch, yaw, roll); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // initialize gizmo - editor.once('viewport:load', (app) => { - gizmo = createEntity(app); - gizmo.root.enabled = false; - app.root.addChild(gizmo.root); - - // on picker hover - editor.on('viewport:pick:hover', (node, picked) => { - const match = gizmo.hoverable.indexOf(node) !== -1; - if (!hover && match) { - // hover - hover = true; - } else if (hover && !match) { - // unhover - hover = false; - } - - if (hover) { - if (node.axis && hoverAxis !== node.axis) { - // set normal material - if (hoverAxis) { - gizmo.line[hoverAxis].material = gizmo.line[hoverAxis].mat; - } - - if (!hoverAxis && !evtTapStart) { - evtTapStart = editor.on('viewport:tap:start', onTapStart); - } - - hoverAxis = node.axis; - - // set active material - gizmo.line[hoverAxis].material = gizmo.matActive; - } - } else { - if (hoverAxis) { - gizmo.line[hoverAxis].material = gizmo.line[hoverAxis].mat; - } - - hoverAxis = ''; - - if (evtTapStart) { - evtTapStart.unbind(); - evtTapStart = null; - } - } - }); - - const lastPoint = new pc.Vec3(); - - // update gizmo - editor.on('viewport:postUpdate', (dt) => { - if (gizmo.root.enabled) { - const camera = editor.call('camera:current'); - const posCamera = camera.getPosition(); - - if (moving && (vecA.copy(posCameraLast).sub(posCamera).length() > 0.01 || mouseTapMoved)) { - const data = pickPlane(mouseTap.x, mouseTap.y); - lastPoint.copy(data.point); - - if (snap) { - data.angle = Math.round((data.angle - angleStart) / snapIncrement) * snapIncrement; - } else { - data.angle -= angleStart; - } - - editor.emit('gizmo:rotate:offset', data.angle, data.point); - - editor.call('viewport:render'); - } - - const posGizmo = gizmo.root.getPosition(); - let scale = 1; - - // scale to screen space - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - const dot = vecA.copy(posGizmo).sub(posCamera).dot(camera.forward); - const denom = 1280 / (2 * Math.tan(camera.camera.fov * pc.math.DEG_TO_RAD / 2)); - scale = Math.max(0.0001, (dot / denom) * 150) * gizmoSize; - } else { - scale = camera.camera.orthoHeight / 3 * gizmoSize; - } - gizmo.root.setLocalScale(scale, scale, scale); - - if (moving && lastPoint) { - vecC.copy(lastPoint).normalize().scale(2 * scale); - quat.copy(startRotation).transformVector(vecC, vecC); - vecC.add(posGizmo); - - const layer = editor.call('gizmo:layers', 'Axis Rotate Gizmo Immediate'); - app.drawLine(posGizmo, vecC, gizmo.colorActive, false, layer); - } - - editor.emit('gizmo:rotate:render', dt); - - posCameraLast.copy(posCamera); - - // calculate viewing angle - vecA - .copy(posCamera) - .sub(posGizmo) - .normalize(); - - // rotate vector by gizmo rotation - quat - .copy(gizmo.root.getRotation()) - .invert() - .transformVector(vecA, vecA); - - // hide plane if viewed from very angle - gizmo.plane.x.model.enabled = Math.abs(vecA.x) > 0.1 && visible; - gizmo.plane.y.model.enabled = Math.abs(vecA.y) > 0.1 && visible; - gizmo.plane.z.model.enabled = Math.abs(vecA.z) > 0.1 && visible; - - const worldTransform = gizmo.root.getWorldTransform(); - - const layer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); - - // draw cull sphere - gizmo.line.cull.node.worldTransform = worldTransform; - app.drawMeshInstance(gizmo.line.cull, layer); - - // render lines - // x - if (moving && hoverAxis === 'x') { - // behind line - app.drawMesh(gizmo.line.x.mesh, gizmo.matBehindActive, worldTransform, layer); - } else { - // behind line - app.drawMesh(gizmo.line.x.mesh, gizmo.matBehindHover.x, worldTransform, layer); - // front line - if (!moving && gizmo.plane.x.model.enabled) { - gizmo.line.x.node.worldTransform = worldTransform; - app.drawMeshInstance(gizmo.line.x, layer); - } - } - - // y - if (moving && hoverAxis === 'y') { - // behind line - app.drawMesh(gizmo.line.y.mesh, gizmo.matBehindActive, worldTransform, layer); - } else { - // behind line - app.drawMesh(gizmo.line.y.mesh, gizmo.matBehindHover.y, worldTransform, layer); - // front line - if (!moving && gizmo.plane.y.model.enabled) { - gizmo.line.y.node.worldTransform = worldTransform; - app.drawMeshInstance(gizmo.line.y, layer); - } - } - // z - if (moving && hoverAxis === 'z') { - // behind line - app.drawMesh(gizmo.line.z.mesh, gizmo.matBehindActive, worldTransform, layer); - } else { - // behind line - app.drawMesh(gizmo.line.z.mesh, gizmo.matBehindHover.z, worldTransform, layer); - // front line - if (!moving && gizmo.plane.z.model.enabled) { - gizmo.line.z.node.worldTransform = worldTransform; - app.drawMeshInstance(gizmo.line.z, layer); - } - } - - - } - - mouseTapMoved = false; - }); - - editor.on('viewport:mouse:move', onTapMove); - editor.on('viewport:tap:end', onTapEnd); - }); -}); diff --git a/src/editor/viewport/gizmo/gizmo-scale.ts b/src/editor/viewport/gizmo/gizmo-scale.ts deleted file mode 100644 index e7d8ceb2b..000000000 --- a/src/editor/viewport/gizmo/gizmo-scale.ts +++ /dev/null @@ -1,534 +0,0 @@ -import { GIZMO_MASK } from '../../../core/constants.ts'; -import { createColorMaterial } from '../viewport-color-material.ts'; - -editor.once('load', () => { - let gizmo = null; - let visible = true; - let moving = false; - let mouseTap = null; - let enabled = false; - let hover = false; - let hoverAxis = ''; - let hoverMiddle = false; - const gizmoSize = 0.4; - const vecA = new pc.Vec3(); - const vecB = new pc.Vec3(); - const vecC = new pc.Vec3(); - const vecD = new pc.Vec3(); - const vecE = new pc.Vec3(); - const quat = new pc.Quat(); - let evtTapStart; - const pickStart = new pc.Vec3(); - - const createMaterial = function (color) { - const mat = createColorMaterial(); - mat.color = color; - if (color.a !== 1) { - mat.blendState = new pc.BlendState(true, pc.BLENDEQUATION_ADD, pc.BLENDMODE_SRC_ALPHA, pc.BLENDMODE_ONE_MINUS_SRC_ALPHA); - } - mat.cull = pc.CULLFACE_NONE; - mat.update(); - return mat; - }; - - const createEntity = function () { - const boxSize = 0.4; - - const obj = { - root: null, - middle: null, - line: { - x: null, - y: null, - z: null - }, - box: { - x: null, - y: null, - z: null - }, - hoverable: [], - matActive: null, - matActiveTransparent: null - }; - - // active mat - obj.matActive = createMaterial(new pc.Color(1, 1, 1, 0.9)); // this has to be transparent otherwise it flickers when you hover over it - obj.matActiveTransparent = createMaterial(new pc.Color(1, 1, 1, 0.25)); - obj.colorLineBehind = new pc.Color(1, 1, 1, 0.05); - obj.colorLine = new pc.Color(1, 1, 1, 0.2); - obj.colorLineActive = new pc.Color(1, 1, 1, 1); - - const layer = editor.call('gizmo:layers', 'Axis Gizmo').id; - - // root entity - const entity = obj.root = new pc.Entity(); - - // middle - const middle = obj.middle = new pc.Entity(); - obj.hoverable.push(middle); - middle.axis = 'xyz'; - middle.middle = true; - middle.addComponent('model', { - type: 'box', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - middle.model.model.meshInstances[0].mask = GIZMO_MASK; - middle.model.material.id = 0xFFFFFFFF; - entity.addChild(middle); - middle.setLocalScale(boxSize * 1.5, boxSize * 1.5, boxSize * 1.5); - middle.mat = middle.model.material = createMaterial(new pc.Color(1.0, 1.0, 1.0, 0.25)); - middle.mat.depthTest = false; - - // line x - const lineX = obj.line.x = new pc.Entity(); - obj.hoverable.push(lineX); - lineX.axis = 'x'; - lineX.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - entity.addChild(lineX); - lineX.setLocalEulerAngles(90, 90, 0); - lineX.setLocalPosition(1.25, 0, 0); - lineX.setLocalScale(boxSize, 1.5, boxSize); - lineX.mat = lineX.model.material = createMaterial(new pc.Color(1, 0, 0, 0)); - - // line y - const lineY = obj.line.y = new pc.Entity(); - obj.hoverable.push(lineY); - lineY.axis = 'y'; - lineY.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - entity.addChild(lineY); - lineY.setLocalEulerAngles(0, 0, 0); - lineY.setLocalPosition(0, 1.25, 0); - lineY.setLocalScale(boxSize, 1.5, boxSize); - lineY.mat = lineY.model.material = createMaterial(new pc.Color(0, 1, 0, 0)); - - // line z - const lineZ = obj.line.z = new pc.Entity(); - obj.hoverable.push(lineZ); - lineZ.axis = 'z'; - lineZ.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - entity.addChild(lineZ); - lineZ.setLocalEulerAngles(90, 0, 0); - lineZ.setLocalPosition(0, 0, 1.25); - lineZ.setLocalScale(boxSize, 1.5, boxSize); - lineZ.mat = lineZ.model.material = createMaterial(new pc.Color(0, 0, 1, 0)); - - // box x - const boxX = obj.box.x = new pc.Entity(); - obj.hoverable.push(boxX); - boxX.axis = 'x'; - boxX.addComponent('model', { - type: 'box', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - boxX.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(boxX); - boxX.setLocalPosition(2.2, 0, 0); - boxX.setLocalScale(boxSize, boxSize, boxSize); - boxX.mat = boxX.model.material = createMaterial(new pc.Color(1, 0, 0, 1.1)); - boxX.color = new pc.Color(1, 0, 0, 1); - - // box y - const boxY = obj.box.y = new pc.Entity(); - obj.hoverable.push(boxY); - boxY.axis = 'y'; - boxY.addComponent('model', { - type: 'box', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - boxY.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(boxY); - boxY.setLocalPosition(0, 2.2, 0); - boxY.setLocalScale(boxSize, boxSize, boxSize); - boxY.mat = boxY.model.material = createMaterial(new pc.Color(0, 1, 0, 1.1)); - boxY.color = new pc.Color(0, 1, 0, 1); - - // box z - const boxZ = obj.box.z = new pc.Entity(); - obj.hoverable.push(boxZ); - boxZ.axis = 'z'; - boxZ.addComponent('model', { - type: 'box', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [layer] - }); - boxZ.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(boxZ); - boxZ.setLocalPosition(0, 0, 2.2); - boxZ.setLocalScale(boxSize, boxSize, boxSize); - boxZ.mat = boxZ.model.material = createMaterial(new pc.Color(0, 0, 1, 1.1)); - boxZ.color = new pc.Color(0, 0, 1, 1); - - return obj; - }; - - const pickPlane = function (x, y) { - const camera = editor.call('camera:current'); - let scale = 1; - const mouseWPos = camera.camera.screenToWorld(x, y, 1); - const posGizmo = gizmo.root.getPosition(); - const rayOrigin = vecA.copy(camera.getPosition()); - const rayDirection = vecB; - const planeNormal = vecC; - - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - rayDirection.copy(mouseWPos).sub(rayOrigin).normalize(); - const dot = vecC.copy(posGizmo).sub(rayOrigin).dot(camera.forward); - const denom = 1280 * Math.tan(camera.camera.fov * pc.math.DEG_TO_RAD); - scale = Math.max(0.0001, (dot / denom) * 150) * gizmoSize; - } else { - rayOrigin.add(mouseWPos); - camera.getWorldTransform().transformVector(vecD.set(0, 0, -1), rayDirection); - scale = camera.camera.orthoHeight / 3 * gizmoSize; - } - - quat.copy(gizmo.root.getRotation()); - - // single axis - if (!hoverMiddle) { - // vector based on selected axis - planeNormal.set(0, 0, 0); - planeNormal[hoverAxis] = 1; - // rotate vector by gizmo rotation - quat.transformVector(planeNormal, planeNormal); - - vecE.copy(rayOrigin).sub(posGizmo).normalize(); - planeNormal.copy(vecE.sub(planeNormal.scale(planeNormal.dot(vecE))).normalize()); - } else { - planeNormal.copy(rayOrigin).sub(posGizmo).normalize(); - } - - const rayPlaneDot = planeNormal.dot(rayDirection); - const planeDist = posGizmo.dot(planeNormal); - const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; - const pickedPos = rayDirection.scale(-pointPlaneDist).add(rayOrigin); - - if (!hoverMiddle) { - // single axis - planeNormal.set(0, 0, 0); - planeNormal[hoverAxis] = 1; - quat.transformVector(planeNormal, planeNormal); - pickedPos.copy(planeNormal.scale(planeNormal.dot(pickedPos))); - quat.invert().transformVector(pickedPos, pickedPos); - - // calculate viewing angle - vecE.copy(rayOrigin).sub(posGizmo).normalize(); - quat.transformVector(vecE, vecE); - - const v = pickedPos[hoverAxis]; - pickedPos.set(0, 0, 0); - pickedPos[hoverAxis] = v / scale; - } else { - vecE.copy(pickedPos).sub(posGizmo).normalize(); - vecD.copy(camera.up).add(camera.right).normalize(); - - const v = (pickedPos.sub(posGizmo).length() / scale / 2) * vecE.dot(vecD); - pickedPos.set(v, v, v); - } - - return pickedPos; - }; - - const onTapStart = function (tap) { - if (tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', false); - - moving = true; - mouseTap = tap; - - if (gizmo.root.enabled) { - pickStart.copy(pickPlane(tap.x, tap.y)); - pickStart.x -= 1; - pickStart.y -= 1; - pickStart.z -= 1; - } - - editor.emit('gizmo:scale:start', hoverAxis, hoverMiddle); - editor.call('gizmo:scale:visible', false); - editor.call('viewport:pick:state', false); - }; - - const onTapMove = function (tap) { - if (!moving) { - return; - } - - mouseTap = tap; - }; - - const onTapEnd = function (tap) { - if (tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', true); - - if (!moving) { - return; - } - - moving = false; - mouseTap = tap; - - editor.emit('gizmo:scale:end'); - editor.call('gizmo:scale:visible', true); - editor.call('viewport:pick:state', true); - }; - - let snap = false; - let snapIncrement = 1; - editor.on('gizmo:snap', (state, increment) => { - snap = state; - snapIncrement = increment; - }); - - // enable/disable gizmo - editor.method('gizmo:scale:toggle', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = state && editor.call('permissions:write'); - enabled = state; - - visible = true; - }); - - editor.on('permissions:writeState', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = enabled && state; - editor.call('viewport:render'); - }); - - // show/hide gizmo - editor.method('gizmo:scale:visible', (state) => { - if (!gizmo) { - return; - } - - visible = state; - - for (let i = 0; i < gizmo.hoverable.length; i++) { - if (!gizmo.hoverable[i].model) { - continue; - } - - gizmo.hoverable[i].model.enabled = state; - } - - editor.call('viewport:render'); - }); - - // position gizmo - editor.method('gizmo:scale:position', (x, y, z) => { - gizmo.root.setPosition(x, y, z); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // rotate gizmo - editor.method('gizmo:scale:rotation', (pitch, yaw, roll) => { - gizmo.root.setEulerAngles(pitch, yaw, roll); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // initialize gizmo - editor.once('viewport:load', (app) => { - gizmo = createEntity(); - gizmo.root.enabled = false; - app.root.addChild(gizmo.root); - - // on picker hover - editor.on('viewport:pick:hover', (node, picked) => { - const match = gizmo.hoverable.indexOf(node) !== -1; - if (!hover && match) { - // hover - hover = true; - } else if (hover && !match) { - // unhover - hover = false; - } - - if (hover) { - if (node.axis && hoverAxis !== node.axis) { - // set normal material - if (hoverAxis) { - if (hoverMiddle) { - gizmo.box.x.model.material = gizmo.box.x.mat; - gizmo.box.y.model.material = gizmo.box.y.mat; - gizmo.box.z.model.material = gizmo.box.z.mat; - } else { - gizmo.box[hoverAxis].model.material = gizmo.box[hoverAxis].mat; - } - } - - if (!hoverAxis && !evtTapStart) { - evtTapStart = editor.on('viewport:tap:start', onTapStart); - } - - hoverAxis = node.axis; - hoverMiddle = node.middle; - - // set active material - if (hoverMiddle) { - gizmo.box.x.model.material = gizmo.matActive; - gizmo.box.y.model.material = gizmo.matActive; - gizmo.box.z.model.material = gizmo.matActive; - } else { - gizmo.box[hoverAxis].model.material = gizmo.matActive; - } - } - } else { - if (hoverAxis) { - if (hoverMiddle) { - gizmo.box.x.model.material = gizmo.box.x.mat; - gizmo.box.y.model.material = gizmo.box.y.mat; - gizmo.box.z.model.material = gizmo.box.z.mat; - } else { - gizmo.box[hoverAxis].model.material = gizmo.box[hoverAxis].mat; - } - } - - hoverAxis = ''; - - if (evtTapStart) { - evtTapStart.unbind(); - evtTapStart = null; - } - } - }); - - // update gizmo - editor.on('viewport:postUpdate', (dt) => { - if (gizmo.root.enabled) { - editor.emit('gizmo:scale:render', dt); - - if (moving) { - const point = pickPlane(mouseTap.x, mouseTap.y); - if (point) { - point.sub(pickStart); - if (snap) { - point.scale(1 / snapIncrement); - point.x = Math.round(point.x); - point.y = Math.round(point.y); - point.z = Math.round(point.z); - point.scale(snapIncrement); - } - editor.emit('gizmo:scale:offset', point.x, point.y, point.z); - } - - editor.call('viewport:render'); - } - - const camera = editor.call('camera:current'); - - const posCamera = camera.getPosition(); - const posGizmo = gizmo.root.getPosition(); - let scale = 1; - - // scale to screen space - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - const dot = vecA.copy(posGizmo).sub(posCamera).dot(camera.forward); - const denom = 1280 / (2 * Math.tan(camera.camera.fov * pc.math.DEG_TO_RAD / 2)); - scale = Math.max(0.0001, (dot / denom) * 150) * gizmoSize; - } else { - scale = camera.camera.orthoHeight / 3 * gizmoSize; - } - gizmo.root.setLocalScale(scale, scale, scale); - - // calculate viewing angle - vecA - .copy(posCamera) - .sub(posGizmo) - .normalize(); - - // rotate vector by gizmo rotation - quat - .copy(gizmo.root.getRotation()) - .invert() - .transformVector(vecA, vecA); - - quat.invert(); - - // hide lines and boxes if viewed from very angle - gizmo.line.x.model.enabled = gizmo.box.x.model.enabled = !(Math.abs(vecA.z) <= 0.15 && Math.abs(vecA.y) <= 0.15) && visible; - gizmo.line.y.model.enabled = gizmo.box.y.model.enabled = !(Math.abs(vecA.x) <= 0.15 && Math.abs(vecA.z) <= 0.15) && visible; - gizmo.line.z.model.enabled = gizmo.box.z.model.enabled = !(Math.abs(vecA.x) <= 0.15 && Math.abs(vecA.y) <= 0.15) && visible; - - const layer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); - - // draw axes lines - // line x - if (gizmo.line.x.model.enabled) { - vecB.set(scale * 0.5, 0, 0); - quat.transformVector(vecB, vecB).add(posGizmo); - vecC.set(scale * 2, 0, 0); - quat.transformVector(vecC, vecC).add(posGizmo); - const color = gizmo.box.x.model.material === gizmo.matActive ? gizmo.matActive.color : gizmo.box.x.color; - app.drawLine(vecB, vecC, color, true, layer); - } - // line y - if (gizmo.line.y.model.enabled) { - vecB.set(0, scale * 0.5, 0); - quat.transformVector(vecB, vecB).add(posGizmo); - vecC.set(0, scale * 2, 0); - quat.transformVector(vecC, vecC).add(posGizmo); - const color = gizmo.box.y.model.material === gizmo.matActive ? gizmo.matActive.color : gizmo.box.y.color; - app.drawLine(vecB, vecC, color, true, layer); - } - // line z - if (gizmo.line.z.model.enabled) { - vecB.set(0, 0, scale * 0.5); - quat.transformVector(vecB, vecB).add(posGizmo); - vecC.set(0, 0, scale * 2); - quat.transformVector(vecC, vecC).add(posGizmo); - const color = gizmo.box.z.model.material === gizmo.matActive ? gizmo.matActive.color : gizmo.box.z.color; - app.drawLine(vecB, vecC, color, true, layer); - } - } - }); - - editor.on('viewport:mouse:move', onTapMove); - editor.on('viewport:tap:end', onTapEnd); - }); -}); diff --git a/src/editor/viewport/gizmo/gizmo-transform.ts b/src/editor/viewport/gizmo/gizmo-transform.ts new file mode 100644 index 000000000..5958ea85f --- /dev/null +++ b/src/editor/viewport/gizmo/gizmo-transform.ts @@ -0,0 +1,292 @@ +import { type EntityObserver } from '@playcanvas/editor-api'; +import { + type Entity, + type MeshInstance, + type TransformGizmo, + type TranslateGizmo, + type RotateGizmo, + type ScaleGizmo, + type Layer +} from 'playcanvas'; + +const GIZMO_SIZE = 1.2; +const GIZMO_ANGLE_MULT = 5; + +let translate: TranslateGizmo | null = null; +let rotate: RotateGizmo | null = null; +let scale: ScaleGizmo | null = null; + +let write: boolean = false; + +type GizmoNodeTransform = { + position: number[]; + rotation: number[]; + scale: number[]; +}; + +const selection = (): EntityObserver[] => { + const type = editor.call('selector:type'); + if (type !== 'entity') { + return []; + } + return editor.call('selector:items'); +}; + +const getTRS = (item: EntityObserver): GizmoNodeTransform => { + const position: number[] = item.entity.getLocalPosition().toArray(); + const rotation: number[] = item.entity.getLocalEulerAngles().toArray(); + const scale: number[] = item.entity.getLocalScale().toArray(); + item.set('position', position); + item.set('rotation', rotation); + item.set('scale', scale); + return { position, rotation, scale }; +}; +const setTRS = (item: EntityObserver, trs: GizmoNodeTransform, history: boolean = true) => { + const historyEnabled = item.history.enabled; + item.history.enabled = history; + item.set('position', trs.position); + item.set('rotation', trs.rotation); + item.set('scale', trs.scale); + item.history.enabled = historyEnabled; +}; + +const initGizmo = (gizmo: T) => { + // general settings + gizmo.size = GIZMO_SIZE; + gizmo.setTheme({ + shapeBase: { + x: pc.Color.RED, + y: pc.Color.GREEN, + z: pc.Color.BLUE, + xyz: pc.Color.WHITE, + f: pc.Color.YELLOW + }, + shapeHover: { + x: pc.Color.WHITE, + y: pc.Color.WHITE, + z: pc.Color.WHITE, + xyz: pc.Color.WHITE, + f: pc.Color.WHITE + }, + guideBase: { + x: pc.Color.WHITE, + y: pc.Color.WHITE, + z: pc.Color.WHITE, + f: pc.Color.WHITE + }, + guideOcclusion: 0.9, + disabled: new pc.Color(0, 0, 0, 0) + }); + + // gizmo specific settings + if (gizmo instanceof pc.TranslateGizmo) { + gizmo.flipAxes = false; + gizmo.dragMode = 'hide'; + gizmo.axisLineThickness = 0.01; + gizmo.axisPlaneGap = 0; + + gizmo.enableShape('xyz', false); // TODO: hide center sphere for now + } + if (gizmo instanceof pc.RotateGizmo) { + gizmo.dragMode = 'selected'; + gizmo.orbitRotation = true; + gizmo.faceTubeRadius = 0.0075; + gizmo.xyzTubeRadius = 0.0075; + gizmo.angleGuideThickness = 0.015; + + gizmo.enableShape('xyz', false); // TODO: hide ball rotation for now + } + if (gizmo instanceof pc.ScaleGizmo) { + gizmo.flipAxes = false; + gizmo.dragMode = 'hide'; + gizmo.axisLineThickness = 0.01; + + gizmo.enableShape('xy', false); // TODO: disable planes as scaling unintuitive right now + gizmo.enableShape('xz', false); // TODO: disable planes as scaling unintuitive right now + gizmo.enableShape('yz', false); // TODO: disable planes as scaling unintuitive right now + } + + // call viewport render when gizmo fires update + gizmo.on(pc.Gizmo.EVENT_RENDERUPDATE, () => { + editor.call('viewport:render'); + }); + + // track hover state + let hovering = false; + gizmo.on(pc.Gizmo.EVENT_POINTERMOVE, (_x: number, _y: number, meshInstance: MeshInstance) => { + if (hovering === !!meshInstance) { + return; + } + hovering = !!meshInstance; + editor.emit('gizmo:transform:hover', hovering); + }); + + // track history + const cache: GizmoNodeTransform[] = []; + gizmo.on(pc.TransformGizmo.EVENT_TRANSFORMSTART, () => { + const items = selection(); + if (!items.length) { + return; + } + for (let i = 0; i < items.length; i++) { + cache[i] = getTRS(items[i]); + items[i].history.enabled = false; + } + + editor.call('camera:toggle', false); + editor.call('viewport:pick:state', false); + }); + gizmo.on(pc.TransformGizmo.EVENT_TRANSFORMMOVE, () => { + const items = selection(); + if (!items.length) { + return; + } + for (let i = 0; i < items.length; i++) { + getTRS(items[i]); + } + }); + gizmo.on(pc.TransformGizmo.EVENT_TRANSFORMEND, () => { + const items = selection(); + if (!items.length) { + return; + } + + // record discrete action + const action: [GizmoNodeTransform, GizmoNodeTransform][] = []; + for (let i = 0; i < items.length; i++) { + action.push([cache[i], getTRS(items[i])]); + items[i].history.enabled = true; + } + cache.length = 0; + + // add discrete action to history + editor.api.globals.history.add({ + name: 'entities.translate', + combine: false, + undo: () => { + for (let i = 0; i < items.length; i++) { + setTRS(items[i].latest() as EntityObserver, action[i][0], false); + } + }, + redo: () => { + for (let i = 0; i < items.length; i++) { + setTRS(items[i].latest() as EntityObserver, action[i][1], false); + } + } + }); + + editor.call('camera:toggle', true); + editor.call('viewport:pick:state', true); + }); + + // manually call prerender and update methods + editor.on('viewport:preRender', gizmo.prerender.bind(gizmo)); + editor.on('viewport:update', gizmo.update.bind(gizmo)); + + return gizmo; +}; + +editor.on('scene:load', () => { + const camera: Entity = editor.call('camera:current'); + const layer: Layer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); + + translate = initGizmo(new pc.TranslateGizmo(camera.camera, layer)); + rotate = initGizmo(new pc.RotateGizmo(camera.camera, layer)); + scale = initGizmo(new pc.ScaleGizmo(camera.camera, layer)); +}); + +editor.on('permissions:writeState', (state) => { + write = state; +}); + +editor.on('camera:change', (camera: Entity) => { + if (!translate || !rotate || !scale) { + return; + } + translate.camera = camera.camera; + rotate.camera = camera.camera; + scale.camera = camera.camera; +}); + +editor.on('gizmo:coordSystem', (space: 'local' | 'world') => { + if (!translate || !rotate || !scale) { + return; + } + translate.coordSpace = space; + rotate.coordSpace = space; + scale.coordSpace = space; +}); + +editor.on('gizmo:snap', (state: boolean, increment: number) => { + translate.snap = state; + translate.snapIncrement = increment; + rotate.snap = state; + rotate.snapIncrement = increment * GIZMO_ANGLE_MULT; + scale.snap = state; + scale.snapIncrement = increment; +}); + +const reflow = () => { + if (!translate || !rotate || !scale) { + return; + } + + // skip if no write permissions + if (!write) { + return; + } + + // skip if not selecting entities (can be assets) + const selectorType: string = editor.call('selector:type'); + if (selectorType !== 'entity') { + translate.detach(); + rotate.detach(); + scale.detach(); + return; + } + + const gizmoType: string = editor.call('gizmo:type'); + const items = editor.call('selector:items').map(item => item.entity); + switch (gizmoType) { + case 'translate': { + translate.attach(items); + rotate.detach(); + scale.detach(); + break; + } + case 'rotate': { + translate.detach(); + rotate.attach(items); + scale.detach(); + break; + } + case 'scale': { + translate.detach(); + rotate.detach(); + scale.attach(items); + break; + } + default: { + translate.detach(); + rotate.detach(); + scale.detach(); + break; + } + } +}; +editor.on('selector:change', reflow); +editor.on('gizmo:type', reflow); +editor.on('gizmo:translate:sync', reflow); +editor.on('gizmo:rotate:sync', reflow); +editor.on('gizmo:scale:sync', reflow); + +const enable = (state: boolean = true) => { + if (!translate || !rotate || !scale) { + return; + } + const enabled = write && state; + translate.enabled = enabled; + rotate.enabled = enabled; + scale.enabled = enabled; +}; +editor.on('gizmo:transform:visible', enable); diff --git a/src/editor/viewport/gizmo/gizmo-translate.ts b/src/editor/viewport/gizmo/gizmo-translate.ts deleted file mode 100644 index 70d31cb5f..000000000 --- a/src/editor/viewport/gizmo/gizmo-translate.ts +++ /dev/null @@ -1,620 +0,0 @@ -import { GIZMO_MASK } from '../../../core/constants.ts'; -import { createColorMaterial } from '../viewport-color-material.ts'; - -editor.once('load', () => { - let gizmo = null; - let visible = true; - let moving = false; - let mouseTap = null; - let mouseTapMoved = false; - const posCameraLast = new pc.Vec3(); - let enabled = false; - let hover = false; - let hoverAxis = ''; - let hoverPlane = false; - const gizmoSize = 0.4; - const arrowRadius = 0.4; - const vecA = new pc.Vec3(); - const vecB = new pc.Vec3(); - const vecC = new pc.Vec3(); - const vecD = new pc.Vec3(); - const quat = new pc.Quat(); - let evtTapStart; - const pickStart = new pc.Vec3(); - - const createMaterial = function (color) { - const mat = createColorMaterial(); - mat.color = color; - if (color.a !== 1) { - mat.blendState = new pc.BlendState(true, pc.BLENDEQUATION_ADD, pc.BLENDMODE_SRC_ALPHA, pc.BLENDMODE_ONE_MINUS_SRC_ALPHA); - } - mat.update(); - return mat; - }; - - const createEntity = function () { - const obj = { - root: null, - plane: { - x: null, - y: null, - z: null - }, - line: { - x: null, - y: null, - z: null - }, - arrow: { - x: null, - y: null, - z: null - }, - hoverable: [], - matActive: null, - matActiveTransparent: null - }; - - // active mat - obj.matActive = createMaterial(new pc.Color(1, 1, 1, 1)); - obj.matActiveTransparent = createMaterial(new pc.Color(1, 1, 1, 0.25)); - obj.matActiveTransparent.cull = pc.CULLFACE_NONE; - - // root entity - const entity = obj.root = new pc.Entity(); - - const gizmoLayer = editor.call('gizmo:layers', 'Axis Gizmo').id; - - // plane x - const planeX = obj.plane.x = new pc.Entity(); - planeX.name = 'planeX'; - obj.hoverable.push(planeX); - planeX.axis = 'x'; - planeX.plane = true; - planeX.addComponent('model', { - type: 'plane', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - planeX.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(planeX); - planeX.setLocalEulerAngles(90, -90, 0); - planeX.setLocalScale(0.8, 0.8, 0.8); - planeX.setLocalPosition(0, 0.4, 0.4); - planeX.mat = planeX.model.material = createMaterial(new pc.Color(1, 0, 0, 0.25)); - planeX.mat.cull = pc.CULLFACE_NONE; - - // plane y - const planeY = obj.plane.y = new pc.Entity(); - planeY.name = 'planeY'; - obj.hoverable.push(planeY); - planeY.axis = 'y'; - planeY.plane = true; - planeY.addComponent('model', { - type: 'plane', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - planeY.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(planeY); - planeY.setLocalEulerAngles(0, 0, 0); - planeY.setLocalScale(0.8, 0.8, 0.8); - planeY.setLocalPosition(-0.4, 0, 0.4); - planeY.mat = planeY.model.material = createMaterial(new pc.Color(0, 1, 0, 0.25)); - planeY.mat.cull = pc.CULLFACE_NONE; - - // plane z - const planeZ = obj.plane.z = new pc.Entity(); - planeZ.name = 'planeZ'; - obj.hoverable.push(planeZ); - planeZ.axis = 'z'; - planeZ.plane = true; - planeZ.addComponent('model', { - type: 'plane', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - planeZ.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(planeZ); - planeZ.setLocalEulerAngles(90, 0, 0); - planeZ.setLocalScale(0.8, 0.8, 0.8); - planeZ.setLocalPosition(-0.4, 0.4, 0); - planeZ.mat = planeZ.model.material = createMaterial(new pc.Color(0, 0, 1, 0.25)); - planeZ.mat.cull = pc.CULLFACE_NONE; - - // line x - const lineX = obj.line.x = new pc.Entity(); - lineX.name = 'lineX'; - obj.hoverable.push(lineX); - lineX.axis = 'x'; - lineX.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - lineX.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(lineX); - lineX.setLocalEulerAngles(90, 90, 0); - lineX.setLocalPosition(1.6, 0, 0); - lineX.setLocalScale(arrowRadius, 0.8, arrowRadius); - lineX.mat = lineX.model.material = createMaterial(new pc.Color(1, 0, 0, 0)); - - // line y - const lineY = obj.line.y = new pc.Entity(); - lineY.name = 'lineY'; - obj.hoverable.push(lineY); - lineY.axis = 'y'; - lineY.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - lineY.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(lineY); - lineY.setLocalEulerAngles(0, 0, 0); - lineY.setLocalPosition(0, 1.6, 0); - lineY.setLocalScale(arrowRadius, 0.8, arrowRadius); - lineY.mat = lineY.model.material = createMaterial(new pc.Color(0, 1, 0, 0)); - - // line z - const lineZ = obj.line.z = new pc.Entity(); - lineZ.name = 'lineZ'; - obj.hoverable.push(lineZ); - lineZ.axis = 'z'; - lineZ.addComponent('model', { - type: 'cylinder', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - lineZ.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(lineZ); - lineZ.setLocalEulerAngles(90, 0, 0); - lineZ.setLocalPosition(0, 0, 1.6); - lineZ.setLocalScale(arrowRadius, 0.8, arrowRadius); - lineZ.mat = lineZ.model.material = createMaterial(new pc.Color(0, 0, 1, 0)); - - // arrow x - const arrowX = obj.arrow.x = new pc.Entity(); - arrowX.name = 'arrowX'; - obj.hoverable.push(arrowX); - arrowX.axis = 'x'; - arrowX.addComponent('model', { - type: 'cone', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - arrowX.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(arrowX); - arrowX.setLocalEulerAngles(90, 90, 0); - arrowX.setLocalPosition(2.3, 0, 0); - arrowX.setLocalScale(arrowRadius, 0.6, arrowRadius); - arrowX.mat = arrowX.model.material = createMaterial(new pc.Color(1, 0, 0, 1)); - - // arrow y - const arrowY = obj.arrow.y = new pc.Entity(); - arrowY.name = 'arrowY'; - obj.hoverable.push(arrowY); - arrowY.axis = 'y'; - arrowY.addComponent('model', { - type: 'cone', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - arrowY.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(arrowY); - arrowY.setLocalEulerAngles(0, 0, 0); - arrowY.setLocalPosition(0, 2.3, 0); - arrowY.setLocalScale(arrowRadius, 0.6, arrowRadius); - arrowY.mat = arrowY.model.material = createMaterial(new pc.Color(0, 1, 0, 1)); - - // arrow z - const arrowZ = obj.arrow.z = new pc.Entity(); - arrowZ.name = 'arrowZ'; - obj.hoverable.push(arrowZ); - arrowZ.axis = 'z'; - arrowZ.addComponent('model', { - type: 'cone', - castShadows: false, - receiveShadows: false, - castShadowsLightmap: false, - layers: [gizmoLayer] - }); - arrowZ.model.model.meshInstances[0].mask = GIZMO_MASK; - entity.addChild(arrowZ); - arrowZ.setLocalEulerAngles(90, 0, 0); - arrowZ.setLocalPosition(0, 0, 2.3); - arrowZ.setLocalScale(arrowRadius, 0.6, arrowRadius); - arrowZ.mat = arrowZ.model.material = createMaterial(new pc.Color(0, 0, 1, 1)); - - return obj; - }; - - let snap = false; - let snapIncrement = 1; - editor.on('gizmo:snap', (state, increment) => { - snap = state; - snapIncrement = increment; - }); - - // enable/disable gizmo - editor.method('gizmo:translate:toggle', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = state && editor.call('permissions:write'); - enabled = state; - - visible = true; - }); - - editor.on('permissions:writeState', (state) => { - if (!gizmo) { - return; - } - - gizmo.root.enabled = enabled && state; - editor.call('viewport:render'); - }); - - // show/hide gizmo - editor.method('gizmo:translate:visible', (state) => { - if (!gizmo) { - return; - } - - visible = state; - - for (let i = 0; i < gizmo.hoverable.length; i++) { - if (!gizmo.hoverable[i].model) { - continue; - } - - gizmo.hoverable[i].model.enabled = state; - } - - editor.call('viewport:render'); - }); - - // position gizmo - editor.method('gizmo:translate:position', (x, y, z) => { - if (x === undefined) { - return gizmo.root.getPosition(); - } - - gizmo.root.setPosition(x, y, z); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // rotate gizmo - editor.method('gizmo:translate:rotation', (pitch, yaw, roll) => { - gizmo.root.setEulerAngles(pitch, yaw, roll); - - if (gizmo.root.enabled) { - editor.call('viewport:render'); - } - }); - - // initialize gizmo - editor.once('viewport:load', (app) => { - gizmo = createEntity(); - gizmo.root.enabled = false; - app.root.addChild(gizmo.root); - - const pickPlane = function (x, y) { - const camera = editor.call('camera:current'); - - const mouseWPos = camera.camera.screenToWorld(x, y, 1); - const posGizmo = gizmo.root.getPosition(); - const rayOrigin = vecA.copy(camera.getPosition()); - const rayDirection = vecB.set(0, 0, 0); - const planeNormal = vecC.set(0, 0, 0); - planeNormal[hoverAxis] = 1; - - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - rayDirection.copy(mouseWPos).sub(rayOrigin).normalize(); - } else { - rayOrigin.add(mouseWPos); - camera.getWorldTransform().transformVector(vecD.set(0, 0, -1), rayDirection); - } - - // rotate vector by gizmo rotation - quat.copy(gizmo.root.getRotation()).transformVector(planeNormal, planeNormal); - - // single axis - if (!hoverPlane) { - vecD.copy(rayOrigin).sub(posGizmo).normalize(); - planeNormal.copy(vecD.sub(planeNormal.scale(planeNormal.dot(vecD))).normalize()); - } - - const rayPlaneDot = planeNormal.dot(rayDirection); - const planeDist = posGizmo.dot(planeNormal); - const pointPlaneDist = (planeNormal.dot(rayOrigin) - planeDist) / rayPlaneDot; - const pickedPos = rayDirection.scale(-pointPlaneDist).add(rayOrigin); - - if (!hoverPlane) { - // single axis - planeNormal.set(0, 0, 0); - planeNormal[hoverAxis] = 1; - quat.transformVector(planeNormal, planeNormal); - pickedPos.copy(planeNormal.scale(planeNormal.dot(pickedPos))); - } - - quat.invert().transformVector(pickedPos, pickedPos); - - if (!hoverPlane) { - const v = pickedPos[hoverAxis]; - pickedPos.set(0, 0, 0); - pickedPos[hoverAxis] = v; - } - - return pickedPos; - }; - - const onTapStart = function (tap) { - if (moving || tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', false); - editor.call('viewport:pick:state', false); - - moving = true; - mouseTap = tap; - - if (gizmo.root.enabled) { - pickStart.copy(pickPlane(tap.x, tap.y)); - } - - editor.emit('gizmo:translate:start', hoverAxis, hoverPlane); - editor.call('gizmo:translate:visible', false); - }; - - const onTapMove = function (tap) { - if (!moving) { - return; - } - - mouseTap = tap; - mouseTapMoved = true; - }; - - const onTapEnd = function (tap) { - if (tap.button !== 0) { - return; - } - - editor.emit('camera:toggle', true); - - if (!moving) { - return; - } - - moving = false; - mouseTap = tap; - - editor.emit('gizmo:translate:end'); - editor.call('gizmo:translate:visible', true); - editor.call('viewport:pick:state', true); - }; - - // on picker hover - editor.on('viewport:pick:hover', (node, picked) => { - const match = gizmo.hoverable.indexOf(node) !== -1; - if (!hover && match) { - // hover - hover = true; - } else if (hover && !match) { - // unhover - hover = false; - } - - if (hover) { - if (node.axis && (hoverAxis !== node.axis || hoverPlane !== node.plane)) { - // set normal material - if (hoverAxis) { - if (hoverPlane) { - gizmo.plane[hoverAxis].model.material = gizmo.plane[hoverAxis].mat; - } else { - gizmo.arrow[hoverAxis].model.material = gizmo.arrow[hoverAxis].mat; - } - } - - if (!hoverAxis && !evtTapStart) { - evtTapStart = editor.on('viewport:tap:start', onTapStart); - } - - hoverAxis = node.axis; - hoverPlane = node.plane; - - // set active material - if (hoverPlane) { - gizmo.plane[hoverAxis].model.material = gizmo.matActiveTransparent; - } else { - gizmo.arrow[hoverAxis].model.material = gizmo.matActive; - } - } - } else { - if (hoverAxis) { - if (hoverPlane) { - gizmo.plane[hoverAxis].model.material = gizmo.plane[hoverAxis].mat; - } else { - gizmo.arrow[hoverAxis].model.material = gizmo.arrow[hoverAxis].mat; - } - } - - hoverAxis = ''; - - if (evtTapStart) { - evtTapStart.unbind(); - evtTapStart = null; - } - } - }); - - // update gizmo - editor.on('viewport:postUpdate', (dt) => { - if (gizmo.root.enabled) { - const camera = editor.call('camera:current'); - const posCamera = camera.getPosition(); - - quat.copy(gizmo.root.getRotation()).invert(); - - if (moving && (vecA.copy(posCameraLast).sub(posCamera).length() > 0.01 || mouseTapMoved)) { - const point = pickPlane(mouseTap.x, mouseTap.y); - if (point) { - point.sub(pickStart); - if (snap) { - point.scale(1 / snapIncrement); - point.x = Math.round(point.x); - point.y = Math.round(point.y); - point.z = Math.round(point.z); - point.scale(snapIncrement); - } - editor.emit('gizmo:translate:offset', point.x, point.y, point.z); - } - - editor.call('viewport:render'); - } - - editor.emit('gizmo:translate:render', dt); - - posCameraLast.copy(posCamera); - - const posGizmo = gizmo.root.getPosition(); - let scale = 1; - - // scale to screen space - if (camera.camera.projection === pc.PROJECTION_PERSPECTIVE) { - const dot = vecA.copy(posGizmo).sub(posCamera).dot(camera.forward); - const denom = 1280 / (2 * Math.tan(camera.camera.fov * pc.math.DEG_TO_RAD / 2)); - scale = Math.max(0.0001, (dot / denom) * 150) * gizmoSize; - } else { - scale = camera.camera.orthoHeight / 3 * gizmoSize; - } - gizmo.root.setLocalScale(scale, scale, scale); - - // calculate viewing angle - vecA - .copy(posCamera) - .sub(posGizmo) - .normalize(); - - // rotate vector by gizmo rotation - quat.transformVector(vecA, vecA); - - // swap sides to face camera - // x - gizmo.plane.x.setLocalPosition(0, (vecA.y > 0) ? 0.4 : -0.4, (vecA.z > 0) ? 0.4 : -0.4); - gizmo.line.x.setLocalPosition((vecA.x > 0) ? 1.5 : 1.1, 0, 0); - gizmo.line.x.setLocalScale(arrowRadius, (vecA.x > 0) ? 1 : 1.8, arrowRadius); - // y - gizmo.plane.y.setLocalPosition((vecA.x > 0) ? 0.4 : -0.4, 0, (vecA.z > 0) ? 0.4 : -0.4); - gizmo.line.y.setLocalPosition(0, (vecA.y > 0) ? 1.5 : 1.1, 0); - gizmo.line.y.setLocalScale(arrowRadius, (vecA.y > 0) ? 1 : 1.8, arrowRadius); - // z - gizmo.plane.z.setLocalPosition((vecA.x > 0) ? 0.4 : -0.4, (vecA.y > 0) ? 0.4 : -0.4, 0); - gizmo.line.z.setLocalPosition(0, 0, (vecA.z > 0) ? 1.5 : 1.1); - gizmo.line.z.setLocalScale(arrowRadius, (vecA.z > 0) ? 1 : 1.8, arrowRadius); - - // hide plane if viewed from very angle - gizmo.plane.x.model.enabled = Math.abs(vecA.x) > 0.15 && visible; - gizmo.plane.y.model.enabled = Math.abs(vecA.y) > 0.15 && visible; - gizmo.plane.z.model.enabled = Math.abs(vecA.z) > 0.15 && visible; - - quat.invert(); - - const layer = editor.call('gizmo:layers', 'Axis Gizmo Immediate'); - - // plane x lines - if (gizmo.plane.x.model.enabled) { - vecB.set(0, 0, (vecA.z > 0) ? scale * 0.8 : -scale * 0.8); - vecC.set(0, (vecA.y > 0) ? scale * 0.8 : -scale * 0.8, (vecA.z > 0) ? scale * 0.8 : -scale * 0.8); - vecD.set(0, (vecA.y > 0) ? scale * 0.8 : -scale * 0.8, 0); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - quat.transformVector(vecD, vecD).add(gizmo.root.getPosition()); - const clr = (hoverAxis === 'x' && hoverPlane) ? gizmo.matActive.color : gizmo.arrow.x.mat.color; - app.drawLines([vecB, vecC, vecC, vecD], [clr, clr, clr, clr], true, layer); - } - // plane y lines - if (gizmo.plane.y.model.enabled) { - vecB.set((vecA.x > 0) ? scale * 0.8 : -scale * 0.8, 0, 0); - vecC.set((vecA.x > 0) ? scale * 0.8 : -scale * 0.8, 0, (vecA.z > 0) ? scale * 0.8 : -scale * 0.8); - vecD.set(0, 0, (vecA.z > 0) ? scale * 0.8 : -scale * 0.8); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - quat.transformVector(vecD, vecD).add(gizmo.root.getPosition()); - const clr = (hoverAxis === 'y' && hoverPlane) ? gizmo.matActive.color : gizmo.arrow.y.mat.color; - app.drawLines([vecB, vecC, vecC, vecD], [clr, clr, clr, clr], true, layer); - } - // plane z lines - if (gizmo.plane.z.model.enabled) { - vecB.set((vecA.x > 0) ? scale * 0.8 : -scale * 0.8, 0, 0); - vecC.set((vecA.x > 0) ? scale * 0.8 : -scale * 0.8, (vecA.y > 0) ? scale * 0.8 : -scale * 0.8, 0); - vecD.set(0, (vecA.y > 0) ? scale * 0.8 : -scale * 0.8, 0); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - quat.transformVector(vecD, vecD).add(gizmo.root.getPosition()); - const clr = (hoverAxis === 'z' && hoverPlane) ? gizmo.matActive.color : gizmo.arrow.z.mat.color; - app.drawLines([vecB, vecC, vecC, vecD], [clr, clr, clr, clr], true, layer); - } - - // hide lines and arrows if viewed from very angle - gizmo.line.x.model.enabled = gizmo.arrow.x.model.enabled = !(Math.abs(vecA.z) <= 0.15 && Math.abs(vecA.y) <= 0.15) && visible; - gizmo.line.y.model.enabled = gizmo.arrow.y.model.enabled = !(Math.abs(vecA.x) <= 0.15 && Math.abs(vecA.z) <= 0.15) && visible; - gizmo.line.z.model.enabled = gizmo.arrow.z.model.enabled = !(Math.abs(vecA.x) <= 0.15 && Math.abs(vecA.y) <= 0.15) && visible; - - // draw axes lines - // line x - if (gizmo.line.x.model.enabled) { - vecB.set(((vecA.x > 0) ? scale * 1 : scale * 0.2), 0, 0); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - vecC.set(scale * 2, 0, 0); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - app.drawLine(vecB, vecC, gizmo.arrow.x.model.material.color, true, layer); - } - // line y - if (gizmo.line.y.model.enabled) { - vecB.set(0, ((vecA.y > 0) ? scale * 1 : scale * 0.2), 0); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - vecC.set(0, scale * 2, 0); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - app.drawLine(vecB, vecC, gizmo.arrow.y.model.material.color, true, layer); - } - // line z - if (gizmo.line.z.model.enabled) { - vecB.set(0, 0, ((vecA.z > 0) ? scale * 1 : scale * 0.2)); - quat.transformVector(vecB, vecB).add(gizmo.root.getPosition()); - vecC.set(0, 0, scale * 2); - quat.transformVector(vecC, vecC).add(gizmo.root.getPosition()); - app.drawLine(vecB, vecC, gizmo.arrow.z.model.material.color, true, layer); - } - } - - mouseTapMoved = false; - }); - - editor.on('viewport:tap:move', onTapMove); - editor.on('viewport:tap:end', onTapEnd); - }); -}); diff --git a/src/editor/viewport/viewport-application.ts b/src/editor/viewport/viewport-application.ts index 4c14c27b1..0cd53432d 100644 --- a/src/editor/viewport/viewport-application.ts +++ b/src/editor/viewport/viewport-application.ts @@ -79,8 +79,8 @@ class ViewportApplication extends pc.Application { editor.emit('viewport:gizmoUpdate', dt); + editor.emit('viewport:preRender'); app.render(); - editor.emit('viewport:postRender'); } }; diff --git a/src/editor/viewport/viewport-pick.ts b/src/editor/viewport/viewport-pick.ts index 2f6b6410f..ca804f372 100644 --- a/src/editor/viewport/viewport-pick.ts +++ b/src/editor/viewport/viewport-pick.ts @@ -1,3 +1,5 @@ +import { FORCE_PICK_TAG } from '../../core/constants.ts'; + editor.once('load', () => { const app = editor.call('viewport:app'); if (!app) { @@ -14,6 +16,11 @@ editor.once('load', () => { let picking = true; let filter = null; let mouseDown = false; + let gizmoHover = false; + + editor.on('gizmo:transform:hover', (state) => { + gizmoHover = state; + }); editor.method('viewport:pick:filter', (fn) => { if (filter === fn) { @@ -40,6 +47,10 @@ editor.once('load', () => { // pick editor.call('viewport:pick', mouseCoords.x, mouseCoords.y, (node, picked) => { + if (gizmoHover && !node?.tags.has(FORCE_PICK_TAG)) { + node = null; + picked = null; + } if (pickedData.node !== node || pickedData.picked !== picked) { pickedData.node = node; pickedData.picked = picked; @@ -125,6 +136,10 @@ editor.once('load', () => { editor.emit('viewport:pick:node', pickedData.node, pickedData.picked); } else { editor.call('viewport:pick', tap.x, tap.y, (node, picked) => { + if (gizmoHover && !node?.tags.has(FORCE_PICK_TAG)) { + node = null; + picked = null; + } if (pickedData.node !== node || pickedData.picked !== picked) { pickedData.node = node; pickedData.picked = picked;