Browse Source

wip, transient, per-instance transformations

Alexander Rose 6 years ago
parent
commit
7bbb8fc7e6

+ 2 - 0
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,

+ 47 - 8
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)
 }

+ 3 - 0
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,
 

+ 5 - 1
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'),
 

+ 14 - 1
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'),

+ 1 - 1
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)

+ 20 - 5
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)
             },

+ 3 - 1
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)

+ 6 - 6
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

+ 4 - 1
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>
 

+ 6 - 3
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)
     }

+ 6 - 6
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

+ 19 - 11
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)
         }

+ 6 - 6
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