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:
AlexandruPopovici
2022-10-31 14:41:36 +02:00
parent b393fb1f4b
commit 62b10797af
10 changed files with 144 additions and 18 deletions
+2
View File
@@ -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'
)
+12
View File
@@ -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))
+13 -1
View File
@@ -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)
})
}
}