Created
July 4, 2016 14:10
-
-
Save leefsmp/286ee567c4376bbecaa96f69e61506f1 to your computer and use it in GitHub Desktop.
Rotation Control for Autodesk Viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import EventsEmitter from 'EventsEmitter' | |
export default class RotateTool extends EventsEmitter { | |
///////////////////////////////////////////////////////////////// | |
// Class constructor | |
// | |
///////////////////////////////////////////////////////////////// | |
constructor (viewer) { | |
super() | |
this.keys = {} | |
this.active = false | |
this.viewer = viewer | |
this.fullTransform = false | |
this.viewer.toolController.registerTool(this) | |
this.onAggregateSelectionChangedHandler = (e) => { | |
this.onAggregateSelectionChanged(e) | |
} | |
} | |
///////////////////////////////////////////////////////////////// | |
// Enable tool | |
// | |
///////////////////////////////////////////////////////////////// | |
enable (enable) { | |
var name = this.getName() | |
if (enable) { | |
this.viewer.toolController.activateTool(name) | |
} else { | |
this.viewer.toolController.deactivateTool(name) | |
} | |
} | |
///////////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////////// | |
getNames () { | |
return ['Viewing.Rotate.Tool'] | |
} | |
///////////////////////////////////////////////////////////////// | |
// | |
// | |
///////////////////////////////////////////////////////////////// | |
getName () { | |
return 'Viewing.Rotate.Tool' | |
} | |
/////////////////////////////////////////////////////////////////// | |
// activate tool | |
// | |
/////////////////////////////////////////////////////////////////// | |
activate () { | |
if (!this.active) { | |
this.active = true | |
this.viewer.addEventListener( | |
Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, | |
this.onAggregateSelectionChangedHandler) | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// deactivate tool | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
deactivate () { | |
if (this.active) { | |
this.active = false | |
if (this.rotateControl) { | |
this.rotateControl.remove() | |
this.rotateControl = null | |
} | |
this.viewer.removeEventListener( | |
Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, | |
this.onAggregateSelectionChangedHandler) | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Component Selection Handler | |
// (use Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT instead of | |
// Autodesk.Viewing.SELECTION_CHANGED_EVENT - deprecated ) | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
onAggregateSelectionChanged (event) { | |
if (this.rotateControl && this.rotateControl.engaged) { | |
this.rotateControl.engaged = false | |
this.viewer.select(this.selection.dbIdArray) | |
return | |
} | |
if (event.selections && event.selections.length) { | |
var selection = event.selections[ 0 ] | |
this.selection = selection | |
this.emit('transform.modelSelected', | |
this.selection) | |
if (this.fullTransform) { | |
this.selection.fragIdsArray = [] | |
var fragCount = selection.model.getFragmentList(). | |
fragments.fragId2dbId.length | |
for (var fragId = 0; fragId < fragCount; ++fragId) { | |
this.selection.fragIdsArray.push(fragId) | |
} | |
this.selection.dbIdArray = [] | |
var instanceTree = selection.model.getData().instanceTree | |
var rootId = instanceTree.getRootId() | |
this.selection.dbIdArray.push(rootId) | |
} | |
this.drawControl() | |
this.viewer.fitToView( | |
this.selection.dbIdArray) | |
} else { | |
this.clearSelection() | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Selection cleared | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
clearSelection () { | |
this.selection = null | |
if (this.rotateControl) { | |
this.rotateControl.remove() | |
this.rotateControl = null | |
this.viewer.impl.sceneUpdated(true) | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw rotate control | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
drawControl () { | |
var bBox = this.geWorldBoundingBox( | |
this.selection.fragIdsArray, | |
this.selection.model.getFragmentList()) | |
this.center = new THREE.Vector3( | |
(bBox.min.x + bBox.max.x) / 2, | |
(bBox.min.y + bBox.max.y) / 2, | |
(bBox.min.z + bBox.max.z) / 2) | |
var size = Math.max( | |
bBox.max.x - bBox.min.x, | |
bBox.max.y - bBox.min.y, | |
bBox.max.z - bBox.min.z) * 0.8 | |
if (this.rotateControl) { | |
this.rotateControl.remove() | |
} | |
this.rotateControl = new RotateControl( | |
this.viewer, this.center, size) | |
this.rotateControl.on('transform.rotate', (data) => { | |
this.rotateFragments( | |
this.selection.model, | |
this.selection.fragIdsArray, | |
data.axis, | |
data.angle, | |
this.center) | |
this.viewer.impl.sceneUpdated(true) | |
}) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
handleButtonDown (event, button) { | |
if (this.rotateControl) { | |
if (this.rotateControl.onPointerDown(event)) { | |
return true | |
} | |
} | |
if (button === 0 && this.keys.Control) { | |
this.isDragging = true | |
this.mousePos = { | |
x: event.clientX, | |
y: event.clientY | |
} | |
return true | |
} | |
return false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
handleButtonUp (event, button) { | |
if (this.rotateControl) { | |
this.rotateControl.onPointerUp(event) | |
} | |
if (button === 0) { | |
this.isDragging = false | |
} | |
return false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
handleMouseMove (event) { | |
if (this.rotateControl) { | |
this.rotateControl.onPointerHover(event) | |
} | |
if (this.isDragging) { | |
if (this.selection) { | |
var offset = { | |
x: this.mousePos.x - event.clientX, | |
y: event.clientY - this.mousePos.y | |
} | |
this.mousePos = { | |
x: event.clientX, | |
y: event.clientY | |
} | |
var angle = Math.sqrt( | |
offset.x * offset.x + | |
offset.y * offset.y) | |
var sidewaysDirection = new THREE.Vector3() | |
var moveDirection = new THREE.Vector3() | |
var eyeDirection = new THREE.Vector3() | |
var upDirection = new THREE.Vector3() | |
var camera = this.viewer.getCamera() | |
var axis = new THREE.Vector3() | |
var eye = new THREE.Vector3() | |
eye.copy(camera.position).sub(camera.target) | |
eyeDirection.copy(eye).normalize() | |
upDirection.copy(camera.up).normalize() | |
sidewaysDirection.crossVectors( | |
upDirection, eyeDirection).normalize() | |
upDirection.setLength(offset.y) | |
sidewaysDirection.setLength(offset.x) | |
moveDirection.copy( | |
upDirection.add( | |
sidewaysDirection)) | |
axis.crossVectors(moveDirection, eye).normalize() | |
this.rotateFragments( | |
this.selection.model, | |
this.selection.fragIdsArray, | |
axis, angle * Math.PI / 180, | |
this.center) | |
this.viewer.impl.sceneUpdated(true) | |
} | |
return true | |
} | |
return false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
handleKeyDown (event, keyCode) { | |
this.keys[event.key] = true | |
return false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
handleKeyUp (event, keyCode) { | |
this.keys[event.key] = false | |
return false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Rotate selected fragments | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
rotateFragments (model, fragIdsArray, axis, angle, center) { | |
var quaternion = new THREE.Quaternion() | |
quaternion.setFromAxisAngle(axis, angle) | |
fragIdsArray.forEach((fragId, idx) => { | |
var fragProxy = this.viewer.impl.getFragmentProxy( | |
model, fragId) | |
fragProxy.getAnimTransform() | |
var position = new THREE.Vector3( | |
fragProxy.position.x - center.x, | |
fragProxy.position.y - center.y, | |
fragProxy.position.z - center.z) | |
position.applyQuaternion(quaternion) | |
position.add(center) | |
fragProxy.position = position | |
fragProxy.quaternion.multiplyQuaternions( | |
quaternion, fragProxy.quaternion) | |
if (idx === 0) { | |
var euler = new THREE.Euler() | |
euler.setFromQuaternion( | |
fragProxy.quaternion, 0) | |
this.emit('transform.rotate', { | |
rotation: euler, | |
model | |
}) | |
} | |
fragProxy.updateAnimTransform() | |
}) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// returns bounding box as it appears in the viewer | |
// (transformations could be applied) | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
geWorldBoundingBox (fragIds, fragList) { | |
var fragbBox = new THREE.Box3() | |
var nodebBox = new THREE.Box3() | |
fragIds.forEach((fragId) => { | |
fragList.getWorldBounds(fragId, fragbBox) | |
nodebBox.union(fragbBox) | |
}) | |
return nodebBox | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// RotateControl Class | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
class RotateControl extends EventsEmitter { | |
constructor (viewer, center, size) { | |
super() | |
this.engaged = false | |
this.overlayScene = 'rotateControlScene' | |
this.domElement = viewer.impl.canvas | |
this.camera = viewer.impl.camera | |
this.viewer = viewer | |
this.center = center | |
this.size = size | |
this.gizmos = [] | |
this.viewer.impl.createOverlayScene( | |
this.overlayScene) | |
this.createAxis( | |
center, new THREE.Vector3(1, 0, 0), | |
size * 0.85, 0xFF0000) | |
this.createAxis( | |
center, new THREE.Vector3(0, 1, 0), | |
size * 0.85, 0x00FF00) | |
this.createAxis( | |
center, new THREE.Vector3(0, 0, 1), | |
size * 0.85, 0x0000FF) | |
// World UP = Y | |
if (this.camera.worldup.y) { | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(0, Math.PI / 2, 0), | |
size * 0.0045, | |
size * 0.8, 0xFF0000, | |
Math.PI, | |
new THREE.Vector3(1, 0, 0))) | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(Math.PI / 2, 0, 0), | |
size * 0.0045, | |
size * 0.8, 0x00FF00, | |
2 * Math.PI, | |
new THREE.Vector3(0, 1, 0))) | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(0, 0, 0), | |
size * 0.0045, | |
size * 0.8, 0x0000FF, | |
Math.PI, | |
new THREE.Vector3(0, 0, 1))) | |
} else { | |
// World UP = Z | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(Math.PI / 2, Math.PI / 2, 0), | |
size * 0.0045, | |
size * 0.8, 0xFF0000, | |
Math.PI, | |
new THREE.Vector3(1, 0, 0))) | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(Math.PI / 2, 0, 0), | |
size * 0.0045, | |
size * 0.8, 0x00FF00, | |
Math.PI, | |
new THREE.Vector3(0, 1, 0))) | |
this.gizmos.push(this.createGizmo( | |
center, | |
new THREE.Euler(0, 0, 0), | |
size * 0.0045, | |
size * 0.8, 0x0000FF, | |
2 * Math.PI, | |
new THREE.Vector3(0, 0, 1))) | |
} | |
this.picker = this.createSphere( | |
size * 0.02) | |
var material = new THREE.LineBasicMaterial({ | |
color: 0xFFFF00, | |
linewidth: 1, | |
depthTest: false, | |
depthWrite: false, | |
transparent: true | |
}) | |
this.angleLine = | |
this.createLine( | |
this.center, | |
this.center, | |
material) | |
viewer.impl.sceneUpdated(true) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw a line | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createLine (start, end, material) { | |
var geometry = new THREE.Geometry() | |
geometry.vertices.push(new THREE.Vector3( | |
start.x, start.y, start.z)) | |
geometry.vertices.push(new THREE.Vector3( | |
end.x, end.y, end.z)) | |
var line = new THREE.Line(geometry, material) | |
this.viewer.impl.addOverlay( | |
this.overlayScene, line) | |
return line | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw a cone | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createCone (start, dir, length, material) { | |
dir.normalize() | |
var end = { | |
x: start.x + dir.x * length, | |
y: start.y + dir.y * length, | |
z: start.z + dir.z * length | |
} | |
var orientation = new THREE.Matrix4() | |
orientation.lookAt( | |
start, | |
end, | |
new THREE.Object3D().up) | |
var matrix = new THREE.Matrix4() | |
matrix.set( | |
1, 0, 0, 0, | |
0, 0, 1, 0, | |
0, -1, 0, 0, | |
0, 0, 0, 1) | |
orientation.multiply(matrix) | |
var geometry = new THREE.CylinderGeometry( | |
0, length * 0.2, length, 128, 1) | |
var cone = new THREE.Mesh(geometry, material) | |
cone.applyMatrix(orientation) | |
cone.position.x = start.x + dir.x * length / 2 | |
cone.position.y = start.y + dir.y * length / 2 | |
cone.position.z = start.z + dir.z * length / 2 | |
this.viewer.impl.addOverlay( | |
this.overlayScene, cone) | |
return cone | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw one axis | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createAxis (start, dir, size, color) { | |
var end = { | |
x: start.x + dir.x * size, | |
y: start.y + dir.y * size, | |
z: start.z + dir.z * size | |
} | |
var material = new THREE.LineBasicMaterial({ | |
color: color, | |
linewidth: 3, | |
depthTest: false, | |
depthWrite: false, | |
transparent: true | |
}) | |
this.createLine( | |
start, end, material) | |
this.createCone( | |
end, dir, size * 0.1, material) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw a rotate gizmo | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createGizmo (center, euler, size, radius, color, range, axis) { | |
var material = new GizmoMaterial({ | |
color: color | |
}) | |
var subMaterial = new GizmoMaterial({ | |
color: color | |
}) | |
var torusGizmo = new THREE.Mesh( | |
new THREE.TorusGeometry( | |
radius, size, 64, 64, range), | |
material) | |
var subTorus = new THREE.Mesh( | |
new THREE.TorusGeometry( | |
radius, size, 64, 64, 2 * Math.PI), | |
subMaterial) | |
subTorus.material.highlight(true) | |
var transform = new THREE.Matrix4() | |
var q = new THREE.Quaternion() | |
q.setFromEuler(euler) | |
var s = new THREE.Vector3(1, 1, 1) | |
transform.compose(center, q, s) | |
torusGizmo.applyMatrix(transform) | |
subTorus.applyMatrix(transform) | |
var plane = this.createBox( | |
this.size * 100, | |
this.size * 100, | |
0.01) | |
plane.applyMatrix(transform) | |
subTorus.visible = false | |
this.viewer.impl.addOverlay( | |
this.overlayScene, torusGizmo) | |
this.viewer.impl.addOverlay( | |
this.overlayScene, subTorus) | |
torusGizmo.subGizmo = subTorus | |
torusGizmo.plane = plane | |
torusGizmo.axis = axis | |
return torusGizmo | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw a box | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createBox (w, h, d) { | |
var material = new GizmoMaterial({ | |
color: 0x000000 | |
}) | |
var geometry = new THREE.BoxGeometry(w, h, d) | |
var box = new THREE.Mesh( | |
geometry, material) | |
box.visible = false | |
this.viewer.impl.addOverlay( | |
this.overlayScene, box) | |
return box | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Draw a sphere | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
createSphere (radius) { | |
var material = new GizmoMaterial({ | |
color: 0xFFFF00 | |
}) | |
var geometry = new THREE.SphereGeometry( | |
radius, 32, 32) | |
var sphere = new THREE.Mesh( | |
geometry, material) | |
sphere.visible = false | |
this.viewer.impl.addOverlay( | |
this.overlayScene, sphere) | |
return sphere | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// Creates Raycatser object from the pointer | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
pointerToRaycaster (pointer) { | |
var pointerVector = new THREE.Vector3() | |
var pointerDir = new THREE.Vector3() | |
var ray = new THREE.Raycaster() | |
var rect = this.domElement.getBoundingClientRect() | |
var x = ((pointer.clientX - rect.left) / rect.width) * 2 - 1 | |
var y = -((pointer.clientY - rect.top) / rect.height) * 2 + 1 | |
if (this.camera.isPerspective) { | |
pointerVector.set(x, y, 0.5) | |
pointerVector.unproject(this.camera) | |
ray.set(this.camera.position, | |
pointerVector.sub( | |
this.camera.position).normalize()) | |
} else { | |
pointerVector.set(x, y, -1) | |
pointerVector.unproject(this.camera) | |
pointerDir.set(0, 0, -1) | |
ray.set(pointerVector, | |
pointerDir.transformDirection( | |
this.camera.matrixWorld)) | |
} | |
return ray | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
onPointerDown (event) { | |
var pointer = event.pointers ? event.pointers[ 0 ] : event | |
if (pointer.button === 0) { | |
var ray = this.pointerToRaycaster(pointer) | |
var intersectResults = ray.intersectObjects( | |
this.gizmos, true) | |
if (intersectResults.length) { | |
this.gizmos.forEach((gizmo) => { | |
gizmo.visible = false | |
}) | |
this.selectedGizmo = intersectResults[0].object | |
this.selectedGizmo.subGizmo.visible = true | |
this.picker.position.copy( | |
intersectResults[0].point) | |
this.angleLine.geometry.vertices[1].copy( | |
intersectResults[0].point) | |
this.lastDir = intersectResults[0].point.sub( | |
this.center).normalize() | |
this.angleLine.geometry.verticesNeedUpdate = true | |
this.angleLine.visible = true | |
this.picker.visible = true | |
} else { | |
this.picker.visible = false | |
} | |
this.engaged = this.picker.visible | |
this.viewer.impl.sceneUpdated(true) | |
} | |
return this.picker.visible | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
onPointerHover (event) { | |
var pointer = event.pointers ? event.pointers[ 0 ] : event | |
if (this.engaged) { | |
var ray = this.pointerToRaycaster(pointer) | |
var intersectResults = ray.intersectObjects( | |
[this.selectedGizmo.plane], true) | |
if (intersectResults.length) { | |
var intersectPoint = intersectResults[0].point | |
var dir = intersectPoint.sub( | |
this.center).normalize() | |
var cross = new THREE.Vector3() | |
cross.crossVectors(this.lastDir, dir) | |
var sign = Math.sign( | |
cross.dot(this.selectedGizmo.axis)) | |
this.emit('transform.rotate', { | |
angle: sign * dir.angleTo(this.lastDir), | |
axis: this.selectedGizmo.axis | |
}) | |
this.lastDir = dir | |
var pickerPoint = new THREE.Vector3( | |
this.center.x + dir.x * this.size * 0.8, | |
this.center.y + dir.y * this.size * 0.8, | |
this.center.z + dir.z * this.size * 0.8) | |
this.picker.position.copy( | |
pickerPoint) | |
this.angleLine.geometry.vertices[1].copy( | |
pickerPoint) | |
} | |
this.angleLine.visible = true | |
this.angleLine.geometry.verticesNeedUpdate = true | |
} else { | |
this.angleLine.visible = false | |
var ray = this.pointerToRaycaster(pointer) | |
var intersectResults = ray.intersectObjects( | |
this.gizmos, true) | |
if (intersectResults.length) { | |
this.picker.position.set( | |
intersectResults[ 0 ].point.x, | |
intersectResults[ 0 ].point.y, | |
intersectResults[ 0 ].point.z) | |
this.picker.visible = true | |
} else { | |
this.picker.visible = false | |
} | |
} | |
this.viewer.impl.sceneUpdated(true) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
onPointerUp (event) { | |
this.angleLine.visible = false | |
this.picker.visible = false | |
this.gizmos.forEach((gizmo) => { | |
gizmo.visible = true | |
gizmo.subGizmo.visible = false | |
}) | |
this.viewer.impl.sceneUpdated(true) | |
setTimeout(() => { | |
this.engaged = false | |
}, 100) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
normalize(screenPoint) { | |
var viewport = this.viewer.navigation.getScreenViewport() | |
var n = { | |
x: (screenPoint.x - viewport.left) / viewport.width, | |
y: (screenPoint.y - viewport.top) / viewport.height | |
} | |
return n | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
projectOntoPlane (worldPoint, normal) { | |
var dist = normal.dot(worldPoint) | |
return new THREE.Vector3( | |
worldPoint.x - dist * normal.x, | |
worldPoint.y - dist * normal.y, | |
worldPoint.z - dist * normal.z) | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
remove () { | |
this.viewer.impl.removeOverlayScene( | |
this.overlayScene) | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// Highlightable Gizmo Material | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
class GizmoMaterial extends THREE.MeshBasicMaterial { | |
constructor (parameters) { | |
super() | |
this.setValues(parameters) | |
this.colorInit = this.color.clone() | |
this.opacityInit = this.opacity | |
this.side = THREE.FrontSide | |
this.depthWrite = false | |
this.transparent = true | |
this.depthTest = false | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
highlight (highlighted) { | |
if (highlighted) { | |
this.color.setRGB(1, 230 / 255, 3 / 255) | |
this.opacity = 1 | |
} else { | |
this.color.copy(this.colorInit) | |
this.opacity = this.opacityInit | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment