transform-data.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ValueCell } from '../../mol-util';
  7. import { Mat4, Mat3 } from '../../mol-math/linear-algebra';
  8. import { fillSerial } from '../../mol-util/array';
  9. export type TransformData = {
  10. /**
  11. * final per-instance transform calculated for instance `i` as
  12. * `aTransform[i] = matrix * transform[i] * extraTransform[i]`
  13. */
  14. aTransform: ValueCell<Float32Array>,
  15. /** global transform, see aTransform */
  16. matrix: ValueCell<Mat4>,
  17. /** base per-instance transform, see aTransform */
  18. transform: ValueCell<Float32Array>,
  19. /** additional per-instance transform, see aTransform */
  20. extraTransform: ValueCell<Float32Array>,
  21. uInstanceCount: ValueCell<number>,
  22. instanceCount: ValueCell<number>,
  23. aInstance: ValueCell<Float32Array>,
  24. hasReflection: ValueCell<boolean>,
  25. }
  26. const _m3 = Mat3();
  27. const _m4 = Mat4();
  28. function checkReflection(transformArray: Float32Array, instanceCount: number) {
  29. for (let i = 0; i < instanceCount; i++) {
  30. Mat3.fromMat4(_m3, Mat4.fromArray(_m4, transformArray, i * 16));
  31. if (Mat3.determinant(_m3) < 0) return true;
  32. }
  33. return false;
  34. }
  35. export function createTransform(transformArray: Float32Array, instanceCount: number, transformData?: TransformData): TransformData {
  36. const hasReflection = checkReflection(transformArray, instanceCount);
  37. if (transformData) {
  38. ValueCell.update(transformData.matrix, transformData.matrix.ref.value);
  39. const transform = transformData.transform.ref.value.length >= instanceCount * 16 ? transformData.transform.ref.value : new Float32Array(instanceCount * 16);
  40. transform.set(transformArray);
  41. ValueCell.update(transformData.transform, transform);
  42. ValueCell.updateIfChanged(transformData.uInstanceCount, instanceCount);
  43. ValueCell.updateIfChanged(transformData.instanceCount, instanceCount);
  44. const aTransform = transformData.aTransform.ref.value.length >= instanceCount * 16 ? transformData.aTransform.ref.value : new Float32Array(instanceCount * 16);
  45. ValueCell.update(transformData.aTransform, aTransform);
  46. // Note that this sets `extraTransform` to identity transforms
  47. const extraTransform = transformData.extraTransform.ref.value.length >= instanceCount * 16 ? transformData.extraTransform.ref.value : new Float32Array(instanceCount * 16);
  48. ValueCell.update(transformData.extraTransform, fillIdentityTransform(extraTransform, instanceCount));
  49. const aInstance = transformData.aInstance.ref.value.length >= instanceCount ? transformData.aInstance.ref.value : new Float32Array(instanceCount);
  50. ValueCell.update(transformData.aInstance, fillSerial(aInstance, instanceCount));
  51. ValueCell.update(transformData.hasReflection, hasReflection);
  52. updateTransformData(transformData);
  53. return transformData;
  54. } else {
  55. return {
  56. aTransform: ValueCell.create(new Float32Array(instanceCount * 16)),
  57. matrix: ValueCell.create(Mat4.identity()),
  58. transform: ValueCell.create(new Float32Array(transformArray)),
  59. extraTransform: ValueCell.create(fillIdentityTransform(new Float32Array(instanceCount * 16), instanceCount)),
  60. uInstanceCount: ValueCell.create(instanceCount),
  61. instanceCount: ValueCell.create(instanceCount),
  62. aInstance: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
  63. hasReflection: ValueCell.create(hasReflection),
  64. };
  65. }
  66. }
  67. const identityTransform = new Float32Array(16);
  68. Mat4.toArray(Mat4.identity(), identityTransform, 0);
  69. export function createIdentityTransform(transformData?: TransformData): TransformData {
  70. return createTransform(new Float32Array(identityTransform), 1, transformData);
  71. }
  72. export function fillIdentityTransform(transform: Float32Array, count: number) {
  73. for (let i = 0; i < count; i++) {
  74. transform.set(identityTransform, i * 16);
  75. }
  76. return transform;
  77. }
  78. /**
  79. * updates per-instance transform calculated for instance `i` as
  80. * `aTransform[i] = matrix * transform[i] * extraTransform[i]`
  81. */
  82. export function updateTransformData(transformData: TransformData) {
  83. const aTransform = transformData.aTransform.ref.value;
  84. const instanceCount = transformData.instanceCount.ref.value;
  85. const matrix = transformData.matrix.ref.value;
  86. const transform = transformData.transform.ref.value;
  87. const extraTransform = transformData.extraTransform.ref.value;
  88. for (let i = 0; i < instanceCount; i++) {
  89. const i16 = i * 16;
  90. Mat4.mulOffset(aTransform, extraTransform, transform, i16, i16, i16);
  91. Mat4.mulOffset(aTransform, matrix, aTransform, i16, 0, i16);
  92. }
  93. ValueCell.update(transformData.aTransform, aTransform);
  94. }