diff --git a/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCaps.ts b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCaps.ts new file mode 100644 index 000000000..3127208fa --- /dev/null +++ b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCaps.ts @@ -0,0 +1,95 @@ +import { + DefaultPipeline, + Extension, + IViewer, + ObjectLayers, + SectionTool, + SectionToolEvent +} from '@speckle/viewer' +import { + FrontSide, + KeepStencilOp, + Matrix4, + Mesh, + MeshBasicMaterial, + NotEqualStencilFunc, + Plane, + PlaneGeometry, + Quaternion, + Vector3 +} from 'three' +import { SectionCapsPipeline } from './SectionCapsPipeline' + +export class SectionCaps extends Extension { + protected planeMesh: Mesh + protected _enabled = false + + public get enabled(): boolean { + return this._enabled + } + public set enabled(value: boolean) { + this._enabled = value + if (value) { + this.viewer.getRenderer().pipeline = new SectionCapsPipeline( + this.viewer.getRenderer() + ) + } else { + this.viewer.getRenderer().pipeline = new DefaultPipeline( + this.viewer.getRenderer() + ) + } + } + + public get inject() { + return [SectionTool] + } + + constructor(viewer: IViewer, protected sectionTool: SectionTool) { + super(viewer) + + this.planeMesh = new Mesh( + new PlaneGeometry(1, 1), + new MeshBasicMaterial({ + color: 0x047efb, + side: FrontSide, + stencilWrite: true, + stencilFunc: NotEqualStencilFunc, + stencilFail: KeepStencilOp, + stencilZFail: KeepStencilOp, + stencilZPass: KeepStencilOp + }) + ) + this.planeMesh.renderOrder = 5 + this.planeMesh.layers.set(ObjectLayers.OVERLAY) + viewer.getRenderer().scene.add(this.planeMesh) + + this.sectionTool.on(SectionToolEvent.Updated, (planes: Plane[]) => { + const obb = this.sectionTool.getBox() + this.planeMesh.matrixAutoUpdate = false + this.planeMesh.matrix.copy( + this.getPlaneTransform( + // Top facing plane + planes[5], + new Vector3(2 * obb.halfSize.x, 2 * obb.halfSize.y, 1) + ) + ) + }) + } + + protected getPlaneTransform(plane: Plane, scale: Vector3) { + const obb = this.sectionTool.getBox() + const n = plane.normal.clone().normalize().negate() + + const up = Math.abs(n.z) < 0.999 ? new Vector3(0, 0, 1) : new Vector3(0, 1, 0) + const t1 = new Vector3().crossVectors(up, n).normalize() + const t2 = new Vector3().crossVectors(n, t1).normalize() + + const basis = new Matrix4().makeBasis(t1, t2, n) + const q = new Quaternion().setFromRotationMatrix(basis) + const p = plane.projectPoint(obb.center.clone(), new Vector3()) + + const worldTRS = new Matrix4().compose(p, q, scale) + + return worldTRS + } +} diff --git a/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCapsPipeline.ts b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCapsPipeline.ts new file mode 100644 index 000000000..f181bb627 --- /dev/null +++ b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/SectionCapsPipeline.ts @@ -0,0 +1,27 @@ +import { + DefaultPipeline, + ObjectLayers, + ObjectVisibility, + PipelineOptions, + SpeckleRenderer +} from '@speckle/viewer' +import { StencilFrontPass } from './StencilFrontPass' +import { StencilBackPass } from './StencilBackPass' + +export class SectionCapsPipeline extends DefaultPipeline { + constructor(speckleRenderer: SpeckleRenderer, options?: PipelineOptions) { + super(speckleRenderer, options) + + const stencilFrontPass = new StencilFrontPass() + stencilFrontPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + stencilFrontPass.setVisibility(ObjectVisibility.OPAQUE) + + const stencilBackPass = new StencilBackPass() + stencilBackPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH]) + stencilBackPass.setVisibility(ObjectVisibility.OPAQUE) + + this.dynamicStage.splice(3, 0, stencilFrontPass, stencilBackPass) + this.progressiveStage.splice(4, 0, stencilFrontPass, stencilBackPass) + this.passthroughStage.splice(1, 0, stencilFrontPass, stencilBackPass) + } +} diff --git a/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilBackPass.ts b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilBackPass.ts new file mode 100644 index 000000000..67796d60e --- /dev/null +++ b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilBackPass.ts @@ -0,0 +1,27 @@ +import { GeometryPass, SpeckleBasicMaterial } from '@speckle/viewer' +import { BackSide, DecrementWrapStencilOp, NoBlending } from 'three' + +export class StencilBackPass extends GeometryPass { + private stencilBackMaterial: SpeckleBasicMaterial + + get displayName(): string { + return 'STENCIL-BACK' + } + + get overrideMaterial(): SpeckleBasicMaterial { + return this.stencilBackMaterial + } + constructor() { + super() + + this.stencilBackMaterial = new SpeckleBasicMaterial({ color: 0x0000ff }, []) + // this.stencilBackMaterial.colorWrite = false + this.stencilBackMaterial.depthWrite = false + this.stencilBackMaterial.side = BackSide + this.stencilBackMaterial.stencilWrite = true + this.stencilBackMaterial.stencilFail = DecrementWrapStencilOp + this.stencilBackMaterial.stencilZFail = DecrementWrapStencilOp + this.stencilBackMaterial.stencilZPass = DecrementWrapStencilOp + this.stencilBackMaterial.blending = NoBlending + } +} diff --git a/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilFrontPass.ts b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilFrontPass.ts new file mode 100644 index 000000000..edbed6b09 --- /dev/null +++ b/packages/viewer-sandbox/src/Extensions/SectionCaps.ts/StencilFrontPass.ts @@ -0,0 +1,25 @@ +import { GeometryPass, SpeckleBasicMaterial } from '@speckle/viewer' +import { FrontSide, IncrementWrapStencilOp, NoBlending } from 'three' + +export class StencilFrontPass extends GeometryPass { + private stencilFrontMaterial: SpeckleBasicMaterial + + get displayName(): string { + return 'STENCIL-FRONT' + } + + get overrideMaterial(): SpeckleBasicMaterial { + return this.stencilFrontMaterial + } + constructor() { + super() + + this.stencilFrontMaterial = new SpeckleBasicMaterial({ color: 0xff0000 }, []) + this.stencilFrontMaterial.side = FrontSide + this.stencilFrontMaterial.stencilWrite = true + this.stencilFrontMaterial.stencilFail = IncrementWrapStencilOp + this.stencilFrontMaterial.stencilZFail = IncrementWrapStencilOp + this.stencilFrontMaterial.stencilZPass = IncrementWrapStencilOp + this.stencilFrontMaterial.blending = NoBlending + } +} diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index 11aba1a1e..0f7e545b6 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -59,6 +59,7 @@ import { ObjectLoader2Flags, ObjectLoader2Factory } from '@speckle/objectloader2' +import { SectionCaps } from './Extensions/SectionCaps.ts/SectionCaps' export default class Sandbox { private viewer: Viewer @@ -433,6 +434,8 @@ export default class Sandbox { } this.viewer.getExtension(SectionTool).setBox(box) this.viewer.getExtension(SectionTool).toggle() + const sectionCaps = this.viewer.getExtension(SectionCaps) + if (sectionCaps) sectionCaps.enabled = !sectionCaps.enabled }) const toggleSectionBoxVisibility = this.tabs.pages[0].addButton({ diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index e40cfdbda..3f689ba2e 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -56,6 +56,7 @@ const createViewer = async (containerName: string, _stream: string) => { const boxSelect = viewer.createExtension(BoxSelection) boxSelect.realtimeSelection = false viewer.createExtension(PassReader) + // viewer.createExtension(SectionCaps) const sandbox = new Sandbox(controlsContainer, viewer, multiSelectList) diff --git a/packages/viewer/src/index.ts b/packages/viewer/src/index.ts index ff980a26c..6d032c6ea 100644 --- a/packages/viewer/src/index.ts +++ b/packages/viewer/src/index.ts @@ -114,6 +114,8 @@ import { ProgressiveGPass } from './modules/pipeline/Passes/GPass.js' import { + PipelineOptions, + BasePipelineOptions, DefaultPipelineOptions, Pipeline } from './modules/pipeline/Pipelines/Pipeline.js' @@ -283,6 +285,8 @@ export { ArcticViewPipeline, TAAPipeline, ShadedViewPipeline, + PipelineOptions, + BasePipelineOptions, DefaultPipelineOptions, DefaultEdgesPipelineOptions, ViewModes,