Skip to content

Commit 8d72eba

Browse files
authored
Merge pull request BabylonJS#3 from torchesburn/decal-map-uv-seam-fix
MeshUVSpaceRenderer Decal Map PR Feedback
2 parents db4d590 + b4b8f8f commit 8d72eba

File tree

1 file changed

+142
-155
lines changed

1 file changed

+142
-155
lines changed

packages/dev/core/src/Meshes/meshUVSpaceRenderer.ts

Lines changed: 142 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ declare module "../scene" {
3030
export interface Scene {
3131
/** @internal */
3232
_meshUVSpaceRendererShader: Nullable<ShaderMaterial>;
33+
/** @internal */
34+
_meshUVSpaceRendererMaskShader: Nullable<ShaderMaterial>;
3335
}
3436
}
3537

@@ -74,27 +76,10 @@ export class MeshUVSpaceRenderer {
7476
private _scene: Scene;
7577
private _options: Required<IMeshUVSpaceRendererOptions>;
7678
private _textureCreatedInternally = false;
77-
/**
78-
* Mask Texture for the UV Edge Blending
79-
*/
80-
_maskTexture: any;
81-
/**
82-
* Final Texture for the UV Edge Blending
83-
*/
84-
_finalTexture: any;
85-
86-
/**
87-
* The decal texture
88-
*/
89-
decalTexture: Texture;
90-
/**
91-
* The final material for the UV Edge Blending
92-
*/
93-
public finalMaterial: ShaderMaterial;
94-
/**
95-
* The final post process for the UV Edge Blending
96-
*/
97-
_finalPostProcess: any;
79+
private _maskTexture: Texture;
80+
private _decalTexture: Texture;
81+
private _finalPostProcess: PostProcess;
82+
private _userCreatedTextureSetup: boolean = false;
9883

9984
private static _GetShader(scene: Scene): ShaderMaterial {
10085
if (!scene._meshUVSpaceRendererShader) {
@@ -126,6 +111,33 @@ export class MeshUVSpaceRenderer {
126111
return scene._meshUVSpaceRendererShader;
127112
}
128113

114+
private static _GetMaskShader(scene: Scene): ShaderMaterial {
115+
if (!scene._meshUVSpaceRendererMaskShader) {
116+
const shader = new ShaderMaterial(
117+
"meshUVSpaceRendererMaskShader",
118+
scene,
119+
{
120+
vertex: "meshUVSpaceRendererMasker",
121+
fragment: "meshUVSpaceRendererMasker",
122+
},
123+
{
124+
attributes: ["position", "uv"],
125+
uniforms: ["worldViewProjection"],
126+
}
127+
);
128+
shader.backFaceCulling = false;
129+
shader.alphaMode = Constants.ALPHA_COMBINE;
130+
131+
scene.onDisposeObservable.add(() => {
132+
scene._meshUVSpaceRendererShader?.dispose();
133+
scene._meshUVSpaceRendererShader = null;
134+
});
135+
136+
scene._meshUVSpaceRendererMaskShader = shader;
137+
}
138+
return scene._meshUVSpaceRendererMaskShader;
139+
}
140+
129141
private static _IsRenderTargetTexture(texture: ThinTexture | RenderTargetTexture): texture is RenderTargetTexture {
130142
return (texture as RenderTargetTexture).renderList !== undefined;
131143
}
@@ -167,11 +179,11 @@ export class MeshUVSpaceRenderer {
167179
* @returns true if the texture is ready to be used
168180
*/
169181
public isReady(): boolean {
170-
if (!this.decalTexture) {
171-
this._createDiffuseRTT();
182+
if (!this.texture) {
183+
this._createDecalDiffuseRTT();
172184
}
173185

174-
return MeshUVSpaceRenderer._IsRenderTargetTexture(this.decalTexture) ? this.decalTexture.isReadyForRendering() : this.decalTexture.isReady();
186+
return MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture) ? this.texture.isReadyForRendering() : this.texture.isReady();
175187
}
176188

177189
/**
@@ -182,10 +194,16 @@ export class MeshUVSpaceRenderer {
182194
* @param size The size of the projection
183195
* @param angle The rotation angle around the direction of the projection
184196
*/
185-
public async renderTexture(texture: BaseTexture, position: Vector3, normal: Vector3, size: Vector3, angle = 0): Promise<void> {
197+
public renderTexture(texture: BaseTexture, position: Vector3, normal: Vector3, size: Vector3, angle = 0): void {
186198
// Create the diffuse render target texture if it doesn't exist
187-
if (!this.decalTexture) {
188-
this._createDiffuseRTT();
199+
if (!this.texture && !this._options.uvEdgeBlending && !this._decalTexture) {
200+
this._updateRTT();
201+
} else if(this.texture && !this._userCreatedTextureSetup && !this._options.uvEdgeBlending && !this._decalTexture) {
202+
this._updateRTT();
203+
} else if(this.texture && !this._userCreatedTextureSetup && this._options.uvEdgeBlending && !this._decalTexture) {
204+
this._createDecalDiffuseRTT();
205+
} else if(!this.texture && this._options.uvEdgeBlending && !this._decalTexture) {
206+
this._createDecalDiffuseRTT();
189207
}
190208

191209
// // Prepare the shader with the decal texture, mask texture, and necessary uniforms
@@ -196,166 +214,134 @@ export class MeshUVSpaceRenderer {
196214
const projectionMatrix = this._createProjectionMatrix(position, normal, size, angle);
197215
shader.setMatrix("projMatrix", projectionMatrix);
198216

199-
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.decalTexture)) {
200-
this.decalTexture.render();
201-
if (this._options.uvEdgeBlending) {
202-
await this._createMaskTexture();
203-
this._createFinalTexture();
204-
} else {
205-
this.texture = this.decalTexture;
217+
218+
if (!this._options.uvEdgeBlending) {
219+
if (this.texture instanceof RenderTargetTexture) {
220+
this.texture.render();
221+
}
222+
} else {
223+
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this._decalTexture)) {
224+
if (this._options.uvEdgeBlending) {
225+
this._decalTexture.render();
226+
this._createMaskTexture();
227+
this._createFinalTexture();
228+
}
206229
}
207230
}
208231
}
232+
/**
233+
* Creates a texture RTT if one doesn't exist,
234+
*/
235+
_updateRTT() {
236+
if(!this.texture) {
237+
this.texture = this._createRenderTargetTexture(this._options.width, this._options.height);
238+
} else {
239+
this._userCreatedTextureSetup = true;
240+
}
241+
if (this.texture instanceof RenderTargetTexture) {
242+
this.texture.renderList = [this._mesh];
243+
this.texture.setMaterialForRendering(this._mesh, MeshUVSpaceRenderer._GetShader(this._scene));
244+
this.texture.onClearObservable.addOnce(() => {
245+
this._scene.getEngine().clear(this.clearColor, true, true, true);
246+
if (this.texture instanceof RenderTargetTexture) {
247+
this.texture.onClearObservable.add(() => {});
248+
}
249+
});
250+
}
251+
}
209252

210253
private _createMaskTexture(): void {
211254
if (this._maskTexture) {
212-
Promise.resolve();
213255
return;
214256
}
215-
try {
216-
// Create a new render target texture for the mask
217-
this._maskTexture = new RenderTargetTexture(
218-
"maskTexture",
219-
{ width: this._options.width, height: this._options.height },
220-
this._scene,
221-
false, // No mipmaps for the mask texture
222-
true,
223-
Constants.TEXTURETYPE_UNSIGNED_BYTE,
224-
false,
225-
undefined,
226-
undefined,
227-
undefined,
228-
undefined,
229-
Constants.TEXTUREFORMAT_R
230-
);
231-
232-
// Set up the mask material
233-
const maskMaterial = new ShaderMaterial(
234-
"meshUVSpaceRendererMaskerShader",
235-
this._scene,
236-
{
237-
vertex: "meshUVSpaceRendererMasker",
238-
fragment: "meshUVSpaceRendererMasker",
239-
},
240-
{
241-
attributes: ["position", "uv"],
242-
uniforms: ["worldViewProjection"],
243-
}
244-
);
245-
246-
let texture = null;
247-
if (this._mesh.material instanceof PBRMaterial) {
248-
texture = (this._mesh.material as PBRMaterial).albedoTexture;
249-
} else if (this._mesh.material instanceof StandardMaterial) {
250-
texture = (this._mesh.material as StandardMaterial).diffuseTexture;
251-
}
257+
// Create a new render target texture for the mask
258+
this._maskTexture = new RenderTargetTexture(
259+
"maskTexture",
260+
{ width: this._options.width, height: this._options.height },
261+
this._scene,
262+
false, // No mipmaps for the mask texture
263+
true,
264+
Constants.TEXTURETYPE_UNSIGNED_BYTE,
265+
false,
266+
undefined,
267+
undefined,
268+
undefined,
269+
undefined,
270+
Constants.TEXTUREFORMAT_R
271+
);
272+
const maskShader = MeshUVSpaceRenderer._GetMaskShader(this._scene);
273+
let texture = null;
274+
if (this._mesh.material instanceof PBRMaterial) {
275+
texture = (this._mesh.material as PBRMaterial).albedoTexture;
276+
} else if (this._mesh.material instanceof StandardMaterial) {
277+
texture = (this._mesh.material as StandardMaterial).diffuseTexture;
278+
}
252279

253-
if (texture) {
254-
maskMaterial.setTexture("textureSampler", texture);
255-
} else {
256-
console.error("Material does not have a valid texture property.");
257-
}
280+
if (texture) {
281+
maskShader.setTexture("textureSampler", texture);
282+
} else {
283+
console.error("Material does not have a valid texture property.");
284+
}
258285

259-
maskMaterial.backFaceCulling = false;
286+
maskShader.backFaceCulling = false;
260287

261-
this._mesh.material = this._mesh.material as PBRMaterial;
262-
maskMaterial.backFaceCulling = false;
288+
this._mesh.material = this._mesh.material as PBRMaterial;
289+
maskShader.backFaceCulling = false;
263290

291+
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this._maskTexture)) {
264292
// Render the mesh with the mask material to the mask texture
265-
this._maskTexture.renderList.push(this._mesh);
266-
this._maskTexture.setMaterialForRendering(this._mesh, maskMaterial);
267-
293+
this._maskTexture.renderList?.push(this._mesh);
294+
this._maskTexture.setMaterialForRendering(this._mesh, maskShader);
268295
// Ensure the mask texture is updated
269296
this._maskTexture.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
270297
this._scene.customRenderTargets.push(this._maskTexture);
271-
Promise.resolve();
272-
} catch (error) {
273-
console.error("Error creating mask texture:", error);
274298
}
275299
}
276300

277301
private _createFinalTexture(): void {
278-
if (!this.texture) {
279-
this.texture = new RenderTargetTexture(
280-
"finalTexture",
281-
{ width: this._options.width, height: this._options.height },
282-
this._scene,
302+
// Create the post-process only if it hasn't been created already
303+
if (!this._finalPostProcess) {
304+
this._finalPostProcess = new PostProcess(
305+
"finalTexturePostProcess",
306+
"meshUVSpaceRendererFinaliser",
307+
["textureSize"],
308+
["textureSampler", "maskTextureSampler"],
309+
1.0,
310+
null,
311+
Texture.NEAREST_SAMPLINGMODE,
312+
this._scene.getEngine(),
283313
false,
284-
true,
314+
null,
285315
this._options.textureType
286316
);
287-
}
288-
289-
try {
290-
// Set up the shader material
291-
this.finalMaterial = new ShaderMaterial(
292-
"meshUVSpaceRendererFinaliserShader",
293-
this._scene,
294-
{
295-
vertex: "meshUVSpaceRendererFinaliser",
296-
fragment: "meshUVSpaceRendererFinaliser",
297-
},
298-
{
299-
attributes: ["position", "uv"],
300-
uniforms: ["worldViewProjection", "textureSize"],
301-
samplers: ["textureSampler", "maskTextureSampler"],
302-
}
303-
);
304-
305-
this.finalMaterial.setTexture("textureSampler", this.decalTexture);
306-
this.finalMaterial.setTexture("maskTextureSampler", this._maskTexture);
307-
this.finalMaterial.setVector2("textureSize", new Vector2(this._options.width, this._options.height));
308-
this.finalMaterial.backFaceCulling = false;
309-
310-
// Create the post-process only if it hasn't been created already
311-
if (!this._finalPostProcess) {
312-
this._finalPostProcess = new PostProcess(
313-
"finalTexturePostProcess",
314-
"meshUVSpaceRendererFinaliser",
315-
["textureSize"],
316-
["textureSampler", "maskTextureSampler"],
317-
1.0,
318-
null,
319-
Texture.NEAREST_SAMPLINGMODE,
320-
this._scene.getEngine(),
321-
false,
322-
null,
323-
this._options.textureType
324-
);
325-
326-
this._finalPostProcess.onApply = (effect: { setTexture: (arg0: string, arg1: Texture) => void; setVector2: (arg0: string, arg1: Vector2) => void }) => {
327-
effect.setTexture("textureSampler", this.decalTexture);
328-
effect.setTexture("maskTextureSampler", this._maskTexture);
329-
effect.setVector2("textureSize", new Vector2(this._options.width, this._options.height));
330-
};
331-
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture)) {
332-
this.texture.addPostProcess(this._finalPostProcess);
333-
}
334-
}
335317

318+
this._finalPostProcess.onApply = (effect: { setTexture: (arg0: string, arg1: Texture) => void; setVector2: (arg0: string, arg1: Vector2) => void }) => {
319+
effect.setTexture("textureSampler", this._decalTexture);
320+
effect.setTexture("maskTextureSampler", this._maskTexture);
321+
effect.setVector2("textureSize", new Vector2(this._options.width, this._options.height));
322+
};
336323
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture)) {
337-
this.texture.render();
324+
this.texture.addPostProcess(this._finalPostProcess);
338325
}
339-
} catch (error) {
340-
console.error("Error creating final texture:", error);
341326
}
342-
}
343327

328+
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture)) {
329+
this.texture.render();
330+
}
331+
}
344332
/**
345333
* Clears the texture map
346334
*/
347335
public clear(): void {
348336
if (MeshUVSpaceRenderer._IsRenderTargetTexture(this.texture) && this.texture.renderTarget) {
349337
const engine = this._scene.getEngine();
350-
351338
engine.bindFramebuffer(this.texture.renderTarget);
352339
engine.clear(this.clearColor, true, true, true);
353340
engine.unBindFramebuffer(this.texture.renderTarget);
354341
}
355342
}
356-
357343
/**
358-
* Disposes of the ressources
344+
* Disposes of the resources
359345
*/
360346
public dispose() {
361347
if (this._textureCreatedInternally) {
@@ -364,18 +350,19 @@ export class MeshUVSpaceRenderer {
364350
}
365351
}
366352

367-
private _createDiffuseRTT(): void {
368-
this._textureCreatedInternally = true;
369-
353+
private _createDecalDiffuseRTT(): void {
370354
const texture = this._createRenderTargetTexture(this._options.width, this._options.height);
371355

372356
texture.setMaterialForRendering(this._mesh, MeshUVSpaceRenderer._GetShader(this._scene));
373357

374-
this.decalTexture = texture;
375-
376-
// Additional check after assignment
377-
if (!this.decalTexture.isReady()) {
378-
console.error("decalTexture is not ready after creation in _createDiffuseRTT.");
358+
this._decalTexture = texture;
359+
if(!this.texture) {
360+
this.texture = new RenderTargetTexture(
361+
this._mesh.name + "_finalUVSpaceTexture",
362+
{ width: this._options.width, height: this._options.height },
363+
this._scene,
364+
);
365+
this._textureCreatedInternally = true;
379366
}
380367
}
381368

0 commit comments

Comments
 (0)