diff --git a/src/mol-canvas3d/helper/bounding-sphere-helper.ts b/src/mol-canvas3d/helper/bounding-sphere-helper.ts index 84bba56ef3442a9043305e96e9dbfc7ab4b43aea..58204e881171262f5cd869950debf45c8caac7ef 100644 --- a/src/mol-canvas3d/helper/bounding-sphere-helper.ts +++ b/src/mol-canvas3d/helper/bounding-sphere-helper.ts @@ -12,23 +12,24 @@ 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'; +import { Vec3, Mat4 } from 'mol-math/linear-algebra'; export const DebugHelperParams = { sceneBoundingSpheres: PD.Boolean(false, { description: 'Show scene bounding spheres.' }), objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }), + instanceBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of instances.' }), } 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 { readonly scene: Scene private readonly parent: Scene private _props: DebugHelperProps private objectsData = new Map<RenderObject, BoundingSphereData>() + private instancesData = new Map<RenderObject, BoundingSphereData>() private sceneData: BoundingSphereData | undefined constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) { @@ -43,9 +44,16 @@ export class BoundingSphereHelper { const oldRO = new Set<RenderObject>() this.parent.forEach((r, ro) => { - let objectData = this.objectsData.get(ro) + const objectData = this.objectsData.get(ro) const newObjectData = updateBoundingSphereData(this.scene, r.boundingSphere, objectData) if (newObjectData) this.objectsData.set(ro, newObjectData) + + if (ro.type === 'mesh' || ro.type === 'lines' || ro.type === 'points') { + const instanceData = this.instancesData.get(ro) + const newInstanceData = updateBoundingSphereData(this.scene, ro.values.invariantBoundingSphere.ref.value, instanceData, ro.values.aTransform.ref.value, ro.values.instanceCount.ref.value) + if (newInstanceData) this.instancesData.set(ro, newInstanceData) + } + oldRO.delete(ro) }) oldRO.forEach(ro => { @@ -65,7 +73,9 @@ export class BoundingSphereHelper { 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') + + const instanceData = this.instancesData.get(ro) + if (instanceData) instanceData.renderObject.state.visible = ro.state.visible && this._props.instanceBoundingSpheres }) } @@ -76,7 +86,7 @@ export class BoundingSphereHelper { } get isEnabled() { - return this._props.sceneBoundingSpheres || this._props.objectBoundingSpheres + return this._props.sceneBoundingSpheres || this._props.objectBoundingSpheres || this._props.instanceBoundingSpheres } get props() { return this._props as Readonly<DebugHelperProps> } @@ -86,21 +96,28 @@ export class BoundingSphereHelper { } } -function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data?: BoundingSphereData) { +function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, transform?: Float32Array, transformCount?: number) { if (!data || !Sphere3D.exactEquals(data.boundingSphere, boundingSphere)) { if (data) scene.remove(data.renderObject) - const renderObject = createBoundingSphereRenderObject(boundingSphere) + const renderObject = createBoundingSphereRenderObject(boundingSphere, transform, transformCount) scene.add(renderObject) return { boundingSphere, renderObject } } } -function createBoundingSphereRenderObject(boundingSphere: Sphere3D) { +const tmpCenter = Vec3.zero() +const tmpM = Mat4.identity() +function createBoundingSphereRenderObject(boundingSphere: Sphere3D, transform?: Float32Array, transformCount?: number) { 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') + if (transform && transformCount) { + // TODO create instanced mesh? + for (let i = 0, _i = transformCount; i < _i; ++i) { + Mat4.fromArray(tmpM, transform, i * 16) + Vec3.transformMat4(tmpCenter, boundingSphere.center, tmpM) + if (boundingSphere.radius) addSphere(builderState, tmpCenter, boundingSphere.radius, 1) + } + } else { + if (boundingSphere.radius) addSphere(builderState, boundingSphere.center, boundingSphere.radius, 2) } const mesh = MeshBuilder.getMesh(builderState) const values = Mesh.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }) diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index c26c7d9c02e93a681a84d899bc35eda7a8bce4f9..66472e954b4d4e69ed7ed9fca40193ba2c90e44e 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -91,7 +91,7 @@ export namespace DirectVolume { const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount } - const boundingSphere = getBoundingSphere(gridDimension.ref.value, gridTransform.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value) + const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(gridDimension.ref.value, gridTransform.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value) const controlPoints = getControlPointsFromVec2Array(props.controlPoints) const transferTex = createTransferFunctionTexture(controlPoints, props.list) @@ -107,6 +107,7 @@ export namespace DirectVolume { aPosition: ValueCell.create(VolumeBox.vertices as Float32Array), elements: ValueCell.create(VolumeBox.indices as Uint32Array), boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), uIsoValue: ValueCell.create(props.isoValue), uBboxMin: bboxMin, @@ -135,10 +136,13 @@ export namespace DirectVolume { } export function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) { - const boundingSphere = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value) + const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value) if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { ValueCell.update(values.boundingSphere, boundingSphere) } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } } export function createRenderableState(props: PD.Values<Params>): RenderableState { diff --git a/src/mol-geo/geometry/lines/lines.ts b/src/mol-geo/geometry/lines/lines.ts index 5f9fc1d1cfa77586af8792468e05a8207e268d0a..d9d377cefab034ee4a72c95ccee3f3ddf7f71b68 100644 --- a/src/mol-geo/geometry/lines/lines.ts +++ b/src/mol-geo/geometry/lines/lines.ts @@ -106,17 +106,9 @@ export namespace Lines { const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount } - const boundingSphere = Sphere3D.addSphere( - calculateBoundingSphere( - lines.startBuffer.ref.value, lines.lineCount, - transform.aTransform.ref.value, transform.instanceCount.ref.value - ), - calculateBoundingSphere( - lines.startBuffer.ref.value, lines.lineCount, - transform.aTransform.ref.value, transform.instanceCount.ref.value - ) - ) - + const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(lines.startBuffer.ref.value, lines.endBuffer.ref.value, lines.lineCount, + transform.aTransform.ref.value, transform.instanceCount.ref.value) + return { aMapping: lines.mappingBuffer, aGroup: lines.groupBuffer, @@ -124,6 +116,7 @@ export namespace Lines { aEnd: lines.endBuffer, elements: lines.indexBuffer, boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), ...color, ...size, ...marker, @@ -142,18 +135,22 @@ export namespace Lines { } export function updateBoundingSphere(values: LinesValues, lines: Lines) { - const boundingSphere = Sphere3D.addSphere( - calculateBoundingSphere( - values.aStart.ref.value, lines.lineCount, - values.aTransform.ref.value, values.instanceCount.ref.value - ), - calculateBoundingSphere( - values.aEnd.ref.value, lines.lineCount, - values.aTransform.ref.value, values.instanceCount.ref.value - ), - ) + const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.aStart.ref.value, values.aEnd.ref.value, lines.lineCount, + values.aTransform.ref.value, values.instanceCount.ref.value) if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { ValueCell.update(values.boundingSphere, boundingSphere) } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } + } +} + +function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) { + const start = calculateBoundingSphere(lineStart, lineCount, transform, transformCount) + const end = calculateBoundingSphere(lineEnd, lineCount, transform, transformCount) + return { + boundingSphere: Sphere3D.addSphere(start.boundingSphere, end.boundingSphere), + invariantBoundingSphere: Sphere3D.addSphere(start.invariantBoundingSphere, end.invariantBoundingSphere) } } \ No newline at end of file diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index f6381256b11423d73cd42726c6d4819e6258185f..c638c8fa69cb1c1f3d5492d456c7bd593cde5803 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -356,7 +356,7 @@ export namespace Mesh { const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount } - const boundingSphere = calculateBoundingSphere( + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( mesh.vertexBuffer.ref.value, mesh.vertexCount, transform.aTransform.ref.value, transform.instanceCount.ref.value ) @@ -367,6 +367,7 @@ export namespace Mesh { aGroup: mesh.groupBuffer, elements: mesh.indexBuffer, boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), ...color, ...marker, ...transform, @@ -386,7 +387,7 @@ export namespace Mesh { const counts = { drawCount: mesh.triangleCount * 3, groupCount: 1, instanceCount: 1 } - const boundingSphere = calculateBoundingSphere( + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( mesh.vertexBuffer.ref.value, mesh.vertexCount, transform.aTransform.ref.value, transform.instanceCount.ref.value ) @@ -397,6 +398,7 @@ export namespace Mesh { aGroup: mesh.groupBuffer, elements: mesh.indexBuffer, boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), ...color, ...marker, ...transform, @@ -416,13 +418,16 @@ export namespace Mesh { } export function updateBoundingSphere(values: MeshValues, mesh: Mesh) { - const boundingSphere = calculateBoundingSphere( + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( values.aPosition.ref.value, mesh.vertexCount, values.aTransform.ref.value, values.instanceCount.ref.value ) if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { ValueCell.update(values.boundingSphere, boundingSphere) } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } } } diff --git a/src/mol-geo/geometry/points/points.ts b/src/mol-geo/geometry/points/points.ts index 2a3e2288065610d2b7a178eab9aa22cd8188a537..598c3b00cec4479bb9461040b9eb4a7739f8960d 100644 --- a/src/mol-geo/geometry/points/points.ts +++ b/src/mol-geo/geometry/points/points.ts @@ -71,7 +71,7 @@ export namespace Points { const counts = { drawCount: points.pointCount, groupCount, instanceCount } - const boundingSphere = calculateBoundingSphere( + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( points.centerBuffer.ref.value, points.pointCount, transform.aTransform.ref.value, transform.instanceCount.ref.value ) @@ -80,6 +80,7 @@ export namespace Points { aPosition: points.centerBuffer, aGroup: points.groupBuffer, boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), ...color, ...size, ...marker, @@ -100,13 +101,16 @@ export namespace Points { } export function updateBoundingSphere(values: PointsValues, points: Points) { - const boundingSphere = calculateBoundingSphere( + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( values.aPosition.ref.value, points.pointCount, values.aTransform.ref.value, values.instanceCount.ref.value ) if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { ValueCell.update(values.boundingSphere, boundingSphere) } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } } export function createRenderableState(props: PD.Values<Params>): RenderableState { diff --git a/src/mol-gl/_spec/renderable.spec.ts b/src/mol-gl/_spec/renderable.spec.ts index 9053ec9d130ac52dd20f2aad02565bc4fc6e029c..ab0f26a504a1bcd49f6a50d44153c0a12f59e67f 100644 --- a/src/mol-gl/_spec/renderable.spec.ts +++ b/src/mol-gl/_spec/renderable.spec.ts @@ -29,12 +29,12 @@ describe('renderable', () => { 2, 0, 0, 0 ]) - const bs = calculateBoundingSphere( + const { boundingSphere } = calculateBoundingSphere( position, position.length / 3, transform, transform.length / 16 ) - expect(bs.radius).toBe(1.5) - expect(bs.center).toEqual([1.5, 0.0, 0.0]) + expect(boundingSphere.radius).toBe(1.5) + expect(boundingSphere.center).toEqual([1.5, 0.0, 0.0]) }) }) diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index c36edb83df75e780804a5e4379d428c274f30aff..64a859e14cd5a44824cad68fd517bc284ecb5ffe 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -58,6 +58,7 @@ function createPoints() { Mat4.toArray(m4, aTransform.ref.value, 0) const boundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2)) + const invariantBoundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2)) const values: PointsValues = { aPosition, @@ -77,6 +78,7 @@ function createPoints() { drawCount: ValueCell.create(3), instanceCount: ValueCell.create(1), boundingSphere, + invariantBoundingSphere, dPointSizeAttenuation: ValueCell.create(true), dPointFilledCircle: ValueCell.create(false), diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index a4c9d533b0956fb733a0ec38e4be70702f0a0d8c..228cdec89e6fb0f5111b0c4e41d7d12d527ef7b9 100644 --- a/src/mol-gl/renderable/direct-volume.ts +++ b/src/mol-gl/renderable/direct-volume.ts @@ -29,6 +29,7 @@ export const DirectVolumeSchema = { drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), boundingSphere: ValueSpec('sphere'), + invariantBoundingSphere: ValueSpec('sphere'), aPosition: AttributeSpec('float32', 3, 0), elements: ElementsSpec('uint32'), diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 9cf03c9b48fd055f3dabaa4dfbeddabfed62e442..3bfac0c71e543d278ab36709b3210c8241a10f63 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -199,6 +199,7 @@ export const BaseSchema = { drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), boundingSphere: ValueSpec('sphere'), + invariantBoundingSphere: ValueSpec('sphere'), dUseFog: DefineSpec('boolean'), } diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index aa77abc80ca4623901e7ac251d87425c630ab345..75d3046f6983a5bf1d0ab3972ace5d05cf43cd0b 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -55,7 +55,7 @@ export function calculateBoundingSphereFromValues(values: PositionValues) { return calculateBoundingSphere(position, positionCount, transform, transformCount) } -export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number): Sphere3D { +export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } { const m = Mat4.zero() @@ -105,5 +105,5 @@ export function calculateBoundingSphere(position: Float32Array, positionCount: n radius = Math.max(radius, Vec3.distance(center, ct) + r) } - return { center, radius }; + return { boundingSphere: { center, radius }, invariantBoundingSphere: { center: c, radius: r } }; } \ No newline at end of file