diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index cec5296a3f2020f29563d803f225698808a964fd..1e6575fbf7ea4bab48105808a5e02b754bf1f0c9 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -18,6 +18,8 @@ import { TransformData } from '../transform-data'; import { createColors } from '../color-data'; import { createMarkers } from '../marker-data'; import { Geometry, Theme } from '../geometry'; +import { transformPositionArray } from 'mol-geo/util'; +import { calculateBoundingSphere } from 'mol-gl/renderable/util'; const VolumeBox = Box() const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][] @@ -77,6 +79,7 @@ export namespace DirectVolume { export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: Props): Promise<DirectVolumeValues> { const { gridTexture, gridTextureDim } = directVolume + const { bboxSize, bboxMin, bboxMax, gridDimension, transform: gridTransform } = directVolume const { instanceCount, groupCount } = locationIt const color = await createColors(ctx, locationIt, theme.color) @@ -84,7 +87,12 @@ export namespace DirectVolume { const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount } - const { bboxSize, bboxMin, bboxMax, gridDimension, transform: gridTransform } = directVolume + const vertices = new Float32Array(VolumeBox.vertices) + transformPositionArray(gridTransform.ref.value, vertices, 0, vertices.length / 3) + const boundingSphere = calculateBoundingSphere( + vertices, vertices.length / 3, + transform.aTransform.ref.value, transform.instanceCount.ref.value + ) const controlPoints = getControlPointsFromString(props.controlPoints) const transferTex = createTransferFunctionTexture(controlPoints) @@ -99,6 +107,7 @@ export namespace DirectVolume { aPosition: ValueCell.create(VolumeBox.vertices as Float32Array), elements: ValueCell.create(VolumeBox.indices as Uint32Array), + boundingSphere: ValueCell.create(boundingSphere), uIsoValue: ValueCell.create(props.isoValueAbsolute), uBboxMin: bboxMin, @@ -117,6 +126,16 @@ export namespace DirectVolume { } export function updateValues(values: DirectVolumeValues, props: Props) { + const vertices = new Float32Array(values.aPosition.ref.value) + transformPositionArray(values.uTransform.ref.value, vertices, 0, vertices.length / 3) + const boundingSphere = calculateBoundingSphere( + vertices, Math.floor(vertices.length / 3), + values.aTransform.ref.value, values.instanceCount.ref.value + ) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + ValueCell.updateIfChanged(values.uIsoValue, props.isoValueAbsolute) ValueCell.updateIfChanged(values.uAlpha, props.alpha) ValueCell.updateIfChanged(values.dUseFog, props.useFog) diff --git a/src/mol-geo/geometry/lines/lines.ts b/src/mol-geo/geometry/lines/lines.ts index 5594a82416818f6f605863bf67367451ee480cdf..54954156927bbeeed26faf7d6b57115a64ad1256 100644 --- a/src/mol-geo/geometry/lines/lines.ts +++ b/src/mol-geo/geometry/lines/lines.ts @@ -18,6 +18,8 @@ import { LinesValues } from 'mol-gl/renderable/lines'; import { Mesh } from '../mesh/mesh'; import { LinesBuilder } from './lines-builder'; import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { Sphere3D } from 'mol-math/geometry'; /** Wide line */ export interface Lines { @@ -105,12 +107,24 @@ 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 + ) + ) + return { aMapping: lines.mappingBuffer, aGroup: lines.groupBuffer, aStart: lines.startBuffer, aEnd: lines.endBuffer, elements: lines.indexBuffer, + boundingSphere: ValueCell.create(boundingSphere), ...color, ...size, ...marker, @@ -124,6 +138,20 @@ export namespace Lines { } export function updateValues(values: LinesValues, props: Props) { + const boundingSphere = Sphere3D.addSphere( + calculateBoundingSphere( + values.aStart.ref.value, Math.floor(values.aStart.ref.value.length / 3), + values.aTransform.ref.value, values.instanceCount.ref.value + ), + calculateBoundingSphere( + values.aEnd.ref.value, Math.floor(values.aEnd.ref.value.length / 3), + values.aTransform.ref.value, values.instanceCount.ref.value + ), + ) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + Geometry.updateValues(values, props) ValueCell.updateIfChanged(values.dLineSizeAttenuation, props.lineSizeAttenuation) } diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index 8bc3fcf1c685e4dae7b28d9b65f6148cc152d8d7..9d4ca0deba32cafc054e75dce3b86452c416eba7 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -17,6 +17,7 @@ import { LocationIterator } from '../../util/location-iterator'; import { createColors } from '../color-data'; import { ChunkedArray } from 'mol-data/util'; import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { calculateBoundingSphere } from 'mol-gl/renderable/util'; export interface Mesh { readonly kind: 'mesh', @@ -353,11 +354,17 @@ export namespace Mesh { const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount } + const boundingSphere = calculateBoundingSphere( + mesh.vertexBuffer.ref.value, mesh.vertexCount, + transform.aTransform.ref.value, transform.instanceCount.ref.value + ) + return { aPosition: mesh.vertexBuffer, aNormal: mesh.normalBuffer, aGroup: mesh.groupBuffer, elements: mesh.indexBuffer, + boundingSphere: ValueCell.create(boundingSphere), ...color, ...marker, ...transform, @@ -370,6 +377,14 @@ export namespace Mesh { } export function updateValues(values: MeshValues, props: Props) { + const boundingSphere = calculateBoundingSphere( + values.aPosition.ref.value, Math.floor(values.aPosition.ref.value.length / 3), + values.aTransform.ref.value, values.instanceCount.ref.value + ) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + Geometry.updateValues(values, props) ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided) ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded) diff --git a/src/mol-geo/geometry/points/points.ts b/src/mol-geo/geometry/points/points.ts index 90aa30d4a76efcee75cb1f8bc1569cc069154b3c..84a191bf7559f158dc926384f9d44200c4902270 100644 --- a/src/mol-geo/geometry/points/points.ts +++ b/src/mol-geo/geometry/points/points.ts @@ -16,6 +16,8 @@ import { createSizes } from '../size-data'; import { TransformData } from '../transform-data'; import { LocationIterator } from '../../util/location-iterator'; import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { Sphere3D } from 'mol-math/geometry'; /** Point cloud */ export interface Points { @@ -69,9 +71,15 @@ export namespace Points { const counts = { drawCount: points.pointCount, groupCount, instanceCount } + const boundingSphere = calculateBoundingSphere( + points.centerBuffer.ref.value, points.pointCount, + transform.aTransform.ref.value, transform.instanceCount.ref.value + ) + return { aPosition: points.centerBuffer, aGroup: points.groupBuffer, + boundingSphere: ValueCell.create(boundingSphere), ...color, ...size, ...marker, @@ -85,6 +93,14 @@ export namespace Points { } export function updateValues(values: PointsValues, props: Props) { + const boundingSphere = calculateBoundingSphere( + values.aPosition.ref.value, Math.floor(values.aPosition.ref.value.length / 3), + values.aTransform.ref.value, values.instanceCount.ref.value + ) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + Geometry.updateValues(values, props) ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation) ValueCell.updateIfChanged(values.dPointFilledCircle, props.pointFilledCircle) diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index 6ede20f9997f0ea08e4b64b06dc1dacb73883dfc..b564c9076680c2eadeb846ec3cf44a2f65e815ea 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -21,6 +21,7 @@ import Scene from '../scene'; import { createEmptyMarkers } from 'mol-geo/geometry/marker-data'; import { fillSerial } from 'mol-util/array'; import { Color } from 'mol-util/color'; +import { Sphere3D } from 'mol-math/geometry'; // function writeImage(gl: WebGLRenderingContext, width: number, height: number) { // const pixels = new Uint8Array(width * height * 4) @@ -56,6 +57,8 @@ function createPoints() { const m4 = Mat4.identity() Mat4.toArray(m4, aTransform.ref.value, 0) + const boundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2)) + const values: PointsValues = { aPosition, aGroup, @@ -71,6 +74,7 @@ function createPoints() { drawCount: ValueCell.create(3), instanceCount: ValueCell.create(1), + boundingSphere, dPointSizeAttenuation: ValueCell.create(true), dPointFilledCircle: ValueCell.create(false), diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 1567603088c3853aec2a892b86a83188db54c62c..8512933e0d78e1beea97d2ba39da357f7bdf8322 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -8,8 +8,6 @@ import { Program } from './webgl/program'; import { RenderableValues, Values, RenderableSchema } from './renderable/schema'; import { RenderVariant, RenderItem } from './webgl/render-item'; import { Sphere3D } from 'mol-math/geometry'; -// import { calculateBoundingSphereFromValues } from './renderable/util'; -// import { Sphere } from 'mol-geo/primitive/sphere'; import { Vec3 } from 'mol-math/linear-algebra'; export type RenderableState = { @@ -30,29 +28,22 @@ export interface Renderable<T extends RenderableValues> { } export function createRenderable<T extends Values<RenderableSchema>>(renderItem: RenderItem, values: T, state: RenderableState): Renderable<T> { - // TODO let boundingSphere: Sphere3D = Sphere3D.create(Vec3.zero(), 50) return { get values () { return values }, get state () { return state }, get boundingSphere () { + if (values.boundingSphere) { + Sphere3D.copy(boundingSphere, values.boundingSphere.ref.value) + } return boundingSphere - // TODO - // if (boundingSphere) return boundingSphere - // boundingSphere = calculateBoundingSphereFromValues(values) - // return boundingSphere }, get opaque () { return values.uAlpha && values.uAlpha.ref.value === 1 }, render: (variant: RenderVariant) => renderItem.render(variant), getProgram: (variant: RenderVariant) => renderItem.getProgram(variant), - update: () => { - renderItem.update() - // TODO - // const valueChanges = renderItem.update() - // if (valueChanges.attributes) boundingSphere = undefined - }, + update: () => renderItem.update(), dispose: () => renderItem.destroy() } } diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index a27ec8edd8ad66f0065c9ddf2d2eafe14238357a..63b2424ed608744a5e6b6651d284b2a991a5412a 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'), 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 0e5826819aece71768921e486e6402fdba701bf2..765f6dc2e237c9a383a5e8163f8b79e248436f54 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -11,11 +11,15 @@ import { DefineKind, DefineValues } from '../shader-code'; import { Vec2, Vec3, Vec4, Mat3, Mat4 } from 'mol-math/linear-algebra'; import { TextureImage, TextureVolume } from './util'; import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, Texture } from '../webgl/texture'; +import { Sphere3D } from 'mol-math/geometry'; export type ValueKindType = { 'number': number 'string': string + 'boolean': string 'any': any + + 'sphere': Sphere3D } export type ValueKind = keyof ValueKindType @@ -51,6 +55,8 @@ export type KindValue = { 'string': string 'boolean': boolean 'any': any + + 'sphere': Sphere3D } export type Values<S extends RenderableSchema> = { [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> } @@ -192,6 +198,7 @@ export const BaseSchema = { drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), + boundingSphere: ValueSpec('sphere'), dUseFog: DefineSpec('boolean'), } diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 8b995572d9fa6491a0b6eebf68d661f006a36399..1ef3fa81c32028813bea5d3d9e2fd805d9cf2930 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -56,7 +56,10 @@ namespace Scene { const object3d = Object3D.create() return { - ...object3d, + get view () { return object3d.view }, + get position () { return object3d.position }, + get direction () { return object3d.direction }, + get up () { return object3d.up }, update: () => { Object3D.update(object3d) @@ -103,7 +106,7 @@ namespace Scene { }, get boundingSphere() { if (boundingSphere) return boundingSphere - // TODO avoid array creation + // TODO avoid object creation boundingSphere = calculateBoundingSphere(renderableMap) return boundingSphere } diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index c38e9275a421d9935b913c028091c5e4370eb11d..380f3f5f2fdcc5fd7f63fdcf2e4193b820d9ca86 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/src/mol-math/geometry/primitives/sphere3d.ts @@ -5,7 +5,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Vec3, Mat4 } from '../../linear-algebra' +import { Vec3, Mat4, EPSILON } from '../../linear-algebra' import { PositionData } from '../common' import { OrderedSet } from 'mol-data/int'; @@ -15,6 +15,12 @@ namespace Sphere3D { export function create(center: Vec3, radius: number): Sphere3D { return { center, radius }; } export function zero(): Sphere3D { return { center: Vec3.zero(), radius: 0 }; } + export function copy(out: Sphere3D, a: Sphere3D) { + Vec3.copy(out.center, a.center) + out.radius = a.radius + return out; + } + export function computeBounding(data: PositionData): Sphere3D { const { x, y, z, indices } = data; let cx = 0, cy = 0, cz = 0; @@ -50,6 +56,39 @@ namespace Sphere3D { out.radius = sphere.radius * Mat4.getMaxScaleOnAxis(m) return out } + + export function toArray(s: Sphere3D, out: Helpers.NumberArray, offset: number) { + Vec3.toArray(s.center, out, offset) + out[offset + 3] = s.radius + } + + export function fromArray(out: Sphere3D, array: Helpers.NumberArray, offset: number) { + Vec3.fromArray(out.center, array, offset) + out.radius = array[offset + 3] + return out + } + + export function addSphere(out: Sphere3D, sphere: Sphere3D) { + out.radius = Math.max(out.radius, Vec3.distance(out.center, sphere.center) + sphere.radius) + return out + } + + /** + * Returns whether or not the spheres have exactly the same center and radius (when compared with ===) + */ + export function exactEquals(a: Sphere3D, b: Sphere3D) { + return a.radius === b.radius && Vec3.exactEquals(a.center, b.center); + } + + /** + * Returns whether or not the spheres have approximately the same center and radius. + */ + export function equals(a: Sphere3D, b: Sphere3D) { + const ar = a.radius; + const br = b.radius; + return (Math.abs(ar - br) <= EPSILON.Value * Math.max(1.0, Math.abs(ar), Math.abs(br)) && + Vec3.equals(a.center, b.center)); + } } export { Sphere3D } \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d/vec4.ts b/src/mol-math/linear-algebra/3d/vec4.ts index 00d682a1628b7f5c8a71820cb29d3e2cb045e3e9..db4f29c33c83b2809c6d58fa05d241cb020965d5 100644 --- a/src/mol-math/linear-algebra/3d/vec4.ts +++ b/src/mol-math/linear-algebra/3d/vec4.ts @@ -18,6 +18,7 @@ */ import Mat4 from './mat4'; +import { EPSILON } from '../3d'; interface Vec4 extends Array<number> { [d: number]: number, '@type': 'vec4', length: 4 } @@ -196,6 +197,25 @@ namespace Vec4 { out[3] = 1.0 / a[3]; return out; } + + /** + * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===) + */ + export function exactEquals(a: Vec4, b: Vec4) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; + } + + /** + * Returns whether or not the vectors have approximately the same elements in the same position. + */ + export function equals(a: Vec4, b: Vec4) { + const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + return (Math.abs(a0 - b0) <= EPSILON.Value * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && + Math.abs(a1 - b1) <= EPSILON.Value * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && + Math.abs(a2 - b2) <= EPSILON.Value * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && + Math.abs(a3 - b3) <= EPSILON.Value * Math.max(1.0, Math.abs(a3), Math.abs(b3))); + } } export default Vec4 \ No newline at end of file