/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose */ import { Renderable } from './renderable' import { Context } from './webgl/context'; import { RenderableValues, BaseValues } from './renderable/schema'; import { RenderObject, createRenderable } from './render-object'; import { Object3D, createObject3D } from './object3d'; import { Sphere3D } from 'mol-math/geometry'; import { Vec3 } from 'mol-math/linear-algebra'; function calculateBoundingSphere(renderableMap: Map>): Sphere3D { let count = 0 const center = Vec3.zero() renderableMap.forEach((r, o) => { if (r.boundingSphere.radius) { Vec3.add(center, center, r.boundingSphere.center) ++count } }) if (count > 0) { Vec3.scale(center, center, 1 / count) } let radius = 0 renderableMap.forEach((r, o) => { if (r.boundingSphere.radius) { radius = Math.max(radius, Vec3.distance(center, r.boundingSphere.center) + r.boundingSphere.radius) } }) return { center, radius }; } interface Scene extends Object3D { readonly count: number readonly boundingSphere: Sphere3D add: (o: RenderObject) => void remove: (o: RenderObject) => void clear: () => void forEach: (callbackFn: (value: Renderable, key: RenderObject) => void) => void eachOpaque: (callbackFn: (value: Renderable, key: RenderObject) => void) => void eachTransparent: (callbackFn: (value: Renderable, key: RenderObject) => void) => void unsetBoundingSphere: () => void } namespace Scene { export function create(ctx: Context): Scene { const renderableMap = new Map>() let boundingSphere: Sphere3D | undefined const { view, position, up, direction, update } = createObject3D() return { // ...createObject3D(), // TODO does not work in conjunction with getter view, position, up, direction, update, add: (o: RenderObject) => { if (!renderableMap.has(o)) { renderableMap.set(o, createRenderable(ctx, o)) boundingSphere = undefined } else { console.warn(`RenderObject with id '${o.id}' already present`) } }, remove: (o: RenderObject) => { const renderable = renderableMap.get(o) if (renderable) { renderable.dispose() renderableMap.delete(o) boundingSphere = undefined } }, clear: () => { renderableMap.forEach(renderable => renderable.dispose()) renderableMap.clear() boundingSphere = undefined }, forEach: (callbackFn: (value: Renderable, key: RenderObject) => void) => { renderableMap.forEach(callbackFn) }, eachOpaque: (callbackFn: (value: Renderable, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { if (o.values.uAlpha.ref.value === 1) callbackFn(r, o) }) }, eachTransparent: (callbackFn: (value: Renderable, key: RenderObject) => void) => { renderableMap.forEach((r, o) => { if (o.values.uAlpha.ref.value < 1) callbackFn(r, o) }) }, unsetBoundingSphere: () => boundingSphere = undefined, get count() { return renderableMap.size }, get boundingSphere() { if (boundingSphere) return boundingSphere // TODO avoid array creation boundingSphere = calculateBoundingSphere(renderableMap) return boundingSphere } } } } export default Scene