Skip to content

Commit bde00a3

Browse files
authored
Merge pull request #13562 from Popov72/texture-decals
Decals: Add Decal Map support
2 parents 1c483dd + 17129f0 commit bde00a3

31 files changed

+622
-1
lines changed

packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
11501150

11511151
this._eventInfo.isReadyForSubMesh = true;
11521152
this._eventInfo.defines = defines;
1153+
this._eventInfo.subMesh = subMesh;
11531154
this._callbackPluginEventIsReadyForSubMesh(this._eventInfo);
11541155

11551156
if (!this._eventInfo.isReadyForSubMesh) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { DecalMapConfiguration } from "../material.decalMapConfiguration";
2+
import { PBRBaseMaterial } from "./pbrBaseMaterial";
3+
4+
declare module "./pbrBaseMaterial" {
5+
export interface PBRBaseMaterial {
6+
/** @internal */
7+
_decalMap: DecalMapConfiguration;
8+
9+
/**
10+
* Defines the decal map parameters for the material.
11+
*/
12+
decalMap: DecalMapConfiguration;
13+
}
14+
}
15+
16+
Object.defineProperty(PBRBaseMaterial.prototype, "decalMap", {
17+
get: function (this: PBRBaseMaterial) {
18+
if (!this._decalMap) {
19+
this._decalMap = new DecalMapConfiguration(this);
20+
}
21+
return this._decalMap;
22+
},
23+
enumerable: true,
24+
configurable: true,
25+
});

packages/dev/core/src/Materials/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ export * from "./materialPluginBase";
2828
export * from "./materialPluginManager";
2929
export * from "./materialPluginEvent";
3030
export * from "./material.detailMapConfiguration";
31+
export * from "./material.decalMapConfiguration";
3132
export * from "./materialPluginFactoryExport";
33+
import "./material.decalMap";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import "./standardMaterial.decalMap";
2+
import "./PBR/pbrMaterial.decalMap";
3+
import "../Meshes/abstractMesh.decalMap";
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { serialize, expandToProperty } from "../Misc/decorators";
2+
import { MaterialDefines } from "./materialDefines";
3+
import { MaterialPluginBase } from "./materialPluginBase";
4+
import { Constants } from "../Engines/constants";
5+
import { MaterialFlags } from "./materialFlags";
6+
import type { Scene } from "core/scene";
7+
import type { Engine } from "core/Engines/engine";
8+
import type { SubMesh } from "core/Meshes/subMesh";
9+
import type { AbstractMesh } from "core/Meshes/abstractMesh";
10+
import { MaterialHelper } from "./materialHelper";
11+
import type { UniformBuffer } from "./uniformBuffer";
12+
import type { PBRBaseMaterial } from "./PBR/pbrBaseMaterial";
13+
import type { StandardMaterial } from "./standardMaterial";
14+
15+
/**
16+
* @internal
17+
*/
18+
export class DecalMapDefines extends MaterialDefines {
19+
DECAL = false;
20+
DECALDIRECTUV = 0;
21+
DECAL_SMOOTHALPHA = false;
22+
GAMMADECAL = false;
23+
}
24+
25+
/**
26+
* Plugin that implements the decal map component of a material
27+
* @since
28+
*/
29+
export class DecalMapConfiguration extends MaterialPluginBase {
30+
private _isEnabled = false;
31+
/**
32+
* Enables or disables the decal map on this material
33+
*/
34+
@serialize()
35+
@expandToProperty("_markAllSubMeshesAsTexturesDirty")
36+
public isEnabled = false;
37+
38+
private _smoothAlpha = false;
39+
40+
/**
41+
* Enables or disables the smooth alpha mode on this material. Default: false.
42+
* When enabled, the alpha value used to blend the decal map will be the squared value and will produce a smoother result.
43+
*/
44+
@serialize()
45+
@expandToProperty("_markAllSubMeshesAsTexturesDirty")
46+
public smoothAlpha = false;
47+
48+
private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
49+
50+
/** @internal */
51+
public _markAllSubMeshesAsTexturesDirty(): void {
52+
this._enable(this._isEnabled);
53+
this._internalMarkAllSubMeshesAsTexturesDirty();
54+
}
55+
56+
/**
57+
* Creates a new DecalMapConfiguration
58+
* @param material The material to attach the decal map plugin to
59+
* @param addToPluginList If the plugin should be added to the material plugin list
60+
*/
61+
constructor(material: PBRBaseMaterial | StandardMaterial, addToPluginList = true) {
62+
super(material, "DecalMap", 150, new DecalMapDefines(), addToPluginList);
63+
64+
this.registerForExtraEvents = true; // because we override the hardBindForSubMesh method
65+
this._internalMarkAllSubMeshesAsTexturesDirty = material._dirtyCallbacks[Constants.MATERIAL_TextureDirtyFlag];
66+
}
67+
68+
public isReadyForSubMesh(defines: DecalMapDefines, scene: Scene, engine: Engine, subMesh: SubMesh): boolean {
69+
const decalMap = subMesh.getMesh().decalMap;
70+
71+
if (!this._isEnabled || !decalMap?.texture || !MaterialFlags.DecalMapEnabled || !scene.texturesEnabled) {
72+
return true;
73+
}
74+
75+
return decalMap.isReady();
76+
}
77+
78+
public prepareDefines(defines: DecalMapDefines, scene: Scene, mesh: AbstractMesh): void {
79+
const decalMap = mesh.decalMap;
80+
81+
if (!this._isEnabled || !decalMap?.texture || !MaterialFlags.DecalMapEnabled || !scene.texturesEnabled) {
82+
const isDirty = defines.DECAL;
83+
if (isDirty) {
84+
defines.markAsTexturesDirty();
85+
}
86+
defines.DECAL = false;
87+
} else {
88+
const isDirty = !defines.DECAL || defines.GAMMADECAL !== decalMap.texture.gammaSpace;
89+
if (isDirty) {
90+
defines.markAsTexturesDirty();
91+
}
92+
defines.DECAL = true;
93+
defines.GAMMADECAL = decalMap.texture.gammaSpace;
94+
defines.DECAL_SMOOTHALPHA = this._smoothAlpha;
95+
MaterialHelper.PrepareDefinesForMergedUV(decalMap.texture, defines, "DECAL");
96+
}
97+
}
98+
99+
/**
100+
* Note that we override hardBindForSubMesh and not bindForSubMesh because the material can be shared by multiple meshes,
101+
* in which case mustRebind could return false even though the decal map is different for each mesh: that's because the decal map
102+
* is not part of the material but hosted by the decalMap of the mesh instead.
103+
*/
104+
public hardBindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, _engine: Engine, subMesh: SubMesh): void {
105+
const decalMap = subMesh.getMesh().decalMap;
106+
107+
if (!this._isEnabled || !decalMap?.texture || !MaterialFlags.DecalMapEnabled || !scene.texturesEnabled) {
108+
return;
109+
}
110+
111+
const isFrozen = this._material.isFrozen;
112+
const texture = decalMap.texture;
113+
114+
if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
115+
uniformBuffer.updateFloat4("vDecalInfos", texture.coordinatesIndex, 0, 0, 0);
116+
MaterialHelper.BindTextureMatrix(texture, uniformBuffer, "decal");
117+
}
118+
119+
uniformBuffer.setTexture("decalSampler", texture);
120+
}
121+
122+
public getClassName(): string {
123+
return "DecalMapConfiguration";
124+
}
125+
126+
public getSamplers(samplers: string[]): void {
127+
samplers.push("decalSampler");
128+
}
129+
130+
public getUniforms(): { ubo?: Array<{ name: string; size: number; type: string }>; vertex?: string; fragment?: string } {
131+
return {
132+
ubo: [
133+
{ name: "vDecalInfos", size: 4, type: "vec4" },
134+
{ name: "decalMatrix", size: 16, type: "mat4" },
135+
],
136+
};
137+
}
138+
}

packages/dev/core/src/Materials/materialFlags.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ export class MaterialFlags {
3838
Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
3939
}
4040

41+
private static _DecalMapEnabled = true;
42+
/**
43+
* Are decal maps enabled in the application.
44+
*/
45+
public static get DecalMapEnabled(): boolean {
46+
return this._DecalMapEnabled;
47+
}
48+
public static set DecalMapEnabled(value: boolean) {
49+
if (this._DecalMapEnabled === value) {
50+
return;
51+
}
52+
53+
this._DecalMapEnabled = value;
54+
Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
55+
}
56+
4157
private static _AmbientTextureEnabled = true;
4258
/**
4359
* Are ambient textures enabled in the application.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { DecalMapConfiguration } from "./material.decalMapConfiguration";
2+
import { StandardMaterial } from "./standardMaterial";
3+
4+
declare module "./standardMaterial" {
5+
export interface StandardMaterial {
6+
/** @internal */
7+
_decalMap: DecalMapConfiguration;
8+
9+
/**
10+
* Defines the decal map parameters for the material.
11+
*/
12+
decalMap: DecalMapConfiguration;
13+
}
14+
}
15+
16+
Object.defineProperty(StandardMaterial.prototype, "decalMap", {
17+
get: function (this: StandardMaterial) {
18+
if (!this._decalMap) {
19+
this._decalMap = new DecalMapConfiguration(this);
20+
}
21+
return this._decalMap;
22+
},
23+
enumerable: true,
24+
configurable: true,
25+
});

packages/dev/core/src/Materials/standardMaterial.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,7 @@ export class StandardMaterial extends PushMaterial {
11231123

11241124
this._eventInfo.isReadyForSubMesh = true;
11251125
this._eventInfo.defines = defines;
1126+
this._eventInfo.subMesh = subMesh;
11261127
this._callbackPluginEventIsReadyForSubMesh(this._eventInfo);
11271128

11281129
if (!this._eventInfo.isReadyForSubMesh) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Nullable } from "../types";
2+
import { AbstractMesh } from "../Meshes/abstractMesh";
3+
import type { MeshUVSpaceRenderer } from "./meshUVSpaceRenderer";
4+
5+
declare module "./abstractMesh" {
6+
export interface AbstractMesh {
7+
/** @internal */
8+
_decalMap: Nullable<MeshUVSpaceRenderer>;
9+
10+
/**
11+
* Gets or sets the decal map for this mesh
12+
*/
13+
decalMap: Nullable<MeshUVSpaceRenderer>;
14+
}
15+
}
16+
17+
Object.defineProperty(AbstractMesh.prototype, "decalMap", {
18+
get: function (this: AbstractMesh) {
19+
return this._decalMap;
20+
},
21+
set: function (this: AbstractMesh, decalMap: Nullable<MeshUVSpaceRenderer>) {
22+
this._decalMap = decalMap;
23+
},
24+
enumerable: true,
25+
configurable: true,
26+
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/* eslint-disable import/export */
22
/* eslint-disable import/no-internal-modules */
33
export * from "./abstractMesh";
4+
import "./abstractMesh.decalMap";
45
export * from "./Compression/index";
56
export * from "./csg";
7+
export * from "./meshUVSpaceRenderer";
68
export * from "./geometry";
79
export * from "./groundMesh";
810
export * from "./goldbergMesh";

0 commit comments

Comments
 (0)