feat(viewer): intermeidate step

This commit is contained in:
Dimitrie Stefanescu
2021-02-20 23:05:54 +00:00
parent 70bbc43acb
commit db2a3f01f6
11 changed files with 1166 additions and 524 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -34,7 +34,8 @@
<div class="twelve columns">
<button onclick="v.sceneManager.zoomExtents()">Zoom Extents</button>
<button onclick="v.postprocessing = !v.postprocessing">Postprocessing Toggle</button>
<button onclick="v.sectionBox.toggleSectionBox()">Show Section Box</button>
<button onclick="v.interactions.toggleSectionBox()">Toggle Section Box</button>
<button onclick="v.interactions.test()">Test Section Box</button>
</div>
<div class="twelve columns">
<input id="objectUrlInput" type="text" name="objectId" placeholder="Object Url" style="width:49%" value="https://staging.speckle.dev/streams/a75ab4f10f/objects/a59617590b0bec4e9b5ee487ee75b1a7"/>
+2 -1
View File
@@ -34,7 +34,8 @@
<div class="twelve columns">
<button onclick="v.sceneManager.zoomExtents()">Zoom Extents</button>
<button onclick="v.postprocessing = !v.postprocessing">Postprocessing Toggle</button>
<button onclick="v.sectionBox.toggleSectionBox()">Show Section Box</button>
<button onclick="v.interactions.toggleSectionBox()">Toggle Section Box</button>
<button onclick="v.interactions.test()">Test Section Box</button>
</div>
<div class="twelve columns">
<input id="objectUrlInput" type="text" name="objectId" placeholder="Object Url" style="width:49%" value="https://staging.speckle.dev/streams/a75ab4f10f/objects/a59617590b0bec4e9b5ee487ee75b1a7"/>
@@ -32,4 +32,8 @@ export default class EventEmitter {
this._events[name].forEach( fireCallbacks )
}
dispose(){
this._events = null
}
}
@@ -0,0 +1,100 @@
import * as THREE from 'three'
import SectionBox from './SectionBox'
import SectionBox2 from './SectionBox2'
import SelectionHelper from './SelectionHelper'
export default class InteractionHandler {
constructor( viewer ) {
this.viewer = viewer
// this.sectionBox = new SectionBox( this.viewer )
this.sectionBoxEnabled = false
this.selectionHelper = new SelectionHelper( this.viewer, this.viewer.sceneManager.userObjects )
this.selectionMaterial = new THREE.MeshLambertMaterial( { color: 0x0B55D2, emissive: 0x0B55D2, side: THREE.DoubleSide } )
// this.selectionMaterial.clippingPlanes = this.sectionBox.planes.map( c => c.plane )
this.selectionEdgesMaterial = new THREE.LineBasicMaterial( { color: 0x23F3BD } )
// this.selectionEdgesMaterial.clippingPlanes = this.sectionBox.planes.map( c => c.plane )
this.selectedObjects = new THREE.Group()
this.viewer.scene.add( this.selectedObjects )
this.selectedObjects.renderOrder = 1000
this.selectionHelper.on( 'object-doubleclicked', this._handleDoubleClick.bind( this ) )
this.selectionHelper.on( 'object-clicked', this._handleSelect.bind( this ) )
}
_handleDoubleClick( objs ) {
if ( !objs || objs.length === 0 ) this.viewer.sceneManager.zoomExtents()
else this.viewer.sceneManager.zoomToObject( objs[0].object )
this.viewer.needsRender = true
}
_handleSelect( obj ) {
if ( obj.length === 0 ) {
this.deselectObjects()
return
}
if ( !this.selectionHelper.multiSelect ) this.deselectObjects()
let mesh = new THREE.Mesh( obj[0].object.geometry, this.selectionMaterial )
this.selectedObjects.add( mesh )
const bbox = new THREE.Box3().setFromObject( mesh )
const size = bbox.getSize( new THREE.Vector3() )
bbox.expandByVector( size.multiplyScalar( 0.1 ) )
const helper = new THREE.Box3Helper( bbox, 0x29308C )
helper.material = this.selectionEdgesMaterial
// TODO: if selection box is active, add planes to helper material clipping
this.selectedObjects.add( helper )
this.viewer.needsRender = true
}
deselectObjects() {
this.selectedObjects.clear()
this.viewer.needsRender = true
}
toggleSectionBox() {
this.sectionBoxEnabled = !this.sectionBoxEnabled
if ( this.sectionBoxEnabled ) {
this.showSelectionBox()
} else {
this.hideSelectionBox()
}
}
showSelectionBox() {
this.viewer.renderer.localClippingEnabled = true
let bbox = null
let setFromSelection = false
if ( this.selectedObjects.children.length > 0 ) {
bbox = new THREE.Box3().setFromObject( this.selectedObjects.children[0] )
setFromSelection = true
} else {
bbox = this.viewer.sceneManager.getSceneBoundingBox()
}
this.viewer.sceneManager.zoomToBox( bbox )
this.sectionBox.setFromBbox( bbox, setFromSelection ? 0.3 : 0.1 )
this.sectionBox.display.visible = true
this.viewer.needsRender = true
this.sectionBoxEnabled = true
}
hideSelectionBox() {
this.viewer.renderer.localClippingEnabled = false
this.sectionBox.display.visible = false
this.viewer.needsRender = true
this.sectionBoxEnabled = false
}
test() {
let tt = new SectionBox2( this.viewer )
}
}
@@ -79,7 +79,7 @@ export default class SceneObjectManager {
// Is it a transparent material?
if ( renderMat.opacity !== 1 ) {
let material = this.transparentMaterial.clone()
material.clippingPlanes = this.viewer.sectionBox.planes.map( p => p.plane )
// material.clippingPlanes = this.viewer.interactions.sectionBox.planes.map( p => p.plane )
material.color = color
material.opacity = renderMat.opacity !== 0 ? renderMat.opacity : 0.2
@@ -88,7 +88,7 @@ export default class SceneObjectManager {
// It's not a transparent material!
} else {
let material = this.solidMaterial.clone()
material.clippingPlanes = this.viewer.sectionBox.planes.map( p => p.plane )
// material.clippingPlanes = this.viewer.interactions.sectionBox.planes.map( p => p.plane )
material.color = color
material.metalness = renderMat.metalness
@@ -99,7 +99,7 @@ export default class SceneObjectManager {
} else {
// If we don't have defined material, just use the default
let material = this.solidMaterial.clone()
material.clippingPlanes = this.viewer.sectionBox.planes.map( p => p.plane )
// material.clippingPlanes = this.viewer.interactions.sectionBox.planes.map( p => p.plane )
this.addSolid( wrapper, material )
}
@@ -164,7 +164,8 @@ export default class SceneObjectManager {
this.lineObjects.clear()
this.pointObjects.clear()
this.viewer.selectionHelper.unselect()
this.viewer.interactions.deselectObjects()
this.viewer.interactions.hideSelectionBox()
this.objectIds = []
this._postLoadFunction()
@@ -173,10 +174,15 @@ export default class SceneObjectManager {
_postLoadFunction() {
this.zoomExtents()
this.viewer.reflectionsNeedUpdate = true
}
let sceneBox = new THREE.Box3().setFromObject( this.viewer.sceneManager.userObjects )
this.viewer.sectionBox.setFromBbox( sceneBox )
getSceneBoundingBox() {
if ( this.objects.length === 0 ) {
let box = new THREE.Box3( new THREE.Vector3( -1,-1,-1 ), new THREE.Vector3( 1,1,1 ) )
return box
}
let box = new THREE.Box3().setFromObject( this.userObjects )
return box
}
zoomToObject( target ) {
@@ -185,14 +191,16 @@ export default class SceneObjectManager {
}
zoomExtents() {
let bboxTarget = this.userObjects
if ( this.objects.length === 0 ) {
let box = new THREE.Box3( new THREE.Vector3( -1,-1,-1 ), new THREE.Vector3( 1,1,1 ) )
this.zoomToBox( box )
this.viewer.controls.setBoundary( box )
return
}
let box = new THREE.Box3().setFromObject( bboxTarget )
let box = new THREE.Box3().setFromObject( this.userObjects )
this.zoomToBox( box )
this.viewer.controls.setBoundary( box )
}
zoomToBox( box ) {
@@ -201,6 +209,7 @@ export default class SceneObjectManager {
const size = box.getSize( new THREE.Vector3() )
let target = new THREE.Sphere()
box.getBoundingSphere( target )
target.radius = target.radius * fitOffset
this.viewer.controls.fitToSphere( target, true )
+121 -113
View File
@@ -19,11 +19,14 @@ const edges = [
]
export default class SectionBox {
constructor( viewer, _vis ){
constructor( viewer, _vis ) {
//defaults to invisible
let vis = _vis || false
this.viewer = viewer
this.orbiting = false
this.viewer.controls.addEventListener( 'wake', () => { this.orbiting = true } )
this.viewer.controls.addEventListener( 'controlend', () => { this.orbiting = false } )
this.display = new THREE.Group()
this.display.visible = vis
@@ -39,34 +42,31 @@ export default class SectionBox {
this.viewer.scene.add( this.display )
// basic display of the section box
this.boxMaterial = new THREE.MeshBasicMaterial( )
this.boxMaterial = new THREE.MeshStandardMaterial( {
transparent: true,
opacity: 0.1,
side: THREE.DoubleSide,
color: 0x0A66FF,
metalness: 0.01,
roughness: 0.75,
} )
// the box itself
this.boxGeo = new THREE.BoxGeometry( 2,2,2 )
this.boxMesh = new THREE.Mesh( this.boxGeo, this.boxMaterial )
this.boxMesh.visible = false
this.boxMesh.geometry.computeBoundingBox()
this.boxMesh.geometry.computeBoundingSphere()
this.displayBox.add( this.boxMesh )
this.lineMaterial = new THREE.LineDashedMaterial( {
color: 0x0A66FF,
linewidth: 4,
} )
// const edges = new THREE.EdgesGeometry( this.boxGeo )
// this.line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xDF66FF } ) )
// show box edges
edges.map( val => {
let pts = [ this.boxGeo.vertices[val[0]].clone(),
this.boxGeo.vertices[val[1]].clone() ]
let geo = new THREE.BufferGeometry().setFromPoints( pts )
let line = new THREE.Line( geo, this.lineMaterial )
this.displayEdges.add( line )
} )
// normal of plane being hovered
this.hoverPlane = new THREE.Vector3()
this.selectionHelper = new SelectionHelper( this.viewer, { subset:this.displayBox, hover:true } )
this.selectionHelper = new SelectionHelper( this.viewer, { subset: this.displayBox, hover:true } )
// pointer position
this.pointer = new THREE.Vector3()
@@ -78,36 +78,34 @@ export default class SectionBox {
this.planes = [
{
axis: '+x', // right, x positive
plane:new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 1 ),
plane: new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 ),
indices: [ 5,4,6,7 ],
},{
axis: '-x', // left, x negative
plane: new THREE.Plane( new THREE.Vector3( -1, 0, 0 ), 1 ),
plane: new THREE.Plane( new THREE.Vector3( -1, 0, 0 ), 0 ),
indices: [ 0,1,3,2 ],
},{
axis: '+y', // out, y positive
plane:new THREE.Plane( new THREE.Vector3( 0, 1, 0 ), 1 ),
plane:new THREE.Plane( new THREE.Vector3( 0, 1, 0 ), 0 ),
indices: [ 2,3,6,7 ],
},{
axis: '-y', // in, y negative
plane:new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 1 ),
plane:new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 0 ),
indices: [ 5,4,1,0 ],
},{
axis: '+z', // up, z positive
plane:new THREE.Plane( new THREE.Vector3( 0, 0, 1 ), 1 ),
plane:new THREE.Plane( new THREE.Vector3( 0, 0, 1 ), 0 ),
indices: [ 1,3,6,4 ],
},{
axis: '-z', // down, z negative
plane:new THREE.Plane( new THREE.Vector3( 0, 0, -1 ), 1 ),
plane:new THREE.Plane( new THREE.Vector3( 0, 0, -1 ), 0 ),
indices: [ 0,2,7,5 ],
} ]
// plane helpers
// this.planeHelpers = this.planes.map( p => this.display.add(new THREE.PlaneHelper( p.plane, 2, 0x000000 ) ));
// adds clipping planes to all materials
// better to add clipping planes to renderer
this.viewer.renderer.localClippingEnabled = true
// this.planes.forEach( p => {
// const helper = new THREE.PlaneHelper( p.plane, 1, 0xffff00 )
// this.display.add( helper )
// } )
this.viewer.sceneManager.objects.forEach( obj => {
obj.material.clippingPlanes = this.planes.map( c => c.plane )
@@ -117,37 +115,10 @@ export default class SectionBox {
transparent: true,
opacity: 0.1,
color: 0x0A66FF,
// color: 0xE91E63,
metalness: 0.1,
roughness: 0.75,
} )
// hovered event handler
this.selectionHelper.on( 'hovered', ( obj, e ) => {
if ( !this.display.visible ) return
if ( obj.length === 0 && !this.dragging ) {
this.displayHover.clear()
this.hoverPlane = new THREE.Vector3()
this.viewer.controls.enabled = true
this.viewer.renderer.domElement.style.cursor = 'default'
return
} else if ( this.dragging ){
return
}
this.viewer.renderer.domElement.style.cursor = 'pointer'
let index = this.planes.findIndex( p => p.plane.normal.equals( obj[0].face.normal.clone().negate() ) )
if ( index < 0 ) return // this should never be the case?
let planeObj = this.planes[index]
let plane = planeObj.plane
if ( plane.normal.equals( this.hoverPlane ) ) return
this.hoverPlane = plane.normal.clone()
this.updateHover( planeObj )
} )
// Selection Helper seems unecessary for this type of thing
this.viewer.renderer.domElement.addEventListener( 'pointerup', ( e ) => {
this.pointer = new THREE.Vector3()
@@ -156,14 +127,67 @@ export default class SectionBox {
this.dragging = false
} )
let cuttingPlane = null
let hoverPlane = null
// hovered event handler
this.selectionHelper.on( 'hovered', ( obj, e ) => {
if ( this.orbiting ) return
if ( obj.length === 0 && !this.dragging ) {
this.viewer.controls.enabled = true
this.displayHover.clear()
this.hoverPlane = new THREE.Vector3()
this.viewer.controls.enabled = true
this.viewer.renderer.domElement.style.cursor = 'default'
return
} else if ( this.dragging ) {
return
}
this.viewer.controls.enabled = false
this.viewer.renderer.domElement.style.cursor = 'pointer'
switch ( obj[0].faceIndex ) {
case 0: case 1:
cuttingPlane = this.planes[0]
hoverPlane = this.planes[1]
break
case 2: case 3:
cuttingPlane = this.planes[1]
hoverPlane = this.planes[0]
break
case 4: case 5:
cuttingPlane = this.planes[2]
hoverPlane = this.planes[3]
break
case 6: case 7:
cuttingPlane = this.planes[3]
hoverPlane = this.planes[2]
break
case 8: case 9:
cuttingPlane = this.planes[4]
hoverPlane = this.planes[5]
break
case 10: case 11:
cuttingPlane = this.planes[5]
hoverPlane = this.planes[4]
break
}
// this.hoverPlane = plane.normal.clone()
this.updateHover( hoverPlane )
} )
// get screen space vector of plane normal
// project mouse displacement vector onto it
// move plane by that much
this.selectionHelper.on( 'object-drag', ( obj, e ) => {
if ( this.orbiting ) return
if ( !this.display.visible ) return
// exit if we don't have a valid hoverPlane
if ( this.hoverPlane.equals( new THREE.Vector3() ) ) return
// if ( this.hoverPlane.equals( new THREE.Vector3() ) ) return
// exit if we're clicking on nothing
if ( !obj.length && !this.dragging ) return
@@ -172,9 +196,7 @@ export default class SectionBox {
this.dragging = true
let index = this.planes.findIndex( p => p.plane.normal.equals( this.hoverPlane ) )
let planeObj = this.planes[index]
let plane = planeObj.plane
let plane = hoverPlane.plane
if ( this.pointer.equals( new THREE.Vector3() ) ) {
this.pointer = new THREE.Vector3( e.x, e.y, 0.0 )
@@ -199,55 +221,61 @@ export default class SectionBox {
d = d * zoom
// limit plane from crossing it's pair
let hoverOpp = this.hoverPlane.clone().negate()
let indexOpp = this.planes.findIndex( p => p.plane.normal.equals( hoverOpp ) )
let planeObjOpp = this.planes[indexOpp]
let dist = planeObj.plane.constant + planeObjOpp.plane.constant
let planeObjOpp = cuttingPlane
let dist = hoverPlane.plane.constant + planeObjOpp.plane.constant
let displacement = new THREE.Vector3( d,d,d ).multiply( plane.normal )
// are we moving towards the limiting plane?
let dot = displacement.clone().normalize().dot( plane.normal )
if ( dist < 0.1 && dot < 0 ) return
// if displacement + padding is greater than limit,
// and we're moving towards the limiting plane
if ( dist < d && dot > 0 ) {
d = dist * 0.001
displacement = new THREE.Vector3( d,d,d ).multiply( plane.normal )
}
plane.translate( displacement )
this.updateBoxFace( planeObj, displacement )
this.updateHover( planeObj )
cuttingPlane.plane.translate( displacement )
this.updateBoxFace( hoverPlane, displacement )
this.updateHover( hoverPlane )
this.viewer.needsRender = true
} )
}
// boxMesh = bbox
setFromBbox( bbox ){
setFromBbox( bbox, offset ){
bbox = bbox.clone()
// add a little padding to the box
bbox.max.addScalar( 10 )
bbox.min.subScalar( 10 )
for ( let p of this.planes ) {
// reset plane
// p.plane.set(p.plane.normal, 1)
let c = 0
// planes point inwards - if negative select max part of bbox
if ( p.plane.normal.dot( new THREE.Vector3( 1,1,1 ) ) > 0 ) {
c = p.plane.normal.clone().multiply( bbox.min )
} else {
c = p.plane.normal.clone().multiply( bbox.max )
}
let diff = c.length() - p.plane.constant
const size = bbox.getSize( new THREE.Vector3() )
if ( offset )
bbox.expandByVector( size.multiplyScalar( offset ) )
// displacement
let d = p.plane.normal.clone().negate().multiplyScalar( diff )
const dimensions = new THREE.Vector3().subVectors( bbox.max, bbox.min )
const boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z )
const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( bbox.min, bbox.max ).multiplyScalar( 0.5 ) )
boxGeo.applyMatrix4( matrix )
this.updateBoxFace( p, d )
p.plane.translate( d )
let k = 0
for ( let i = 0; i < boxGeo.faces.length; i += 2 ) {
let plane = this.planes[k]
let face = boxGeo.faces[i]
plane.plane.setFromCoplanarPoints( boxGeo.vertices[face.c], boxGeo.vertices[face.b], boxGeo.vertices[face.a] )
k++
}
// update box geometry
for ( let i = 0; i< boxGeo.vertices.length; i++ ) {
let vert = boxGeo.vertices[i]
this.boxMesh.geometry.vertices[i].set( vert.x, vert.y, vert.z )
}
this.boxMesh.geometry.verticesNeedUpdate = true
this.boxMesh.geometry.computeBoundingBox()
this.boxMesh.geometry.computeBoundingSphere()
const edges = new THREE.EdgesGeometry( this.boxMesh.geometry )
const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xffffff } ) )
this.displayEdges.add( line )
}
updateBoxFace( planeObj, displacement ){
this.boxMesh.geometry.vertices.map( ( v,i ) => {
if ( !planeObj.indices.includes( i ) ) return
@@ -258,25 +286,7 @@ export default class SectionBox {
this.boxMesh.geometry.computeBoundingBox()
this.boxMesh.geometry.computeBoundingSphere()
this.updateEdges()
}
updateEdges(){
this.displayEdges.clear()
edges.map( val => {
let ptA = this.boxMesh.geometry.vertices[val[0]].clone()
let ptB = this.boxMesh.geometry.vertices[val[1]].clone()
// translation
ptA.add( this.boxMesh.position )
ptB.add( this.boxMesh.position )
this.drawLine( [ ptA, ptB ] )
} )
}
drawLine( pts ){
let geo = new THREE.BufferGeometry().setFromPoints( pts )
let line = new THREE.Line( geo, this.lineMaterial )
this.displayEdges.add( line )
// this.updateEdges()
}
updateHover( planeObj ){
@@ -323,6 +333,7 @@ export default class SectionBox {
let hoverMesh = new THREE.Mesh( hoverGeo, this.hoverMat )
this.displayHover.add( hoverMesh )
this.viewer.needsRender = true
}
toggleSectionBox( _bool ){
@@ -330,8 +341,5 @@ export default class SectionBox {
this.visible = bool
this.display.visible = bool
this.viewer.needsRender = true
// what's the tradeoff for having the clipping planes in material vs in the renderer?
// this.viewer.renderer.clippingPlanes = bool ? this.planes.reduce((p,c) => [...p,c.plane],[]) : []
}
}
+241
View File
@@ -0,0 +1,241 @@
import * as THREE from 'three'
import SelectionHelper from './SelectionHelper'
import { FaceNormalsHelper } from 'three/examples/jsm/helpers/FaceNormalsHelper.js'
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'
export default class SectionBox {
constructor( viewer, bbox ) {
this.viewer = viewer
this.orbiting = false
this.viewer.controls.addEventListener( 'wake', () => { this.orbiting = true } )
this.viewer.controls.addEventListener( 'controlend', () => { this.orbiting = false } )
this.box = bbox || this.viewer.sceneManager.getSceneBoundingBox()
const dimensions = new THREE.Vector3().subVectors( this.box.max, this.box.min )
this.boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z )
const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( this.box.min, this.box.max ).multiplyScalar( 0.5 ) )
this.boxGeo.applyMatrix4( matrix )
this.boxMesh = new THREE.Mesh( this.boxGeo, new THREE.MeshBasicMaterial( {
transparent: true,
opacity: 0.31,
wireframe: true,
side: THREE.DoubleSide,
color: 0x0A66FF
} ) )
const plane = new THREE.PlaneGeometry( 1, 1 )
this.hoverPlane = new THREE.Mesh( plane, new THREE.MeshStandardMaterial( {
transparent: true,
side: THREE.DoubleSide,
opacity: 0.3,
color: 0x23F3BD,
metalness: 0.1,
roughness: 0.75,
} ) )
this.display = new THREE.Group()
this.display.add( this.boxMesh )
this.boxMesh.attach( this.hoverPlane )
this.display.add( this.hoverPlane )
this.viewer.scene.add( this.display )
let vertex = this.boxGeo.vertices[ 5 ].clone().addScalar( 0.1 )
// vertex.addScalar( 0.1 )
let sphereG = new THREE.SphereGeometry( 0.001 )
this.gizmoSphere = new THREE.Mesh( sphereG, new THREE.MeshBasicMaterial( 0x29308C ) )
this.gizmoSphere.position.copy( vertex )
this.display.add( this.gizmoSphere )
this.boxMesh.userData.planes = []
this.boxMesh.userData.indices = []
this.planes = []
// this.normalHelper = new FaceNormalsHelper( this.boxMesh, 2, 0x00ff00, 1 )
// this.display.add( this.normalHelper )
this.controls = new TransformControls( this.viewer.camera, this.viewer.renderer.domElement )
this.controls.setSize( 0.5 )
this.controls.attach( this.gizmoSphere )
this.display.add( this.controls )
this.planeControls = new TransformControls( this.viewer.camera, this.viewer.renderer.domElement )
// this.planeControls.attach( this.hoverPlane )
// this.planeControls.visible = false
this.display.add( this.planeControls )
let prevGizmoPos = this.gizmoSphere.position.clone()
this.controls.addEventListener( 'change', ( event ) => {
prevGizmoPos.sub( this.gizmoSphere.position )
this.boxMesh.translateX( -prevGizmoPos.x )
this.boxMesh.translateY( -prevGizmoPos.y )
this.boxMesh.translateZ( -prevGizmoPos.z )
prevGizmoPos = this.gizmoSphere.position.clone()
this.setFromBox( new THREE.Box3().setFromObject( this.boxMesh ) )
this.viewer.render()
} )
this.controls.addEventListener( 'dragging-changed', ( event ) => {
this.viewer.controls.enabled = !event.value
// if ( this.viewer.controls.enabled ) this.viewer.sceneManager.zoomToObject( this.boxMesh )
} )
for ( let i = 0; i < this.boxGeo.faces.length; i += 2 ) {
let face = this.boxGeo.faces[i]
let pairFace = this.boxGeo.faces[i+1]
let plane = new THREE.Plane()
plane.setFromCoplanarPoints( this.boxGeo.vertices[face.c], this.boxGeo.vertices[face.b], this.boxGeo.vertices[face.a] ) // invert pts
const helper = new THREE.PlaneHelper( plane, 1, 0xffff00 )
this.display.add( helper )
// adding it twice for ease of use
this.boxMesh.userData.planes.push( plane )
this.boxMesh.userData.planes.push( plane )
this.boxMesh.userData.indices.push( [ face.a, face.b, face.c, pairFace.b ] )
this.boxMesh.userData.indices.push( [ face.a, face.b, face.c, pairFace.b ] )
this.planes.push( plane )
}
this.selectionHelper = new SelectionHelper( this.viewer, { subset: this.boxMesh, hover: true } )
let prevIndex = -1
let prevPointer = new THREE.Vector3()
document.addEventListener( 'pointerup', ( e ) => {
this.viewer.controls.enabled = true
if ( this.dragging ) {
this.viewer.sceneManager.zoomToObject( this.boxMesh )
}
this.dragging = false
this.viewer.renderer.domElement.style.cursor = 'default'
prevIndex = -1
prevPointer = new THREE.Vector3()
} )
// let faceIndex
this.selectionHelper.on( 'hovered', ( obj, e ) => {
if ( obj.length === 0 && !this.dragging ) {
this.viewer.renderer.domElement.style.cursor = 'default'
this.hoverPlane.visible = false
this.planeControls.detach()
this.viewer.controls.enabled = true
this.viewer.needsRender = true
prevIndex = -1
return
}
if ( this.orbiting ) return
if ( this.dragging ) return
this.hoverPlane.visible = true
this.viewer.renderer.domElement.style.cursor = 'pointer'
let centre = new THREE.Vector3()
for ( let i = 0; i < 4; i++ ) {
let vertex = this.boxGeo.vertices[ obj[0].object.userData.indices[ obj[0].faceIndex ][i] ].clone()
let vertexClone = vertex.clone()
vertex.applyMatrix4( this.boxMesh.matrixWorld )
centre.add( vertex )
this.hoverPlane.geometry.vertices[i].set( vertexClone.x / 4, vertexClone.y /4 , vertexClone.z/4 )
}
centre.multiplyScalar( 0.25 )
this.hoverPlane.position.copy( centre )
this.hoverPlane.geometry.verticesNeedUpdate = true
this.hoverPlane.geometry.computeBoundingBox()
this.hoverPlane.geometry.computeBoundingSphere()
this.planeControls.attach( this.hoverPlane )
if ( obj[0].faceIndex !== prevIndex ){
this.viewer.needsRender = true
prevIndex = obj[0].faceIndex
}
} )
this.selectionHelper.on( 'object-drag', ( obj, e ) => {
if ( this.orbiting || !this.display.visible ) return
if ( prevIndex === -1 ) return
this.dragging = true
this.viewer.renderer.domElement.style.cursor = 'move'
this.viewer.controls.enabled = false
let plane = this.boxMesh.userData.planes[ prevIndex ]
let normal = plane.normal.clone()
this.viewer.camera.updateMatrixWorld()
normal.negate().project( this.viewer.camera )
normal.setComponent( 2, 0 ).normalize()
if ( prevPointer.equals( new THREE.Vector3() ) ) prevPointer = new THREE.Vector3( e.x, e.y, 0 )
let currentPointer = new THREE.Vector3( e.x, e.y, 0 )
let mouseDeltaVector = prevPointer.clone().sub( currentPointer )
let dot = normal.dot( mouseDeltaVector )
const bbox = new THREE.Box3().setFromObject( this.boxMesh )
const dims = new THREE.Vector3().subVectors( bbox.max, bbox.min )
if ( dot > 0 && ( dims.x < 0.2 || dims.y < 0.2 || dims.z < 0.2 ) ) return
let zoom = this.viewer.camera.getWorldPosition( new THREE.Vector3() ).sub( new THREE.Vector3() ).length()
zoom *= 0.5
dot *= zoom
let displacement = new THREE.Vector3( dot, dot, dot ).multiply( plane.normal )
plane.translate( displacement )
let indices = this.boxMesh.userData.indices[ prevIndex ]
for ( let i = 0; i < 4; i++ ) {
let index = indices[i]
this.boxMesh.geometry.vertices[index].add( displacement )
this.hoverPlane.geometry.vertices[i].add( displacement )
}
this.boxMesh.geometry.verticesNeedUpdate = true
this.boxMesh.geometry.computeBoundingBox()
this.boxMesh.geometry.computeBoundingSphere()
this.hoverPlane.geometry.verticesNeedUpdate = true
this.hoverPlane.geometry.computeBoundingBox()
this.hoverPlane.geometry.computeBoundingSphere()
let gizmoPos = this.boxGeo.vertices[ 5 ].clone()
gizmoPos.addScalar( 0.1 )
gizmoPos.applyMatrix4( this.boxMesh.matrixWorld )
this.gizmoSphere.position.copy( gizmoPos )
prevGizmoPos = gizmoPos
this.viewer.needsRender = true
prevPointer = currentPointer.clone()
} )
}
setFromBox( box ) {
const dimensions = new THREE.Vector3().subVectors( box.max, box.min )
let boxGeo = new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z )
const matrix = new THREE.Matrix4().setPosition( dimensions.addVectors( box.min, box.max ).multiplyScalar( 0.5 ) )
boxGeo.applyMatrix4( matrix )
for ( let i = 0; i < this.boxGeo.faces.length; i += 2 ) {
let face = boxGeo.faces[i]
let plane = this.boxMesh.userData.planes[i]
plane.setFromCoplanarPoints( boxGeo.vertices[face.c], boxGeo.vertices[face.b], boxGeo.vertices[face.a] ) // invert pts
}
this.boxMesh.geometry.verticesNeedUpdate = true
// TODO: gizmo moving
}
dispose() {
this.selectionHelper.dispose()
this.display.clear()
}
}
+8 -15
View File
@@ -29,14 +29,9 @@ export default class SelectionHelper extends EventEmitter {
// Handle clicks during camera moves
this.orbiting = false
// this.viewer.controls.addEventListener( 'control', debounce( () => { this.orbiting = false; console.log( 'ctrlstart '+ this.orbiting ) }, 200 ) )
this.viewer.controls.addEventListener( 'wake', () => { this.orbiting = true; console.log( 'wake' ) } )
// this.viewer.controls.addEventListener( 'controlend', () => { this.orbiting = false; console.log( 'controlend' ) } )
this.viewer.controls.addEventListener( 'sleep', () => { this.orbiting = false; console.log( 'sleep' ) } )
// this.viewer.controls.addEventListener( 'change', debounce( () => { this.orbiting = false }, 100 ) )
// this.viewer.controls.addEventListener( 'start', debounce( () => { this.orbiting = true }, 200 ) )
// this.viewer.controls.addEventListener( 'end', debounce( () => { this.orbiting = false }, 200 ) )
this.viewer.controls.addEventListener( 'wake', () => { this.orbiting = true } )
this.viewer.controls.addEventListener( 'sleep', () => { this.orbiting = false } )
// optional param allows for raycasting against a subset of objects
// this.subset = typeof _options !== 'undefined' && typeof _options.subset !== 'undefined' ? _options.subset : null;
@@ -73,16 +68,15 @@ export default class SelectionHelper extends EventEmitter {
}
// Handle mouseclicks
let mdTime
this.viewer.renderer.domElement.addEventListener( 'pointerdown', ( e ) => {
this.viewer.renderer.domElement.addEventListener( 'pointerdown', ( ) => {
mdTime = new Date().getTime()
} )
this.viewer.renderer.domElement.addEventListener( 'pointerup', ( e ) => {
let delta = new Date().getTime() - mdTime
console.log( delta )
this.pointerDown = false
console.log( 'pointerup: ' + this.orbiting )
if ( this.orbiting && delta > 250 ) return
let selectionObjects = this.getClickedObjects( e )
@@ -114,21 +108,19 @@ export default class SelectionHelper extends EventEmitter {
} )
this.viewer.renderer.domElement.addEventListener( 'dblclick', ( e ) => {
// if ( this.orbiting ) return // not needed for zoom to thing?
let selectionObjects = this.getClickedObjects( e )
this.emit( 'object-doubleclicked', selectionObjects )
// this.handleDoubleClick( selectionObjects )
} )
// Handle multiple object selection
this.multiSelect = false
document.addEventListener( 'keydown', ( e ) => {
if ( e.isComposing || e.keyCode === 229 ) return
if ( e.key === 'Shift' ) this.multiSelect = true
if ( e.key === 'Escape' ) this.unselect( )
} )
document.addEventListener( 'keyup', ( e ) => {
if ( e.isComposing || e.keyCode === 229 ) return
if ( e.key === 'Shift' ) this.multiSelect = false
@@ -146,7 +138,7 @@ export default class SelectionHelper extends EventEmitter {
this.raycaster.setFromCamera( normalizedPosition, this.viewer.camera )
let intersectedObjects = this.raycaster.intersectObjects( this.subset ? this._getGroupChildren( this.subset ) : this.viewer.sceneManager.objects )
intersectedObjects = intersectedObjects.filter( obj => this.viewer.sectionPlaneHelper.activePlanes.every( pl => pl.distanceToPoint( obj.point ) > 0 ) )
// intersectedObjects = intersectedObjects.filter( obj => this.viewer.sectionPlaneHelper.activePlanes.every( pl => pl.distanceToPoint( obj.point ) > 0 ) )
return intersectedObjects
}
@@ -175,6 +167,7 @@ export default class SelectionHelper extends EventEmitter {
}
dispose() {
super.dispose()
this.unselect()
this.originalSelectionObjects = null
}
+5 -48
View File
@@ -6,11 +6,9 @@ import { SSAOPass } from 'three/examples/jsm/postprocessing/SSAOPass.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'
import ObjectManager from './SceneObjectManager'
import SelectionHelper from './SelectionHelper'
import SectionPlaneHelper from './SectionPlaneHelper'
import ViewerObjectLoader from './ViewerObjectLoader'
import EventEmitter from './EventEmitter'
import SectionBox from './SectionBox'
import InteractionHandler from './InteractionHandler'
export default class Viewer extends EventEmitter {
@@ -46,8 +44,7 @@ export default class Viewer extends EventEmitter {
CameraControls.install( { THREE: THREE } )
this.controls = new CameraControls( this.camera, this.renderer.domElement )
this.controls.maxPolarAngle = Math.PI / 2
// this.controls.dampingFactor = 0.1
// this.controls.maxPolarAngle = Math.PI / 2
this.composer = new EffectComposer( this.renderer )
@@ -63,16 +60,8 @@ export default class Viewer extends EventEmitter {
this.controls.addEventListener( 'wake', () => { this.pauseSSAO = true } )
this.controls.addEventListener( 'sleep', () => { this.pauseSSAO = false; this.needsRender = true } )
// Selected Objects
this.selectionMaterial = new THREE.MeshLambertMaterial( { color: 0x0B55D2, emissive: 0x0B55D2, side: THREE.DoubleSide } )
this.selectedObjects = new THREE.Group()
this.scene.add( this.selectedObjects )
this.selectedObjects.renderOrder = 1000
this.selectionHelper = new SelectionHelper( this )
// Viewer registers double click event and supplies handler
this.selectionHelper.on( 'object-doubleclicked', this.handleDoubleClick.bind( this ) )
this.selectionHelper.on( 'object-clicked', this.handleSelect.bind( this ) )
// Keeps track of loaded objects
this.sceneManager = new ObjectManager( this )
if ( showStats ) {
this.stats = new Stats()
@@ -81,13 +70,7 @@ export default class Viewer extends EventEmitter {
window.addEventListener( 'resize', this.onWindowResize.bind( this ), false )
this.sectionPlaneHelper = new SectionPlaneHelper( this )
this.sceneManager = new ObjectManager( this )
this.sectionPlaneHelper.createSectionPlane()
// Section Box
this.sectionBox = new SectionBox( this )
this.interactions = new InteractionHandler( this )
this.needsRender = true
this.sceneLights()
@@ -96,32 +79,6 @@ export default class Viewer extends EventEmitter {
this.loaders = []
}
// handleDoubleClick moved from SelectionHelper
handleDoubleClick( objs ) {
if ( !objs || objs.length === 0 ) this.sceneManager.zoomExtents()
else this.sceneManager.zoomToObject( objs[0].object )
this.needsRender = true
}
// handleSelect moved from SelectionHelper
handleSelect( obj ) {
if ( obj.length === 0 ) {
this.deselect()
return
}
if ( !this.selectionHelper.multiSelect ) this.deselect()
let mesh = new THREE.Mesh( obj[0].object.geometry, this.selectionMaterial )
this.selectedObjects.add( mesh )
this.needsRender = true
}
deselect(){
this.selectedObjects.clear()
this.needsRender = true
}
sceneLights() {
let ambientLight = new THREE.AmbientLight( 0xffffff )
this.scene.add( ambientLight )