instancedMesh.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. import { Nullable, FloatArray, IndicesArray } from "../types";
  2. import { Vector3, Matrix, TmpVectors } from "../Maths/math.vector";
  3. import { Logger } from "../Misc/logger";
  4. import { Camera } from "../Cameras/camera";
  5. import { Node } from "../node";
  6. import { AbstractMesh } from "../Meshes/abstractMesh";
  7. import { Mesh } from "../Meshes/mesh";
  8. import { Material } from "../Materials/material";
  9. import { Skeleton } from "../Bones/skeleton";
  10. import { DeepCopier } from "../Misc/deepCopier";
  11. import { TransformNode } from './transformNode';
  12. import { Light } from '../Lights/light';
  13. Mesh._instancedMeshFactory = (name: string, mesh: Mesh): InstancedMesh => {
  14. return new InstancedMesh(name, mesh);
  15. };
  16. /**
  17. * Creates an instance based on a source mesh.
  18. */
  19. export class InstancedMesh extends AbstractMesh {
  20. private _sourceMesh: Mesh;
  21. private _currentLOD: Mesh;
  22. /** @hidden */
  23. public _indexInSourceMeshInstanceArray = -1;
  24. constructor(name: string, source: Mesh) {
  25. super(name, source.getScene());
  26. source.addInstance(this);
  27. this._sourceMesh = source;
  28. this._unIndexed = source._unIndexed;
  29. this.position.copyFrom(source.position);
  30. this.rotation.copyFrom(source.rotation);
  31. this.scaling.copyFrom(source.scaling);
  32. if (source.rotationQuaternion) {
  33. this.rotationQuaternion = source.rotationQuaternion.clone();
  34. }
  35. this.infiniteDistance = source.infiniteDistance;
  36. this.setPivotMatrix(source.getPivotMatrix());
  37. this.refreshBoundingInfo();
  38. this._syncSubMeshes();
  39. }
  40. /**
  41. * Returns the string "InstancedMesh".
  42. */
  43. public getClassName(): string {
  44. return "InstancedMesh";
  45. }
  46. /** Gets the list of lights affecting that mesh */
  47. public get lightSources(): Light[] {
  48. return this._sourceMesh._lightSources;
  49. }
  50. public _resyncLightSources(): void {
  51. // Do nothing as all the work will be done by source mesh
  52. }
  53. public _resyncLighSource(light: Light): void {
  54. // Do nothing as all the work will be done by source mesh
  55. }
  56. public _removeLightSource(light: Light, dispose: boolean): void {
  57. // Do nothing as all the work will be done by source mesh
  58. }
  59. // Methods
  60. /**
  61. * If the source mesh receives shadows
  62. */
  63. public get receiveShadows(): boolean {
  64. return this._sourceMesh.receiveShadows;
  65. }
  66. /**
  67. * The material of the source mesh
  68. */
  69. public get material(): Nullable<Material> {
  70. return this._sourceMesh.material;
  71. }
  72. /**
  73. * Visibility of the source mesh
  74. */
  75. public get visibility(): number {
  76. return this._sourceMesh.visibility;
  77. }
  78. /**
  79. * Skeleton of the source mesh
  80. */
  81. public get skeleton(): Nullable<Skeleton> {
  82. return this._sourceMesh.skeleton;
  83. }
  84. /**
  85. * Rendering ground id of the source mesh
  86. */
  87. public get renderingGroupId(): number {
  88. return this._sourceMesh.renderingGroupId;
  89. }
  90. public set renderingGroupId(value: number) {
  91. if (!this._sourceMesh || value === this._sourceMesh.renderingGroupId) {
  92. return;
  93. }
  94. //no-op with warning
  95. Logger.Warn("Note - setting renderingGroupId of an instanced mesh has no effect on the scene");
  96. }
  97. /**
  98. * Returns the total number of vertices (integer).
  99. */
  100. public getTotalVertices(): number {
  101. return this._sourceMesh ? this._sourceMesh.getTotalVertices() : 0;
  102. }
  103. /**
  104. * Returns a positive integer : the total number of indices in this mesh geometry.
  105. * @returns the numner of indices or zero if the mesh has no geometry.
  106. */
  107. public getTotalIndices(): number {
  108. return this._sourceMesh.getTotalIndices();
  109. }
  110. /**
  111. * The source mesh of the instance
  112. */
  113. public get sourceMesh(): Mesh {
  114. return this._sourceMesh;
  115. }
  116. /**
  117. * Is this node ready to be used/rendered
  118. * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
  119. * @return {boolean} is it ready
  120. */
  121. public isReady(completeCheck = false): boolean {
  122. return this._sourceMesh.isReady(completeCheck, true);
  123. }
  124. /**
  125. * Returns an array of integers or a typed array (Int32Array, Uint32Array, Uint16Array) populated with the mesh indices.
  126. * @param kind kind of verticies to retreive (eg. positons, normals, uvs, etc.)
  127. * @param copyWhenShared If true (default false) and and if the mesh geometry is shared among some other meshes, the returned array is a copy of the internal one.
  128. * @returns a float array or a Float32Array of the requested kind of data : positons, normals, uvs, etc.
  129. */
  130. public getVerticesData(kind: string, copyWhenShared?: boolean): Nullable<FloatArray> {
  131. return this._sourceMesh.getVerticesData(kind, copyWhenShared);
  132. }
  133. /**
  134. * Sets the vertex data of the mesh geometry for the requested `kind`.
  135. * If the mesh has no geometry, a new Geometry object is set to the mesh and then passed this vertex data.
  136. * The `data` are either a numeric array either a Float32Array.
  137. * The parameter `updatable` is passed as is to the underlying Geometry object constructor (if initianilly none) or updater.
  138. * The parameter `stride` is an optional positive integer, it is usually automatically deducted from the `kind` (3 for positions or normals, 2 for UV, etc).
  139. * Note that a new underlying VertexBuffer object is created each call.
  140. * If the `kind` is the `PositionKind`, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
  141. *
  142. * Possible `kind` values :
  143. * - VertexBuffer.PositionKind
  144. * - VertexBuffer.UVKind
  145. * - VertexBuffer.UV2Kind
  146. * - VertexBuffer.UV3Kind
  147. * - VertexBuffer.UV4Kind
  148. * - VertexBuffer.UV5Kind
  149. * - VertexBuffer.UV6Kind
  150. * - VertexBuffer.ColorKind
  151. * - VertexBuffer.MatricesIndicesKind
  152. * - VertexBuffer.MatricesIndicesExtraKind
  153. * - VertexBuffer.MatricesWeightsKind
  154. * - VertexBuffer.MatricesWeightsExtraKind
  155. *
  156. * Returns the Mesh.
  157. */
  158. public setVerticesData(kind: string, data: FloatArray, updatable?: boolean, stride?: number): AbstractMesh {
  159. if (this.sourceMesh) {
  160. this.sourceMesh.setVerticesData(kind, data, updatable, stride);
  161. }
  162. return this.sourceMesh;
  163. }
  164. /**
  165. * Updates the existing vertex data of the mesh geometry for the requested `kind`.
  166. * If the mesh has no geometry, it is simply returned as it is.
  167. * The `data` are either a numeric array either a Float32Array.
  168. * No new underlying VertexBuffer object is created.
  169. * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
  170. * If the parameter `makeItUnique` is true, a new global geometry is created from this positions and is set to the mesh.
  171. *
  172. * Possible `kind` values :
  173. * - VertexBuffer.PositionKind
  174. * - VertexBuffer.UVKind
  175. * - VertexBuffer.UV2Kind
  176. * - VertexBuffer.UV3Kind
  177. * - VertexBuffer.UV4Kind
  178. * - VertexBuffer.UV5Kind
  179. * - VertexBuffer.UV6Kind
  180. * - VertexBuffer.ColorKind
  181. * - VertexBuffer.MatricesIndicesKind
  182. * - VertexBuffer.MatricesIndicesExtraKind
  183. * - VertexBuffer.MatricesWeightsKind
  184. * - VertexBuffer.MatricesWeightsExtraKind
  185. *
  186. * Returns the Mesh.
  187. */
  188. public updateVerticesData(kind: string, data: FloatArray, updateExtends?: boolean, makeItUnique?: boolean): Mesh {
  189. if (this.sourceMesh) {
  190. this.sourceMesh.updateVerticesData(kind, data, updateExtends, makeItUnique);
  191. }
  192. return this.sourceMesh;
  193. }
  194. /**
  195. * Sets the mesh indices.
  196. * Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array).
  197. * If the mesh has no geometry, a new Geometry object is created and set to the mesh.
  198. * This method creates a new index buffer each call.
  199. * Returns the Mesh.
  200. */
  201. public setIndices(indices: IndicesArray, totalVertices: Nullable<number> = null): Mesh {
  202. if (this.sourceMesh) {
  203. this.sourceMesh.setIndices(indices, totalVertices);
  204. }
  205. return this.sourceMesh;
  206. }
  207. /**
  208. * Boolean : True if the mesh owns the requested kind of data.
  209. */
  210. public isVerticesDataPresent(kind: string): boolean {
  211. return this._sourceMesh.isVerticesDataPresent(kind);
  212. }
  213. /**
  214. * Returns an array of indices (IndicesArray).
  215. */
  216. public getIndices(): Nullable<IndicesArray> {
  217. return this._sourceMesh.getIndices();
  218. }
  219. public get _positions(): Nullable<Vector3[]> {
  220. return this._sourceMesh._positions;
  221. }
  222. /**
  223. * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
  224. * This means the mesh underlying bounding box and sphere are recomputed.
  225. * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
  226. * @returns the current mesh
  227. */
  228. public refreshBoundingInfo(applySkeleton: boolean = false): InstancedMesh {
  229. if (this._boundingInfo && this._boundingInfo.isLocked) {
  230. return this;
  231. }
  232. const bias = this._sourceMesh.geometry ? this._sourceMesh.geometry.boundingBias : null;
  233. this._refreshBoundingInfo(this._sourceMesh._getPositionData(applySkeleton), bias);
  234. return this;
  235. }
  236. /** @hidden */
  237. public _preActivate(): InstancedMesh {
  238. if (this._currentLOD) {
  239. this._currentLOD._preActivate();
  240. }
  241. return this;
  242. }
  243. /** @hidden */
  244. public _activate(renderId: number, intermediateRendering: boolean): boolean {
  245. if (!this._sourceMesh.subMeshes) {
  246. Logger.Warn("Instances should only be created for meshes with geometry.");
  247. }
  248. if (this._currentLOD) {
  249. let differentSign = (this._currentLOD._getWorldMatrixDeterminant() > 0) !== (this._getWorldMatrixDeterminant() > 0);
  250. if (differentSign) {
  251. this._internalAbstractMeshDataInfo._actAsRegularMesh = true;
  252. return true;
  253. }
  254. this._internalAbstractMeshDataInfo._actAsRegularMesh = false;
  255. this._currentLOD._registerInstanceForRenderId(this, renderId);
  256. if (intermediateRendering) {
  257. if (!this._currentLOD._internalAbstractMeshDataInfo._isActiveIntermediate) {
  258. this._currentLOD._internalAbstractMeshDataInfo._onlyForInstancesIntermediate = true;
  259. return true;
  260. }
  261. } else {
  262. if (!this._currentLOD._internalAbstractMeshDataInfo._isActive) {
  263. this._currentLOD._internalAbstractMeshDataInfo._onlyForInstances = true;
  264. return true;
  265. }
  266. }
  267. }
  268. return false;
  269. }
  270. /** @hidden */
  271. public _postActivate(): void {
  272. if (this._edgesRenderer && this._edgesRenderer.isEnabled && this._sourceMesh._renderingGroup) {
  273. this._sourceMesh._renderingGroup._edgesRenderers.push(this._edgesRenderer);
  274. }
  275. }
  276. public getWorldMatrix(): Matrix {
  277. if (this._currentLOD && this._currentLOD.billboardMode !== TransformNode.BILLBOARDMODE_NONE && this._currentLOD._masterMesh !== this) {
  278. let tempMaster = this._currentLOD._masterMesh;
  279. this._currentLOD._masterMesh = this;
  280. TmpVectors.Matrix[0].copyFrom(this._currentLOD.computeWorldMatrix(true));
  281. this._currentLOD._masterMesh = tempMaster;
  282. return TmpVectors.Matrix[0];
  283. }
  284. return super.getWorldMatrix();
  285. }
  286. public get isAnInstance(): boolean {
  287. return true;
  288. }
  289. /**
  290. * Returns the current associated LOD AbstractMesh.
  291. */
  292. public getLOD(camera: Camera): AbstractMesh {
  293. if (!camera) {
  294. return this;
  295. }
  296. let boundingInfo = this.getBoundingInfo();
  297. this._currentLOD = <Mesh>this.sourceMesh.getLOD(camera, boundingInfo.boundingSphere);
  298. if (this._currentLOD === this.sourceMesh) {
  299. return this.sourceMesh;
  300. }
  301. return this._currentLOD;
  302. }
  303. /** @hidden */
  304. public _syncSubMeshes(): InstancedMesh {
  305. this.releaseSubMeshes();
  306. if (this._sourceMesh.subMeshes) {
  307. for (var index = 0; index < this._sourceMesh.subMeshes.length; index++) {
  308. this._sourceMesh.subMeshes[index].clone(this, this._sourceMesh);
  309. }
  310. }
  311. return this;
  312. }
  313. /** @hidden */
  314. public _generatePointsArray(): boolean {
  315. return this._sourceMesh._generatePointsArray();
  316. }
  317. /**
  318. * Creates a new InstancedMesh from the current mesh.
  319. * - name (string) : the cloned mesh name
  320. * - newParent (optional Node) : the optional Node to parent the clone to.
  321. * - doNotCloneChildren (optional boolean, default `false`) : if `true` the model children aren't cloned.
  322. *
  323. * Returns the clone.
  324. */
  325. public clone(name: string, newParent: Nullable<Node>= null, doNotCloneChildren?: boolean): Nullable<AbstractMesh> {
  326. var result = this._sourceMesh.createInstance(name);
  327. // Deep copy
  328. DeepCopier.DeepCopy(this, result, ["name", "subMeshes", "uniqueId"], []);
  329. // Bounding info
  330. this.refreshBoundingInfo();
  331. // Parent
  332. if (newParent) {
  333. result.parent = newParent;
  334. }
  335. if (!doNotCloneChildren) {
  336. // Children
  337. for (var index = 0; index < this.getScene().meshes.length; index++) {
  338. var mesh = this.getScene().meshes[index];
  339. if (mesh.parent === this) {
  340. mesh.clone(mesh.name, result);
  341. }
  342. }
  343. }
  344. result.computeWorldMatrix(true);
  345. return result;
  346. }
  347. /**
  348. * Disposes the InstancedMesh.
  349. * Returns nothing.
  350. */
  351. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  352. // Remove from mesh
  353. this._sourceMesh.removeInstance(this);
  354. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  355. }
  356. }