Handled #1110. Added the concept of 'ObjectLayers' which uses three's existing implementation for selective layer rendering/lighting/picking. Also added the BaseSpecklePass abstract class which provides a default implementation for any subclassing pass that needs to use specific layers when rendering.
This commit is contained in:
@@ -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'
|
||||
)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = []) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user