@dcl/ecs 7.18.2-21450088960.commit-3c16004 → 7.18.2-21453292414.commit-1da934f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/extended/Material.d.ts +203 -2
- package/dist/components/extended/Material.js +475 -0
- package/dist/components/types.d.ts +1 -1
- package/dist/runtime/helpers/index.d.ts +1 -0
- package/dist/runtime/helpers/index.js +1 -0
- package/dist/runtime/helpers/timers.d.ts +85 -0
- package/dist/runtime/helpers/timers.js +69 -0
- package/dist/runtime/helpers/tree.d.ts +61 -0
- package/dist/runtime/helpers/tree.js +238 -0
- package/dist/runtime/initialization/index.d.ts +7 -0
- package/dist/runtime/initialization/index.js +11 -0
- package/dist-cjs/components/extended/Material.d.ts +203 -2
- package/dist-cjs/components/extended/Material.js +475 -0
- package/dist-cjs/components/types.d.ts +1 -1
- package/dist-cjs/runtime/helpers/index.d.ts +1 -0
- package/dist-cjs/runtime/helpers/index.js +3 -0
- package/dist-cjs/runtime/helpers/timers.d.ts +85 -0
- package/dist-cjs/runtime/helpers/timers.js +73 -0
- package/dist-cjs/runtime/helpers/tree.d.ts +61 -0
- package/dist-cjs/runtime/helpers/tree.js +242 -1
- package/dist-cjs/runtime/initialization/index.d.ts +7 -0
- package/dist-cjs/runtime/initialization/index.js +12 -1
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LastWriteWinElementSetComponentDefinition, Entity, IEngine } from '../../engine';
|
|
2
|
-
import { PBMaterial, PBMaterial_PbrMaterial, PBMaterial_UnlitMaterial } from '../generated/index.gen';
|
|
3
|
-
import { AvatarTexture, Texture, TextureUnion, VideoTexture } from '../generated/types.gen';
|
|
2
|
+
import { MaterialTransparencyMode, PBMaterial, PBMaterial_PbrMaterial, PBMaterial_UnlitMaterial } from '../generated/index.gen';
|
|
3
|
+
import { AvatarTexture, Texture, TextureFilterMode, TextureUnion, TextureWrapMode, VideoTexture } from '../generated/types.gen';
|
|
4
|
+
import { Color3, Color4 } from '../generated/pb/decentraland/common/colors.gen';
|
|
4
5
|
/**
|
|
5
6
|
* @public
|
|
6
7
|
*/
|
|
@@ -18,6 +19,170 @@ export interface TextureHelper {
|
|
|
18
19
|
*/
|
|
19
20
|
Video: (videoTexture: VideoTexture) => TextureUnion;
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Flattened texture interface for simplified property access
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export interface FlatTexture {
|
|
27
|
+
/** Path to the texture file */
|
|
28
|
+
src: string | undefined;
|
|
29
|
+
/** Texture wrapping behavior */
|
|
30
|
+
wrapMode: TextureWrapMode | undefined;
|
|
31
|
+
/** Texture filtering mode */
|
|
32
|
+
filterMode: TextureFilterMode | undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Flattened material interface for simplified property access
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
export interface FlatMaterial {
|
|
39
|
+
/**
|
|
40
|
+
* Access to the main texture properties (works for both PBR and Unlit materials)
|
|
41
|
+
*/
|
|
42
|
+
readonly texture: FlatTexture;
|
|
43
|
+
/**
|
|
44
|
+
* Access to the alpha texture properties (works for both PBR and Unlit materials)
|
|
45
|
+
*/
|
|
46
|
+
readonly alphaTexture: FlatTexture;
|
|
47
|
+
/**
|
|
48
|
+
* Access to the emissive texture properties (PBR only - returns undefined for Unlit materials)
|
|
49
|
+
*/
|
|
50
|
+
readonly emissiveTexture: FlatTexture | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Access to the bump/normal texture properties (PBR only - returns undefined for Unlit materials)
|
|
53
|
+
*/
|
|
54
|
+
readonly bumpTexture: FlatTexture | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Alpha test threshold (0-1). Default: 0.5
|
|
57
|
+
*/
|
|
58
|
+
alphaTest: number | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the material casts shadows. Default: true
|
|
61
|
+
*/
|
|
62
|
+
castShadows: boolean | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Albedo/base color (PBR only). Default: white
|
|
65
|
+
*/
|
|
66
|
+
albedoColor: Color4 | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Emissive color (PBR only). Default: black
|
|
69
|
+
*/
|
|
70
|
+
emissiveColor: Color3 | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Reflectivity color (PBR only). Default: white
|
|
73
|
+
*/
|
|
74
|
+
reflectivityColor: Color3 | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Transparency mode (PBR only). Default: MTM_AUTO
|
|
77
|
+
*/
|
|
78
|
+
transparencyMode: MaterialTransparencyMode | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Metallic value 0-1 (PBR only). Default: 0.5
|
|
81
|
+
*/
|
|
82
|
+
metallic: number | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Roughness value 0-1 (PBR only). Default: 0.5
|
|
85
|
+
*/
|
|
86
|
+
roughness: number | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Specular intensity (PBR only). Default: 1
|
|
89
|
+
*/
|
|
90
|
+
specularIntensity: number | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Emissive intensity (PBR only). Default: 2
|
|
93
|
+
*/
|
|
94
|
+
emissiveIntensity: number | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Direct light intensity (PBR only). Default: 1
|
|
97
|
+
*/
|
|
98
|
+
directIntensity: number | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Diffuse color (Unlit only). Default: white
|
|
101
|
+
*/
|
|
102
|
+
diffuseColor: Color4 | undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Readonly flattened texture interface for read-only property access
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export interface ReadonlyFlatTexture {
|
|
109
|
+
/** Path to the texture file */
|
|
110
|
+
readonly src: string | undefined;
|
|
111
|
+
/** Texture wrapping behavior */
|
|
112
|
+
readonly wrapMode: TextureWrapMode | undefined;
|
|
113
|
+
/** Texture filtering mode */
|
|
114
|
+
readonly filterMode: TextureFilterMode | undefined;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Readonly flattened material interface for read-only property access
|
|
118
|
+
* @public
|
|
119
|
+
*/
|
|
120
|
+
export interface ReadonlyFlatMaterial {
|
|
121
|
+
/**
|
|
122
|
+
* Access to the main texture properties (works for both PBR and Unlit materials)
|
|
123
|
+
*/
|
|
124
|
+
readonly texture: ReadonlyFlatTexture;
|
|
125
|
+
/**
|
|
126
|
+
* Access to the alpha texture properties (works for both PBR and Unlit materials)
|
|
127
|
+
*/
|
|
128
|
+
readonly alphaTexture: ReadonlyFlatTexture;
|
|
129
|
+
/**
|
|
130
|
+
* Access to the emissive texture properties (PBR only - returns undefined for Unlit materials)
|
|
131
|
+
*/
|
|
132
|
+
readonly emissiveTexture: ReadonlyFlatTexture | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Access to the bump/normal texture properties (PBR only - returns undefined for Unlit materials)
|
|
135
|
+
*/
|
|
136
|
+
readonly bumpTexture: ReadonlyFlatTexture | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* Alpha test threshold (0-1). Default: 0.5
|
|
139
|
+
*/
|
|
140
|
+
readonly alphaTest: number | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* Whether the material casts shadows. Default: true
|
|
143
|
+
*/
|
|
144
|
+
readonly castShadows: boolean | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Albedo/base color (PBR only). Default: white
|
|
147
|
+
*/
|
|
148
|
+
readonly albedoColor: Color4 | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* Emissive color (PBR only). Default: black
|
|
151
|
+
*/
|
|
152
|
+
readonly emissiveColor: Color3 | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* Reflectivity color (PBR only). Default: white
|
|
155
|
+
*/
|
|
156
|
+
readonly reflectivityColor: Color3 | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Transparency mode (PBR only). Default: MTM_AUTO
|
|
159
|
+
*/
|
|
160
|
+
readonly transparencyMode: MaterialTransparencyMode | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* Metallic value 0-1 (PBR only). Default: 0.5
|
|
163
|
+
*/
|
|
164
|
+
readonly metallic: number | undefined;
|
|
165
|
+
/**
|
|
166
|
+
* Roughness value 0-1 (PBR only). Default: 0.5
|
|
167
|
+
*/
|
|
168
|
+
readonly roughness: number | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* Specular intensity (PBR only). Default: 1
|
|
171
|
+
*/
|
|
172
|
+
readonly specularIntensity: number | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* Emissive intensity (PBR only). Default: 2
|
|
175
|
+
*/
|
|
176
|
+
readonly emissiveIntensity: number | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Direct light intensity (PBR only). Default: 1
|
|
179
|
+
*/
|
|
180
|
+
readonly directIntensity: number | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* Diffuse color (Unlit only). Default: white
|
|
183
|
+
*/
|
|
184
|
+
readonly diffuseColor: Color4 | undefined;
|
|
185
|
+
}
|
|
21
186
|
/**
|
|
22
187
|
* @public
|
|
23
188
|
*/
|
|
@@ -38,5 +203,41 @@ export interface MaterialComponentDefinitionExtended extends LastWriteWinElement
|
|
|
38
203
|
* @param material - the PBR data for this material
|
|
39
204
|
*/
|
|
40
205
|
setPbrMaterial: (entity: Entity, material: PBMaterial_PbrMaterial) => void;
|
|
206
|
+
/**
|
|
207
|
+
* Get a readonly flattened accessor for the material's properties.
|
|
208
|
+
* Simplifies reading material properties without navigating nested unions.
|
|
209
|
+
* Works for both PBR and Unlit materials.
|
|
210
|
+
* Throws if the entity does not have a Material component.
|
|
211
|
+
* @param entity - Entity with the Material component
|
|
212
|
+
* @returns A readonly accessor object with direct access to material properties
|
|
213
|
+
*
|
|
214
|
+
*/
|
|
215
|
+
getFlat: (entity: Entity) => ReadonlyFlatMaterial;
|
|
216
|
+
/**
|
|
217
|
+
* Get a readonly flattened accessor for the material's properties, or null if the entity
|
|
218
|
+
* does not have a Material component.
|
|
219
|
+
* @param entity - Entity to check for Material component
|
|
220
|
+
* @returns A readonly accessor object, or null if no Material component exists
|
|
221
|
+
*
|
|
222
|
+
*/
|
|
223
|
+
getFlatOrNull: (entity: Entity) => ReadonlyFlatMaterial | null;
|
|
224
|
+
/**
|
|
225
|
+
* Get a mutable flattened accessor for the material's properties.
|
|
226
|
+
* Simplifies reading/writing material properties without navigating nested unions.
|
|
227
|
+
* Works for both PBR and Unlit materials.
|
|
228
|
+
* Throws if the entity does not have a Material component.
|
|
229
|
+
* @param entity - Entity with the Material component
|
|
230
|
+
* @returns A mutable accessor object with direct access to material properties
|
|
231
|
+
*
|
|
232
|
+
*/
|
|
233
|
+
getFlatMutable: (entity: Entity) => FlatMaterial;
|
|
234
|
+
/**
|
|
235
|
+
* Get a mutable flattened accessor for the material's properties, or null if the entity
|
|
236
|
+
* does not have a Material component.
|
|
237
|
+
* @param entity - Entity to check for Material component
|
|
238
|
+
* @returns A mutable accessor object, or null if no Material component exists
|
|
239
|
+
*
|
|
240
|
+
*/
|
|
241
|
+
getFlatMutableOrNull: (entity: Entity) => FlatMaterial | null;
|
|
41
242
|
}
|
|
42
243
|
export declare function defineMaterialComponent(engine: Pick<IEngine, 'defineComponentFromSchema'>): MaterialComponentDefinitionExtended;
|
|
@@ -25,6 +25,456 @@ const TextureHelper = {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Helper to get the inner material object (pbr or unlit) from a PBMaterial
|
|
30
|
+
*/
|
|
31
|
+
function getInnerMaterial(mat) {
|
|
32
|
+
if (mat.material?.$case === 'pbr') {
|
|
33
|
+
return mat.material.pbr;
|
|
34
|
+
}
|
|
35
|
+
else if (mat.material?.$case === 'unlit') {
|
|
36
|
+
return mat.material.unlit;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Helper to get the Texture object from a TextureUnion (only for regular textures)
|
|
42
|
+
*/
|
|
43
|
+
function getTextureFromUnion(textureUnion) {
|
|
44
|
+
if (textureUnion?.tex?.$case === 'texture') {
|
|
45
|
+
return textureUnion.tex.texture;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if a texture field is PBR-only
|
|
51
|
+
*/
|
|
52
|
+
function isPbrOnlyTexture(field) {
|
|
53
|
+
return field === 'emissiveTexture' || field === 'bumpTexture';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the texture union from the inner material based on field name
|
|
57
|
+
*/
|
|
58
|
+
function getTextureField(innerMat, field, isPbr) {
|
|
59
|
+
if (!innerMat)
|
|
60
|
+
return undefined;
|
|
61
|
+
// For PBR-only textures on Unlit materials, return undefined
|
|
62
|
+
if (isPbrOnlyTexture(field) && !isPbr) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
switch (field) {
|
|
66
|
+
case 'texture':
|
|
67
|
+
return innerMat.texture;
|
|
68
|
+
case 'alphaTexture':
|
|
69
|
+
return innerMat.alphaTexture;
|
|
70
|
+
case 'emissiveTexture':
|
|
71
|
+
return innerMat.emissiveTexture;
|
|
72
|
+
case 'bumpTexture':
|
|
73
|
+
return innerMat.bumpTexture;
|
|
74
|
+
default:
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Helper to validate that a texture is not an Avatar or Video texture
|
|
80
|
+
* Throws an error if it is, since flat accessors only work with regular textures
|
|
81
|
+
*/
|
|
82
|
+
function validateNotSpecialTexture(textureUnion, field) {
|
|
83
|
+
if (textureUnion?.tex?.$case === 'avatarTexture') {
|
|
84
|
+
throw new Error(`Cannot set ${field} properties on Avatar texture. Use setPbrMaterial/setBasicMaterial with Material.Texture.Common() to set a regular texture.`);
|
|
85
|
+
}
|
|
86
|
+
if (textureUnion?.tex?.$case === 'videoTexture') {
|
|
87
|
+
throw new Error(`Cannot set ${field} properties on Video texture. Use setPbrMaterial/setBasicMaterial with Material.Texture.Common() to set a regular texture.`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Helper to ensure the material has a texture structure for the given field and return it for modification
|
|
92
|
+
*/
|
|
93
|
+
function ensureTextureStructure(mat, field) {
|
|
94
|
+
if (!mat.material) {
|
|
95
|
+
throw new Error('Material structure is invalid. Use setPbrMaterial or setBasicMaterial first.');
|
|
96
|
+
}
|
|
97
|
+
// Check if trying to set a PBR-only texture on Unlit material
|
|
98
|
+
if (isPbrOnlyTexture(field) && mat.material.$case !== 'pbr') {
|
|
99
|
+
throw new Error(`Cannot set ${field} on Unlit material. Use PBR material instead.`);
|
|
100
|
+
}
|
|
101
|
+
const innerMat = mat.material.$case === 'pbr' ? mat.material.pbr : mat.material.unlit;
|
|
102
|
+
const isPbr = mat.material.$case === 'pbr';
|
|
103
|
+
// Get or create the texture union for the specified field
|
|
104
|
+
let textureUnion;
|
|
105
|
+
switch (field) {
|
|
106
|
+
case 'texture':
|
|
107
|
+
validateNotSpecialTexture(innerMat.texture, field);
|
|
108
|
+
if (!innerMat.texture || innerMat.texture.tex?.$case !== 'texture') {
|
|
109
|
+
innerMat.texture = { tex: { $case: 'texture', texture: { src: '' } } };
|
|
110
|
+
}
|
|
111
|
+
textureUnion = innerMat.texture;
|
|
112
|
+
break;
|
|
113
|
+
case 'alphaTexture':
|
|
114
|
+
validateNotSpecialTexture(innerMat.alphaTexture, field);
|
|
115
|
+
if (!innerMat.alphaTexture || innerMat.alphaTexture.tex?.$case !== 'texture') {
|
|
116
|
+
innerMat.alphaTexture = { tex: { $case: 'texture', texture: { src: '' } } };
|
|
117
|
+
}
|
|
118
|
+
textureUnion = innerMat.alphaTexture;
|
|
119
|
+
break;
|
|
120
|
+
case 'emissiveTexture':
|
|
121
|
+
if (isPbr) {
|
|
122
|
+
const pbrMat = innerMat;
|
|
123
|
+
validateNotSpecialTexture(pbrMat.emissiveTexture, field);
|
|
124
|
+
if (!pbrMat.emissiveTexture || pbrMat.emissiveTexture.tex?.$case !== 'texture') {
|
|
125
|
+
pbrMat.emissiveTexture = { tex: { $case: 'texture', texture: { src: '' } } };
|
|
126
|
+
}
|
|
127
|
+
textureUnion = pbrMat.emissiveTexture;
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
case 'bumpTexture':
|
|
131
|
+
if (isPbr) {
|
|
132
|
+
const pbrMat = innerMat;
|
|
133
|
+
validateNotSpecialTexture(pbrMat.bumpTexture, field);
|
|
134
|
+
if (!pbrMat.bumpTexture || pbrMat.bumpTexture.tex?.$case !== 'texture') {
|
|
135
|
+
pbrMat.bumpTexture = { tex: { $case: 'texture', texture: { src: '' } } };
|
|
136
|
+
}
|
|
137
|
+
textureUnion = pbrMat.bumpTexture;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
// At this point we know tex.$case is 'texture', but TypeScript doesn't narrow it so we use a type assertion
|
|
142
|
+
const tex = textureUnion.tex;
|
|
143
|
+
return tex.texture;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Class-based accessor for FlatTexture properties.
|
|
147
|
+
* Provides getters/setters that read/write to the nested material structure.
|
|
148
|
+
*/
|
|
149
|
+
class FlatTextureAccessor {
|
|
150
|
+
constructor(getMaterial, field) {
|
|
151
|
+
this.getMaterial = getMaterial;
|
|
152
|
+
this.field = field;
|
|
153
|
+
}
|
|
154
|
+
get src() {
|
|
155
|
+
const mat = this.getMaterial();
|
|
156
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
157
|
+
const innerMat = getInnerMaterial(mat);
|
|
158
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
159
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
160
|
+
return texture?.src;
|
|
161
|
+
}
|
|
162
|
+
set src(value) {
|
|
163
|
+
if (value === undefined)
|
|
164
|
+
return;
|
|
165
|
+
const mat = this.getMaterial();
|
|
166
|
+
const texture = ensureTextureStructure(mat, this.field);
|
|
167
|
+
texture.src = value;
|
|
168
|
+
}
|
|
169
|
+
get wrapMode() {
|
|
170
|
+
const mat = this.getMaterial();
|
|
171
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
172
|
+
const innerMat = getInnerMaterial(mat);
|
|
173
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
174
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
175
|
+
return texture?.wrapMode;
|
|
176
|
+
}
|
|
177
|
+
set wrapMode(value) {
|
|
178
|
+
if (value === undefined)
|
|
179
|
+
return;
|
|
180
|
+
const mat = this.getMaterial();
|
|
181
|
+
const texture = ensureTextureStructure(mat, this.field);
|
|
182
|
+
texture.wrapMode = value;
|
|
183
|
+
}
|
|
184
|
+
get filterMode() {
|
|
185
|
+
const mat = this.getMaterial();
|
|
186
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
187
|
+
const innerMat = getInnerMaterial(mat);
|
|
188
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
189
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
190
|
+
return texture?.filterMode;
|
|
191
|
+
}
|
|
192
|
+
set filterMode(value) {
|
|
193
|
+
if (value === undefined)
|
|
194
|
+
return;
|
|
195
|
+
const mat = this.getMaterial();
|
|
196
|
+
const texture = ensureTextureStructure(mat, this.field);
|
|
197
|
+
texture.filterMode = value;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Class-based accessor for FlatMaterial properties.
|
|
202
|
+
* Provides getters/setters for all material properties with proper type safety.
|
|
203
|
+
*/
|
|
204
|
+
class FlatMaterialAccessor {
|
|
205
|
+
constructor(getMaterial) {
|
|
206
|
+
this.getMaterial = getMaterial;
|
|
207
|
+
}
|
|
208
|
+
// ==================== Private Helpers ====================
|
|
209
|
+
/**
|
|
210
|
+
* Get PBR material if available, undefined otherwise
|
|
211
|
+
*/
|
|
212
|
+
getPbrMaterial() {
|
|
213
|
+
const mat = this.getMaterial();
|
|
214
|
+
if (mat.material?.$case !== 'pbr')
|
|
215
|
+
return undefined;
|
|
216
|
+
return mat.material.pbr;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Ensure material is PBR and return it, throw otherwise
|
|
220
|
+
*/
|
|
221
|
+
ensurePbrMaterial(propertyName) {
|
|
222
|
+
const mat = this.getMaterial();
|
|
223
|
+
if (!mat.material) {
|
|
224
|
+
throw new Error('Material structure is invalid. Use setPbrMaterial or setBasicMaterial first.');
|
|
225
|
+
}
|
|
226
|
+
if (mat.material.$case !== 'pbr') {
|
|
227
|
+
throw new Error(`Cannot set ${propertyName} on Unlit material. Use PBR material instead.`);
|
|
228
|
+
}
|
|
229
|
+
return mat.material.pbr;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Ensure inner material exists and return it, throw otherwise
|
|
233
|
+
*/
|
|
234
|
+
ensureInnerMaterial() {
|
|
235
|
+
const mat = this.getMaterial();
|
|
236
|
+
if (!mat.material) {
|
|
237
|
+
throw new Error('Material structure is invalid. Use setPbrMaterial or setBasicMaterial first.');
|
|
238
|
+
}
|
|
239
|
+
return mat.material.$case === 'pbr' ? mat.material.pbr : mat.material.unlit;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get Unlit material if available, undefined otherwise
|
|
243
|
+
*/
|
|
244
|
+
getUnlitMaterial() {
|
|
245
|
+
const mat = this.getMaterial();
|
|
246
|
+
if (mat.material?.$case !== 'unlit')
|
|
247
|
+
return undefined;
|
|
248
|
+
return mat.material.unlit;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Ensure material is Unlit and return it, throw otherwise
|
|
252
|
+
*/
|
|
253
|
+
ensureUnlitMaterial(propertyName) {
|
|
254
|
+
const mat = this.getMaterial();
|
|
255
|
+
if (!mat.material) {
|
|
256
|
+
throw new Error('Material structure is invalid. Use setPbrMaterial or setBasicMaterial first.');
|
|
257
|
+
}
|
|
258
|
+
if (mat.material.$case !== 'unlit') {
|
|
259
|
+
throw new Error(`Cannot set ${propertyName} on PBR material. Use Unlit material instead.`);
|
|
260
|
+
}
|
|
261
|
+
return mat.material.unlit;
|
|
262
|
+
}
|
|
263
|
+
// ==================== Texture Accessors ====================
|
|
264
|
+
get texture() {
|
|
265
|
+
return new FlatTextureAccessor(this.getMaterial, 'texture');
|
|
266
|
+
}
|
|
267
|
+
get alphaTexture() {
|
|
268
|
+
return new FlatTextureAccessor(this.getMaterial, 'alphaTexture');
|
|
269
|
+
}
|
|
270
|
+
get emissiveTexture() {
|
|
271
|
+
const mat = this.getMaterial();
|
|
272
|
+
if (mat.material?.$case !== 'pbr')
|
|
273
|
+
return undefined;
|
|
274
|
+
return new FlatTextureAccessor(this.getMaterial, 'emissiveTexture');
|
|
275
|
+
}
|
|
276
|
+
get bumpTexture() {
|
|
277
|
+
const mat = this.getMaterial();
|
|
278
|
+
if (mat.material?.$case !== 'pbr')
|
|
279
|
+
return undefined;
|
|
280
|
+
return new FlatTextureAccessor(this.getMaterial, 'bumpTexture');
|
|
281
|
+
}
|
|
282
|
+
// ==================== Shared Properties (PBR + Unlit) ====================
|
|
283
|
+
get alphaTest() {
|
|
284
|
+
return getInnerMaterial(this.getMaterial())?.alphaTest;
|
|
285
|
+
}
|
|
286
|
+
set alphaTest(value) {
|
|
287
|
+
this.ensureInnerMaterial().alphaTest = value;
|
|
288
|
+
}
|
|
289
|
+
get castShadows() {
|
|
290
|
+
return getInnerMaterial(this.getMaterial())?.castShadows;
|
|
291
|
+
}
|
|
292
|
+
set castShadows(value) {
|
|
293
|
+
this.ensureInnerMaterial().castShadows = value;
|
|
294
|
+
}
|
|
295
|
+
// ==================== PBR-only Properties ====================
|
|
296
|
+
get albedoColor() {
|
|
297
|
+
return this.getPbrMaterial()?.albedoColor;
|
|
298
|
+
}
|
|
299
|
+
set albedoColor(value) {
|
|
300
|
+
this.ensurePbrMaterial('albedoColor').albedoColor = value;
|
|
301
|
+
}
|
|
302
|
+
get emissiveColor() {
|
|
303
|
+
return this.getPbrMaterial()?.emissiveColor;
|
|
304
|
+
}
|
|
305
|
+
set emissiveColor(value) {
|
|
306
|
+
this.ensurePbrMaterial('emissiveColor').emissiveColor = value;
|
|
307
|
+
}
|
|
308
|
+
get reflectivityColor() {
|
|
309
|
+
return this.getPbrMaterial()?.reflectivityColor;
|
|
310
|
+
}
|
|
311
|
+
set reflectivityColor(value) {
|
|
312
|
+
this.ensurePbrMaterial('reflectivityColor').reflectivityColor = value;
|
|
313
|
+
}
|
|
314
|
+
get transparencyMode() {
|
|
315
|
+
return this.getPbrMaterial()?.transparencyMode;
|
|
316
|
+
}
|
|
317
|
+
set transparencyMode(value) {
|
|
318
|
+
this.ensurePbrMaterial('transparencyMode').transparencyMode = value;
|
|
319
|
+
}
|
|
320
|
+
get metallic() {
|
|
321
|
+
return this.getPbrMaterial()?.metallic;
|
|
322
|
+
}
|
|
323
|
+
set metallic(value) {
|
|
324
|
+
this.ensurePbrMaterial('metallic').metallic = value;
|
|
325
|
+
}
|
|
326
|
+
get roughness() {
|
|
327
|
+
return this.getPbrMaterial()?.roughness;
|
|
328
|
+
}
|
|
329
|
+
set roughness(value) {
|
|
330
|
+
this.ensurePbrMaterial('roughness').roughness = value;
|
|
331
|
+
}
|
|
332
|
+
get specularIntensity() {
|
|
333
|
+
return this.getPbrMaterial()?.specularIntensity;
|
|
334
|
+
}
|
|
335
|
+
set specularIntensity(value) {
|
|
336
|
+
this.ensurePbrMaterial('specularIntensity').specularIntensity = value;
|
|
337
|
+
}
|
|
338
|
+
get emissiveIntensity() {
|
|
339
|
+
return this.getPbrMaterial()?.emissiveIntensity;
|
|
340
|
+
}
|
|
341
|
+
set emissiveIntensity(value) {
|
|
342
|
+
this.ensurePbrMaterial('emissiveIntensity').emissiveIntensity = value;
|
|
343
|
+
}
|
|
344
|
+
get directIntensity() {
|
|
345
|
+
return this.getPbrMaterial()?.directIntensity;
|
|
346
|
+
}
|
|
347
|
+
set directIntensity(value) {
|
|
348
|
+
this.ensurePbrMaterial('directIntensity').directIntensity = value;
|
|
349
|
+
}
|
|
350
|
+
// ==================== Unlit-only Property ====================
|
|
351
|
+
get diffuseColor() {
|
|
352
|
+
return this.getUnlitMaterial()?.diffuseColor;
|
|
353
|
+
}
|
|
354
|
+
set diffuseColor(value) {
|
|
355
|
+
this.ensureUnlitMaterial('diffuseColor').diffuseColor = value;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Readonly class-based accessor for FlatTexture properties.
|
|
360
|
+
* Provides only getters for read-only access to the nested material structure.
|
|
361
|
+
*/
|
|
362
|
+
class ReadonlyFlatTextureAccessor {
|
|
363
|
+
constructor(getMaterial, field) {
|
|
364
|
+
this.getMaterial = getMaterial;
|
|
365
|
+
this.field = field;
|
|
366
|
+
}
|
|
367
|
+
get src() {
|
|
368
|
+
const mat = this.getMaterial();
|
|
369
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
370
|
+
const innerMat = getInnerMaterial(mat);
|
|
371
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
372
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
373
|
+
return texture?.src;
|
|
374
|
+
}
|
|
375
|
+
get wrapMode() {
|
|
376
|
+
const mat = this.getMaterial();
|
|
377
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
378
|
+
const innerMat = getInnerMaterial(mat);
|
|
379
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
380
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
381
|
+
return texture?.wrapMode;
|
|
382
|
+
}
|
|
383
|
+
get filterMode() {
|
|
384
|
+
const mat = this.getMaterial();
|
|
385
|
+
const isPbr = mat.material?.$case === 'pbr';
|
|
386
|
+
const innerMat = getInnerMaterial(mat);
|
|
387
|
+
const textureUnion = getTextureField(innerMat, this.field, isPbr);
|
|
388
|
+
const texture = getTextureFromUnion(textureUnion);
|
|
389
|
+
return texture?.filterMode;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Readonly class-based accessor for FlatMaterial properties.
|
|
394
|
+
* Provides only getters for read-only access to material properties.
|
|
395
|
+
*/
|
|
396
|
+
class ReadonlyFlatMaterialAccessor {
|
|
397
|
+
constructor(getMaterial) {
|
|
398
|
+
this.getMaterial = getMaterial;
|
|
399
|
+
}
|
|
400
|
+
// ==================== Private Helpers ====================
|
|
401
|
+
/**
|
|
402
|
+
* Get PBR material if available, undefined otherwise
|
|
403
|
+
*/
|
|
404
|
+
getPbrMaterial() {
|
|
405
|
+
const mat = this.getMaterial();
|
|
406
|
+
if (mat.material?.$case !== 'pbr')
|
|
407
|
+
return undefined;
|
|
408
|
+
return mat.material.pbr;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Get Unlit material if available, undefined otherwise
|
|
412
|
+
*/
|
|
413
|
+
getUnlitMaterial() {
|
|
414
|
+
const mat = this.getMaterial();
|
|
415
|
+
if (mat.material?.$case !== 'unlit')
|
|
416
|
+
return undefined;
|
|
417
|
+
return mat.material.unlit;
|
|
418
|
+
}
|
|
419
|
+
// ==================== Texture Accessors ====================
|
|
420
|
+
get texture() {
|
|
421
|
+
return new ReadonlyFlatTextureAccessor(this.getMaterial, 'texture');
|
|
422
|
+
}
|
|
423
|
+
get alphaTexture() {
|
|
424
|
+
return new ReadonlyFlatTextureAccessor(this.getMaterial, 'alphaTexture');
|
|
425
|
+
}
|
|
426
|
+
get emissiveTexture() {
|
|
427
|
+
const mat = this.getMaterial();
|
|
428
|
+
if (mat.material?.$case !== 'pbr')
|
|
429
|
+
return undefined;
|
|
430
|
+
return new ReadonlyFlatTextureAccessor(this.getMaterial, 'emissiveTexture');
|
|
431
|
+
}
|
|
432
|
+
get bumpTexture() {
|
|
433
|
+
const mat = this.getMaterial();
|
|
434
|
+
if (mat.material?.$case !== 'pbr')
|
|
435
|
+
return undefined;
|
|
436
|
+
return new ReadonlyFlatTextureAccessor(this.getMaterial, 'bumpTexture');
|
|
437
|
+
}
|
|
438
|
+
// ==================== Shared Properties (PBR + Unlit) ====================
|
|
439
|
+
get alphaTest() {
|
|
440
|
+
return getInnerMaterial(this.getMaterial())?.alphaTest;
|
|
441
|
+
}
|
|
442
|
+
get castShadows() {
|
|
443
|
+
return getInnerMaterial(this.getMaterial())?.castShadows;
|
|
444
|
+
}
|
|
445
|
+
// ==================== PBR-only Properties ====================
|
|
446
|
+
get albedoColor() {
|
|
447
|
+
return this.getPbrMaterial()?.albedoColor;
|
|
448
|
+
}
|
|
449
|
+
get emissiveColor() {
|
|
450
|
+
return this.getPbrMaterial()?.emissiveColor;
|
|
451
|
+
}
|
|
452
|
+
get reflectivityColor() {
|
|
453
|
+
return this.getPbrMaterial()?.reflectivityColor;
|
|
454
|
+
}
|
|
455
|
+
get transparencyMode() {
|
|
456
|
+
return this.getPbrMaterial()?.transparencyMode;
|
|
457
|
+
}
|
|
458
|
+
get metallic() {
|
|
459
|
+
return this.getPbrMaterial()?.metallic;
|
|
460
|
+
}
|
|
461
|
+
get roughness() {
|
|
462
|
+
return this.getPbrMaterial()?.roughness;
|
|
463
|
+
}
|
|
464
|
+
get specularIntensity() {
|
|
465
|
+
return this.getPbrMaterial()?.specularIntensity;
|
|
466
|
+
}
|
|
467
|
+
get emissiveIntensity() {
|
|
468
|
+
return this.getPbrMaterial()?.emissiveIntensity;
|
|
469
|
+
}
|
|
470
|
+
get directIntensity() {
|
|
471
|
+
return this.getPbrMaterial()?.directIntensity;
|
|
472
|
+
}
|
|
473
|
+
// ==================== Unlit-only Property ====================
|
|
474
|
+
get diffuseColor() {
|
|
475
|
+
return this.getUnlitMaterial()?.diffuseColor;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
28
478
|
export function defineMaterialComponent(engine) {
|
|
29
479
|
const theComponent = Material(engine);
|
|
30
480
|
return {
|
|
@@ -45,6 +495,31 @@ export function defineMaterialComponent(engine) {
|
|
|
45
495
|
pbr: material
|
|
46
496
|
}
|
|
47
497
|
});
|
|
498
|
+
},
|
|
499
|
+
getFlat(entity) {
|
|
500
|
+
const mat = theComponent.get(entity);
|
|
501
|
+
return new ReadonlyFlatMaterialAccessor(() => mat);
|
|
502
|
+
},
|
|
503
|
+
getFlatOrNull(entity) {
|
|
504
|
+
const mat = theComponent.getOrNull(entity);
|
|
505
|
+
if (!mat)
|
|
506
|
+
return null;
|
|
507
|
+
return new ReadonlyFlatMaterialAccessor(() => mat);
|
|
508
|
+
},
|
|
509
|
+
getFlatMutable(entity) {
|
|
510
|
+
const getMaterial = () => theComponent.getMutable(entity);
|
|
511
|
+
// Verify component exists immediately (fail-fast)
|
|
512
|
+
getMaterial();
|
|
513
|
+
return new FlatMaterialAccessor(getMaterial);
|
|
514
|
+
},
|
|
515
|
+
getFlatMutableOrNull(entity) {
|
|
516
|
+
const mat = theComponent.getMutableOrNull(entity);
|
|
517
|
+
if (!mat)
|
|
518
|
+
return null;
|
|
519
|
+
return new FlatMaterialAccessor(() => theComponent.getMutable(entity));
|
|
48
520
|
}
|
|
49
521
|
};
|
|
50
522
|
}
|
|
523
|
+
// Force compile error if assertions fail
|
|
524
|
+
const _pbrSyncCheck = true;
|
|
525
|
+
const _unlitSyncCheck = true;
|