meshUVSpaceRenderer.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Matrix } from "../Maths/math.vector.js";
  2. import { ShaderMaterial } from "../Materials/shaderMaterial.js";
  3. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture.js";
  4. import { Color4 } from "../Maths/math.color.js";
  5. import { PostProcess } from "../PostProcesses/postProcess.js";
  6. import "../Shaders/meshUVSpaceRenderer.vertex.js";
  7. import "../Shaders/meshUVSpaceRenderer.fragment.js";
  8. import "../Shaders/meshUVSpaceRendererMasker.vertex.js";
  9. import "../Shaders/meshUVSpaceRendererMasker.fragment.js";
  10. import "../Shaders/meshUVSpaceRendererFinaliser.fragment.js";
  11. import "../Shaders/meshUVSpaceRendererFinaliser.vertex.js";
  12. /**
  13. * Class used to render in the mesh UV space
  14. * @since 5.49.1
  15. */
  16. export class MeshUVSpaceRenderer {
  17. static _GetShader(scene) {
  18. if (!scene._meshUVSpaceRendererShader) {
  19. const shader = new ShaderMaterial("meshUVSpaceRendererShader", scene, {
  20. vertex: "meshUVSpaceRenderer",
  21. fragment: "meshUVSpaceRenderer",
  22. }, {
  23. attributes: ["position", "normal", "uv"],
  24. uniforms: ["world", "projMatrix"],
  25. samplers: ["textureSampler"],
  26. needAlphaBlending: true,
  27. });
  28. shader.backFaceCulling = false;
  29. shader.alphaMode = 2;
  30. scene.onDisposeObservable.add(() => {
  31. scene._meshUVSpaceRendererShader?.dispose();
  32. scene._meshUVSpaceRendererShader = null;
  33. });
  34. scene._meshUVSpaceRendererShader = shader;
  35. }
  36. return scene._meshUVSpaceRendererShader;
  37. }
  38. static _GetMaskShader(scene) {
  39. if (!scene._meshUVSpaceRendererMaskShader) {
  40. const shader = new ShaderMaterial("meshUVSpaceRendererMaskShader", scene, {
  41. vertex: "meshUVSpaceRendererMasker",
  42. fragment: "meshUVSpaceRendererMasker",
  43. }, {
  44. attributes: ["position", "uv"],
  45. uniforms: ["worldViewProjection"],
  46. });
  47. shader.backFaceCulling = false;
  48. shader.alphaMode = 2;
  49. scene.onDisposeObservable.add(() => {
  50. scene._meshUVSpaceRendererMaskShader?.dispose();
  51. scene._meshUVSpaceRendererMaskShader = null;
  52. });
  53. scene._meshUVSpaceRendererMaskShader = shader;
  54. }
  55. return scene._meshUVSpaceRendererMaskShader;
  56. }
  57. static _IsRenderTargetTexture(texture) {
  58. return texture.renderList !== undefined;
  59. }
  60. /**
  61. * Creates a new MeshUVSpaceRenderer
  62. * @param mesh The mesh used for the source UV space
  63. * @param scene The scene the mesh belongs to
  64. * @param options The options to use when creating the texture
  65. */
  66. constructor(mesh, scene, options) {
  67. this._textureCreatedInternally = false;
  68. this._configureUserCreatedTexture = true;
  69. this._maskTexture = null;
  70. this._finalPostProcess = null;
  71. /**
  72. * Clear color of the texture
  73. */
  74. this.clearColor = new Color4(0, 0, 0, 0);
  75. this._mesh = mesh;
  76. this._scene = scene;
  77. this._options = {
  78. width: 1024,
  79. height: 1024,
  80. textureType: 0,
  81. generateMipMaps: true,
  82. optimizeUVAllocation: true,
  83. uvEdgeBlending: false,
  84. ...options,
  85. };
  86. }
  87. /**
  88. * Checks if the texture is ready to be used
  89. * @returns true if the texture is ready to be used
  90. */
  91. isReady() {
  92. if (!this.texture) {
  93. this._createDiffuseRTT();
  94. }
  95. const textureIsReady = MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture) ? this.texture.isReadyForRendering() : this.texture.isReady();
  96. const maskIsReady = this._maskTexture?.isReadyForRendering() ?? true;
  97. const postProcessIsReady = this._finalPostProcess?.isReady() ?? true;
  98. return textureIsReady && maskIsReady && postProcessIsReady;
  99. }
  100. /**
  101. * Projects and renders a texture in the mesh UV space
  102. * @param texture The texture
  103. * @param position The position of the center of projection (world space coordinates)
  104. * @param normal The direction of the projection (world space coordinates)
  105. * @param size The size of the projection
  106. * @param angle The rotation angle around the direction of the projection
  107. */
  108. renderTexture(texture, position, normal, size, angle = 0) {
  109. if (!this.texture) {
  110. this._createDiffuseRTT();
  111. }
  112. else if (this._configureUserCreatedTexture) {
  113. this._configureUserCreatedRTT();
  114. }
  115. if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture)) {
  116. const matrix = this._createProjectionMatrix(position, normal, size, angle);
  117. const shader = MeshUVSpaceRenderer._GetShader(this._scene);
  118. shader.setTexture("textureSampler", texture);
  119. shader.setMatrix("projMatrix", matrix);
  120. this.texture.render();
  121. }
  122. }
  123. /**
  124. * Clears the texture map
  125. */
  126. clear() {
  127. if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture) && this.texture.renderTarget) {
  128. const engine = this._scene.getEngine();
  129. engine.bindFramebuffer(this.texture.renderTarget);
  130. engine.clear(this.clearColor, true, true, true);
  131. engine.unBindFramebuffer(this.texture.renderTarget);
  132. }
  133. if (this._finalPostProcess?.inputTexture) {
  134. const engine = this._scene.getEngine();
  135. engine.bindFramebuffer(this._finalPostProcess?.inputTexture);
  136. engine.clear(this.clearColor, true, true, true);
  137. engine.unBindFramebuffer(this._finalPostProcess?.inputTexture);
  138. }
  139. }
  140. /**
  141. * Disposes of the resources
  142. */
  143. dispose() {
  144. if (this._textureCreatedInternally) {
  145. this.texture.dispose();
  146. this._textureCreatedInternally = false;
  147. }
  148. this._configureUserCreatedTexture = true;
  149. this._maskTexture?.dispose();
  150. this._maskTexture = null;
  151. this._finalPostProcess?.dispose();
  152. this._finalPostProcess = null;
  153. }
  154. _configureUserCreatedRTT() {
  155. this._configureUserCreatedTexture = false;
  156. if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture)) {
  157. this.texture.setMaterialForRendering(this._mesh, MeshUVSpaceRenderer._GetShader(this._scene));
  158. this.texture.onClearObservable.add(() => { });
  159. this.texture.renderList = [this._mesh];
  160. if (this._options.uvEdgeBlending) {
  161. this._createMaskTexture();
  162. this._createPostProcess();
  163. this.texture.addPostProcess(this._finalPostProcess);
  164. }
  165. }
  166. }
  167. _createDiffuseRTT() {
  168. this._textureCreatedInternally = true;
  169. const texture = this._createRenderTargetTexture(this._options.width, this._options.height);
  170. texture.setMaterialForRendering(this._mesh, MeshUVSpaceRenderer._GetShader(this._scene));
  171. this.texture = texture;
  172. this._configureUserCreatedTexture = false;
  173. if (this._options.uvEdgeBlending) {
  174. this._createMaskTexture();
  175. this._createPostProcess();
  176. texture.addPostProcess(this._finalPostProcess);
  177. }
  178. }
  179. _createMaskTexture() {
  180. if (this._maskTexture) {
  181. return;
  182. }
  183. this._maskTexture = new RenderTargetTexture(this._mesh.name + "_maskTexture", { width: this._options.width, height: this._options.height }, this._scene, false, // No mipmaps for the mask texture
  184. true, 0, false, 2, undefined, undefined, undefined, 6);
  185. this._maskTexture.clearColor = new Color4(0, 0, 0, 0);
  186. // Render the mesh with the mask material to the mask texture
  187. this._maskTexture.renderList.push(this._mesh);
  188. this._maskTexture.setMaterialForRendering(this._mesh, MeshUVSpaceRenderer._GetMaskShader(this._scene));
  189. // Ensure the mask texture is updated
  190. this._maskTexture.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  191. this._scene.customRenderTargets.push(this._maskTexture);
  192. }
  193. _createPostProcess() {
  194. if (this._finalPostProcess) {
  195. return;
  196. }
  197. this._finalPostProcess = new PostProcess(this._mesh.name + "_fixSeamsPostProcess", "meshUVSpaceRendererFinaliser", ["textureSize"], ["textureSampler", "maskTextureSampler"], 1.0, null, 1, this._scene.getEngine(), false, null, this._options.textureType);
  198. this._finalPostProcess.onApplyObservable.add((effect) => {
  199. effect.setTexture("maskTextureSampler", this._maskTexture);
  200. effect.setFloat2("textureSize", this._options.width, this._options.height);
  201. });
  202. }
  203. _createRenderTargetTexture(width, height) {
  204. const rtt = new RenderTargetTexture(this._mesh.name + "_uvspaceTexture", { width, height }, this._scene, this._options.generateMipMaps, true, this._options.textureType, false, this._options.generateMipMaps ? 3 : 2, false, false, false, 5);
  205. rtt.renderParticles = false;
  206. rtt.optimizeUVAllocation = !!this._options.optimizeUVAllocation;
  207. rtt.onClearObservable.addOnce(() => {
  208. this._scene.getEngine().clear(this.clearColor, true, true, true);
  209. rtt.onClearObservable.add(() => { }); // this disables clearing the texture for the next frames
  210. });
  211. rtt.renderList = [this._mesh];
  212. return rtt;
  213. }
  214. _createProjectionMatrix(position, normal, size, angle = 0) {
  215. const yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
  216. const len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
  217. const pitch = Math.atan2(normal.y, len);
  218. const p = position.add(normal.scale(size.z * 0.5));
  219. const projWorldMatrix = Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(Matrix.Translation(p.x, p.y, p.z));
  220. const inverseProjWorldMatrix = Matrix.Invert(projWorldMatrix);
  221. const projMatrix = Matrix.FromArray([2 / size.x, 0, 0, 0, 0, 2 / size.y, 0, 0, 0, 0, 1 / size.z, 0, 0, 0, 0, 1]);
  222. const screenMatrix = Matrix.FromArray([0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1, 0, 0.5, 0.5, 0.0, 1]);
  223. return inverseProjWorldMatrix.multiply(projMatrix).multiply(screenMatrix);
  224. }
  225. }
  226. //# sourceMappingURL=meshUVSpaceRenderer.js.map