EXT_mesh_gpu_instancing.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { Vector3, Quaternion, Matrix } from 'babylonjs/Maths/math.vector';
  2. import { Mesh } from 'babylonjs/Meshes/mesh';
  3. import { TransformNode } from "babylonjs/Meshes/transformNode";
  4. import { Nullable } from "babylonjs/types";
  5. import { GLTFLoader, ArrayItem } from "../glTFLoader";
  6. import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
  7. import { INode } from "../glTFLoaderInterfaces";
  8. import { TmpVectors } from 'babylonjs/Maths/math.vector';
  9. const NAME = "EXT_mesh_gpu_instancing";
  10. interface IEXTMeshGpuInstancing {
  11. mesh?: number;
  12. attributes: { [name: string]: number };
  13. }
  14. /**
  15. * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1691)
  16. * [Playground Sample](https://playground.babylonjs.com/#QFIGLW#9)
  17. * !!! Experimental Extension Subject to Changes !!!
  18. */
  19. export class EXT_mesh_gpu_instancing implements IGLTFLoaderExtension {
  20. /**
  21. * The name of this extension.
  22. */
  23. public readonly name = NAME;
  24. /**
  25. * Defines whether this extension is enabled.
  26. */
  27. public enabled: boolean;
  28. private _loader: GLTFLoader;
  29. /** @hidden */
  30. constructor(loader: GLTFLoader) {
  31. this._loader = loader;
  32. this.enabled = this._loader.isExtensionUsed(NAME);
  33. }
  34. /** @hidden */
  35. public dispose() {
  36. delete this._loader;
  37. }
  38. /** @hidden */
  39. public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
  40. return GLTFLoader.LoadExtensionAsync<IEXTMeshGpuInstancing, TransformNode>(context, node, this.name, (extensionContext, extension) => {
  41. this._loader._disableInstancedMesh++;
  42. const promise = this._loader.loadNodeAsync(`#/nodes/${node.index}`, node, assign);
  43. this._loader._disableInstancedMesh--;
  44. if (!node._primitiveBabylonMeshes) {
  45. return promise;
  46. }
  47. const promises = new Array<Promise<Nullable<Float32Array>>>();
  48. let instanceCount = 0;
  49. const loadAttribute = (attribute: string) => {
  50. if (extension.attributes[attribute] == undefined) {
  51. promises.push(Promise.resolve(null));
  52. return;
  53. }
  54. const accessor = ArrayItem.Get(`${extensionContext}/attributes/${attribute}`, this._loader.gltf.accessors, extension.attributes[attribute]);
  55. promises.push(this._loader._loadFloatAccessorAsync(`/accessors/${accessor.bufferView}`, accessor));
  56. if (instanceCount === 0) {
  57. instanceCount = accessor.count;
  58. } else if (instanceCount !== accessor.count) {
  59. throw new Error(`${extensionContext}/attributes: Instance buffer accessors do not have the same count.`);
  60. }
  61. };
  62. loadAttribute("TRANSLATION");
  63. loadAttribute("ROTATION");
  64. loadAttribute("SCALE");
  65. return promise.then((babylonTransformNode) => {
  66. return Promise.all(promises).then(([translationBuffer, rotationBuffer, scaleBuffer]) => {
  67. const matrices = new Float32Array(instanceCount * 16);
  68. TmpVectors.Vector3[0].copyFromFloats(0, 0, 0); // translation
  69. TmpVectors.Quaternion[0].copyFromFloats(0, 0, 0, 1); // rotation
  70. TmpVectors.Vector3[1].copyFromFloats(1, 1, 1); // scale
  71. for (let i = 0; i < instanceCount; ++i) {
  72. translationBuffer && Vector3.FromArrayToRef(translationBuffer, i * 3, TmpVectors.Vector3[0]);
  73. rotationBuffer && Quaternion.FromArrayToRef(rotationBuffer, i * 4, TmpVectors.Quaternion[0]);
  74. scaleBuffer && Vector3.FromArrayToRef(scaleBuffer, i * 3, TmpVectors.Vector3[1]);
  75. Matrix.ComposeToRef(TmpVectors.Vector3[1], TmpVectors.Quaternion[0], TmpVectors.Vector3[0], TmpVectors.Matrix[0]);
  76. TmpVectors.Matrix[0].copyToArray(matrices, i * 16);
  77. }
  78. for (const babylonMesh of node._primitiveBabylonMeshes!) {
  79. (babylonMesh as Mesh).thinInstanceSetBuffer("matrix", matrices, 16, true);
  80. }
  81. return babylonTransformNode;
  82. });
  83. });
  84. });
  85. }
  86. }
  87. GLTFLoader.RegisterExtension(NAME, (loader) => new EXT_mesh_gpu_instancing(loader));