fluidRenderer.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. import { Scene } from "../../scene.js";
  2. import { SceneComponentConstants } from "../../sceneComponent.js";
  3. import { FluidRenderingObjectParticleSystem } from "./fluidRenderingObjectParticleSystem.js";
  4. import { FluidRenderingTargetRenderer } from "./fluidRenderingTargetRenderer.js";
  5. import { FluidRenderingObjectCustomParticles } from "./fluidRenderingObjectCustomParticles.js";
  6. import { FluidRenderingDepthTextureCopy } from "./fluidRenderingDepthTextureCopy.js";
  7. import "../../Shaders/fluidRenderingParticleDepth.vertex.js";
  8. import "../../Shaders/fluidRenderingParticleDepth.fragment.js";
  9. import "../../Shaders/fluidRenderingParticleThickness.vertex.js";
  10. import "../../Shaders/fluidRenderingParticleThickness.fragment.js";
  11. import "../../Shaders/fluidRenderingParticleDiffuse.vertex.js";
  12. import "../../Shaders/fluidRenderingParticleDiffuse.fragment.js";
  13. import "../../Shaders/fluidRenderingBilateralBlur.fragment.js";
  14. import "../../Shaders/fluidRenderingStandardBlur.fragment.js";
  15. import "../../Shaders/fluidRenderingRender.fragment.js";
  16. Object.defineProperty(Scene.prototype, "fluidRenderer", {
  17. get: function () {
  18. return this._fluidRenderer;
  19. },
  20. set: function (value) {
  21. this._fluidRenderer = value;
  22. },
  23. enumerable: true,
  24. configurable: true,
  25. });
  26. Scene.prototype.enableFluidRenderer = function () {
  27. if (this._fluidRenderer) {
  28. return this._fluidRenderer;
  29. }
  30. this._fluidRenderer = new FluidRenderer(this);
  31. return this._fluidRenderer;
  32. };
  33. Scene.prototype.disableFluidRenderer = function () {
  34. this._fluidRenderer?.dispose();
  35. this._fluidRenderer = null;
  36. };
  37. function IsParticleSystemObject(obj) {
  38. return !!obj.particleSystem;
  39. }
  40. function IsCustomParticlesObject(obj) {
  41. return !!obj.addBuffers;
  42. }
  43. /**
  44. * Defines the fluid renderer scene component responsible to render objects as fluids
  45. */
  46. export class FluidRendererSceneComponent {
  47. /**
  48. * Creates a new instance of the component for the given scene
  49. * @param scene Defines the scene to register the component in
  50. */
  51. constructor(scene) {
  52. /**
  53. * The component name helpful to identify the component in the list of scene components.
  54. */
  55. this.name = SceneComponentConstants.NAME_FLUIDRENDERER;
  56. this.scene = scene;
  57. }
  58. /**
  59. * Registers the component in a given scene
  60. */
  61. register() {
  62. this.scene._gatherActiveCameraRenderTargetsStage.registerStep(SceneComponentConstants.STEP_GATHERACTIVECAMERARENDERTARGETS_FLUIDRENDERER, this, this._gatherActiveCameraRenderTargets);
  63. this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_FLUIDRENDERER, this, this._afterCameraDraw);
  64. }
  65. _gatherActiveCameraRenderTargets(_renderTargets) {
  66. this.scene.fluidRenderer?._prepareRendering();
  67. }
  68. _afterCameraDraw(camera) {
  69. this.scene.fluidRenderer?._render(camera);
  70. }
  71. /**
  72. * Rebuilds the elements related to this component in case of
  73. * context lost for instance.
  74. */
  75. rebuild() {
  76. const fluidRenderer = this.scene.fluidRenderer;
  77. if (!fluidRenderer) {
  78. return;
  79. }
  80. const buffers = new Set();
  81. for (let i = 0; i < fluidRenderer.renderObjects.length; ++i) {
  82. const obj = fluidRenderer.renderObjects[i].object;
  83. if (IsCustomParticlesObject(obj)) {
  84. const vbuffers = obj.vertexBuffers;
  85. for (const name in vbuffers) {
  86. buffers.add(vbuffers[name].getWrapperBuffer());
  87. }
  88. }
  89. }
  90. buffers.forEach((buffer) => {
  91. buffer._rebuild();
  92. });
  93. }
  94. /**
  95. * Disposes the component and the associated resources
  96. */
  97. dispose() {
  98. this.scene.disableFluidRenderer();
  99. }
  100. }
  101. /**
  102. * Class responsible for fluid rendering.
  103. * It is implementing the method described in https://developer.download.nvidia.com/presentations/2010/gdc/Direct3D_Effects.pdf
  104. */
  105. export class FluidRenderer {
  106. /** @internal */
  107. static _SceneComponentInitialization(scene) {
  108. let component = scene._getComponent(SceneComponentConstants.NAME_FLUIDRENDERER);
  109. if (!component) {
  110. component = new FluidRendererSceneComponent(scene);
  111. scene._addComponent(component);
  112. }
  113. }
  114. /**
  115. * Initializes the class
  116. * @param scene Scene in which the objects are part of
  117. */
  118. constructor(scene) {
  119. this._scene = scene;
  120. this._engine = scene.getEngine();
  121. this._onEngineResizeObserver = null;
  122. this.renderObjects = [];
  123. this.targetRenderers = [];
  124. this._cameras = new Map();
  125. FluidRenderer._SceneComponentInitialization(this._scene);
  126. this._onEngineResizeObserver = this._engine.onResizeObservable.add(() => {
  127. this._initialize();
  128. });
  129. }
  130. /**
  131. * Reinitializes the class
  132. * Can be used if you change the object priority (FluidRenderingObject.priority), to make sure the objects are rendered in the right order
  133. */
  134. recreate() {
  135. this._sortRenderingObjects();
  136. this._initialize();
  137. }
  138. /**
  139. * Gets the render object corresponding to a particle system (null if the particle system is not rendered as a fluid)
  140. * @param ps The particle system
  141. * @returns the render object corresponding to this particle system if any, otherwise null
  142. */
  143. getRenderObjectFromParticleSystem(ps) {
  144. const index = this._getParticleSystemIndex(ps);
  145. return index !== -1 ? this.renderObjects[index] : null;
  146. }
  147. /**
  148. * Adds a particle system to the fluid renderer.
  149. * @param ps particle system
  150. * @param generateDiffuseTexture True if you want to generate a diffuse texture from the particle system and use it as part of the fluid rendering (default: false)
  151. * @param targetRenderer The target renderer used to display the particle system as a fluid. If not provided, the method will create a new one
  152. * @param camera The camera used by the target renderer (if the target renderer is created by the method)
  153. * @returns the render object corresponding to the particle system
  154. */
  155. addParticleSystem(ps, generateDiffuseTexture, targetRenderer, camera) {
  156. const object = new FluidRenderingObjectParticleSystem(this._scene, ps);
  157. object.onParticleSizeChanged.add(() => this._setParticleSizeForRenderTargets());
  158. if (!targetRenderer) {
  159. targetRenderer = new FluidRenderingTargetRenderer(this._scene, camera);
  160. this.targetRenderers.push(targetRenderer);
  161. }
  162. if (!targetRenderer._onUseVelocityChanged.hasObservers()) {
  163. targetRenderer._onUseVelocityChanged.add(() => this._setUseVelocityForRenderObject());
  164. }
  165. if (generateDiffuseTexture !== undefined) {
  166. targetRenderer.generateDiffuseTexture = generateDiffuseTexture;
  167. }
  168. const renderObject = { object, targetRenderer };
  169. this.renderObjects.push(renderObject);
  170. this._sortRenderingObjects();
  171. this._setParticleSizeForRenderTargets();
  172. return renderObject;
  173. }
  174. /**
  175. * Adds a custom particle set to the fluid renderer.
  176. * @param buffers The list of buffers (should contain at least a "position" buffer!)
  177. * @param numParticles Number of particles in each buffer
  178. * @param generateDiffuseTexture True if you want to generate a diffuse texture from buffers and use it as part of the fluid rendering (default: false). For the texture to be generated correctly, you need a "color" buffer in the set!
  179. * @param targetRenderer The target renderer used to display the particle system as a fluid. If not provided, the method will create a new one
  180. * @param camera The camera used by the target renderer (if the target renderer is created by the method)
  181. * @returns the render object corresponding to the custom particle set
  182. */
  183. addCustomParticles(buffers, numParticles, generateDiffuseTexture, targetRenderer, camera) {
  184. const object = new FluidRenderingObjectCustomParticles(this._scene, buffers, numParticles);
  185. object.onParticleSizeChanged.add(() => this._setParticleSizeForRenderTargets());
  186. if (!targetRenderer) {
  187. targetRenderer = new FluidRenderingTargetRenderer(this._scene, camera);
  188. this.targetRenderers.push(targetRenderer);
  189. }
  190. if (!targetRenderer._onUseVelocityChanged.hasObservers()) {
  191. targetRenderer._onUseVelocityChanged.add(() => this._setUseVelocityForRenderObject());
  192. }
  193. if (generateDiffuseTexture !== undefined) {
  194. targetRenderer.generateDiffuseTexture = generateDiffuseTexture;
  195. }
  196. const renderObject = { object, targetRenderer };
  197. this.renderObjects.push(renderObject);
  198. this._sortRenderingObjects();
  199. this._setParticleSizeForRenderTargets();
  200. return renderObject;
  201. }
  202. /**
  203. * Removes a render object from the fluid renderer
  204. * @param renderObject the render object to remove
  205. * @param removeUnusedTargetRenderer True to remove/dispose of the target renderer if it's not used anymore (default: true)
  206. * @returns True if the render object has been found and released, else false
  207. */
  208. removeRenderObject(renderObject, removeUnusedTargetRenderer = true) {
  209. const index = this.renderObjects.indexOf(renderObject);
  210. if (index === -1) {
  211. return false;
  212. }
  213. renderObject.object.dispose();
  214. this.renderObjects.splice(index, 1);
  215. if (removeUnusedTargetRenderer && this._removeUnusedTargetRenderers()) {
  216. this._initialize();
  217. }
  218. else {
  219. this._setParticleSizeForRenderTargets();
  220. }
  221. return true;
  222. }
  223. _sortRenderingObjects() {
  224. this.renderObjects.sort((a, b) => {
  225. return a.object.priority < b.object.priority ? -1 : a.object.priority > b.object.priority ? 1 : 0;
  226. });
  227. }
  228. _removeUnusedTargetRenderers() {
  229. const indexes = {};
  230. for (let i = 0; i < this.renderObjects.length; ++i) {
  231. const targetRenderer = this.renderObjects[i].targetRenderer;
  232. indexes[this.targetRenderers.indexOf(targetRenderer)] = true;
  233. }
  234. let removed = false;
  235. const newList = [];
  236. for (let i = 0; i < this.targetRenderers.length; ++i) {
  237. if (!indexes[i]) {
  238. this.targetRenderers[i].dispose();
  239. removed = true;
  240. }
  241. else {
  242. newList.push(this.targetRenderers[i]);
  243. }
  244. }
  245. if (removed) {
  246. this.targetRenderers.length = 0;
  247. this.targetRenderers.push(...newList);
  248. }
  249. return removed;
  250. }
  251. _getParticleSystemIndex(ps) {
  252. for (let i = 0; i < this.renderObjects.length; ++i) {
  253. const obj = this.renderObjects[i].object;
  254. if (IsParticleSystemObject(obj) && obj.particleSystem === ps) {
  255. return i;
  256. }
  257. }
  258. return -1;
  259. }
  260. _initialize() {
  261. for (let i = 0; i < this.targetRenderers.length; ++i) {
  262. this.targetRenderers[i].dispose();
  263. }
  264. const cameras = new Map();
  265. for (let i = 0; i < this.targetRenderers.length; ++i) {
  266. const targetRenderer = this.targetRenderers[i];
  267. targetRenderer._initialize();
  268. if (targetRenderer.camera && targetRenderer._renderPostProcess) {
  269. let list = cameras.get(targetRenderer.camera);
  270. if (!list) {
  271. list = [[], {}];
  272. cameras.set(targetRenderer.camera, list);
  273. }
  274. list[0].push(targetRenderer);
  275. targetRenderer.camera.attachPostProcess(targetRenderer._renderPostProcess, i);
  276. }
  277. }
  278. let iterator = cameras.keys();
  279. for (let key = iterator.next(); key.done !== true; key = iterator.next()) {
  280. const camera = key.value;
  281. const list = cameras.get(camera);
  282. const firstPostProcess = camera._getFirstPostProcess();
  283. if (!firstPostProcess) {
  284. continue;
  285. }
  286. const [targetRenderers, copyDepthTextures] = list;
  287. firstPostProcess.onSizeChangedObservable.add(() => {
  288. if (!firstPostProcess.inputTexture.depthStencilTexture) {
  289. firstPostProcess.inputTexture.createDepthStencilTexture(0, true, this._engine.isStencilEnable, targetRenderers[0].samples, this._engine.isStencilEnable ? 13 : 14, `PostProcessRTTDepthStencil-${firstPostProcess.name}`);
  290. }
  291. for (const targetRenderer of targetRenderers) {
  292. const thicknessRT = targetRenderer._thicknessRenderTarget?.renderTarget;
  293. const thicknessTexture = thicknessRT?.texture;
  294. if (thicknessRT && thicknessTexture) {
  295. const key = thicknessTexture.width + "_" + thicknessTexture.height;
  296. let copyDepthTexture = copyDepthTextures[key];
  297. if (!copyDepthTexture) {
  298. copyDepthTexture = copyDepthTextures[key] = new FluidRenderingDepthTextureCopy(this._engine, thicknessTexture.width, thicknessTexture.height);
  299. }
  300. copyDepthTexture.depthRTWrapper.shareDepth(thicknessRT);
  301. }
  302. }
  303. });
  304. }
  305. // Dispose the CopyDepthTexture instances that we don't need anymore
  306. iterator = this._cameras.keys();
  307. for (let key = iterator.next(); key.done !== true; key = iterator.next()) {
  308. const camera = key.value;
  309. const list = this._cameras.get(camera);
  310. const copyDepthTextures = list[1];
  311. const list2 = cameras.get(camera);
  312. if (!list2) {
  313. for (const key in copyDepthTextures) {
  314. copyDepthTextures[key].dispose();
  315. }
  316. }
  317. else {
  318. for (const key in copyDepthTextures) {
  319. if (!list2[1][key]) {
  320. copyDepthTextures[key].dispose();
  321. }
  322. }
  323. }
  324. }
  325. this._cameras.clear();
  326. this._cameras = cameras;
  327. this._setParticleSizeForRenderTargets();
  328. }
  329. _setParticleSizeForRenderTargets() {
  330. const particleSizes = new Map();
  331. for (let i = 0; i < this.renderObjects.length; ++i) {
  332. const renderingObject = this.renderObjects[i];
  333. let curSize = particleSizes.get(renderingObject.targetRenderer);
  334. if (curSize === undefined) {
  335. curSize = 0;
  336. }
  337. particleSizes.set(renderingObject.targetRenderer, Math.max(curSize, renderingObject.object.particleSize));
  338. }
  339. particleSizes.forEach((particleSize, targetRenderer) => {
  340. if (targetRenderer._depthRenderTarget) {
  341. targetRenderer._depthRenderTarget.particleSize = particleSize;
  342. }
  343. });
  344. }
  345. _setUseVelocityForRenderObject() {
  346. for (const renderingObject of this.renderObjects) {
  347. renderingObject.object.useVelocity = renderingObject.targetRenderer.useVelocity;
  348. }
  349. }
  350. /** @internal */
  351. _prepareRendering() {
  352. for (const renderer of this.targetRenderers) {
  353. if (renderer.needInitialization) {
  354. this._initialize();
  355. return;
  356. }
  357. }
  358. }
  359. /** @internal */
  360. _render(forCamera) {
  361. for (let i = 0; i < this.targetRenderers.length; ++i) {
  362. if (!forCamera || this.targetRenderers[i].camera === forCamera) {
  363. this.targetRenderers[i]._clearTargets();
  364. }
  365. }
  366. const iterator = this._cameras.keys();
  367. for (let key = iterator.next(); key.done !== true; key = iterator.next()) {
  368. const camera = key.value;
  369. const list = this._cameras.get(camera);
  370. if (forCamera && camera !== forCamera) {
  371. continue;
  372. }
  373. const firstPostProcess = camera._getFirstPostProcess();
  374. if (!firstPostProcess) {
  375. continue;
  376. }
  377. const sourceCopyDepth = firstPostProcess.inputTexture?.depthStencilTexture;
  378. if (sourceCopyDepth) {
  379. const [targetRenderers, copyDepthTextures] = list;
  380. for (const targetRenderer of targetRenderers) {
  381. targetRenderer._bgDepthTexture = sourceCopyDepth;
  382. }
  383. for (const key in copyDepthTextures) {
  384. copyDepthTextures[key].copy(sourceCopyDepth);
  385. }
  386. }
  387. }
  388. for (let i = 0; i < this.renderObjects.length; ++i) {
  389. const renderingObject = this.renderObjects[i];
  390. if (!forCamera || renderingObject.targetRenderer.camera === forCamera) {
  391. renderingObject.targetRenderer._render(renderingObject.object);
  392. }
  393. }
  394. }
  395. /**
  396. * Disposes of all the ressources used by the class
  397. */
  398. dispose() {
  399. this._engine.onResizeObservable.remove(this._onEngineResizeObserver);
  400. this._onEngineResizeObserver = null;
  401. for (let i = 0; i < this.renderObjects.length; ++i) {
  402. this.renderObjects[i].object.dispose();
  403. }
  404. for (let i = 0; i < this.targetRenderers.length; ++i) {
  405. this.targetRenderers[i].dispose();
  406. }
  407. this._cameras.forEach((list) => {
  408. const copyDepthTextures = list[1];
  409. for (const key in copyDepthTextures) {
  410. copyDepthTextures[key].dispose();
  411. }
  412. });
  413. this.renderObjects = [];
  414. this.targetRenderers = [];
  415. this._cameras.clear();
  416. }
  417. }
  418. //# sourceMappingURL=fluidRenderer.js.map