thinInstanceMesh.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import { Mesh } from "../Meshes/mesh.js";
  2. import { VertexBuffer, Buffer } from "../Buffers/buffer.js";
  3. import { Matrix, Vector3, TmpVectors } from "../Maths/math.vector.js";
  4. import { Logger } from "../Misc/logger.js";
  5. import { BoundingInfo } from "../Culling/boundingInfo.js";
  6. Mesh.prototype.thinInstanceAdd = function (matrix, refresh = true) {
  7. if (!this.getScene().getEngine().getCaps().instancedArrays) {
  8. Logger.Error("Thin Instances are not supported on this device as Instanced Array extension not supported");
  9. return -1;
  10. }
  11. this._thinInstanceUpdateBufferSize("matrix", Array.isArray(matrix) ? matrix.length : 1);
  12. const index = this._thinInstanceDataStorage.instancesCount;
  13. if (Array.isArray(matrix)) {
  14. for (let i = 0; i < matrix.length; ++i) {
  15. this.thinInstanceSetMatrixAt(this._thinInstanceDataStorage.instancesCount++, matrix[i], i === matrix.length - 1 && refresh);
  16. }
  17. }
  18. else {
  19. this.thinInstanceSetMatrixAt(this._thinInstanceDataStorage.instancesCount++, matrix, refresh);
  20. }
  21. return index;
  22. };
  23. Mesh.prototype.thinInstanceAddSelf = function (refresh = true) {
  24. return this.thinInstanceAdd(Matrix.IdentityReadOnly, refresh);
  25. };
  26. Mesh.prototype.thinInstanceRegisterAttribute = function (kind, stride) {
  27. // preserve backward compatibility
  28. if (kind === VertexBuffer.ColorKind) {
  29. kind = VertexBuffer.ColorInstanceKind;
  30. }
  31. this.removeVerticesData(kind);
  32. this._thinInstanceInitializeUserStorage();
  33. this._userThinInstanceBuffersStorage.strides[kind] = stride;
  34. this._userThinInstanceBuffersStorage.sizes[kind] = stride * Math.max(32, this._thinInstanceDataStorage.instancesCount); // Initial size
  35. this._userThinInstanceBuffersStorage.data[kind] = new Float32Array(this._userThinInstanceBuffersStorage.sizes[kind]);
  36. this._userThinInstanceBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), this._userThinInstanceBuffersStorage.data[kind], kind, true, false, stride, true);
  37. this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[kind]);
  38. };
  39. Mesh.prototype.thinInstanceSetMatrixAt = function (index, matrix, refresh = true) {
  40. if (!this._thinInstanceDataStorage.matrixData || index >= this._thinInstanceDataStorage.instancesCount) {
  41. return false;
  42. }
  43. const matrixData = this._thinInstanceDataStorage.matrixData;
  44. matrix.copyToArray(matrixData, index * 16);
  45. if (this._thinInstanceDataStorage.worldMatrices) {
  46. this._thinInstanceDataStorage.worldMatrices[index] = matrix;
  47. }
  48. if (refresh) {
  49. this.thinInstanceBufferUpdated("matrix");
  50. if (!this.doNotSyncBoundingInfo) {
  51. this.thinInstanceRefreshBoundingInfo(false);
  52. }
  53. }
  54. return true;
  55. };
  56. Mesh.prototype.thinInstanceSetAttributeAt = function (kind, index, value, refresh = true) {
  57. // preserve backward compatibility
  58. if (kind === VertexBuffer.ColorKind) {
  59. kind = VertexBuffer.ColorInstanceKind;
  60. }
  61. if (!this._userThinInstanceBuffersStorage || !this._userThinInstanceBuffersStorage.data[kind] || index >= this._thinInstanceDataStorage.instancesCount) {
  62. return false;
  63. }
  64. this._thinInstanceUpdateBufferSize(kind, 0); // make sur the buffer for the kind attribute is big enough
  65. this._userThinInstanceBuffersStorage.data[kind].set(value, index * this._userThinInstanceBuffersStorage.strides[kind]);
  66. if (refresh) {
  67. this.thinInstanceBufferUpdated(kind);
  68. }
  69. return true;
  70. };
  71. Object.defineProperty(Mesh.prototype, "thinInstanceCount", {
  72. get: function () {
  73. return this._thinInstanceDataStorage.instancesCount;
  74. },
  75. set: function (value) {
  76. const matrixData = this._thinInstanceDataStorage.matrixData ?? this.source?._thinInstanceDataStorage.matrixData;
  77. const numMaxInstances = matrixData ? matrixData.length / 16 : 0;
  78. if (value <= numMaxInstances) {
  79. this._thinInstanceDataStorage.instancesCount = value;
  80. }
  81. },
  82. enumerable: true,
  83. configurable: true,
  84. });
  85. Mesh.prototype._thinInstanceCreateMatrixBuffer = function (kind, buffer, staticBuffer = true) {
  86. const matrixBuffer = new Buffer(this.getEngine(), buffer, !staticBuffer, 16, false, true);
  87. for (let i = 0; i < 4; i++) {
  88. this.setVerticesBuffer(matrixBuffer.createVertexBuffer(kind + i, i * 4, 4));
  89. }
  90. return matrixBuffer;
  91. };
  92. Mesh.prototype.thinInstanceSetBuffer = function (kind, buffer, stride = 0, staticBuffer = true) {
  93. stride = stride || 16;
  94. if (kind === "matrix") {
  95. this._thinInstanceDataStorage.matrixBuffer?.dispose();
  96. this._thinInstanceDataStorage.matrixBuffer = null;
  97. this._thinInstanceDataStorage.matrixBufferSize = buffer ? buffer.length : 32 * stride;
  98. this._thinInstanceDataStorage.matrixData = buffer;
  99. this._thinInstanceDataStorage.worldMatrices = null;
  100. if (buffer !== null) {
  101. this._thinInstanceDataStorage.instancesCount = buffer.length / stride;
  102. this._thinInstanceDataStorage.matrixBuffer = this._thinInstanceCreateMatrixBuffer("world", buffer, staticBuffer);
  103. if (!this.doNotSyncBoundingInfo) {
  104. this.thinInstanceRefreshBoundingInfo(false);
  105. }
  106. }
  107. else {
  108. this._thinInstanceDataStorage.instancesCount = 0;
  109. if (!this.doNotSyncBoundingInfo) {
  110. // mesh has no more thin instances, so need to recompute the bounding box because it's the regular mesh that will now be displayed
  111. this.refreshBoundingInfo();
  112. }
  113. }
  114. }
  115. else if (kind === "previousMatrix") {
  116. this._thinInstanceDataStorage.previousMatrixBuffer?.dispose();
  117. this._thinInstanceDataStorage.previousMatrixBuffer = null;
  118. this._thinInstanceDataStorage.previousMatrixData = buffer;
  119. if (buffer !== null) {
  120. this._thinInstanceDataStorage.previousMatrixBuffer = this._thinInstanceCreateMatrixBuffer("previousWorld", buffer, staticBuffer);
  121. }
  122. }
  123. else {
  124. // color for instanced mesh is ColorInstanceKind and not ColorKind because of native that needs to do the differenciation
  125. // hot switching kind here to preserve backward compatibility
  126. if (kind === VertexBuffer.ColorKind) {
  127. kind = VertexBuffer.ColorInstanceKind;
  128. }
  129. if (buffer === null) {
  130. if (this._userThinInstanceBuffersStorage?.data[kind]) {
  131. this.removeVerticesData(kind);
  132. delete this._userThinInstanceBuffersStorage.data[kind];
  133. delete this._userThinInstanceBuffersStorage.strides[kind];
  134. delete this._userThinInstanceBuffersStorage.sizes[kind];
  135. delete this._userThinInstanceBuffersStorage.vertexBuffers[kind];
  136. }
  137. }
  138. else {
  139. this._thinInstanceInitializeUserStorage();
  140. this._userThinInstanceBuffersStorage.data[kind] = buffer;
  141. this._userThinInstanceBuffersStorage.strides[kind] = stride;
  142. this._userThinInstanceBuffersStorage.sizes[kind] = buffer.length;
  143. this._userThinInstanceBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), buffer, kind, !staticBuffer, false, stride, true);
  144. this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[kind]);
  145. }
  146. }
  147. };
  148. Mesh.prototype.thinInstanceBufferUpdated = function (kind) {
  149. if (kind === "matrix") {
  150. if (this.thinInstanceAllowAutomaticStaticBufferRecreation && this._thinInstanceDataStorage.matrixBuffer && !this._thinInstanceDataStorage.matrixBuffer.isUpdatable()) {
  151. this._thinInstanceRecreateBuffer(kind);
  152. }
  153. this._thinInstanceDataStorage.matrixBuffer?.updateDirectly(this._thinInstanceDataStorage.matrixData, 0, this._thinInstanceDataStorage.instancesCount);
  154. }
  155. else if (kind === "previousMatrix") {
  156. if (this.thinInstanceAllowAutomaticStaticBufferRecreation &&
  157. this._thinInstanceDataStorage.previousMatrixBuffer &&
  158. !this._thinInstanceDataStorage.previousMatrixBuffer.isUpdatable()) {
  159. this._thinInstanceRecreateBuffer(kind);
  160. }
  161. this._thinInstanceDataStorage.previousMatrixBuffer?.updateDirectly(this._thinInstanceDataStorage.previousMatrixData, 0, this._thinInstanceDataStorage.instancesCount);
  162. }
  163. else {
  164. // preserve backward compatibility
  165. if (kind === VertexBuffer.ColorKind) {
  166. kind = VertexBuffer.ColorInstanceKind;
  167. }
  168. if (this._userThinInstanceBuffersStorage?.vertexBuffers[kind]) {
  169. if (this.thinInstanceAllowAutomaticStaticBufferRecreation && !this._userThinInstanceBuffersStorage.vertexBuffers[kind].isUpdatable()) {
  170. this._thinInstanceRecreateBuffer(kind);
  171. }
  172. this._userThinInstanceBuffersStorage.vertexBuffers[kind].updateDirectly(this._userThinInstanceBuffersStorage.data[kind], 0);
  173. }
  174. }
  175. };
  176. Mesh.prototype.thinInstancePartialBufferUpdate = function (kind, data, offset) {
  177. if (kind === "matrix") {
  178. if (this._thinInstanceDataStorage.matrixBuffer) {
  179. this._thinInstanceDataStorage.matrixBuffer.updateDirectly(data, offset);
  180. }
  181. }
  182. else {
  183. // preserve backward compatibility
  184. if (kind === VertexBuffer.ColorKind) {
  185. kind = VertexBuffer.ColorInstanceKind;
  186. }
  187. if (this._userThinInstanceBuffersStorage?.vertexBuffers[kind]) {
  188. this._userThinInstanceBuffersStorage.vertexBuffers[kind].updateDirectly(data, offset);
  189. }
  190. }
  191. };
  192. Mesh.prototype.thinInstanceGetWorldMatrices = function () {
  193. if (!this._thinInstanceDataStorage.matrixData || !this._thinInstanceDataStorage.matrixBuffer) {
  194. return [];
  195. }
  196. const matrixData = this._thinInstanceDataStorage.matrixData;
  197. if (!this._thinInstanceDataStorage.worldMatrices) {
  198. this._thinInstanceDataStorage.worldMatrices = [];
  199. for (let i = 0; i < this._thinInstanceDataStorage.instancesCount; ++i) {
  200. this._thinInstanceDataStorage.worldMatrices[i] = Matrix.FromArray(matrixData, i * 16);
  201. }
  202. }
  203. return this._thinInstanceDataStorage.worldMatrices;
  204. };
  205. Mesh.prototype.thinInstanceRefreshBoundingInfo = function (forceRefreshParentInfo = false, applySkeleton = false, applyMorph = false) {
  206. if (!this._thinInstanceDataStorage.matrixData || !this._thinInstanceDataStorage.matrixBuffer) {
  207. return;
  208. }
  209. const vectors = this._thinInstanceDataStorage.boundingVectors;
  210. if (forceRefreshParentInfo || !this.rawBoundingInfo) {
  211. vectors.length = 0;
  212. this.refreshBoundingInfo(applySkeleton, applyMorph);
  213. const boundingInfo = this.getBoundingInfo();
  214. this.rawBoundingInfo = new BoundingInfo(boundingInfo.minimum, boundingInfo.maximum);
  215. }
  216. const boundingInfo = this.getBoundingInfo();
  217. const matrixData = this._thinInstanceDataStorage.matrixData;
  218. if (vectors.length === 0) {
  219. for (let v = 0; v < boundingInfo.boundingBox.vectors.length; ++v) {
  220. vectors.push(boundingInfo.boundingBox.vectors[v].clone());
  221. }
  222. }
  223. TmpVectors.Vector3[0].setAll(Number.POSITIVE_INFINITY); // min
  224. TmpVectors.Vector3[1].setAll(Number.NEGATIVE_INFINITY); // max
  225. for (let i = 0; i < this._thinInstanceDataStorage.instancesCount; ++i) {
  226. Matrix.FromArrayToRef(matrixData, i * 16, TmpVectors.Matrix[0]);
  227. for (let v = 0; v < vectors.length; ++v) {
  228. Vector3.TransformCoordinatesToRef(vectors[v], TmpVectors.Matrix[0], TmpVectors.Vector3[2]);
  229. TmpVectors.Vector3[0].minimizeInPlace(TmpVectors.Vector3[2]);
  230. TmpVectors.Vector3[1].maximizeInPlace(TmpVectors.Vector3[2]);
  231. }
  232. }
  233. boundingInfo.reConstruct(TmpVectors.Vector3[0], TmpVectors.Vector3[1]);
  234. this._updateBoundingInfo();
  235. };
  236. Mesh.prototype._thinInstanceRecreateBuffer = function (kind, staticBuffer = true) {
  237. if (kind === "matrix") {
  238. this._thinInstanceDataStorage.matrixBuffer?.dispose();
  239. this._thinInstanceDataStorage.matrixBuffer = this._thinInstanceCreateMatrixBuffer("world", this._thinInstanceDataStorage.matrixData, staticBuffer);
  240. }
  241. else if (kind === "previousMatrix") {
  242. if (this._scene.needsPreviousWorldMatrices) {
  243. this._thinInstanceDataStorage.previousMatrixBuffer?.dispose();
  244. this._thinInstanceDataStorage.previousMatrixBuffer = this._thinInstanceCreateMatrixBuffer("previousWorld", this._thinInstanceDataStorage.previousMatrixData ?? this._thinInstanceDataStorage.matrixData, staticBuffer);
  245. }
  246. }
  247. else {
  248. if (kind === VertexBuffer.ColorKind) {
  249. kind = VertexBuffer.ColorInstanceKind;
  250. }
  251. this._userThinInstanceBuffersStorage.vertexBuffers[kind]?.dispose();
  252. this._userThinInstanceBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), this._userThinInstanceBuffersStorage.data[kind], kind, !staticBuffer, false, this._userThinInstanceBuffersStorage.strides[kind], true);
  253. this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[kind]);
  254. }
  255. };
  256. Mesh.prototype._thinInstanceUpdateBufferSize = function (kind, numInstances = 1) {
  257. // preserve backward compatibility
  258. if (kind === VertexBuffer.ColorKind) {
  259. kind = VertexBuffer.ColorInstanceKind;
  260. }
  261. const kindIsMatrix = kind === "matrix";
  262. if (!kindIsMatrix && (!this._userThinInstanceBuffersStorage || !this._userThinInstanceBuffersStorage.strides[kind])) {
  263. return;
  264. }
  265. const stride = kindIsMatrix ? 16 : this._userThinInstanceBuffersStorage.strides[kind];
  266. const currentSize = kindIsMatrix ? this._thinInstanceDataStorage.matrixBufferSize : this._userThinInstanceBuffersStorage.sizes[kind];
  267. let data = kindIsMatrix ? this._thinInstanceDataStorage.matrixData : this._userThinInstanceBuffersStorage.data[kind];
  268. const bufferSize = (this._thinInstanceDataStorage.instancesCount + numInstances) * stride;
  269. let newSize = currentSize;
  270. while (newSize < bufferSize) {
  271. newSize *= 2;
  272. }
  273. if (!data || currentSize != newSize) {
  274. if (!data) {
  275. data = new Float32Array(newSize);
  276. }
  277. else {
  278. const newData = new Float32Array(newSize);
  279. newData.set(data, 0);
  280. data = newData;
  281. }
  282. if (kindIsMatrix) {
  283. this._thinInstanceDataStorage.matrixBuffer?.dispose();
  284. this._thinInstanceDataStorage.matrixBuffer = this._thinInstanceCreateMatrixBuffer("world", data, false);
  285. this._thinInstanceDataStorage.matrixData = data;
  286. this._thinInstanceDataStorage.matrixBufferSize = newSize;
  287. if (this._scene.needsPreviousWorldMatrices && !this._thinInstanceDataStorage.previousMatrixData) {
  288. this._thinInstanceDataStorage.previousMatrixBuffer?.dispose();
  289. this._thinInstanceDataStorage.previousMatrixBuffer = this._thinInstanceCreateMatrixBuffer("previousWorld", data, false);
  290. }
  291. }
  292. else {
  293. this._userThinInstanceBuffersStorage.vertexBuffers[kind]?.dispose();
  294. this._userThinInstanceBuffersStorage.data[kind] = data;
  295. this._userThinInstanceBuffersStorage.sizes[kind] = newSize;
  296. this._userThinInstanceBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), data, kind, true, false, stride, true);
  297. this.setVerticesBuffer(this._userThinInstanceBuffersStorage.vertexBuffers[kind]);
  298. }
  299. }
  300. };
  301. Mesh.prototype._thinInstanceInitializeUserStorage = function () {
  302. if (!this._userThinInstanceBuffersStorage) {
  303. this._userThinInstanceBuffersStorage = {
  304. data: {},
  305. sizes: {},
  306. vertexBuffers: {},
  307. strides: {},
  308. };
  309. }
  310. };
  311. Mesh.prototype._disposeThinInstanceSpecificData = function () {
  312. if (this._thinInstanceDataStorage?.matrixBuffer) {
  313. this._thinInstanceDataStorage.matrixBuffer.dispose();
  314. this._thinInstanceDataStorage.matrixBuffer = null;
  315. }
  316. };
  317. //# sourceMappingURL=thinInstanceMesh.js.map