diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index af389e925..349a1cb3a 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -124,4 +124,6 @@ await sandbox.loadUrl( // 'https://latest.speckle.dev/streams/c1faab5c62/commits/78bdd8eb76' // Point cloud // 'https://latest.speckle.dev/streams/2d19273d31/commits/9ceb423feb' + // Luis sphere + // 'https://speckle.xyz/streams/b85d53c3b4/commits/b47f21b707' ) diff --git a/packages/viewer/src/modules/SectionBox.js b/packages/viewer/src/modules/SectionBox.js index 0ca744058..e785005c3 100644 --- a/packages/viewer/src/modules/SectionBox.js +++ b/packages/viewer/src/modules/SectionBox.js @@ -3,6 +3,7 @@ import SelectionHelper from './legacy/SelectionHelper' import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js' import { Box3 } from 'three' import { ViewerEvent } from '../IViewer' +import { ObjectLayers } from './SpeckleRenderer' export default class SectionBox { constructor(viewer) { @@ -13,6 +14,7 @@ export default class SectionBox { this.dragging = false this.display = new THREE.Group() this.display.name = 'SectionBox' + this.display.layers.set(ObjectLayers.PROPS) this.viewer.speckleRenderer.scene.add(this.display) // box @@ -25,11 +27,13 @@ export default class SectionBox { }) this.cube = new THREE.Mesh(this.boxGeometry, this.material) this.cube.visible = false + this.cube.layers.set(ObjectLayers.PROPS) this.display.add(this.cube) this.boxHelper = new THREE.BoxHelper(this.cube, 0x0a66ff) this.boxHelper.material.opacity = 0.4 + this.boxHelper.layers.set(ObjectLayers.PROPS) this.display.add(this.boxHelper) // we're attaching the gizmo mover to this sphere in the box centre @@ -38,6 +42,7 @@ export default class SectionBox { sphere, new THREE.MeshStandardMaterial({ color: 0x00ffff }) ) + this.sphere.layers.set(ObjectLayers.PROPS) this.sphere.visible = false this.display.add(this.sphere) @@ -56,6 +61,7 @@ export default class SectionBox { }) ) this.hoverPlane.visible = false + this.hoverPlane.layers.set(ObjectLayers.PROPS) this.display.add(this.hoverPlane) this.dragging = false @@ -125,6 +131,12 @@ export default class SectionBox { this.viewer.cameraHandler.activeCam.camera, this.viewer.speckleRenderer.renderer.domElement ) + for (let k = 0; k < this.controls.children.length; k++) { + this.controls.children[k].traverse((obj) => { + obj.layers.set(ObjectLayers.PROPS) + }) + } + this.controls.getRaycaster().layers.set(ObjectLayers.PROPS) this.controls.setSize(0.75) this.display.add(this.controls) this.controls.addEventListener('change', this._draggingChangeHandler.bind(this)) diff --git a/packages/viewer/src/modules/SpeckleRenderer.ts b/packages/viewer/src/modules/SpeckleRenderer.ts index cc74c2d2b..e29a503af 100644 --- a/packages/viewer/src/modules/SpeckleRenderer.ts +++ b/packages/viewer/src/modules/SpeckleRenderer.ts @@ -51,6 +51,11 @@ import { RenderType } from './pipeline/Pipeline' +export enum ObjectLayers { + STREAM_CONTENT = 1, + PROPS = 2 +} + export default class SpeckleRenderer { private readonly SHOW_HELPERS = false private readonly ANGLE_EPSILON = 0.0001 @@ -140,6 +145,7 @@ export default class SpeckleRenderer { this._scene = new Scene() this.rootGroup = new Group() this.rootGroup.name = 'ContentGroup' + this.rootGroup.layers.set(ObjectLayers.STREAM_CONTENT) this._scene.add(this.rootGroup) this.batcher = new Batcher() @@ -168,7 +174,7 @@ export default class SpeckleRenderer { container.appendChild(this._renderer.domElement) this.pipeline = new Pipeline(this._renderer, this.batcher) - this.pipeline.configure(this._scene, this.viewer.cameraHandler.activeCam.camera) + this.pipeline.configure() this.pipeline.pipelineOptions = DefaultPipelineOptions this.input = new Input(this._renderer.domElement, InputOptionsDefault) @@ -184,14 +190,17 @@ export default class SpeckleRenderer { const sceneBoxHelper = new Box3Helper(this.sceneBox, new Color(0x0000ff)) sceneBoxHelper.name = 'SceneBoxHelper' + sceneBoxHelper.layers.set(ObjectLayers.PROPS) helpers.add(sceneBoxHelper) const dirLightHelper = new DirectionalLightHelper(this.sun, 50, 0xff0000) dirLightHelper.name = 'DirLightHelper' + dirLightHelper.layers.set(ObjectLayers.PROPS) helpers.add(dirLightHelper) const camHelper = new CameraHelper(this.sun.shadow.camera) camHelper.name = 'CamHelper' + camHelper.layers.set(ObjectLayers.PROPS) helpers.add(camHelper) } @@ -376,11 +385,13 @@ export default class SpeckleRenderer { const subtreeGroup = new Group() subtreeGroup.name = subtreeId + subtreeGroup.layers.set(ObjectLayers.STREAM_CONTENT) this.rootGroup.add(subtreeGroup) const batches = this.batcher.getBatches(subtreeId) batches.forEach((batch: Batch) => { const batchRenderable = batch.renderObject + batchRenderable.layers.set(ObjectLayers.STREAM_CONTENT) subtreeGroup.add(batch.renderObject) if (batch.geometryType === GeometryType.MESH) { const mesh = batchRenderable as unknown as Mesh @@ -452,6 +463,7 @@ export default class SpeckleRenderer { private addDirectLights() { this.sun = new DirectionalLight(0xffffff, 5) this.sun.name = 'sun' + this.sun.layers.set(ObjectLayers.STREAM_CONTENT) this._scene.add(this.sun) this.sun.castShadow = true diff --git a/packages/viewer/src/modules/legacy/SelectionHelper.js b/packages/viewer/src/modules/legacy/SelectionHelper.js index da704fd02..35d87902e 100644 --- a/packages/viewer/src/modules/legacy/SelectionHelper.js +++ b/packages/viewer/src/modules/legacy/SelectionHelper.js @@ -1,5 +1,6 @@ import * as THREE from 'three' import EventEmitter from '../EventEmitter' +import { ObjectLayers } from '../SpeckleRenderer' /** * Selects and deselects user added objects in the scene. Emits the array of all intersected objects on click. @@ -19,6 +20,7 @@ export default class _SelectionHelper extends EventEmitter { this.raycaster.params.Line.threshold = 0.1 this.raycaster.params.Line2 = {} this.raycaster.params.Line2.threshold = 1 + this.raycaster.layers.set(ObjectLayers.PROPS) // optional param allows for raycasting against a subset of objects // this.subset = typeof _options !== 'undefined' && typeof _options.subset !== 'undefined' ? _options.subset : null; diff --git a/packages/viewer/src/modules/objects/SpeckleRaycaster.ts b/packages/viewer/src/modules/objects/SpeckleRaycaster.ts index 1a400f647..a7907b59f 100644 --- a/packages/viewer/src/modules/objects/SpeckleRaycaster.ts +++ b/packages/viewer/src/modules/objects/SpeckleRaycaster.ts @@ -1,10 +1,12 @@ import { Object3D, Raycaster } from 'three' +import { ObjectLayers } from '../SpeckleRenderer' export class SpeckleRaycaster extends Raycaster { public onObjectIntersectionTest: (object: Object3D) => void = null constructor(origin?, direction?, near = 0, far = Infinity) { super(origin, direction, near, far) + this.layers.set(ObjectLayers.STREAM_CONTENT) } public intersectObjects(objects, recursive = true, intersects = []) { diff --git a/packages/viewer/src/modules/pipeline/ColorPass.ts b/packages/viewer/src/modules/pipeline/ColorPass.ts index b83a3c845..3a477aa03 100644 --- a/packages/viewer/src/modules/pipeline/ColorPass.ts +++ b/packages/viewer/src/modules/pipeline/ColorPass.ts @@ -1,10 +1,17 @@ -import { Camera, Scene, Texture } from 'three' -import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass' -import { SpecklePass } from './SpecklePass' +import { Camera, Color, Material, Scene, Texture } from 'three' +import { BaseSpecklePass, SpecklePass } from './SpecklePass' -export class ColorPass extends RenderPass implements SpecklePass { - public constructor(scene: Scene, camera: Camera) { - super(scene, camera) +export class ColorPass extends BaseSpecklePass implements SpecklePass { + private camera: Camera + private scene: Scene + private overrideMaterial: Material = null + private _oldClearColor: Color = new Color() + private clearColor: Color = null + private clearAlpha = 0 + private clearDepth = true + + public constructor() { + super() } public get displayName(): string { return 'COLOR' @@ -12,4 +19,56 @@ export class ColorPass extends RenderPass implements SpecklePass { public get outputTexture(): Texture { return null } + + public update(scene: Scene, camera: Camera) { + this.camera = camera + this.scene = scene + } + + render(renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */) { + const oldAutoClear = renderer.autoClear + renderer.autoClear = false + + let oldClearAlpha, oldOverrideMaterial + + if (this.overrideMaterial !== undefined) { + oldOverrideMaterial = this.scene.overrideMaterial + + this.scene.overrideMaterial = this.overrideMaterial + } + + if (this.clearColor) { + renderer.getClearColor(this._oldClearColor) + oldClearAlpha = renderer.getClearAlpha() + + renderer.setClearColor(this.clearColor, this.clearAlpha) + } + + if (this.clearDepth) { + renderer.clearDepth() + } + + this.applyLayers(this.camera) + + renderer.setRenderTarget(this.renderToScreen ? null : readBuffer) + + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if (this.clear) + renderer.clear( + renderer.autoClearColor, + renderer.autoClearDepth, + renderer.autoClearStencil + ) + renderer.render(this.scene, this.camera) + + if (this.clearColor) { + renderer.setClearColor(this._oldClearColor, oldClearAlpha) + } + + if (this.overrideMaterial !== undefined) { + this.scene.overrideMaterial = oldOverrideMaterial + } + + renderer.autoClear = oldAutoClear + } } diff --git a/packages/viewer/src/modules/pipeline/DepthPass.ts b/packages/viewer/src/modules/pipeline/DepthPass.ts index e0b4f3b5a..fa4ff4824 100644 --- a/packages/viewer/src/modules/pipeline/DepthPass.ts +++ b/packages/viewer/src/modules/pipeline/DepthPass.ts @@ -12,9 +12,8 @@ import { Texture, WebGLRenderTarget } from 'three' -import { Pass } from 'three/examples/jsm/postprocessing/Pass' import SpeckleDepthMaterial from '../materials/SpeckleDepthMaterial' -import { SpecklePass } from './SpecklePass' +import { BaseSpecklePass, SpecklePass } from './SpecklePass' export enum DepthType { PERSPECTIVE_DEPTH, @@ -26,7 +25,7 @@ export enum DepthSize { HALF } -export class DepthPass extends Pass implements SpecklePass { +export class DepthPass extends BaseSpecklePass implements SpecklePass { private renderTarget: WebGLRenderTarget private renderTargetHalf: WebGLRenderTarget private depthMaterial: SpeckleDepthMaterial = null @@ -134,6 +133,7 @@ export class DepthPass extends Pass implements SpecklePass { this.scene.overrideMaterial = this.depthMaterial renderer.shadowMap.enabled = false renderer.shadowMap.needsUpdate = false + this.applyLayers(this.camera) renderer.render(this.scene, this.camera) renderer.shadowMap.enabled = shadowmapEnabled renderer.shadowMap.needsUpdate = shadowmapNeedsUpdate diff --git a/packages/viewer/src/modules/pipeline/NormalsPass.ts b/packages/viewer/src/modules/pipeline/NormalsPass.ts index 771d0b165..d4aa86872 100644 --- a/packages/viewer/src/modules/pipeline/NormalsPass.ts +++ b/packages/viewer/src/modules/pipeline/NormalsPass.ts @@ -8,11 +8,10 @@ import { Texture, WebGLRenderTarget } from 'three' -import { Pass } from 'three/examples/jsm/postprocessing/Pass' import SpeckleNormalMaterial from '../materials/SpeckleNormalMaterial' -import { SpecklePass } from './SpecklePass' +import { BaseSpecklePass, SpecklePass } from './SpecklePass' -export class NormalsPass extends Pass implements SpecklePass { +export class NormalsPass extends BaseSpecklePass implements SpecklePass { private renderTarget: WebGLRenderTarget private normalsMaterial: SpeckleNormalMaterial = null private scene: Scene @@ -77,6 +76,7 @@ export class NormalsPass extends Pass implements SpecklePass { this.scene.overrideMaterial = this.normalsMaterial renderer.shadowMap.enabled = false renderer.shadowMap.needsUpdate = false + this.applyLayers(this.camera) renderer.render(this.scene, this.camera) renderer.shadowMap.enabled = shadowmapEnabled renderer.shadowMap.needsUpdate = shadowmapNeedsUpdate diff --git a/packages/viewer/src/modules/pipeline/Pipeline.ts b/packages/viewer/src/modules/pipeline/Pipeline.ts index f0d119092..ecf67f272 100644 --- a/packages/viewer/src/modules/pipeline/Pipeline.ts +++ b/packages/viewer/src/modules/pipeline/Pipeline.ts @@ -1,10 +1,10 @@ -import { Camera, Plane, Scene, Vector2, WebGLRenderer } from 'three' +import { Plane, Vector2, WebGLRenderer } from 'three' import { EffectComposer, Pass } from 'three/examples/jsm/postprocessing/EffectComposer.js' import Batcher from '../batching/Batcher' -import SpeckleRenderer from '../SpeckleRenderer' +import SpeckleRenderer, { ObjectLayers } from '../SpeckleRenderer' import { ApplySAOPass } from './ApplyAOPass' import { CopyOutputPass } from './CopyOutputPass' import { DepthPass, DepthSize, DepthType } from './DepthPass' @@ -220,17 +220,21 @@ export class Pipeline { this.composer.writeBuffer = null } - public configure(scene: Scene, camera: Camera) { + public configure() { this.depthPass = new DepthPass() this.normalsPass = new NormalsPass() this.dynamicAoPass = new DynamicSAOPass() - this.renderPass = new ColorPass(scene, camera) + this.renderPass = new ColorPass() this.applySaoPass = new ApplySAOPass() this.staticAoPass = new StaticAOPass() this.copyOutputPass = new CopyOutputPass() this.copyOutputPass.renderToScreen = true + this.depthPass.setLayers([ObjectLayers.STREAM_CONTENT]) + this.normalsPass.setLayers([ObjectLayers.STREAM_CONTENT]) + this.renderPass.setLayers([ObjectLayers.PROPS, ObjectLayers.STREAM_CONTENT]) + let restoreVisibility this.depthPass.onBeforeRender = () => { restoreVisibility = this._batcher.saveVisiblity() @@ -308,7 +312,7 @@ export class Pipeline { } public update(renderer: SpeckleRenderer) { - this.renderPass.camera = renderer.camera + this.renderPass.update(renderer.scene, renderer.camera) this.depthPass.update(renderer.scene, renderer.camera) this.dynamicAoPass.update(renderer.scene, renderer.camera) this.normalsPass.update(renderer.scene, renderer.camera) diff --git a/packages/viewer/src/modules/pipeline/SpecklePass.ts b/packages/viewer/src/modules/pipeline/SpecklePass.ts index f014fbec0..6d323bb7b 100644 --- a/packages/viewer/src/modules/pipeline/SpecklePass.ts +++ b/packages/viewer/src/modules/pipeline/SpecklePass.ts @@ -1,4 +1,6 @@ import { Camera, Plane, Scene, Texture } from 'three' +import { Pass } from 'three/examples/jsm/postprocessing/Pass' +import { ObjectLayers } from '../SpeckleRenderer' import { RenderType } from './Pipeline' export type InputColorTextureUniform = 'tDiffuse' @@ -17,9 +19,40 @@ export interface SpecklePass { setTexture?(uName: string, texture: Texture) setParams?(params: unknown) setClippingPlanes?(planes: Plane[]) + setLayers?(layers: ObjectLayers[]) } export interface SpeckleProgressivePass extends SpecklePass { setFrameIndex(index: number) setRenderType?(type: RenderType) } + +export abstract class BaseSpecklePass extends Pass implements SpecklePass { + protected layers: ObjectLayers[] = null + + constructor() { + super() + } + + get displayName(): string { + return 'BASE' + } + get outputTexture(): Texture { + return null + } + + public setLayers(layers: ObjectLayers[]) { + this.layers = layers + } + + protected applyLayers(camera: Camera) { + if (this.layers === null) { + camera.layers.enableAll() + return + } + camera.layers.disableAll() + this.layers.forEach((layer) => { + camera.layers.enable(layer) + }) + } +}