UNPKG

@croquet/microverse-library

Version:

An npm package version of Microverse

983 lines (811 loc) 33.2 kB
// the following import statement is solely for the type checking and // autocompletion features in IDE. A Behavior cannot inherit from // another behavior or a base class but can use the methods and // properties of the card to which it is installed. // The prototype classes ActorBehavior and PawnBehavior provide // the features defined at the card object. import {ActorBehavior, PawnBehavior} from "../PrototypeBehavior"; class GizmoActor extends ActorBehavior { setup() { this.target = this._cardData.target; let targetParent = this._cardData.targetParent; this.creatorId = this._cardData.creatorId; this.set({parent: targetParent}); this.set({translation: this.target.translation}); this.listen("cycleModes", "cycleModes"); this.isGizmoManipulator = true; this.cycleModes(); this.addPropertySheetButton(); this.subscribe(this.target.id, "translationSet", "translateTarget"); this.subscribe(this.target.id, "rotationSet", "rotateTarget"); this.subscribe(this.target.id, "scaleSet", "scaleTarget"); } /* initializeGizmo(data) { let {parent, target, creatorId} = data; if (parent) { this.set({parent}); } this.target = target; this.set({translation: target.translation}); this.creatorId = creatorId; } */ getScale(m) { let x = [m[0], m[1], m[2]]; let y = [m[4], m[5], m[6]]; let z = [m[8], m[9], m[10]]; let length = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); return [length(x), length(y), length(z)]; } translateTarget() { let t = this.target.translation; let s = this.target.parent ? this.getScale(this.target.parent.global) : [1, 1, 1]; this.set({translation: t, scale: [1 / s[0], 1 / s[1], 1 / s[2]]}); } rotateTarget() { // in this case, gizmo itself should not rotate but the gyro should rotate so the three rings // show the euler angle of them in a sane way. if (this.gizmoMode === "move") { this.set({rotation: [0, 0, 0, 1]}); } else { let r = this.target.rotation; this.set({rotation: r}); } } scaleTarget() {} closestCorner(creatorId) { let avatar = [...this.service("ActorManager").actors].find(([_k, actor]) => { return actor.playerId === creatorId; }); if (avatar) { avatar = avatar[1]; } if (!avatar) {return;} let {m4_identity, v3_transform, v3_magnitude, v3_sub, v3_add} = Microverse; let target = this.target; let parentGlobal = target._parent ? target._parent.global : m4_identity(); let t = target.translation; let g = v3_transform(t, parentGlobal); let a = avatar.translation; let offsets = [ [ 2, 1, 2], [-2, 1, 2], [ 2, -1, 2], [-2, -1, 2], [ 2, 1, -2], [-2, 1, -2], [ 2, -1, -2], [-2, -1, -2] ]; let locals = offsets.map((o) => v3_add(t, o)); let parents = locals.map((l) => v3_transform(l, parentGlobal)); let dist = Number.MAX_VALUE; let min = -1; for (let i = 0; i < parents.length; i++) { let thisDist = v3_magnitude(v3_sub(a, parents[i])); if (thisDist <= dist && parents[i][1] > g[1]) { dist = thisDist; min = i; } } return offsets[min]; } addPropertySheetButton() { if (this.propertySheetButton) { this.propertySheetButton.destroy(); } let t = this.closestCorner(this.creatorId); this.dataLocation = "3ryPlwPIvSHXjABwnuJjrMQYPp1JH2OnghLGR_cdAbCEGgYGAgFIXV0UGx4XAVwHAVwRAB0DBxcGXBsdXQddNRYkEAseOwEzGSMRMCoWQTUKEwQLBSc5JSsrQF0bHVwRAB0DBxcGXB8bEQAdBBcAARddBRckQ0E8AEUcRwABPTExOhMCHEMeNENFIAglOitFNBg0OjNGPiYQIl81Sl0WEwYTXUMeCgYrHDc7HkECPScbJxYKOSE6RCgXK0YgFwYgCzAYPioRCkEkAEYVLRU"; this.propertySheetButton = this.createCard({ name: "property sheet button", dataLocation: this.dataLocation, fileName: "/prop-plain.svg", modelType: "svg", shadow: true, singleSided: true, scale: [0.5, 0.5, 0.5], type: "2d", fullBright: true, behaviorModules: ["GizmoPropertySheetButton", "Billboard"], parent: this, noSave: true, translation: t, }); this.subscribe(this.propertySheetButton.id, "openPropertySheet", "openPropertySheet"); } openPropertySheet(toWhom) { this.target.showControls(toWhom); } cycleModes() { console.log("cycling modes, before: ", this.gizmoMode); if (!this.gizmoMode || this.gizmoMode === "scale") { if (this.gizmoMode === "scale") { this.scaleX.destroy(); this.scaleY.destroy(); this.scaleZ.destroy(); } this.gizmoMode = "move"; let s = this.target.parent ? this.getScale(this.target.parent.global) : [1, 1, 1]; this.set({rotation: [0, 0, 0, 1], scale: [1 / s[0], 1 / s[1], 1 / s[2]]}); this.moveX = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisX', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [1, 0, 0], color: 0xff0000, hoverColor: 0xffff00 }); this.moveY = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisY', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [0, 1, 0], color: 0x00ff00, hoverColor: 0xffff00 }); this.moveZ = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisZ', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [0, 0, 1], color: 0x0000ff, hoverColor: 0xffff00 }); this.moveNX = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisX', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [-1, 0, 0], color: 0xff0000, hoverColor: 0xffff00 }); this.moveNY = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisY', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [0, -1, 0], color: 0x00ff00, hoverColor: 0xffff00 }); this.moveNZ = this.createCard({ translation: [0, 0, 0], name: 'gizmoAxisZ', behaviorModules: ["GizmoAxis"], parent: this, type: "object", noSave: true, axis: [0, 0, -1], color: 0x0000ff, hoverColor: 0xffff00 }); } else if (this.gizmoMode === "move") { this.gizmoMode = "rotate"; this.moveX.destroy(); this.moveY.destroy(); this.moveZ.destroy(); this.moveNX.destroy(); this.moveNY.destroy(); this.moveNZ.destroy(); if (this.propertySheetButton) { this.propertySheetButton.destroy(); } let s = this.target.parent ? this.getScale(this.target.parent.global) : [1, 1, 1]; this.set({rotation: this.target.rotation, scale: [1 / s[0], 1 / s[1], 1 / s[2]]}); this.rotateX = this.createCard({ translation: [0, 0, 0], name: 'gizmoRotorX', behaviorModules: ["GizmoRotor"], parent: this, type: "object", noSave: true, axis: [1, 0, 0], color: 0xff0000, hoverColor: 0xffff00 }); this.rotateY = this.createCard({ translation: [0, 0, 0], name: 'gizmoRotorY', behaviorModules: ["GizmoRotor"], parent: this, type: "object", noSave: true, axis: [0, 1, 0], color: 0x00ff00, hoverColor: 0xffff00 }); this.rotateZ = this.createCard({ translation: [0, 0, 0], name: 'gizmoRotorZ', behaviorModules: ["GizmoRotor"], parent: this, type: "object", noSave: true, axis: [0, 0, 1], color: 0x0000ff, hoverColor: 0xffff00 }); } else if (this.gizmoMode === "rotate") { this.gizmoMode = "scale"; this.rotateX.destroy(); this.rotateY.destroy(); this.rotateZ.destroy(); this.scaleX = this.createCard({ translation: [0, 0, 0], name: 'gizmoScalerX', behaviorModules: ["GizmoScaler"], parent: this, type: "object", noSave: true, axis: [1, 0, 0], color: 0xff0000, hoverColor: 0xffff00 }); this.scaleY = this.createCard({ translation: [0, 0, 0], name: 'gizmoScalerY', behaviorModules: ["GizmoScaler"], parent: this, type: "object", noSave: true, axis: [0, 1, 0], color: 0x00ff00, hoverColor: 0xffff00 }); this.scaleZ = this.createCard({ translation: [0, 0, 0], name: 'gizmoScalerZ', behaviorModules: ["GizmoScaler"], parent: this, type: "object", noSave: true, axis: [0, 0, 1], color: 0x0000ff, hoverColor: 0xffff00 }); } console.log("cycled modes, now: ", this.gizmoMode); } teardown() { if (this.target) { this.target.sayUnselectEdit(); } } } class GizmoPawn extends PawnBehavior { setup() { this.lastTime = this.now(); this.isMine = this.actor.creatorId === this.viewId; if (this.isMine && !this.interval) { this.interval = setInterval(() => this.checkInteraction(), 1000); } let assetManager = this.service("AssetManager").assetManager; let dataLocation = this.actor.dataLocation; this.getBuffer(dataLocation).then((buffer) => { assetManager.setCache(dataLocation, buffer, "global"); }); this.subscribe(this.id, "interaction", "interaction"); } checkInteraction() { let now = this.now(); if (now - this.lastTime > 15000) { if (this.interval) { clearInterval(this.interval); } let avatar = this.getMyAvatar(); if ((!avatar.actor.gizmo) || avatar.actor.gizmo.id !== this.actor.id) {return;} this.publish(avatar.actor.id, "goodByeGizmo", this.actor.id); } } interaction() { this.lastTime = this.now(); } forceOnTop(obj3d) { obj3d.traverse((obj) => { if (obj.geometry) { obj.renderOrder = 10000; // draw last let m = obj.material; let mat; mat = Array.isArray(m) ? m : [m]; mat.forEach(m => { m.opacity = 0.9999; m.transparent = true; m.depthTest = false; m.depthWrite = false; }); } }); } } class GizmoAxisActor extends ActorBehavior { setup() { this.isGizmoManipulator = true; this.subscribe(this.parent.id, "translateTarget", "translateTarget"); } translateTarget(translation) { this.parent.target.set({translation}); } } class GizmoAxisPawn extends PawnBehavior { setup() { this.originalColor = this.actor._cardData.color; let isMine = this.parent?.actor.creatorId === this.viewId; this.isMine = isMine; this.shape.add(this.makeAxisHelper(isMine)); if (isMine) { this.addEventListener("pointerDown", "startDrag"); this.addEventListener("pointerMove", "drag"); this.addEventListener("pointerUp", "endDrag"); this.addEventListener("pointerEnter", "pointerEnter"); this.addEventListener("pointerLeave", "pointerLeave"); } } makeAxisHelper(isMine) { this.axis = this.actor._cardData.axis; let arrow = new Microverse.THREE.ArrowHelper( new Microverse.THREE.Vector3(...this.axis), new Microverse.THREE.Vector3(0, 0, 0), 3, isMine ? this.originalColor : 0xffffff ); this.parent.call("Gizmo$GizmoPawn", "forceOnTop", arrow); return arrow; } startDrag(event) { let avatar = Microverse.GetPawn(event.avatarId); let target = this.actor.parent.target; if (!event.ray) {return;} // avatar.addFirstResponder("pointerMove", {shiftKey: true}, this); avatar.addFirstResponder("pointerMove", {}, this); let {THREE, m4_invert, v3_normalize, v3_sub, m4_identity, v3_cross} = Microverse; // ensure the plane is parallel to the arrow let direction = event.ray.direction; let up = v3_cross(this.axis, direction); direction = v3_cross(up, this.axis); let parentGlobal = target._parent ? target._parent.global : m4_identity(); this.gizmoParentInvert = m4_invert(parentGlobal); this.gizmoPositionAtDragStart = target.translation; this.gizmoDragStart = event.xyz; // if we are dragging along the Y axis this.intersectionPlane = new Microverse.THREE.Plane(); this.intersectionPlane.setFromNormalAndCoplanarPoint( new THREE.Vector3(...v3_sub([0, 0, 0], v3_normalize(direction))), new Microverse.THREE.Vector3(...this.gizmoDragStart) ); this.publish(this.parent.id, "interaction"); } drag(event) { if (!this.gizmoDragStart || !event.ray) {return;} let origin = new Microverse.THREE.Vector3(...event.ray.origin); let direction = new Microverse.THREE.Vector3(...event.ray.direction); let ray = new Microverse.THREE.Ray(origin, direction); let intersectionPoint = ray.intersectPlane( this.intersectionPlane, new Microverse.THREE.Vector3() ); if (!intersectionPoint) {return;} let globalHere = intersectionPoint.toArray(); let globalStart = this.gizmoDragStart; let localHere = Microverse.v3_transform(globalHere, this.gizmoParentInvert); let localStart = Microverse.v3_transform(globalStart, this.gizmoParentInvert); let delta3D = Microverse.v3_sub(localHere, localStart); let nextPosition = [...this.gizmoPositionAtDragStart]; if (this.actor._cardData.axis[0] === 1 || this.actor._cardData.axis[0] === -1) { nextPosition[0] += delta3D[0]; } else if (this.actor._cardData.axis[1] === 1 || this.actor._cardData.axis[1] === -1) { nextPosition[1] += delta3D[1]; } else if (this.actor._cardData.axis[2] === 1 || this.actor._cardData.axis[2] === -1) { nextPosition[2] += delta3D[2]; } // console.log(nextPosition); this.publish(this.parent.actor.id, "translateTarget", nextPosition); this.publish(this.parent.id, "interaction"); // this.set({translation: nextPosition}) } endDrag(event) { delete this.gizmoDragStart; delete this.gizmoParentInvert; delete this.gizmoPositionAtDragStart; const avatar = Microverse.GetPawn(event.avatarId); // avatar.removeFirstResponder("pointerMove", {shiftKey: true}, this); avatar.removeFirstResponder("pointerMove", {}, this); this.publish(this.parent.id, "interaction"); } pointerEnter() { this.shape.children[0].setColor(this.actor._cardData.hoverColor); this.publish(this.parent.id, "interaction"); } pointerLeave() { this.shape.children[0].setColor(this.originalColor); this.publish(this.parent.id, "interaction"); } } class GizmoRotorActor extends ActorBehavior { setup() { this.isGizmoManipulator = true; this.subscribe(this.parent.id, "rotateTarget", "rotateTarget"); } rotateTarget(rotation) { this.parent.target.set({rotation}) } } class GizmoRotorPawn extends PawnBehavior { setup() { this.originalColor = this.actor._cardData.color; let isMine = this.parent?.actor.creatorId === this.viewId; this.isMine = isMine; this.shape.add(this.createCircle(isMine ? this.actor._cardData.color : 0xffffff, this.actor._cardData.axis)); if (isMine) { this.addEventListener("pointerDown", "startDrag"); this.addEventListener("pointerMove", "drag"); this.addEventListener("pointerUp", "endDrag"); this.addEventListener("pointerEnter", "pointerEnter"); this.addEventListener("pointerLeave", "pointerLeave"); } } createCircle(color, axis) { const curve = new Microverse.THREE.EllipseCurve( 0.0, 0.0, // Center x, y 2.0, 2.0, // x radius, y radius 0.0, 2.0 * Math.PI, // Start angle, stop angle ); const pts = curve.getSpacedPoints(256); const geo = new Microverse.THREE.BufferGeometry().setFromPoints(pts); if (axis[1] === 1) { geo.rotateX(Math.PI / 2); } else if (axis[0] === 1) { geo.rotateY(Math.PI / 2); } const mat = new Microverse.THREE.LineBasicMaterial({color, toneMapped: false, linewidth: 2}); const circle = new Microverse.THREE.LineLoop(geo, mat); this.parent.call("Gizmo$GizmoPawn", "forceOnTop", circle); return circle; } localRotationAxis() { return Microverse.v3_rotate(this.actor._cardData.axis, this.gizmoRotationAtDragStart); // wrong? } rotationInteractionPlane(_event) { let {THREE} = Microverse; let interactionPlane = new THREE.Plane(); interactionPlane.setFromNormalAndCoplanarPoint( new THREE.Vector3(...this.localRotationAxis()), new THREE.Vector3(...this.gizmoGlobalTranslationAtStart)); /* if (window.planeHelper) { this.shape.parent.remove(window.planeHelper); window.planeHelper = undefined; } window.planeHelper = new Microverse.THREE.PlaneHelper( interactionPlane, 10, 0xffff00 ) this.shape.parent.add(window.planeHelper); */ return interactionPlane; } startDrag(event) { let avatar = Microverse.GetPawn(event.avatarId); let target = this.parent.actor.target; if (!event.ray) {return;} // avatar.addFirstResponder("pointerMove", {shiftKey: true}, this); avatar.addFirstResponder("pointerMove", {}, this); let {THREE, v3_normalize, v3_transform, m4_getTranslation, m4_invert} = Microverse; this.gizmoTargetInvert = m4_invert(target.global); this.gizmoRotationAtDragStart = target.rotation; this.gizmoGlobalTranslationAtStart = m4_getTranslation(target.global); let origin = new THREE.Vector3(...event.ray.origin); let direction = new THREE.Vector3(...event.ray.direction); let ray = new THREE.Ray(origin, direction); let dragPoint = ray.intersectPlane( this.rotationInteractionPlane(), new Microverse.THREE.Vector3() ); if (dragPoint) { let localPoint = v3_transform(dragPoint.toArray(), this.gizmoTargetInvert); let dir = v3_normalize(localPoint); this.dragStart = dir; } this.publish(this.parent.id, "interaction"); } drag(event) { if (!this.dragStart || !event.ray) {return;} let {THREE, v3_transform, v3_normalize, v3_cross, v3_dot, q_multiply, q_axisAngle} = Microverse; let origin = new THREE.Vector3(...event.ray.origin); let direction = new THREE.Vector3(...event.ray.direction); let ray = new THREE.Ray(origin, direction); let newDragPoint; let dragPoint = ray.intersectPlane( this.rotationInteractionPlane(), new Microverse.THREE.Vector3() ); if (dragPoint) { let localPoint = v3_transform(dragPoint.toArray(), this.gizmoTargetInvert); let dir = v3_normalize(localPoint); newDragPoint = dir; } if (!newDragPoint) {return;} let projStartDirection = this.dragStart; let projNewDirection = newDragPoint; let normal = this.localRotationAxis(); let nSign; if (this.actor._cardData.axis[0] === 1) { nSign = normal[0] < 0 ? 1 : -1; } else if (this.actor._cardData.axis[1] === 1) { nSign = normal[1] < 0 ? 1 : -1; } else if (this.actor._cardData.axis[2] === 1) { nSign = normal[2] < 0 ? 1 : -1; } let dot = v3_dot(projStartDirection, projNewDirection); let cross = v3_cross(projNewDirection, projStartDirection); let dotCross = v3_dot(cross, normal); let acos = Math.acos(dot); let sign = Math.sign(dotCross); let axisAngle = q_axisAngle(this.actor._cardData.axis, acos * sign * nSign); let nextRotation = q_multiply(axisAngle, this.gizmoRotationAtDragStart); this.publish(this.parent.actor.id, "rotateTarget", nextRotation) this.publish(this.parent.id, "interaction"); } endDrag(event) { delete this.gizmoTargetInvert; delete this.gizmoRotationAtDragStart; delete this.gizmoGlobalTranslationAtStart; const avatar = Microverse.GetPawn(event.avatarId); // avatar.removeFirstResponder("pointerMove", {shiftKey: true}, this); avatar.removeFirstResponder("pointerMove", {}, this); this.publish(this.parent.id, "interaction"); } pointerEnter() { this.shape.children[0].material.color.set(this.actor._cardData.hoverColor); this.publish(this.parent.id, "interaction"); } pointerLeave() { this.shape.children[0].material.color.set(this.originalColor); this.publish(this.parent.id, "interaction"); } } class GizmoScalerActor extends ActorBehavior { setup() { this.isGizmoManipulator = true; this.subscribe(this.parent.id, "scaleTarget", "scaleTarget"); } scaleTarget(scale) { this.parent.target.set({scale}); } } class GizmoScalerPawn extends PawnBehavior { setup() { this.originalColor = this.actor._cardData.color; let isMine = this.parent?.actor.creatorId === this.viewId; this.isMine = isMine; this.targetScaleSet(); this.subscribe(this.actor.parent.target.id, "scaleSet", "targetScaleSet"); if (isMine) { this.addEventListener("pointerDown", "startDrag"); this.addEventListener("pointerMove", "drag"); this.addEventListener("pointerUp", "endDrag"); this.addEventListener("pointerEnter", "pointerEnter"); this.addEventListener("pointerLeave", "pointerLeave"); } } getGlobalLength() { let {THREE, GetPawn, m4_invert, v3_transform, v3_multiply} = Microverse; let target = this.actor.parent?.target; let targetPawn = GetPawn(target.id); let invert = m4_invert(target.global); let box = new THREE.Box3().setFromObject(targetPawn.shape); let min = v3_transform(box.min.toArray(), invert); let max = v3_transform(box.max.toArray(), invert); let s = this.actor.parent.call("Gizmo$GizmoActor", "getScale", target.global); min = v3_multiply(min, s); max = v3_multiply(max, s); return {min, max}; } targetScaleSet(_data) { let isMine = this.parent?.actor.creatorId === this.viewId; let box = this.getGlobalLength(); this.shape.add(this.makeScaleHandles(isMine, box, 1.2)); } makeScaleHandles(isMine, box3, margin) { let points = []; let {THREE} = Microverse; if (this.handleGroup) { [...this.handleGroup.children].forEach((c) => { c.material.dispose(); c.geometry.dispose(); c.removeFromParent(); }); this.handleGroup.removeFromParent(); } let group = new THREE.Group(); this.handleGroup = group; points.push(new THREE.Vector3(0, 0, 0)); let s = this.actor._cardData.axis.indexOf(1); points.push((new THREE.Vector3(...this.actor._cardData.axis)).multiplyScalar(box3.max[s] * margin)); let geometry = new THREE.BufferGeometry().setFromPoints(points); let material = new THREE.LineBasicMaterial({color: isMine ? this.originalColor : 0xffffff, toneMapped: false}); let line = new THREE.Line(geometry, material); let boxGeometry = new THREE.BoxGeometry(0.3, 0.3, 0.3); let boxMaterial = new THREE.MeshBasicMaterial({color: isMine ? this.originalColor : 0xffffff, toneMapped: false}); let box = new THREE.Mesh(boxGeometry, boxMaterial); box.translateOnAxis(new THREE.Vector3(...this.actor._cardData.axis), box3.max[s] * margin); group.add(line); group.add(box); this.parent.call("Gizmo$GizmoPawn", "forceOnTop", group); return group; } startDrag(event) { // avatar.addFirstResponder("pointerMove", {shiftKey: true}, this); let {THREE, m4_invert, v3_normalize, v3_sub, GetPawn} = Microverse; let avatar = GetPawn(event.avatarId); avatar.addFirstResponder("pointerMove", {}, this); let target = this.actor.parent.target; if (!event.ray) {return;} this.scaleAtDragStart = target.scale; this.dragStart = event.xyz; this.targetInvert = m4_invert(target.global); // if we are dragging along the Y axis this.intersectionPlane = new Microverse.THREE.Plane(); this.intersectionPlane.setFromNormalAndCoplanarPoint( new THREE.Vector3(...v3_sub([0, 0, 0], v3_normalize(event.ray.direction))), new Microverse.THREE.Vector3(...this.dragStart) ); this.publish(this.parent.id, "interaction"); } drag(event) { if (!this.dragStart || !event.ray) {return;} let {THREE, v3_scale, v3_sub, v3_divide} = Microverse; let origin = new THREE.Vector3(...event.ray.origin); let direction = new THREE.Vector3(...event.ray.direction); let ray = new THREE.Ray(origin, direction); let intersectionPoint = ray.intersectPlane( this.intersectionPlane, new Microverse.THREE.Vector3() ); if (!intersectionPoint) {return;} let globalHere = intersectionPoint.toArray(); let globalStart = this.dragStart; let localHere = Microverse.v3_transform(globalHere, this.targetInvert); let localStart = Microverse.v3_transform(globalStart, this.targetInvert); let localOrigin = [0, 0, 0]; let ratio = v3_divide(v3_sub(localHere, localOrigin), v3_sub(localStart, localOrigin)); let nextScale = [...this.scaleAtDragStart]; let s; if (this.actor._cardData.axis[0] === 1) { s = ratio[0]; } else if (this.actor._cardData.axis[1] === 1) { s = ratio[1]; } else if (this.actor._cardData.axis[2] === 1) { s = ratio[2]; } let realNextScale = v3_scale(nextScale, s); this.publish(this.parent.actor.id, "scaleTarget", realNextScale); this.publish(this.parent.id, "interaction"); } endDrag(event) { this.dragStart = undefined; let avatar = Microverse.GetPawn(event.avatarId); // avatar.removeFirstResponder("pointerMove", {shiftKey: true}, this); avatar.removeFirstResponder("pointerMove", {}, this); this.publish(this.parent.id, "interaction"); } pointerEnter() { this.shape.children[0].children.forEach(child => child.material.color.set(this.actor._cardData.hoverColor)); } pointerLeave() { this.shape.children[0].children.forEach(child => child.material.color.set(this.originalColor)); } } class GizmoPropertySheetButtonActor extends ActorBehavior { setup() { this.isGizmoManipulator = true; } } class GizmoPropertySheetButtonPawn extends PawnBehavior { setup() { let isMine = this.parent?.actor.creatorId === this.viewId; this.isMine = isMine; this.subscribe(this.id, "2dModelLoaded", "svgLoaded"); this.parent.call("Gizmo$GizmoPawn", "forceOnTop", this.shape); if (isMine) { this.addEventListener("pointerMove", "nop"); this.addEventListener("pointerEnter", "hilite"); this.addEventListener("pointerLeave", "unhilite"); this.addEventListener("pointerTap", "openPropertySheet"); // effectively prevent propagation this.addEventListener("pointerDown", "nop"); this.addEventListener("pointerUp", "nop"); } } svgLoaded() { if (this.shape.children[0]) { this.parent.call("Gizmo$GizmoPawn", "forceOnTop", this.shape.children[0]); } } setColor() { let svg = this.shape.children[0]; let backdrop; if (svg) { backdrop = svg.children[0]; } let baseColor = this.entered ? 0x888888 : 0x4a4a4a; if (backdrop.material && backdrop.material[0]) { backdrop.material && backdrop.material[0].color.setHex(baseColor); } } hilite() { this.entered = true; this.setColor(); this.publish(this.parent.id, "interaction"); } unhilite() { this.entered = false; this.setColor(); this.publish(this.parent.id, "interaction"); } openPropertySheet(event) { let avatarPawn = Microverse.GetPawn(event.avatarId); let avatar = avatarPawn.actor; if (!event.shiftKey) { this.say("openPropertySheet", {avatar: avatar.id, distance: avatarPawn.targetDistance}); this.destroy(); // remove button } else { let target = this.actor.parent.target; let targetPawn = Microverse.GetPawn(target.id); let args = {target, targetPawn, avatar, avatarPawn}; // log func name and args by default (and reminder that func can be set) let fn = window.microverseShiftClickPropFunc; let keepGizmo = false; if (fn) { keepGizmo = fn(args); } else { console.log("microverseShiftClickPropFunc (not set)", args); } if (!keepGizmo) { this.publish(avatar.actor.id, "goodByeGizmo", this.actor.id); } } // this.say("openPropertySheet", {avatar: avatar.id, distance: avatarPawn.targetDistance}); } } export default { modules: [ { name: "Gizmo", actorBehaviors: [GizmoActor], pawnBehaviors: [GizmoPawn], }, { name: "GizmoAxis", actorBehaviors: [GizmoAxisActor], pawnBehaviors: [GizmoAxisPawn], }, { name: "GizmoRotor", actorBehaviors: [GizmoRotorActor], pawnBehaviors: [GizmoRotorPawn], }, { name: "GizmoScaler", actorBehaviors: [GizmoScalerActor], pawnBehaviors: [GizmoScalerPawn], }, { name: "GizmoPropertySheetButton", actorBehaviors: [GizmoPropertySheetButtonActor], pawnBehaviors: [GizmoPropertySheetButtonPawn], } ] } /* globals Microverse */