Integrating section box variable thikness outlines in the viewer

This commit is contained in:
AlexandruPopovici
2022-11-22 16:46:32 +02:00
parent f148281ae2
commit fa828cf427
4 changed files with 190 additions and 8 deletions
+2 -1
View File
@@ -83,7 +83,7 @@ sandbox.makeBatchesUI()
await sandbox.loadUrl(
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
// Revit sample house (good for bim-like stuff with many display meshes)
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'Super' heavy revit shit
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
// IFC building (good for a tree based structure)
@@ -135,4 +135,5 @@ await sandbox.loadUrl(
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/7f0c4d2fc1/'
// Classroom
// 'https://speckle.xyz/streams/0208ffb67b/commits/a980292728'
'https://latest.speckle.dev/streams/4658eb53b9/commits/328bd99997'
)
+12 -3
View File
@@ -4,9 +4,16 @@ import { TransformControls } from 'three/examples/jsm/controls/TransformControls
import { Box3 } from 'three'
import { ViewerEvent } from '../IViewer'
import { ObjectLayers } from './SpeckleRenderer'
import EventEmitter from './EventEmitter'
export default class SectionBox {
export const SectionBoxEvent = {
DRAG_START: 'section-box-drag-start',
DRAG_END: 'section-box-drag-end'
}
export default class SectionBox extends EventEmitter {
constructor(viewer) {
super()
this.viewer = viewer
this.viewer.speckleRenderer.renderer.localClippingEnabled = true
@@ -144,12 +151,14 @@ export default class SectionBox {
if (!this.display.visible) return
const val = !!event.value
if (val) {
this.emit(SectionBoxEvent.DRAG_START)
this.dragging = val
//@Dim: Not sure what this needs to do in the new viewer
//@Alex: this prevents(?) involuntary selection happening on mobile
// this.viewer.interactions.preventSelection = val
this.viewer.cameraHandler.enabled = !val
} else {
this.emit(SectionBoxEvent.DRAG_END)
setTimeout(() => {
this.dragging = val
//@Dim: Not sure what this needs to do in the new viewer
@@ -450,14 +459,14 @@ export default class SectionBox {
this.viewer.needsRender = true
}
off() {
disable() {
this.display.visible = false
this.viewer.speckleRenderer.renderer.localClippingEnabled = false
this.viewer.emit('section-box-changed', this.getCurrentBox())
this.viewer.needsRender = true
}
on() {
enable() {
this.display.visible = true
this.viewer.speckleRenderer.renderer.localClippingEnabled = true
this.viewer.emit('section-box-changed', this.getCurrentBox())
@@ -10,6 +10,7 @@ import {
DirectionalLightHelper,
DynamicDrawUsage,
Group,
InterleavedBufferAttribute,
Intersection,
Line3,
LineBasicMaterial,
@@ -26,6 +27,7 @@ import {
Spherical,
sRGBEncoding,
Texture,
Vector2,
Vector3,
VSMShadowMap,
WebGLRenderer
@@ -60,6 +62,9 @@ import {
} from './pipeline/Pipeline'
import { MeshBVHVisualizer } from 'three-mesh-bvh'
import MeshBatch from './batching/MeshBatch'
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
export enum ObjectLayers {
STREAM_CONTENT = 1,
@@ -493,6 +498,165 @@ export default class SpeckleRenderer {
this.pipeline.updateClippingPlanes(planes)
this.renderer.shadowMap.needsUpdate = true
this.resetPipeline()
// console.log('Updated planes -> ', this.viewer.sectionBox.planes[2])
}
public onSectionBoxDragStart() {
const clipOutline: Mesh = this.allObjects.getObjectByName('clip-outline') as Mesh
if (clipOutline) {
clipOutline.visible = false
}
}
public onSectionBoxDragEnd() {
const generate = () => {
let clipOutline: LineSegments2 = this.allObjects.getObjectByName(
'clip-outline'
) as LineSegments2
if (!clipOutline) {
// const lineGeometry = new BufferGeometry()
// const linePosAttr = new BufferAttribute(new Float32Array(50000), 3, false)
const lineGeometry = new LineSegmentsGeometry()
lineGeometry.setPositions(new Float32Array(60000))
// linePosAttr.setUsage(DynamicDrawUsage)
// lineGeometry.setAttribute('position', linePosAttr)
const material = new LineMaterial({
color: 0x047efb,
linewidth: 5, // in world units with size attenuation, pixels otherwise
worldUnits: false,
vertexColors: false,
alphaToCoverage: false,
resolution: new Vector2(919, 848)
})
material.color = new Color(0x047efb)
material.color.convertSRGBToLinear()
material.linewidth = 5
material.clipIntersection = true
material.worldUnits = false
clipOutline = new LineSegments2(lineGeometry, material)
clipOutline.name = 'clip-outline'
clipOutline.frustumCulled = false
clipOutline.renderOrder = 1
clipOutline.layers.set(ObjectLayers.PROPS)
// ;(clipOutline.material as Material).clipIntersection = true
this.allObjects.add(clipOutline)
}
const tempVector = new Vector3()
const tempVector1 = new Vector3()
const tempVector2 = new Vector3()
const tempVector3 = new Vector3()
const tempLine = new Line3()
const batches: MeshBatch[] = this.batcher.getBatches(
undefined,
GeometryType.MESH
) as MeshBatch[]
let index = 0
for (let b = 0; b < batches.length; b++) {
const posAttr = (
clipOutline.geometry.attributes['instanceStart'] as InterleavedBufferAttribute
).data
posAttr.setUsage(DynamicDrawUsage)
const posArray = posAttr.array as number[]
batches[b].boundsTree.shapecast({
intersectsBounds: (box) => {
const localPlane = this.viewer.sectionBox.planes[2]
return localPlane.intersectsBox(box)
},
intersectsTriangle: (tri) => {
// check each triangle edge to see if it intersects with the plane. If so then
// add it to the list of segments.
const localPlane = this.viewer.sectionBox.planes[2]
let count = 0
tempLine.start.copy(tri.a)
tempLine.end.copy(tri.b)
if (localPlane.intersectLine(tempLine, tempVector)) {
posArray[index * 3] = tempVector.x
posArray[index * 3 + 1] = tempVector.y
posArray[index * 3 + 2] = tempVector.z
index++
count++
}
tempLine.start.copy(tri.b)
tempLine.end.copy(tri.c)
if (localPlane.intersectLine(tempLine, tempVector)) {
posArray[index * 3] = tempVector.x
posArray[index * 3 + 1] = tempVector.y
posArray[index * 3 + 2] = tempVector.z
count++
index++
}
tempLine.start.copy(tri.c)
tempLine.end.copy(tri.a)
if (localPlane.intersectLine(tempLine, tempVector)) {
posArray[index * 3] = tempVector.x
posArray[index * 3 + 1] = tempVector.y
posArray[index * 3 + 2] = tempVector.z
count++
index++
}
// When the plane passes through a vertex and one of the edges of the triangle, there will be three intersections, two of which must be repeated
if (count === 3) {
// tempVector1.fromBufferAttribute(posAttr, index - 3)
// tempVector2.fromBufferAttribute(posAttr, index - 2)
// tempVector3.fromBufferAttribute(posAttr, index - 1)
tempVector1.set(
posArray[(index - 3) * 3],
posArray[(index - 3) * 3 + 1],
posArray[(index - 3) * 3 + 2]
)
tempVector2.set(
posArray[(index - 2) * 3],
posArray[(index - 2) * 3 + 1],
posArray[(index - 2) * 3 + 2]
)
tempVector3.set(
posArray[(index - 1) * 3],
posArray[(index - 1) * 3 + 1],
posArray[(index - 1) * 3 + 2]
)
// If the last point is a duplicate intersection
if (tempVector3.equals(tempVector1) || tempVector3.equals(tempVector2)) {
count--
index--
} else if (tempVector1.equals(tempVector2)) {
// If the last point is not a duplicate intersection
// Set the penultimate point as a distinct point and delete the last point
posArray[(index - 2) * 3] = tempVector.x
posArray[(index - 2) * 3 + 1] = tempVector.y
posArray[(index - 2) * 3 + 2] = tempVector.z
count--
index--
}
}
// If we only intersected with one or three sides then just remove it. This could be handled
// more gracefully.
if (count !== 2) {
index -= count
}
}
})
posAttr.needsUpdate = true
posAttr.updateRange = { offset: 0, count: posArray.length }
}
clipOutline.visible = true
// clipOutline.geometry.setDrawRange(0, index)
clipOutline.geometry.attributes['instanceStart'].needsUpdate = true
clipOutline.geometry.attributes['instanceEnd'].needsUpdate = true
clipOutline.geometry.computeBoundingBox()
clipOutline.geometry.computeBoundingSphere()
// console.log('Index -> ', index)
this.viewer.removeListener(ViewerEvent.SectionBoxUpdated, generate)
}
this.viewer.on(ViewerEvent.SectionBoxUpdated, generate)
}
public clipTest() {
+12 -4
View File
@@ -5,7 +5,7 @@ import ViewerObjectLoader from './ViewerObjectLoader'
import EventEmitter from './EventEmitter'
import CameraHandler from './context/CameraHanlder'
import SectionBox from './SectionBox'
import SectionBox, { SectionBoxEvent } from './SectionBox'
import { Clock, Texture } from 'three'
import { Assets } from './Assets'
import { Optional } from '../helpers/typeHelper'
@@ -84,10 +84,18 @@ export class Viewer extends EventEmitter implements IViewer {
;(window as any)._V = this // For debugging!
this.sectionBox = new SectionBox(this)
this.sectionBox.off()
this.sectionBox.disable()
this.on(ViewerEvent.SectionBoxUpdated, () => {
this.speckleRenderer.updateClippingPlanes(this.sectionBox.planes)
})
this.sectionBox.on(
SectionBoxEvent.DRAG_START,
this.speckleRenderer.onSectionBoxDragStart.bind(this.speckleRenderer)
)
this.sectionBox.on(
SectionBoxEvent.DRAG_END,
this.speckleRenderer.onSectionBoxDragEnd.bind(this.speckleRenderer)
)
this.frame()
this.resize()
@@ -301,11 +309,11 @@ export class Viewer extends EventEmitter implements IViewer {
}
public sectionBoxOff() {
this.sectionBox.off()
this.sectionBox.disable()
}
public sectionBoxOn() {
this.sectionBox.on()
this.sectionBox.enable()
}
public zoom(objectIds?: string[], fit?: number, transition?: boolean) {