diff --git a/src/mol-canvas3d/helper/bounding-sphere-helper.ts b/src/mol-canvas3d/helper/bounding-sphere-helper.ts index 6fc1a26c2c333b98b693375f70ee656e2355dca4..03f52d9fa66fd5d5b5edbd6d284fad7794b6c8b7 100644 --- a/src/mol-canvas3d/helper/bounding-sphere-helper.ts +++ b/src/mol-canvas3d/helper/bounding-sphere-helper.ts @@ -57,7 +57,9 @@ export class BoundingSphereHelper { const instanceData = this.instancesData.get(ro) const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, { aTransform: ro.values.aTransform, + matrix: ro.values.matrix, transform: ro.values.transform, + extraTransform: ro.values.extraTransform, uInstanceCount: ro.values.uInstanceCount, instanceCount: ro.values.instanceCount, aInstance: ro.values.aInstance, diff --git a/src/mol-geo/geometry/transform-data.ts b/src/mol-geo/geometry/transform-data.ts index 03e107a88ec684ae447cd1ec06d18a6d57ba26ef..87f0b7ac9867ac1f21a9a2effe3151c1dfbb876e 100644 --- a/src/mol-geo/geometry/transform-data.ts +++ b/src/mol-geo/geometry/transform-data.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -9,8 +9,18 @@ import { Mat4 } from 'mol-math/linear-algebra'; import { fillSerial } from 'mol-util/array'; export type TransformData = { + /** + * final per-instance transform calculated for instance `i` as + * `aTransform[i] = matrix * transform[i] * extraTransform[i]` + */ aTransform: ValueCell<Float32Array>, + /** global transform, see aTransform */ + matrix: ValueCell<Mat4>, + /** base per-instance transform, see aTransform */ transform: ValueCell<Float32Array>, + /** additional per-instance transform, see aTransform */ + extraTransform: ValueCell<Float32Array>, + uInstanceCount: ValueCell<number>, instanceCount: ValueCell<number>, aInstance: ValueCell<Float32Array>, @@ -18,17 +28,30 @@ export type TransformData = { export function createTransform(transformArray: Float32Array, instanceCount: number, transformData?: TransformData): TransformData { if (transformData) { - ValueCell.update(transformData.aTransform, transformArray) - ValueCell.update(transformData.transform, new Float32Array(transformArray)) + ValueCell.update(transformData.matrix, transformData.matrix.ref.value) + ValueCell.update(transformData.transform, transformArray) ValueCell.update(transformData.uInstanceCount, instanceCount) ValueCell.update(transformData.instanceCount, instanceCount) + + const aTransform = transformData.aTransform.ref.value.length >= instanceCount * 16 ? transformData.aTransform.ref.value : new Float32Array(instanceCount * 16) + aTransform.set(transformArray) + ValueCell.update(transformData.aTransform, aTransform) + + // Note that this sets `extraTransform` to identity transforms + const extraTransform = transformData.extraTransform.ref.value.length >= instanceCount * 16 ? transformData.extraTransform.ref.value : new Float32Array(instanceCount * 16) + ValueCell.update(transformData.extraTransform, fillIdentityTransform(extraTransform, instanceCount)) + const aInstance = transformData.aInstance.ref.value.length >= instanceCount ? transformData.aInstance.ref.value : new Float32Array(instanceCount) ValueCell.update(transformData.aInstance, fillSerial(aInstance, instanceCount)) + + updateTransformData(transformData) return transformData } else { return { - aTransform: ValueCell.create(transformArray), - transform: ValueCell.create(new Float32Array(transformArray)), + aTransform: ValueCell.create(new Float32Array(transformArray)), + matrix: ValueCell.create(Mat4.identity()), + transform: ValueCell.create(transformArray), + extraTransform: ValueCell.create(fillIdentityTransform(new Float32Array(instanceCount * 16), instanceCount)), uInstanceCount: ValueCell.create(instanceCount), instanceCount: ValueCell.create(instanceCount), aInstance: ValueCell.create(fillSerial(new Float32Array(instanceCount))) @@ -38,16 +61,32 @@ export function createTransform(transformArray: Float32Array, instanceCount: num const identityTransform = new Float32Array(16) Mat4.toArray(Mat4.identity(), identityTransform, 0) + export function createIdentityTransform(transformData?: TransformData): TransformData { return createTransform(new Float32Array(identityTransform), 1, transformData) } -export function setTransformData(matrix: Mat4, transformData: TransformData) { +export function fillIdentityTransform(transform: Float32Array, count: number) { + for (let i = 0; i < count; i++) { + transform.set(identityTransform, i * 16) + } + return transform +} + +/** + * updates per-instance transform calculated for instance `i` as + * `aTransform[i] = matrix * transform[i] * extraTransform[i]` + */ +export function updateTransformData(transformData: TransformData) { + const aTransform = transformData.aTransform.ref.value const instanceCount = transformData.instanceCount.ref.value + const matrix = transformData.matrix.ref.value const transform = transformData.transform.ref.value - const aTransform = transformData.aTransform.ref.value + const extraTransform = transformData.extraTransform.ref.value for (let i = 0; i < instanceCount; i++) { - Mat4.mulOffset(aTransform, transform, matrix, i * 16, i * 16, 0) + const i16 = i * 16 + Mat4.mulOffset(aTransform, transform, extraTransform, i16, i16, i16) + Mat4.mulOffset(aTransform, aTransform, matrix, i16, i16, 0) } ValueCell.update(transformData.aTransform, aTransform) } \ No newline at end of file diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index 10b09294ee8f53f87e578860883b2802d1a9871d..f9b34b7031ef4bbeff341fe8f2c3f91477aa05e0 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -57,6 +57,7 @@ function createPoints() { const m4 = Mat4.identity() Mat4.toArray(m4, aTransform.ref.value, 0) const transform = ValueCell.create(new Float32Array(aTransform.ref.value)) + const extraTransform = ValueCell.create(new Float32Array(aTransform.ref.value)) const boundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2)) const invariantBoundingSphere = ValueCell.create(Sphere3D.create(Vec3.zero(), 2)) @@ -78,7 +79,9 @@ function createPoints() { drawCount: ValueCell.create(3), instanceCount: ValueCell.create(1), + matrix: ValueCell.create(m4), transform, + extraTransform, boundingSphere, invariantBoundingSphere, diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index 6010ff6fe79e4e51cdd2fa0bcf3a44038ddf5916..0ec65356f66f78ab92bff6deeae1849e346bc559 100644 --- a/src/mol-gl/renderable/direct-volume.ts +++ b/src/mol-gl/renderable/direct-volume.ts @@ -28,7 +28,11 @@ export const DirectVolumeSchema = { drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), - transform: AttributeSpec('float32', 16, 1), + + matrix: ValueSpec('m4'), + transform: ValueSpec('float32'), + extraTransform: ValueSpec('float32'), + boundingSphere: ValueSpec('sphere'), invariantBoundingSphere: ValueSpec('sphere'), diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index bfcc32ca8a5003a773e0957379e07fe467555e48..820f7674028596f85fa733646eba58b0beebd744 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -19,8 +19,8 @@ export type ValueKindType = { 'boolean': string 'any': any + 'm4': Mat4, 'float32': Float32Array - 'sphere': Sphere3D } export type ValueKind = keyof ValueKindType @@ -191,6 +191,10 @@ export const BaseSchema = { aInstance: AttributeSpec('float32', 1, 1), aGroup: AttributeSpec('float32', 1, 0), + /** + * final per-instance transform calculated for instance `i` as + * `aTransform[i] = matrix * transform[i] * extraTransform[i]` + */ aTransform: AttributeSpec('float32', 16, 1), uAlpha: UniformSpec('f'), @@ -204,8 +208,17 @@ export const BaseSchema = { drawCount: ValueSpec('number'), instanceCount: ValueSpec('number'), + + /** global transform, see aTransform */ + matrix: ValueSpec('m4'), + /** base per-instance transform, see aTransform */ transform: ValueSpec('float32'), + /** additional per-instance transform, see aTransform */ + extraTransform: ValueSpec('float32'), + + /** bounding sphere taking aTransform into account */ boundingSphere: ValueSpec('sphere'), + /** bounding sphere NOT taking aTransform into account */ invariantBoundingSphere: ValueSpec('sphere'), dUseFog: DefineSpec('boolean'), diff --git a/src/mol-plugin/behavior/dynamic/animation.ts b/src/mol-plugin/behavior/dynamic/animation.ts index 62a77c0dc6e6b0ccf85eedf6f8f7f5b0faec210d..e4b27ba0d9d6c36900dfc29d02ad109e2ccf1667 100644 --- a/src/mol-plugin/behavior/dynamic/animation.ts +++ b/src/mol-plugin/behavior/dynamic/animation.ts @@ -134,7 +134,7 @@ export const StructureAnimation = PluginBehavior.create<StructureAnimationProps> register(): void { } update(p: StructureAnimationProps) { - let updated = PD.areEqual(StructureAnimationParams, this.params, p) + let updated = !PD.areEqual(StructureAnimationParams, this.params, p) if (this.params.rotate !== p.rotate) { this.params.rotate = p.rotate this.animateRotate(this.params.rotate) diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts index 41369fd550e27e223c926e648a694b1b0ea42a9f..edba78b11ac0be2af2b9c1b737aa5f9bacb5689b 100644 --- a/src/mol-repr/representation.ts +++ b/src/mol-repr/representation.ts @@ -18,6 +18,7 @@ import { Theme, ThemeRegistryContext, createEmptyTheme } from 'mol-theme/theme'; import { Subject } from 'rxjs'; import { Mat4 } from 'mol-math/linear-algebra'; import { BaseGeometry } from 'mol-geo/geometry/base'; +import { Visual } from './visual'; // export interface RepresentationProps { // visuals?: string[] @@ -115,15 +116,27 @@ namespace Representation { syncManually: boolean /** A transformation applied to the representation's renderobjects */ transform: Mat4 + /** + * A set of transformations applied to the instances within the representation's renderobjects, + * laid out as Mat4's in a Float32Array + */ + instanceTransforms: Float32Array } - export function createState() { - return { visible: false, pickable: false, syncManually: false, transform: Mat4.identity() } + export function createState(): State { + return { visible: false, pickable: false, syncManually: false, transform: Mat4.identity(), instanceTransforms: new Float32Array(Mat4.identity()) } } export function updateState(state: State, update: Partial<State>) { if (update.visible !== undefined) state.visible = update.visible if (update.pickable !== undefined) state.pickable = update.pickable if (update.syncManually !== undefined) state.syncManually = update.syncManually if (update.transform !== undefined) Mat4.copy(state.transform, update.transform) + if (update.instanceTransforms !== undefined) { + if (update.instanceTransforms.length !== state.instanceTransforms.length) { + state.instanceTransforms = new Float32Array(update.instanceTransforms) + } else { + state.instanceTransforms.set(update.instanceTransforms) + } + } } export type Any = Representation<any, any> @@ -278,9 +291,11 @@ namespace Representation { return false }, setState: (state: Partial<State>) => { - if (state.visible !== undefined) renderObject.state.visible = state.visible - if (state.pickable !== undefined) renderObject.state.pickable = state.pickable - // TODO transform + if (state.visible !== undefined) Visual.setVisibility(renderObject, state.visible) + if (state.pickable !== undefined) Visual.setPickable(renderObject, state.pickable) + if (state.transform !== undefined || state.instanceTransforms !== undefined) { + Visual.setTransform(renderObject, state.transform, state.instanceTransforms) + } Representation.updateState(currentState, state) }, diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts index d0cec8c695b11ae764bce218f07d7e4c6c2de871..96faec9f89fd2641d31a033efaa6d5de920a9ee8 100644 --- a/src/mol-repr/shape/representation.ts +++ b/src/mol-repr/shape/representation.ts @@ -196,7 +196,9 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa if (_renderObject) { if (state.visible !== undefined) Visual.setVisibility(_renderObject, state.visible) if (state.pickable !== undefined) Visual.setPickable(_renderObject, state.pickable) - if (state.transform !== undefined) Visual.setTransform(_renderObject, state.transform) + if (state.transform !== undefined || state.instanceTransforms !== undefined) { + Visual.setTransform(_renderObject, state.transform, state.instanceTransforms) + } } Representation.updateState(_state, state) diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index d84f2c495d1908d80445ce12b0775250afb9306c..e32467d1acbd72e416302a8f979f436ad058b7d1 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -194,14 +194,14 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom } return changed }, - setVisibility(value: boolean) { - Visual.setVisibility(renderObject, value) + setVisibility(visible: boolean) { + Visual.setVisibility(renderObject, visible) }, - setPickable(value: boolean) { - Visual.setPickable(renderObject, value) + setPickable(pickable: boolean) { + Visual.setPickable(renderObject, pickable) }, - setTransform(value: Mat4) { - Visual.setTransform(renderObject, value) + setTransform(matrix?: Mat4, instanceMatrices?: Float32Array) { + Visual.setTransform(renderObject, matrix, instanceMatrices) }, destroy() { // TODO diff --git a/src/mol-repr/structure/representation.ts b/src/mol-repr/structure/representation.ts index 6e2991d13f4973d5f922e355c22fbeebb92b85e6..d42c7935cc01eb29d6d759d20cac218f78f9a6e2 100644 --- a/src/mol-repr/structure/representation.ts +++ b/src/mol-repr/structure/representation.ts @@ -14,8 +14,11 @@ import { Points } from 'mol-geo/geometry/points/points'; import { Lines } from 'mol-geo/geometry/lines/lines'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; import { Spheres } from 'mol-geo/geometry/spheres/spheres'; +// import { Mat4 } from 'mol-math/linear-algebra'; -export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { } +export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { + // setUnitsTransform(unitTransforms: { [id: number]: Mat4 }): void +} export type StructureRepresentationProvider<P extends PD.Params> = RepresentationProvider<Structure, P> diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index d5a3d64761dc34f58045351b07a2db673ac5e7f2..ffe186030e06f3aa5edd3a175b208d79b58c1933 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -163,9 +163,12 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R } function setState(state: Partial<Representation.State>) { - if (state.visible !== undefined) visuals.forEach(({ visual }) => visual.setVisibility(state.visible!)) - if (state.pickable !== undefined) visuals.forEach(({ visual }) => visual.setPickable(state.pickable!)) - if (state.transform !== undefined) visuals.forEach(({ visual }) => visual.setTransform(state.transform!)) + const { visible, pickable, transform, instanceTransforms } = state + if (visible !== undefined) visuals.forEach(({ visual }) => visual.setVisibility(visible)) + if (pickable !== undefined) visuals.forEach(({ visual }) => visual.setPickable(pickable)) + if (transform !== undefined || instanceTransforms !== undefined) { + visuals.forEach(({ visual }) => visual.setTransform(transform, instanceTransforms)) + } Representation.updateState(_state, state) } diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index 94454569810f767c8d12643313b1011b77ee3282..0d16a1029ed7208ac2bd4bf323a2361338e0e327 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -240,14 +240,14 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry } return changed }, - setVisibility(value: boolean) { - Visual.setVisibility(renderObject, value) + setVisibility(visible: boolean) { + Visual.setVisibility(renderObject, visible) }, - setPickable(value: boolean) { - Visual.setPickable(renderObject, value) + setPickable(pickable: boolean) { + Visual.setPickable(renderObject, pickable) }, - setTransform(value: Mat4) { - Visual.setTransform(renderObject, value) + setTransform(matrix?: Mat4, instanceMatrices?: Float32Array) { + Visual.setTransform(renderObject, matrix, instanceMatrices) }, destroy() { // TODO diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts index d7776973cc4f1fc701cec85e3e526b88431d3b4c..e72cf38f2b5ebdc6f5e6ddf363e4887ee2c675db 100644 --- a/src/mol-repr/visual.ts +++ b/src/mol-repr/visual.ts @@ -13,7 +13,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { WebGLContext } from 'mol-gl/webgl/context'; import { Theme } from 'mol-theme/theme'; import { Mat4 } from 'mol-math/linear-algebra'; -import { setTransformData } from 'mol-geo/geometry/transform-data'; +import { updateTransformData } from 'mol-geo/geometry/transform-data'; import { calculateTransformBoundingSphere } from 'mol-gl/renderable/util'; import { ValueCell } from 'mol-util'; @@ -31,24 +31,32 @@ interface Visual<D, P extends PD.Params> { createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<PD.Values<P>>, data?: D) => Promise<void> | void getLoci: (pickingId: PickingId) => Loci mark: (loci: Loci, action: MarkerAction) => boolean - setVisibility: (value: boolean) => void - setPickable: (value: boolean) => void - setTransform: (value: Mat4) => void + setVisibility: (visible: boolean) => void + setPickable: (pickable: boolean) => void + setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array) => void destroy: () => void } namespace Visual { - export function setVisibility(renderObject: GraphicsRenderObject | undefined, value: boolean) { - if (renderObject) renderObject.state.visible = value + export function setVisibility(renderObject: GraphicsRenderObject | undefined, visible: boolean) { + if (renderObject) renderObject.state.visible = visible } - export function setPickable(renderObject: GraphicsRenderObject | undefined, value: boolean) { - if (renderObject) renderObject.state.pickable = value + export function setPickable(renderObject: GraphicsRenderObject | undefined, pickable: boolean) { + if (renderObject) renderObject.state.pickable = pickable } - export function setTransform(renderObject: GraphicsRenderObject | undefined, value: Mat4) { - if (renderObject) { + export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array) { + if (renderObject && (transform || instanceTransforms)) { const { values } = renderObject - setTransformData(value, values) + if (transform) { + Mat4.copy(values.matrix.ref.value, transform) + ValueCell.update(values.matrix, values.matrix.ref.value) + } + if (instanceTransforms) { + values.extraTransform.ref.value.set(instanceTransforms) + ValueCell.update(values.extraTransform, values.extraTransform.ref.value) + } + updateTransformData(values) const boundingSphere = calculateTransformBoundingSphere(values.invariantBoundingSphere.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value) ValueCell.update(values.boundingSphere, boundingSphere) } diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index 080463a6fca463c2bf000f49f27d190d39c37529..84836600bb5fa30b9fd2e1f25b9afe24716afaa1 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -123,14 +123,14 @@ export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeomet } return changed }, - setVisibility(value: boolean) { - Visual.setVisibility(renderObject, value) + setVisibility(visible: boolean) { + Visual.setVisibility(renderObject, visible) }, - setPickable(value: boolean) { - Visual.setPickable(renderObject, value) + setPickable(pickable: boolean) { + Visual.setPickable(renderObject, pickable) }, - setTransform(value: Mat4) { - Visual.setTransform(renderObject, value) + setTransform(matrix?: Mat4, instanceMatrices?: Float32Array) { + Visual.setTransform(renderObject, matrix, instanceMatrices) }, destroy() { // TODO