scene.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Renderable } from './renderable'
  7. import { WebGLContext } from './webgl/context';
  8. import { RenderableValues, BaseValues } from './renderable/schema';
  9. import { GraphicsRenderObject, createRenderable } from './render-object';
  10. import { Object3D } from './object3d';
  11. import { Sphere3D } from 'mol-math/geometry';
  12. import { Vec3 } from 'mol-math/linear-algebra';
  13. import { BoundaryHelper } from 'mol-math/geometry/boundary-helper';
  14. const boundaryHelper = new BoundaryHelper();
  15. function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D {
  16. boundaryHelper.reset(0.1);
  17. for (let i = 0, il = renderables.length; i < il; ++i) {
  18. const boundingSphere = renderables[i].values.boundingSphere.ref.value
  19. if (!boundingSphere.radius) continue;
  20. boundaryHelper.boundaryStep(boundingSphere.center, boundingSphere.radius);
  21. }
  22. boundaryHelper.finishBoundaryStep();
  23. for (let i = 0, il = renderables.length; i < il; ++i) {
  24. const boundingSphere = renderables[i].values.boundingSphere.ref.value
  25. if (!boundingSphere.radius) continue;
  26. boundaryHelper.extendStep(boundingSphere.center, boundingSphere.radius);
  27. }
  28. Vec3.copy(boundingSphere.center, boundaryHelper.center);
  29. boundingSphere.radius = boundaryHelper.radius;
  30. return boundingSphere;
  31. }
  32. function renderableSort(a: Renderable<RenderableValues & BaseValues>, b: Renderable<RenderableValues & BaseValues>) {
  33. const drawProgramIdA = a.getProgram('draw').id
  34. const drawProgramIdB = b.getProgram('draw').id
  35. const materialIdA = a.materialId
  36. const materialIdB = b.materialId
  37. const zA = a.values.boundingSphere.ref.value.center[2]
  38. const zB = b.values.boundingSphere.ref.value.center[2]
  39. if (drawProgramIdA !== drawProgramIdB) {
  40. return drawProgramIdA - drawProgramIdB // sort by program id to minimize gl state changes
  41. } else if (materialIdA !== materialIdB) {
  42. return materialIdA - materialIdB // sort by material id to minimize gl state changes
  43. } else if (zA !== zB) {
  44. return a.state.opaque
  45. ? zA - zB // when opaque, draw closer elements first to minimize overdraw
  46. : zB - zA // when transparent, draw elements last to maximize partial visibility
  47. } else {
  48. return a.id - b.id;
  49. }
  50. }
  51. interface Scene extends Object3D {
  52. readonly count: number
  53. readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>>
  54. readonly boundingSphere: Sphere3D
  55. update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean) => void
  56. add: (o: GraphicsRenderObject) => Renderable<any>
  57. remove: (o: GraphicsRenderObject) => void
  58. has: (o: GraphicsRenderObject) => boolean
  59. clear: () => void
  60. forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void
  61. }
  62. namespace Scene {
  63. export function create(ctx: WebGLContext): Scene {
  64. const renderableMap = new Map<GraphicsRenderObject, Renderable<RenderableValues & BaseValues>>()
  65. const renderables: Renderable<RenderableValues & BaseValues>[] = []
  66. const boundingSphere = Sphere3D.zero()
  67. let boundingSphereDirty = true
  68. const object3d = Object3D.create()
  69. return {
  70. get view () { return object3d.view },
  71. get position () { return object3d.position },
  72. get direction () { return object3d.direction },
  73. get up () { return object3d.up },
  74. update(objects, keepBoundingSphere) {
  75. Object3D.update(object3d)
  76. if (objects) {
  77. for (let i = 0, il = objects.length; i < il; ++i) {
  78. const o = renderableMap.get(objects[i]);
  79. if (!o) continue;
  80. o.update();
  81. }
  82. } else {
  83. for (let i = 0, il = renderables.length; i < il; ++i) {
  84. renderables[i].update()
  85. }
  86. }
  87. if (!keepBoundingSphere) boundingSphereDirty = true
  88. },
  89. add: (o: GraphicsRenderObject) => {
  90. if (!renderableMap.has(o)) {
  91. const renderable = createRenderable(ctx, o)
  92. renderables.push(renderable)
  93. renderables.sort(renderableSort)
  94. renderableMap.set(o, renderable)
  95. boundingSphereDirty = true
  96. return renderable;
  97. } else {
  98. console.warn(`RenderObject with id '${o.id}' already present`)
  99. return renderableMap.get(o)!
  100. }
  101. },
  102. remove: (o: GraphicsRenderObject) => {
  103. const renderable = renderableMap.get(o)
  104. if (renderable) {
  105. renderable.dispose()
  106. renderables.splice(renderables.indexOf(renderable), 1)
  107. renderables.sort(renderableSort)
  108. renderableMap.delete(o)
  109. boundingSphereDirty = true
  110. }
  111. },
  112. has: (o: GraphicsRenderObject) => {
  113. return renderableMap.has(o)
  114. },
  115. clear: () => {
  116. for (let i = 0, il = renderables.length; i < il; ++i) {
  117. renderables[i].dispose()
  118. }
  119. renderables.length = 0
  120. renderableMap.clear()
  121. boundingSphereDirty = true
  122. },
  123. forEach: (callbackFn: (value: Renderable<any>, key: GraphicsRenderObject) => void) => {
  124. renderableMap.forEach(callbackFn)
  125. },
  126. get count() {
  127. return renderables.length
  128. },
  129. renderables,
  130. get boundingSphere() {
  131. if (boundingSphereDirty) calculateBoundingSphere(renderables, boundingSphere)
  132. boundingSphereDirty = false
  133. return boundingSphere
  134. }
  135. }
  136. }
  137. }
  138. export default Scene