Bladeren bron

wip, renderable bounding sphere

Alexander Rose 6 jaren geleden
bovenliggende
commit
e2dd67a756

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

+ 28 - 0
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)
     }

+ 15 - 0
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)

+ 16 - 0
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)

+ 4 - 0
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),

+ 4 - 13
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()
     }
 }

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

+ 7 - 0
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'),
 }

+ 5 - 2
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
             }

+ 40 - 1
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 }

+ 20 - 0
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