EXT_mesh_gpu_instancing.ts 4.9 KB

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