Skip to content
Snippets Groups Projects
Commit 8ef5a2a5 authored by Alexander Rose's avatar Alexander Rose
Browse files

improved bounding sphere helper

parent 8c13f7b8
No related branches found
No related tags found
No related merge requests found
......@@ -27,7 +27,7 @@ import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
import { Color } from 'mol-util/color';
import { Camera } from './camera';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { BoundingSphereHelper } from './helper/bounding-sphere-helper';
import { BoundingSphereHelper, DebugHelperParams } from './helper/bounding-sphere-helper';
export const Canvas3DParams = {
// TODO: FPS cap?
......@@ -37,9 +37,7 @@ export const Canvas3DParams = {
clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
debug: PD.Group({
showBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
})
debug: PD.Group(DebugHelperParams)
}
export type Canvas3DParams = PD.Values<typeof Canvas3DParams>
......@@ -48,9 +46,6 @@ export { Canvas3D }
interface Canvas3D {
readonly webgl: WebGLContext,
hide: (repr: Representation.Any) => void
show: (repr: Representation.Any) => void
add: (repr: Representation.Any) => void
remove: (repr: Representation.Any) => void
update: () => void
......@@ -128,7 +123,7 @@ namespace Canvas3D {
let drawPending = false
let lastRenderTime = -1
const boundingSphereHelper = new BoundingSphereHelper(scene, p.debug.showBoundingSpheres)
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug)
function getLoci(pickingId: PickingId) {
let loci: Loci = EmptyLoci
......@@ -192,16 +187,24 @@ namespace Canvas3D {
switch (variant) {
case 'pick':
objectPickTarget.bind();
renderer.clear()
renderer.render(scene, 'pickObject');
instancePickTarget.bind();
renderer.clear()
renderer.render(scene, 'pickInstance');
groupPickTarget.bind();
renderer.clear()
renderer.render(scene, 'pickGroup');
break;
case 'draw':
webgl.unbindFramebuffer();
renderer.setViewport(0, 0, canvas.width, canvas.height);
renderer.clear()
renderer.render(scene, variant);
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, 'draw')
}
lastRenderTime = now()
pickDirty = true
break;
......@@ -298,8 +301,8 @@ namespace Canvas3D {
}
reprRenderObjects.set(repr, newRO)
reprCount.next(reprRenderObjects.size)
boundingSphereHelper.update()
scene.update()
if (debugHelper.isEnabled) debugHelper.update()
isUpdating = false
requestDraw(true)
}
......@@ -309,15 +312,6 @@ namespace Canvas3D {
return {
webgl,
hide: (repr: Representation.Any) => {
const renderObjectSet = reprRenderObjects.get(repr)
if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false)
},
show: (repr: Representation.Any) => {
const renderObjectSet = reprRenderObjects.get(repr)
if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = true)
},
add: (repr: Representation.Any) => {
add(repr)
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
......@@ -335,8 +329,8 @@ namespace Canvas3D {
renderObjects.forEach(o => scene.remove(o))
reprRenderObjects.delete(repr)
reprCount.next(reprRenderObjects.size)
boundingSphereHelper.update()
scene.update()
if (debugHelper.isEnabled) debugHelper.update()
isUpdating = false
requestDraw(true)
}
......@@ -345,6 +339,7 @@ namespace Canvas3D {
clear: () => {
reprRenderObjects.clear()
scene.clear()
debugHelper.clear()
},
// draw,
......@@ -387,8 +382,8 @@ namespace Canvas3D {
if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== renderer.props.pickingAlphaThreshold) {
renderer.setPickingAlphaThreshold(props.pickingAlphaThreshold)
}
if (props.debug && props.debug.showBoundingSpheres !== undefined) {
boundingSphereHelper.visible = props.debug.showBoundingSpheres
if (props.debug) {
debugHelper.setProps(props.debug)
}
requestDraw(true)
},
......@@ -400,9 +395,7 @@ namespace Canvas3D {
clip: p.clip,
fog: p.fog,
pickingAlphaThreshold: renderer.props.pickingAlphaThreshold,
debug: {
showBoundingSpheres: boundingSphereHelper.visible
}
debug: { ...debugHelper.props }
}
},
get input() {
......@@ -413,6 +406,7 @@ namespace Canvas3D {
},
dispose: () => {
scene.clear()
debugHelper.clear()
input.dispose()
controls.dispose()
renderer.dispose()
......
......@@ -4,39 +4,105 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
import { createMeshRenderObject, RenderObject } from 'mol-gl/render-object'
import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
import { Mesh } from 'mol-geo/geometry/mesh/mesh';
import { Geometry } from 'mol-geo/geometry/geometry';
import { ValueCell } from 'mol-util';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import Scene from 'mol-gl/scene';
import { WebGLContext } from 'mol-gl/webgl/context';
import { Sphere3D } from 'mol-math/geometry';
export const DebugHelperParams = {
sceneBoundingSpheres: PD.Boolean(false, { description: 'Show scene bounding spheres.' }),
objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }),
}
export type DebugHelperParams = typeof DebugHelperParams
export type DebugHelperProps = PD.Values<DebugHelperParams>
type BoundingSphereData = { boundingSphere: Sphere3D, renderObject: RenderObject }
// TODO per-object-transform bounding spheres
export class BoundingSphereHelper {
private mesh: Mesh
private renderObject: MeshRenderObject
readonly scene: Scene
private readonly parent: Scene
private _props: DebugHelperProps
private objectsData = new Map<RenderObject, BoundingSphereData>()
private sceneData: BoundingSphereData | undefined
constructor(private scene: Scene, visible: boolean) {
this.mesh = MeshBuilder.getMesh(MeshBuilder.createState(1024, 512))
const values = Mesh.createValuesSimple(this.mesh, { alpha: 0.1, doubleSided: false })
this.renderObject = createMeshRenderObject(values, { visible, pickable: false, opaque: false })
scene.add(this.renderObject)
constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
this.scene = Scene.create(ctx)
this.parent = parent
this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props }
}
update() {
const builderState = MeshBuilder.createState(1024, 512, this.mesh)
if (this.scene.boundingSphere.radius) {
addSphere(builderState, this.scene.boundingSphere.center, this.scene.boundingSphere.radius, 2)
}
this.scene.forEach(r => {
if (r.boundingSphere.radius) {
addSphere(builderState, r.boundingSphere.center, r.boundingSphere.radius, 2)
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData)
if (newSceneData) this.sceneData = newSceneData
const oldRO = new Set<RenderObject>()
this.parent.forEach((r, ro) => {
let objectData = this.objectsData.get(ro)
const newObjectData = updateBoundingSphereData(this.scene, r.boundingSphere, objectData)
if (newObjectData) this.objectsData.set(ro, newObjectData)
oldRO.delete(ro)
})
oldRO.forEach(ro => {
const objectData = this.objectsData.get(ro)
if (objectData) {
this.scene.remove(objectData.renderObject)
this.objectsData.delete(ro)
}
})
this.mesh = MeshBuilder.getMesh(builderState)
ValueCell.update(this.renderObject.values.drawCount, Geometry.getDrawCount(this.mesh))
}
get visible() { return this.renderObject.state.visible }
set visible(value: boolean) { this.renderObject.state.visible = value }
syncVisibility() {
if(this.sceneData) {
this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres
}
this.parent.forEach((_, ro) => {
const objectData = this.objectsData.get(ro)
if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres
else console.error('expected to have debug render object')
})
}
clear() {
this.sceneData = undefined
this.objectsData.clear()
this.scene.clear()
}
get isEnabled() {
return this._props.sceneBoundingSpheres || this._props.objectBoundingSpheres
}
get props() { return this._props as Readonly<DebugHelperProps> }
setProps (props: Partial<DebugHelperProps>) {
Object.assign(this._props, props)
if (this.isEnabled) this.update()
}
}
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data?: BoundingSphereData) {
if (!data || !Sphere3D.exactEquals(data.boundingSphere, boundingSphere)) {
if (data) scene.remove(data.renderObject)
const renderObject = createBoundingSphereRenderObject(boundingSphere)
scene.add(renderObject)
return { boundingSphere, renderObject }
}
}
function createBoundingSphereRenderObject(boundingSphere: Sphere3D) {
const builderState = MeshBuilder.createState(1024, 512)
if (boundingSphere.radius) {
addSphere(builderState, boundingSphere.center, boundingSphere.radius, 2)
} else if (isNaN(boundingSphere.radius)) {
console.warn('boundingSphere.radius is NaN')
}
const mesh = MeshBuilder.getMesh(builderState)
const values = Mesh.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false })
return createMeshRenderObject(values, { visible: true, pickable: false, opaque: false })
}
\ No newline at end of file
......@@ -36,6 +36,7 @@ interface Renderer {
readonly stats: RendererStats
readonly props: RendererProps
clear: () => void
render: (scene: Scene, variant: RenderVariant) => void
setViewport: (x: number, y: number, width: number, height: number) => void
setClearColor: (color: Color) => void
......@@ -162,9 +163,6 @@ namespace Renderer {
currentProgramId = -1
gl.depthMask(true)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.disable(gl.BLEND)
gl.enable(gl.DEPTH_TEST)
scene.eachOpaque((r) => renderObject(r, variant))
......@@ -177,6 +175,10 @@ namespace Renderer {
}
return {
clear: () => {
gl.depthMask(true)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
},
render,
setClearColor,
......
......@@ -111,14 +111,10 @@ namespace Scene {
renderableMap.forEach(callbackFn)
},
eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
renderableMap.forEach((r, o) => {
if (r.state.opaque) callbackFn(r, o)
})
renderableMap.forEach((r, o) => { if (r.state.opaque) callbackFn(r, o) })
},
eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
renderableMap.forEach((r, o) => {
if (!r.state.opaque) callbackFn(r, o)
})
renderableMap.forEach((r, o) => { if (!r.state.opaque) callbackFn(r, o) })
},
get count() {
return renderableMap.size
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment