@zephyr3d/scene 0.9.6 → 0.9.8

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.
Files changed (205) hide show
  1. package/dist/animation/animationset.js +52 -55
  2. package/dist/animation/animationset.js.map +1 -1
  3. package/dist/animation/joint_dynamics/controller.js +92 -32
  4. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  5. package/dist/animation/joint_dynamics/convex_collider.js +320 -0
  6. package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
  7. package/dist/animation/joint_dynamics/joint_dynamics_system.js +57 -19
  8. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  9. package/dist/animation/joint_dynamics/solver.js +71 -27
  10. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  11. package/dist/animation/joint_dynamics_modifier.js +4 -3
  12. package/dist/animation/joint_dynamics_modifier.js.map +1 -1
  13. package/dist/animation/skeleton.js +7 -10
  14. package/dist/animation/skeleton.js.map +1 -1
  15. package/dist/app/engine.js +3 -9
  16. package/dist/app/engine.js.map +1 -1
  17. package/dist/app/scriptregistry.js +47 -16
  18. package/dist/app/scriptregistry.js.map +1 -1
  19. package/dist/asset/assetmanager.js +3 -1
  20. package/dist/asset/assetmanager.js.map +1 -1
  21. package/dist/asset/model.js +346 -7
  22. package/dist/asset/model.js.map +1 -1
  23. package/dist/avatar/wardrobe.js +7 -3
  24. package/dist/avatar/wardrobe.js.map +1 -1
  25. package/dist/camera/camera.js +240 -34
  26. package/dist/camera/camera.js.map +1 -1
  27. package/dist/index.d.ts +1350 -622
  28. package/dist/index.js +6 -11
  29. package/dist/index.js.map +1 -1
  30. package/dist/material/blinn.js +1 -1
  31. package/dist/material/blinn.js.map +1 -1
  32. package/dist/material/lambert.js +1 -1
  33. package/dist/material/lambert.js.map +1 -1
  34. package/dist/material/material.js +23 -1
  35. package/dist/material/material.js.map +1 -1
  36. package/dist/material/meshmaterial.js +178 -23
  37. package/dist/material/meshmaterial.js.map +1 -1
  38. package/dist/material/mixins/albedocolor.js +4 -4
  39. package/dist/material/mixins/albedocolor.js.map +1 -1
  40. package/dist/material/mixins/lightmodel/blinnphong.js +2 -1
  41. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  42. package/dist/material/mixins/lightmodel/lambert.js +2 -1
  43. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  44. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +63 -16
  45. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  46. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +100 -27
  47. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  48. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +16 -6
  49. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  50. package/dist/material/mixins/lit.js +1 -1
  51. package/dist/material/mixins/lit.js.map +1 -1
  52. package/dist/material/mixins/pbr/common.js +276 -65
  53. package/dist/material/mixins/pbr/common.js.map +1 -1
  54. package/dist/material/mixins/texture.js +3 -3
  55. package/dist/material/mixins/texture.js.map +1 -1
  56. package/dist/material/mixins/vertexcolor.js +2 -2
  57. package/dist/material/mixins/vertexcolor.js.map +1 -1
  58. package/dist/material/mtoon.js +574 -0
  59. package/dist/material/mtoon.js.map +1 -0
  60. package/dist/material/pbrblueprint.js +494 -331
  61. package/dist/material/pbrblueprint.js.map +1 -1
  62. package/dist/material/pbrmr.js +118 -100
  63. package/dist/material/pbrmr.js.map +1 -1
  64. package/dist/material/pbrsg.js +1 -1
  65. package/dist/material/pbrsg.js.map +1 -1
  66. package/dist/material/shader/helper.js +45 -15
  67. package/dist/material/shader/helper.js.map +1 -1
  68. package/dist/material/subsurfaceprofile.js +812 -0
  69. package/dist/material/subsurfaceprofile.js.map +1 -0
  70. package/dist/material/unlit.js +1 -1
  71. package/dist/material/unlit.js.map +1 -1
  72. package/dist/posteffect/compositor.js +46 -11
  73. package/dist/posteffect/compositor.js.map +1 -1
  74. package/dist/posteffect/ssr.js +413 -87
  75. package/dist/posteffect/ssr.js.map +1 -1
  76. package/dist/posteffect/sss.js +1053 -0
  77. package/dist/posteffect/sss.js.map +1 -0
  78. package/dist/posteffect/taa.js +61 -60
  79. package/dist/posteffect/taa.js.map +1 -1
  80. package/dist/render/abuffer_oit.js +63 -12
  81. package/dist/render/abuffer_oit.js.map +1 -1
  82. package/dist/render/cluster_light.js +6 -5
  83. package/dist/render/cluster_light.js.map +1 -1
  84. package/dist/render/drawable.js +1 -5
  85. package/dist/render/drawable.js.map +1 -1
  86. package/dist/render/drawable_mixin.js +2 -1
  87. package/dist/render/drawable_mixin.js.map +1 -1
  88. package/dist/render/fbm_wavegenerator.js +2 -1
  89. package/dist/render/fbm_wavegenerator.js.map +1 -1
  90. package/dist/render/gerstner_wavegenerator.js +2 -1
  91. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  92. package/dist/render/globalbindgroup_allocator.js +2 -1
  93. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  94. package/dist/render/lightpass.js +105 -18
  95. package/dist/render/lightpass.js.map +1 -1
  96. package/dist/render/render_queue.js +51 -8
  97. package/dist/render/render_queue.js.map +1 -1
  98. package/dist/render/renderer.js +73 -59
  99. package/dist/render/renderer.js.map +1 -1
  100. package/dist/render/rendergraph/device_pool_allocator.js +8 -0
  101. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  102. package/dist/render/rendergraph/forward_plus_builder.js +481 -95
  103. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  104. package/dist/render/rendergraph/history_resource_manager.js +55 -0
  105. package/dist/render/rendergraph/history_resource_manager.js.map +1 -1
  106. package/dist/render/rendergraph/history_resources.js +3 -1
  107. package/dist/render/rendergraph/history_resources.js.map +1 -1
  108. package/dist/render/rendergraph/types.js.map +1 -1
  109. package/dist/render/renderpass.js +10 -4
  110. package/dist/render/renderpass.js.map +1 -1
  111. package/dist/render/sky.js +31 -1
  112. package/dist/render/sky.js.map +1 -1
  113. package/dist/render/weightedblended_oit.js +23 -5
  114. package/dist/render/weightedblended_oit.js.map +1 -1
  115. package/dist/scene/basesprite.js +4 -2
  116. package/dist/scene/basesprite.js.map +1 -1
  117. package/dist/scene/batchgroup.js +3 -1
  118. package/dist/scene/batchgroup.js.map +1 -1
  119. package/dist/scene/environment.js +11 -1
  120. package/dist/scene/environment.js.map +1 -1
  121. package/dist/scene/light.js +21 -3
  122. package/dist/scene/light.js.map +1 -1
  123. package/dist/scene/mesh.js +29 -14
  124. package/dist/scene/mesh.js.map +1 -1
  125. package/dist/scene/meshdrawable.js +6 -0
  126. package/dist/scene/meshdrawable.js.map +1 -1
  127. package/dist/scene/msdftext.js +5 -2
  128. package/dist/scene/msdftext.js.map +1 -1
  129. package/dist/scene/msdftextsprite.js +5 -3
  130. package/dist/scene/msdftextsprite.js.map +1 -1
  131. package/dist/scene/particlesys.js +3 -1
  132. package/dist/scene/particlesys.js.map +1 -1
  133. package/dist/scene/scene.js +6 -5
  134. package/dist/scene/scene.js.map +1 -1
  135. package/dist/scene/terrain-cm/grass.js +4 -2
  136. package/dist/scene/terrain-cm/grass.js.map +1 -1
  137. package/dist/scene/terrain-cm/grassmaterial.js +2 -1
  138. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  139. package/dist/scene/terrain-cm/terrain-cm.js +4 -2
  140. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  141. package/dist/scene/water.js +4 -2
  142. package/dist/scene/water.js.map +1 -1
  143. package/dist/shaders/shadow.js +26 -393
  144. package/dist/shaders/shadow.js.map +1 -1
  145. package/dist/shadow/esm.js +112 -12
  146. package/dist/shadow/esm.js.map +1 -1
  147. package/dist/shadow/pcf_opt.js +2 -2
  148. package/dist/shadow/pcf_opt.js.map +1 -1
  149. package/dist/shadow/pcf_pd.js +19 -10
  150. package/dist/shadow/pcf_pd.js.map +1 -1
  151. package/dist/shadow/shader.js +58 -7
  152. package/dist/shadow/shader.js.map +1 -1
  153. package/dist/shadow/shadow_impl.js +6 -0
  154. package/dist/shadow/shadow_impl.js.map +1 -1
  155. package/dist/shadow/shadow_region.js +229 -0
  156. package/dist/shadow/shadow_region.js.map +1 -0
  157. package/dist/shadow/shadowmapper.js +67 -59
  158. package/dist/shadow/shadowmapper.js.map +1 -1
  159. package/dist/shadow/vsm.js +45 -19
  160. package/dist/shadow/vsm.js.map +1 -1
  161. package/dist/utility/blueprint/common/constants.js +8 -8
  162. package/dist/utility/blueprint/common/constants.js.map +1 -1
  163. package/dist/utility/blueprint/material/pbr.js +31 -4
  164. package/dist/utility/blueprint/material/pbr.js.map +1 -1
  165. package/dist/utility/blueprint/material/texture.js +1 -17
  166. package/dist/utility/blueprint/material/texture.js.map +1 -1
  167. package/dist/utility/serialization/manager.js +66 -98
  168. package/dist/utility/serialization/manager.js.map +1 -1
  169. package/dist/utility/serialization/scene/animation.js +5 -2
  170. package/dist/utility/serialization/scene/animation.js.map +1 -1
  171. package/dist/utility/serialization/scene/batch.js +4 -1
  172. package/dist/utility/serialization/scene/batch.js.map +1 -1
  173. package/dist/utility/serialization/scene/camera.js +249 -54
  174. package/dist/utility/serialization/scene/camera.js.map +1 -1
  175. package/dist/utility/serialization/scene/common.js +37 -1
  176. package/dist/utility/serialization/scene/common.js.map +1 -1
  177. package/dist/utility/serialization/scene/light.js +42 -3
  178. package/dist/utility/serialization/scene/light.js.map +1 -1
  179. package/dist/utility/serialization/scene/material.js +1112 -646
  180. package/dist/utility/serialization/scene/material.js.map +1 -1
  181. package/dist/utility/serialization/scene/mesh.js +7 -4
  182. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  183. package/dist/utility/serialization/scene/node.js +31 -12
  184. package/dist/utility/serialization/scene/node.js.map +1 -1
  185. package/dist/utility/serialization/scene/particle.js +4 -1
  186. package/dist/utility/serialization/scene/particle.js.map +1 -1
  187. package/dist/utility/serialization/scene/primitive.js +3 -1
  188. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  189. package/dist/utility/serialization/scene/scene.js +18 -0
  190. package/dist/utility/serialization/scene/scene.js.map +1 -1
  191. package/dist/utility/serialization/scene/sprite.js +6 -3
  192. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  193. package/dist/utility/serialization/scene/terrain.js +5 -2
  194. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  195. package/dist/utility/serialization/scene/text.js +3 -1
  196. package/dist/utility/serialization/scene/text.js.map +1 -1
  197. package/dist/utility/serialization/scene/water.js +4 -1
  198. package/dist/utility/serialization/scene/water.js.map +1 -1
  199. package/dist/utility/textures/ltcdata.js +6 -0
  200. package/dist/utility/textures/ltcdata.js.map +1 -0
  201. package/dist/utility/textures/ltclut.js +61 -0
  202. package/dist/utility/textures/ltclut.js.map +1 -0
  203. package/dist/values.js +9 -1
  204. package/dist/values.js.map +1 -1
  205. package/package.json +3 -3
@@ -1,61 +1,43 @@
1
- import { applyMaterialMixins, MeshMaterial } from './meshmaterial.js';
1
+ import { DRef, Vector4 } from '@zephyr3d/base';
2
2
  import { ShaderHelper } from './shader/helper.js';
3
- import { RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags } from '../values.js';
4
- import { Vector3, Vector2, DRef, Vector4 } from '@zephyr3d/base';
5
- import { mixinPBRBluePrint } from './mixins/lightmodel/pbrblueprintmixin.js';
6
3
  import { MaterialBlueprintIR } from '../utility/blueprint/material/ir.js';
7
4
  import { PBRBlockNode, VertexBlockNode } from '../utility/blueprint/material/pbr.js';
5
+ import { PBRMetallicRoughnessMaterial } from './pbrmr.js';
8
6
 
9
- const PBR_REFLECTION_MODE = {
10
- none: 0,
11
- ggx: 1,
12
- anisotropic: 2,
13
- glint: 3
14
- };
7
+ const DEFAULT_OUTPUT_NAMES = [
8
+ 'BaseColor',
9
+ 'Metallic',
10
+ 'Roughness',
11
+ 'Specular',
12
+ 'Emissive',
13
+ 'Normal',
14
+ 'Tangent',
15
+ 'Opacity',
16
+ 'SpecularWeight',
17
+ 'AO'
18
+ ];
15
19
  /**
16
- * Physically-based rendering material driven by blueprint graphs.
20
+ * Physically-based rendering material driven by blueprint graphs, but shaded by the
21
+ * same PBR metallic/roughness backend used by {@link PBRMetallicRoughnessMaterial}.
17
22
  *
18
23
  * @remarks
19
- * This material extends {@link MeshMaterial} with PBR behavior via
20
- * {@link mixinPBRBluePrint}, and uses {@link MaterialBlueprintIR}
21
- * graphs for both vertex and fragment stages.
22
- *
23
- * - The **vertex blueprint IR** (`vertexIR`) controls vertex
24
- * transformations and per-vertex data.
25
- * - The **fragment blueprint IR** (`fragmentIR`) produces inputs
26
- * for the PBR shading model (albedo, roughness, metalness, etc.).
27
- *
28
- * Uniform values and textures for the blueprints are provided via
29
- * {@link PBRBluePrintMaterial.uniformValues} and
30
- * {@link PBRBluePrintMaterial.uniformTextures}.
24
+ * The blueprint graph only produces surface inputs. Final direct/indirect lighting,
25
+ * transmission, clearcoat, sheen, iridescence and anisotropic reflection still run
26
+ * through the standard PBRM backend so imported GLTF materials and blueprint materials
27
+ * stay on the same shading path.
31
28
  *
32
29
  * @public
33
- */ class PBRBluePrintMaterial extends applyMaterialMixins(MeshMaterial, mixinPBRBluePrint) {
34
- /** @internal */ static FEATURE_VERTEX_COLOR = this.defineFeature();
35
- /** @internal */ static FEATURE_VERTEX_UV = this.defineFeature();
36
- /** @internal */ static FEATURE_SUBSURFACE_SCATTERING = this.defineFeature();
30
+ */ class PBRBluePrintMaterial extends PBRMetallicRoughnessMaterial {
31
+ /** @internal */ static FEATURE_BLUEPRINT_VERTEX_COLOR = this.defineFeature();
32
+ /** @internal */ static FEATURE_BLUEPRINT_VERTEX_UV = this.defineFeature();
37
33
  /** @internal */ _irFrag;
38
34
  /** @internal */ _irVertex;
39
35
  /** @internal */ _uniformValues;
40
36
  /** @internal */ _uniformTextures;
41
- /** @internal */ _reflectionMode;
42
- /** @internal */ _subsurfaceColor;
43
- /** @internal */ _subsurfaceScale;
44
- /** @internal */ _subsurfacePower;
45
- /** @internal */ _subsurfaceIntensity;
46
- /** @internal */ _anisotropy;
47
- /** @internal */ _anisotropyDirection;
48
- /** @internal */ _anisotropyDirectionScaleBias;
49
- /**
50
- * Creates a new {@link PBRBluePrintMaterial} instance.
51
- *
52
- * @param irFrag - Optional fragment blueprint IR. If omitted, a default
53
- * IR containing a single {@link PBRBlockNode} is created.
54
- * @param irVertex - Optional vertex blueprint IR. If omitted, a default
55
- * IR containing a single {@link VertexBlockNode} is created.
56
- * @param uniformValues - Optional initial list of uniform value descriptors.
57
- * @param uniformTextures - Optional initial list of texture uniform descriptors.
58
- */ constructor(irFrag, irVertex, uniformValues, uniformTextures){
37
+ /** @internal */ _connectedOutputs;
38
+ /** @internal */ _vertexUsesColor;
39
+ /** @internal */ _vertexUsesUV;
40
+ constructor(irFrag, irVertex, uniformValues, uniformTextures){
59
41
  super();
60
42
  this._irFrag = irFrag ?? new MaterialBlueprintIR({
61
43
  nodeMap: {
@@ -115,126 +97,34 @@ const PBR_REFLECTION_MODE = {
115
97
  });
116
98
  this._uniformValues = uniformValues ?? [];
117
99
  this._uniformTextures = uniformTextures ?? [];
118
- this._reflectionMode = 'ggx';
119
- this._subsurfaceColor = new Vector3(1, 0.3, 0.2);
120
- this._subsurfaceScale = 0.5;
121
- this._subsurfacePower = 1.5;
122
- this._subsurfaceIntensity = 0.5;
123
- this._anisotropy = 0.75;
124
- this._anisotropyDirection = 0;
125
- this._anisotropyDirectionScaleBias = new Vector2(1, 0);
126
- this.useFeature(PBRBluePrintMaterial.FEATURE_SUBSURFACE_SCATTERING, false);
127
- this.useFeature(PBRBluePrintMaterial.FEATURE_VERTEX_COLOR, this._irVertex.behaviors.useVertexColor);
128
- this.useFeature(PBRBluePrintMaterial.FEATURE_VERTEX_UV, this._irVertex.behaviors.useVertexUV);
129
- }
130
- get reflectionMode() {
131
- return this._reflectionMode;
132
- }
133
- set reflectionMode(val) {
134
- if (val !== this._reflectionMode) {
135
- this._reflectionMode = val;
136
- this.uniformChanged();
137
- }
138
- }
139
- get subsurfaceScattering() {
140
- return this.featureUsed(PBRBluePrintMaterial.FEATURE_SUBSURFACE_SCATTERING);
141
- }
142
- set subsurfaceScattering(val) {
143
- this.useFeature(PBRBluePrintMaterial.FEATURE_SUBSURFACE_SCATTERING, !!val);
144
- }
145
- get subsurfaceColor() {
146
- return this._subsurfaceColor;
147
- }
148
- set subsurfaceColor(val) {
149
- if (!val.equalsTo(this._subsurfaceColor)) {
150
- this._subsurfaceColor.set(val);
151
- this.uniformChanged();
152
- }
153
- }
154
- get subsurfaceScale() {
155
- return this._subsurfaceScale;
100
+ this._connectedOutputs = new Set();
101
+ this._vertexUsesColor = false;
102
+ this._vertexUsesUV = false;
103
+ this.syncBlueprintMetadata();
156
104
  }
157
- set subsurfaceScale(val) {
158
- if (val !== this._subsurfaceScale) {
159
- this._subsurfaceScale = val;
160
- this.uniformChanged();
161
- }
162
- }
163
- get subsurfacePower() {
164
- return this._subsurfacePower;
165
- }
166
- set subsurfacePower(val) {
167
- if (val !== this._subsurfacePower) {
168
- this._subsurfacePower = val;
169
- this.uniformChanged();
170
- }
171
- }
172
- get subsurfaceIntensity() {
173
- return this._subsurfaceIntensity;
174
- }
175
- set subsurfaceIntensity(val) {
176
- if (val !== this._subsurfaceIntensity) {
177
- this._subsurfaceIntensity = val;
178
- this.uniformChanged();
179
- }
180
- }
181
- get anisotropy() {
182
- return this._anisotropy;
183
- }
184
- set anisotropy(val) {
185
- const clamped = Math.max(-0.95, Math.min(0.95, val));
186
- if (clamped !== this._anisotropy) {
187
- this._anisotropy = clamped;
188
- this.uniformChanged();
189
- }
190
- }
191
- get anisotropyDirection() {
192
- return this._anisotropyDirection;
193
- }
194
- set anisotropyDirection(val) {
195
- if (val !== this._anisotropyDirection) {
196
- this._anisotropyDirection = val;
197
- this.uniformChanged();
198
- }
199
- }
200
- get anisotropyDirectionScaleBias() {
201
- return this._anisotropyDirectionScaleBias;
202
- }
203
- set anisotropyDirectionScaleBias(val) {
204
- if (!val.equalsTo(this._anisotropyDirectionScaleBias)) {
205
- this._anisotropyDirectionScaleBias.set(val);
206
- this.uniformChanged();
207
- }
208
- }
209
- /**
210
- * Gets the fragment blueprint IR.
211
- */ get fragmentIR() {
105
+ get fragmentIR() {
212
106
  return this._irFrag;
213
107
  }
214
108
  set fragmentIR(ir) {
215
109
  if (ir && ir !== this._irFrag) {
216
110
  this._irFrag = ir;
111
+ this.syncBlueprintMetadata();
217
112
  this.clearCache();
218
113
  this.optionChanged(true);
219
114
  }
220
115
  }
221
- /**
222
- * Gets the vertex blueprint IR.
223
- */ get vertexIR() {
116
+ get vertexIR() {
224
117
  return this._irVertex;
225
118
  }
226
119
  set vertexIR(ir) {
227
120
  if (ir && ir !== this._irVertex) {
228
121
  this._irVertex = ir;
229
- this.useFeature(PBRBluePrintMaterial.FEATURE_VERTEX_COLOR, this._irVertex.behaviors.useVertexColor);
230
- this.useFeature(PBRBluePrintMaterial.FEATURE_VERTEX_UV, this._irVertex.behaviors.useVertexUV);
122
+ this.syncBlueprintMetadata();
231
123
  this.clearCache();
232
124
  this.optionChanged(true);
233
125
  }
234
126
  }
235
- /**
236
- * Gets the list of uniform value descriptors used by the blueprints.
237
- */ get uniformValues() {
127
+ get uniformValues() {
238
128
  return this._uniformValues;
239
129
  }
240
130
  set uniformValues(val) {
@@ -243,262 +133,535 @@ const PBR_REFLECTION_MODE = {
243
133
  }));
244
134
  this.uniformChanged();
245
135
  }
246
- /**
247
- * Gets the list of texture uniform descriptors used by the blueprints.
248
- */ get uniformTextures() {
136
+ get uniformTextures() {
249
137
  return this._uniformTextures;
250
138
  }
251
139
  set uniformTextures(val) {
252
140
  if (val !== this._uniformTextures) {
253
- const newUniforms = val.map((v)=>({
254
- finalTexture: new DRef(v.finalTexture.get()),
141
+ const newUniforms = (val ?? []).map((v)=>({
142
+ finalTexture: new DRef(v.finalTexture?.get() ?? null),
255
143
  finalSampler: v.finalSampler,
144
+ inFragmentShader: v.inFragmentShader,
145
+ inVertexShader: v.inVertexShader,
146
+ magFilter: v.magFilter,
147
+ minFilter: v.minFilter,
148
+ mipFilter: v.mipFilter,
256
149
  name: v.name,
257
150
  params: v.params?.clone() ?? Vector4.zero(),
151
+ sRGB: v.sRGB,
258
152
  texture: v.texture,
259
153
  type: v.type,
260
- sRGB: v.sRGB,
261
154
  wrapS: v.wrapS,
262
- wrapT: v.wrapT,
263
- inFragmentShader: v.inFragmentShader,
264
- inVertexShader: v.inVertexShader,
265
- minFilter: v.minFilter,
266
- magFilter: v.magFilter,
267
- mipFilter: v.mipFilter
155
+ wrapT: v.wrapT
268
156
  }));
269
157
  for (const u of this._uniformTextures){
270
- u.finalTexture.dispose();
158
+ u.finalTexture?.dispose();
271
159
  }
272
160
  this._uniformTextures = newUniforms;
273
161
  this.uniformChanged();
274
162
  }
275
163
  }
276
- /**
277
- * Creates a deep copy of this material.
278
- *
279
- * @remarks
280
- * The clone shares the same vertex/fragment IR references and copies
281
- * the current blueprint uniform descriptors, then calls `copyFrom`
282
- * to copy base-class and mixin state.
283
- *
284
- * @returns A new {@link PBRBluePrintMaterial} instance.
285
- */ clone() {
164
+ hasConnectedOutput(name) {
165
+ return this._connectedOutputs.has(name);
166
+ }
167
+ clone() {
286
168
  const other = new PBRBluePrintMaterial(this._irFrag, this._irVertex, this._uniformValues, this._uniformTextures);
287
169
  other.copyFrom(this);
288
170
  return other;
289
171
  }
290
172
  copyFrom(other) {
291
173
  super.copyFrom(other);
292
- this.reflectionMode = other.reflectionMode;
293
- this.subsurfaceScattering = other.subsurfaceScattering;
294
- this.subsurfaceColor = other.subsurfaceColor;
295
- this.subsurfaceScale = other.subsurfaceScale;
296
- this.subsurfacePower = other.subsurfacePower;
297
- this.subsurfaceIntensity = other.subsurfaceIntensity;
298
- this.anisotropy = other.anisotropy;
299
- this.anisotropyDirection = other.anisotropyDirection;
300
- this.anisotropyDirectionScaleBias = other.anisotropyDirectionScaleBias;
301
- }
302
- /**
303
- * Builds the vertex shader for this PBR blueprint material.
304
- *
305
- * @param scope - The current vertex shader function scope.
306
- */ vertexShader(scope) {
174
+ if (other instanceof PBRBluePrintMaterial) {
175
+ this.fragmentIR = other.fragmentIR;
176
+ this.vertexIR = other.vertexIR;
177
+ this.uniformValues = other.uniformValues;
178
+ this.uniformTextures = other.uniformTextures;
179
+ }
180
+ }
181
+ vertexShader(scope) {
307
182
  super.vertexShader(scope);
308
183
  const pb = scope.$builder;
309
- scope.$inputs.zVertexPos = pb.vec3().attrib('position');
310
- scope.$inputs.zVertexNormal = pb.vec3().attrib('normal');
311
- scope.$inputs.zVertexTangent = pb.vec4().attrib('tangent');
312
- if (this.featureUsed(PBRBluePrintMaterial.FEATURE_VERTEX_COLOR)) {
313
- scope.$inputs.zVertexColor = pb.vec4().attrib('diffuse');
184
+ scope.zVertexColor = this.getBlueprintVertexColor(scope);
185
+ scope.zVertexUV = this.getBlueprintVertexUV(scope);
186
+ scope.zVertexNormal = scope.$outputs.wNorm ?? pb.vec3(0, 0, 1);
187
+ scope.zVertexTangent = scope.$outputs.wTangent ?? pb.vec3(1, 0, 0);
188
+ scope.zVertexBinormal = scope.$outputs.wBinormal ?? pb.vec3(0, 1, 0);
189
+ scope.zWorldPos = scope.$outputs.worldPos;
190
+ if (this._vertexUsesColor && !scope.$outputs.zOutDiffuse) {
191
+ scope.$outputs.zOutDiffuse = scope.zVertexColor;
314
192
  }
315
- if (this.featureUsed(PBRBluePrintMaterial.FEATURE_VERTEX_UV)) {
316
- scope.$inputs.zVertexUV = pb.vec2().attrib('texCoord0');
193
+ if (this._vertexUsesUV) {
194
+ scope.$outputs.zVertexUV = scope.zVertexUV;
317
195
  }
318
196
  for (const u of [
319
197
  ...this._uniformValues,
320
198
  ...this._uniformTextures
321
199
  ]){
322
200
  if (u.inVertexShader) {
323
- // @ts-ignore
201
+ // @ts-ignore dynamic shader type constructor
324
202
  pb.getGlobalScope()[u.name] = pb[u.type]().uniform(2);
325
203
  }
326
204
  }
327
205
  const outputs = this._irVertex.create(pb);
328
- scope.$l.oPos = this.getOutput(outputs, 'Position') ?? ShaderHelper.resolveVertexPosition(scope);
206
+ if (!outputs) {
207
+ return;
208
+ }
209
+ const vertexOut = this.toVertexOutputMap(outputs);
329
210
  const worldMatrix = ShaderHelper.getWorldMatrix(scope);
330
- scope.$outputs.worldPos = pb.mul(worldMatrix, pb.vec4(scope.oPos, 1)).xyz;
331
- scope.$l.csPos = pb.mul(ShaderHelper.getViewProjectionMatrix(scope), pb.vec4(scope.$outputs.worldPos, 1));
332
- ShaderHelper.setClipSpacePosition(scope, scope.csPos);
333
- scope.$outputs.zVertexColor = this.getOutput(outputs, 'Color') ?? (this.featureUsed(PBRBluePrintMaterial.FEATURE_VERTEX_COLOR) ? scope.$inputs.zVertexColor : pb.vec4(1));
334
- scope.$outputs.zVertexUV = this.getOutput(outputs, 'UV') ?? (this.featureUsed(PBRBluePrintMaterial.FEATURE_VERTEX_UV) ? scope.$inputs.zVertexUV : pb.vec2(0));
335
- scope.$l.oNorm = this.getOutput(outputs, 'Normal') ?? ShaderHelper.resolveVertexNormal(scope);
336
- scope.$outputs.zVertexNormal = pb.mul(ShaderHelper.getNormalMatrix(scope), pb.vec4(scope.oNorm, 0)).xyz;
337
- scope.$l.oTangent = this.getOutput(outputs, 'Tangent') ?? ShaderHelper.resolveVertexTangent(scope);
338
- scope.$outputs.zVertexTangent = pb.mul(ShaderHelper.getNormalMatrix(scope), pb.vec4(scope.oTangent.xyz, 0)).xyz;
339
- scope.$outputs.zVertexBinormal = pb.mul(pb.cross(scope.$outputs.zVertexNormal, scope.$outputs.zVertexTangent), scope.oTangent.w);
211
+ if (vertexOut.Position) {
212
+ scope.$l.oPos = vertexOut.Position;
213
+ scope.$outputs.worldPos = pb.mul(worldMatrix, pb.vec4(scope.oPos, 1)).xyz;
214
+ scope.zWorldPos = scope.$outputs.worldPos;
215
+ scope.$l.csPos = pb.mul(ShaderHelper.getViewProjectionMatrix(scope), pb.vec4(scope.$outputs.worldPos, 1));
216
+ ShaderHelper.setClipSpacePosition(scope, scope.csPos);
217
+ }
218
+ if (vertexOut.Color) {
219
+ scope.zVertexColor = this.toVec4(scope, vertexOut.Color, 1);
220
+ if (scope.$outputs.zOutDiffuse) {
221
+ scope.$outputs.zOutDiffuse = scope.zVertexColor;
222
+ }
223
+ }
224
+ if (vertexOut.UV) {
225
+ scope.zVertexUV = this.toVec2(scope, vertexOut.UV);
226
+ if (this._vertexUsesUV) {
227
+ scope.$outputs.zVertexUV = scope.zVertexUV;
228
+ }
229
+ }
230
+ if (vertexOut.Normal) {
231
+ scope.$l.oNorm = this.toVec3(scope, vertexOut.Normal);
232
+ scope.$outputs.wNorm = pb.mul(ShaderHelper.getNormalMatrix(scope), pb.vec4(scope.oNorm, 0)).xyz;
233
+ scope.zVertexNormal = scope.$outputs.wNorm;
234
+ }
235
+ if (vertexOut.Tangent) {
236
+ const tangent = vertexOut.Tangent;
237
+ scope.$l.oTangent = tangent.getTypeName() === 'vec4' ? tangent : pb.vec4(this.toVec3(scope, tangent), 1);
238
+ scope.$outputs.wTangent = pb.mul(ShaderHelper.getNormalMatrix(scope), pb.vec4(scope.oTangent.xyz, 0)).xyz;
239
+ scope.$outputs.wBinormal = pb.mul(pb.cross(scope.$outputs.wNorm, scope.$outputs.wTangent), scope.oTangent.w);
240
+ scope.zVertexTangent = scope.$outputs.wTangent;
241
+ scope.zVertexBinormal = scope.$outputs.wBinormal;
242
+ }
340
243
  for (const outputName of [
341
244
  'Output1',
342
245
  'Output2',
343
246
  'Output3',
344
247
  'Output4'
345
248
  ]){
346
- const output = this.getOutput(outputs, outputName);
249
+ const output = outputs.find((output)=>output.name === outputName)?.exp;
347
250
  if (output) {
348
251
  scope.$outputs[outputName] = output;
349
252
  }
350
253
  }
351
254
  }
352
- /**
353
- * Builds the fragment shader for this PBR blueprint material.
354
- *
355
- * @param scope - The current fragment shader function scope.
356
- */ fragmentShader(scope) {
357
- super.fragmentShader(scope);
255
+ fragmentShader(scope) {
358
256
  const pb = scope.$builder;
359
- if (this.subsurfaceScattering && this.needFragmentColor() && this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
360
- scope.zSubsurfaceColor = pb.vec3().uniform(2);
361
- scope.zSubsurfaceScale = pb.float().uniform(2);
362
- scope.zSubsurfacePower = pb.float().uniform(2);
363
- scope.zSubsurfaceIntensity = pb.float().uniform(2);
364
- }
365
- if (this.needFragmentColor()) {
366
- scope.zAnisotropy = pb.float().uniform(2);
367
- scope.zAnisotropyDirection = pb.float().uniform(2);
368
- scope.zAnisotropyDirectionScaleBias = pb.vec2().uniform(2);
257
+ if (this.needFragmentColorInput()) {
369
258
  for (const u of [
370
259
  ...this._uniformValues,
371
260
  ...this._uniformTextures
372
261
  ]){
373
262
  if (u.inFragmentShader) {
374
- // @ts-ignore
263
+ // @ts-ignore dynamic shader type constructor
375
264
  pb.getGlobalScope()[u.name] = pb[u.type]().uniform(2);
376
265
  }
377
266
  }
378
- scope.$l.viewVec = this.calculateViewVector(scope, scope.$inputs.worldPos);
379
- scope.$l.commonData = this.getCommonDatasStruct(scope)();
380
- this.getCommonData(scope, scope.commonData, scope.viewVec, scope.$inputs.worldPos, scope.$inputs.zVertexNormal, scope.$inputs.zVertexTangent, scope.$inputs.zVertexBinormal, scope.$inputs.zVertexColor, scope.$inputs.zVertexUV, this._irFrag);
381
- if (this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
382
- if (this.drawContext.materialFlags & MaterialVaryingFlags.SSR_STORE_ROUGHNESS) {
383
- scope.$l.outRoughness = pb.vec4();
384
- scope.$l.litColor = this.PBRLight(scope, scope.$inputs.worldPos, scope.viewVec, scope.commonData, scope.outRoughness);
385
- if (this.subsurfaceScattering) {
386
- scope.litColor = this.applySubsurfaceScattering(scope, scope.litColor, scope.commonData.albedo.rgb, scope.commonData.normal, scope.viewVec);
387
- }
388
- this.outputFragmentColor(scope, scope.$inputs.worldPos, scope.litColor, scope.outRoughness, pb.vec4(pb.add(pb.mul(scope.commonData.normal, 0.5), pb.vec3(0.5)), 1));
389
- } else {
390
- scope.$l.litColor = this.PBRLight(scope, scope.$inputs.worldPos, scope.viewVec, scope.commonData);
391
- if (this.subsurfaceScattering) {
392
- scope.litColor = this.applySubsurfaceScattering(scope, scope.litColor, scope.commonData.albedo.rgb, scope.commonData.normal, scope.viewVec);
393
- }
394
- this.outputFragmentColor(scope, scope.$inputs.worldPos, scope.litColor);
267
+ scope.zVertexColor = scope.$inputs.zOutDiffuse ?? pb.vec4(1);
268
+ scope.zVertexUV = scope.$inputs.zVertexUV ?? pb.vec2(0);
269
+ scope.zVertexNormal = scope.$inputs.wNorm ?? pb.vec3(0, 0, 1);
270
+ scope.zVertexTangent = scope.$inputs.wTangent ?? pb.vec3(1, 0, 0);
271
+ scope.zVertexBinormal = scope.$inputs.wBinormal ?? pb.vec3(0, 1, 0);
272
+ scope.zWorldPos = scope.$inputs.worldPos;
273
+ }
274
+ super.fragmentShader(scope);
275
+ }
276
+ applyUniformValues(bindGroup, ctx, pass) {
277
+ super.applyUniformValues(bindGroup, ctx, pass);
278
+ if (this.needFragmentColorInput(ctx)) {
279
+ for (const u of this._uniformValues){
280
+ bindGroup.setValue(u.name, u.finalValue);
281
+ }
282
+ for (const u of this._uniformTextures){
283
+ const texture = u.finalTexture?.get();
284
+ if (texture) {
285
+ bindGroup.setTexture(u.name, texture, u.finalSampler);
395
286
  }
396
- } else {
397
- this.outputFragmentColor(scope, scope.$inputs.worldPos, scope.commonData.albedo);
398
287
  }
288
+ }
289
+ }
290
+ createProgram(ctx, pass) {
291
+ return super.createProgram(ctx, pass);
292
+ }
293
+ _createHash() {
294
+ return `${super._createHash()}:${this._irFrag.hash}:${this._irVertex.hash}:${this._connectedOutputHash()}`;
295
+ }
296
+ onDispose() {
297
+ super.onDispose();
298
+ for (const u of this._uniformTextures){
299
+ u.finalTexture?.dispose();
300
+ }
301
+ }
302
+ calculateAlbedoColor(scope, uv) {
303
+ const baseColor = this.getBlueprintOutput(scope, 'BaseColor');
304
+ const opacity = this.getBlueprintOutput(scope, 'Opacity');
305
+ if (baseColor !== undefined) {
306
+ const pb = scope.$builder;
307
+ return pb.vec4(this.toVec3(scope, baseColor), opacity ?? 1);
308
+ }
309
+ const albedo = super.calculateAlbedoColor(scope, uv);
310
+ if (opacity !== undefined) {
311
+ const pb = scope.$builder;
312
+ return pb.vec4(albedo.rgb, opacity);
313
+ }
314
+ return albedo;
315
+ }
316
+ calculateMetallic(scope, albedo, normal) {
317
+ const value = this.getBlueprintOutput(scope, 'Metallic');
318
+ if (value !== undefined) {
319
+ return this.toFloat(scope, value);
320
+ }
321
+ return super.calculateMetallic(scope, albedo, normal);
322
+ }
323
+ calculateRoughness(scope, albedo, normal) {
324
+ const value = this.getBlueprintOutput(scope, 'Roughness');
325
+ if (value !== undefined) {
326
+ return this.toFloat(scope, value);
327
+ }
328
+ return super.calculateRoughness(scope, albedo, normal);
329
+ }
330
+ calculateSpecularFactor(scope, albedo, normal) {
331
+ const pb = scope.$builder;
332
+ const base = super.calculateSpecularFactor(scope, albedo, normal);
333
+ const specular = this.getBlueprintOutput(scope, 'Specular');
334
+ const weight = this.getBlueprintOutput(scope, 'SpecularWeight');
335
+ if (specular !== undefined || weight !== undefined) {
336
+ return pb.vec4(specular !== undefined ? this.toVec3(scope, specular) : base.rgb, weight ?? base.a);
337
+ }
338
+ return base;
339
+ }
340
+ calculateEmissiveColor(scope) {
341
+ const emissive = this.getBlueprintOutput(scope, 'Emissive');
342
+ if (emissive !== undefined) {
343
+ return this.toVec3(scope, emissive);
344
+ }
345
+ return super.calculateEmissiveColor(scope);
346
+ }
347
+ calculateNormalAndTBN(scope, worldPos, worldNormal, worldTangent, worldBinormal) {
348
+ const outputNormal = this.getBlueprintOutput(scope, 'Normal');
349
+ const outputTangent = this.getBlueprintOutput(scope, 'Tangent');
350
+ if (outputNormal === undefined && outputTangent === undefined) {
351
+ return super.calculateNormalAndTBN(scope, worldPos, worldNormal, worldTangent, worldBinormal);
352
+ }
353
+ const pb = scope.$builder;
354
+ const NormalStruct = pb.defineStruct([
355
+ pb.mat3('TBN'),
356
+ pb.vec3('normal')
357
+ ]);
358
+ const funcName = 'Z_calculateBlueprintNormalAndTBN';
359
+ const that = this;
360
+ pb.func(funcName, [
361
+ pb.vec3('worldPos'),
362
+ pb.vec3('worldNormal'),
363
+ pb.vec3('worldTangent'),
364
+ pb.vec3('worldBinormal'),
365
+ pb.vec3('surfaceNormal'),
366
+ pb.vec3('surfaceTangent')
367
+ ], function() {
368
+ this.$l.TBN = that.calculateTBN(this, this.worldPos, this.worldNormal, this.worldTangent, this.worldBinormal);
369
+ this.$l.ng = this.TBN[2];
370
+ this.$l.t = this.TBN[0];
371
+ this.$l.b = this.TBN[1];
372
+ this.$if(pb.greaterThan(pb.length(this.surfaceTangent), 0.0001), function() {
373
+ this.$l.t_ = pb.normalize(this.surfaceTangent);
374
+ this.t = pb.normalize(pb.sub(this.t_, pb.mul(this.ng, pb.dot(this.ng, this.t_))));
375
+ this.b = pb.normalize(pb.cross(this.ng, this.t));
376
+ });
377
+ this.TBN = pb.mat3(this.t, this.b, this.ng);
378
+ this.$l.surfaceNormalTS = this.surfaceNormal;
379
+ this.$if(pb.lessThanEqual(pb.length(this.surfaceNormalTS), 0.0001), function() {
380
+ this.surfaceNormalTS = pb.vec3(0, 0, 1);
381
+ });
382
+ this.$l.surfaceNormalWS = pb.normalize(pb.mul(this.TBN, this.surfaceNormalTS));
383
+ this.$return(NormalStruct(this.TBN, this.surfaceNormalWS));
384
+ });
385
+ return pb.getGlobalScope()[funcName](worldPos, worldNormal ?? scope.zVertexNormal, worldTangent ?? scope.zVertexTangent, worldBinormal ?? scope.zVertexBinormal, outputNormal !== undefined ? this.toVec3(scope, outputNormal) : pb.vec3(0, 0, 1), outputTangent !== undefined ? this.toVec3(scope, outputTangent) : pb.vec3(0));
386
+ }
387
+ indirectLighting(scope, normal, viewVec, commonData, outColor, outRoughness, outDiffuseColor) {
388
+ const ao = this.getBlueprintOutput(scope, 'AO');
389
+ if (ao === undefined) {
390
+ super.indirectLighting(scope, normal, viewVec, commonData, outColor, outRoughness, outDiffuseColor);
391
+ return;
392
+ }
393
+ const pb = scope.$builder;
394
+ const funcName = `Z_applyBlueprintAO${outDiffuseColor ? '_D' : ''}`;
395
+ pb.func(funcName, [
396
+ pb.vec3('outColor').inout(),
397
+ ...outDiffuseColor ? [
398
+ pb.vec3('outDiffuseColor').inout()
399
+ ] : [],
400
+ pb.float('ao')
401
+ ], function() {
402
+ this.outColor = pb.mul(this.outColor, this.ao);
403
+ if (outDiffuseColor) {
404
+ this.outDiffuseColor = pb.mul(this.outDiffuseColor, this.ao);
405
+ }
406
+ });
407
+ super.indirectLighting(scope, normal, viewVec, commonData, outColor, outRoughness, outDiffuseColor);
408
+ if (outDiffuseColor) {
409
+ pb.getGlobalScope()[funcName](outColor, outDiffuseColor, ao);
399
410
  } else {
400
- this.outputFragmentColor(scope, scope.$inputs.worldPos, null);
401
- }
402
- }
403
- /**
404
- * Applies runtime uniform values and textures to the given bind group.
405
- *
406
- * @remarks
407
- * - Calls the base implementation first to bind standard mesh/PBR uniforms.
408
- * - If fragment color is needed for the current context, all blueprint
409
- * scalar/vector uniform values and textures are then bound by name.
410
- *
411
- * @param bindGroup - The bind group to which material resources are bound.
412
- * @param ctx - The current draw context.
413
- * @param pass - Index of the active render pass.
414
- */ applyUniformValues(bindGroup, ctx, pass) {
415
- super.applyUniformValues(bindGroup, ctx, pass);
416
- if (this.needFragmentColor(ctx)) {
417
- bindGroup.setValue('zReflectionMode', PBR_REFLECTION_MODE[this._reflectionMode]);
418
- bindGroup.setValue('zAnisotropy', this._anisotropy);
419
- bindGroup.setValue('zAnisotropyDirection', this._anisotropyDirection);
420
- bindGroup.setValue('zAnisotropyDirectionScaleBias', this._anisotropyDirectionScaleBias);
421
- if (this.subsurfaceScattering && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
422
- bindGroup.setValue('zSubsurfaceColor', this._subsurfaceColor);
423
- bindGroup.setValue('zSubsurfaceScale', this._subsurfaceScale);
424
- bindGroup.setValue('zSubsurfacePower', this._subsurfacePower);
425
- bindGroup.setValue('zSubsurfaceIntensity', this._subsurfaceIntensity);
411
+ pb.getGlobalScope()[funcName](outColor, ao);
412
+ }
413
+ }
414
+ syncBlueprintMetadata() {
415
+ this._connectedOutputs = this.collectConnectedOutputs(this._irFrag, PBRBlockNode);
416
+ const fragmentUsesColor = this._irFrag.behaviors.useVertexColor;
417
+ const fragmentUsesUV = this._irFrag.behaviors.useVertexUV;
418
+ const vertexUsesColor = this._irVertex.behaviors.useVertexColor;
419
+ const vertexUsesUV = this._irVertex.behaviors.useVertexUV;
420
+ this._vertexUsesColor = fragmentUsesColor || vertexUsesColor;
421
+ this._vertexUsesUV = fragmentUsesUV || vertexUsesUV;
422
+ this.useFeature(PBRBluePrintMaterial.FEATURE_BLUEPRINT_VERTEX_COLOR, this._vertexUsesColor);
423
+ this.useFeature(PBRBluePrintMaterial.FEATURE_BLUEPRINT_VERTEX_UV, this._vertexUsesUV);
424
+ }
425
+ collectConnectedOutputs(ir, rootCtor) {
426
+ const outputs = new Set();
427
+ for (const rootId of ir.DAG.roots){
428
+ const rootNode = ir.DAG.nodeMap[rootId];
429
+ if (!(rootNode instanceof rootCtor)) {
430
+ continue;
431
+ }
432
+ for (const input of rootNode.inputs){
433
+ if (input.inputNode) {
434
+ outputs.add(input.name);
435
+ }
426
436
  }
427
437
  }
428
- for (const u of this._uniformValues){
429
- if (u.inVertexShader || this.needFragmentColor(ctx)) {
430
- bindGroup.setValue(u.name, u.finalValue);
438
+ return outputs;
439
+ }
440
+ getBlueprintVertexColor(scope) {
441
+ const pb = scope.$builder;
442
+ if (this.vertexColor) {
443
+ return this.getVertexColor(scope);
444
+ }
445
+ if (this._vertexUsesColor) {
446
+ if (scope.$inputs.zDiffuse) {
447
+ return scope.$inputs.zDiffuse;
448
+ }
449
+ if (scope.$getVertexAttrib('diffuse')) {
450
+ return scope.$getVertexAttrib('diffuse');
431
451
  }
452
+ scope.$inputs.zDiffuse = pb.vec4().attrib('diffuse');
453
+ return scope.$inputs.zDiffuse;
432
454
  }
433
- for (const u of this._uniformTextures){
434
- if (u.inVertexShader || this.needFragmentColor(ctx)) {
435
- bindGroup.setTexture(u.name, u.finalTexture.get(), u.finalSampler);
455
+ return pb.vec4(1);
456
+ }
457
+ getBlueprintVertexUV(scope) {
458
+ const pb = scope.$builder;
459
+ if (this._vertexUsesUV) {
460
+ if (scope.$inputs.zVertexUV) {
461
+ return scope.$inputs.zVertexUV;
436
462
  }
463
+ if (scope.$getVertexAttrib('texCoord0')) {
464
+ return scope.$getVertexAttrib('texCoord0');
465
+ }
466
+ scope.$inputs.zVertexUV = pb.vec2().attrib('texCoord0');
467
+ return scope.$inputs.zVertexUV;
468
+ }
469
+ if (this.albedoTexture) {
470
+ return this.getAlbedoTexCoord(scope) ?? pb.vec2(0);
437
471
  }
472
+ return pb.vec2(0);
438
473
  }
439
- /**
440
- * Creates a unique hash string used for program caching.
441
- *
442
- * @remarks
443
- * The hash includes:
444
- * - The base material hash (`super._createHash()`).
445
- * - The fragment IR hash.
446
- * - The vertex IR hash.
447
- *
448
- * Different blueprint graphs will therefore produce different programs.
449
- *
450
- * @returns A hash string that uniquely identifies this material configuration.
451
- */ _createHash() {
452
- return `${super._createHash()}:${this._irFrag.hash}:${this._irVertex.hash}`;
453
- }
454
- /**
455
- * Creates the GPU program for this blueprint PBR material.
456
- *
457
- * @remarks
458
- * This calls the base implementation and returns its result.
459
- * Commented-out `console.log` lines are provided for debugging the
460
- * generated vertex and fragment shader sources.
461
- *
462
- * @param ctx - The current draw context.
463
- * @param pass - Index of the active render pass.
464
- * @returns The created GPU program.
465
- */ createProgram(ctx, pass) {
466
- const program = super.createProgram(ctx, pass);
467
- //console.log(program.getShaderSource('vertex'));
468
- //console.log(program.getShaderSource('fragment'));
469
- return program;
470
- }
471
- /**
472
- * Disposes resources associated with this material.
473
- *
474
- * @remarks
475
- * - Calls the base `onDispose` to clean up inherited resources.
476
- * - Disposes all `finalTexture` references from the blueprint
477
- * texture uniform descriptors.
478
- *
479
- * This method is intended to be called by the engine's resource
480
- * management system rather than directly by user code.
481
- */ onDispose() {
482
- super.onDispose();
483
- for (const u of this._uniformTextures){
484
- u.finalTexture.dispose();
474
+ getBlueprintOutput(scope, name) {
475
+ if (!this._connectedOutputs.has(name)) {
476
+ return undefined;
485
477
  }
478
+ return this.getBlueprintOutputMap(scope)[name];
486
479
  }
487
- /**
488
- * Retrieves a named output expression from a blueprint output list.
489
- *
490
- * @param outputs - The list of outputs generated by a blueprint graph.
491
- * @param name - The desired output name (e.g. `"Position"`, `"Color"`, `"UV"`).
492
- * @returns The expression associated with the given name, or `undefined` if not found.
493
- */ getOutput(outputs, name) {
494
- return outputs.find((output)=>output.name === name)?.exp;
480
+ getBlueprintOutputMap(scope) {
481
+ const pb = scope.$builder;
482
+ const funcName = 'Z_GetPBRBlueprintOutputs';
483
+ const that = this;
484
+ const outputsStruct = pb.defineStruct(DEFAULT_OUTPUT_NAMES.map((name)=>{
485
+ switch(name){
486
+ case 'BaseColor':
487
+ return pb.vec3(name);
488
+ case 'Specular':
489
+ case 'Emissive':
490
+ case 'Normal':
491
+ case 'Tangent':
492
+ return pb.vec3(name);
493
+ default:
494
+ return pb.float(name);
495
+ }
496
+ }));
497
+ if (!pb.getGlobalScope()[funcName]) {
498
+ pb.func(funcName, [
499
+ pb.vec3('worldPos'),
500
+ pb.vec4('vertexColor'),
501
+ pb.vec2('vertexUV'),
502
+ pb.vec3('vertexNormal'),
503
+ pb.vec3('vertexTangent'),
504
+ pb.vec3('vertexBinormal')
505
+ ], function() {
506
+ this.zWorldPos = this.worldPos;
507
+ this.zVertexColor = this.vertexColor;
508
+ this.zVertexUV = this.vertexUV;
509
+ this.zVertexNormal = this.vertexNormal;
510
+ this.zVertexTangent = this.vertexTangent;
511
+ this.zVertexBinormal = this.vertexBinormal;
512
+ const outputs = that._irFrag.create(pb);
513
+ let baseColor = pb.vec3(1);
514
+ let metallic = 0;
515
+ let roughness = 1;
516
+ let specular = pb.vec3(1);
517
+ let emissive = pb.vec3(0);
518
+ let normal = pb.vec3(0);
519
+ let tangent = pb.vec3(0);
520
+ let opacity = 1;
521
+ let specularWeight = 1;
522
+ let ao = 1;
523
+ if (outputs) {
524
+ const map = that.toOutputMap(outputs);
525
+ if (map.BaseColor !== undefined) {
526
+ baseColor = that.toVec3(scope, map.BaseColor);
527
+ }
528
+ if (map.Metallic !== undefined) {
529
+ metallic = map.Metallic;
530
+ }
531
+ if (map.Roughness !== undefined) {
532
+ roughness = map.Roughness;
533
+ }
534
+ if (map.Specular !== undefined) {
535
+ specular = that.toVec3(scope, map.Specular);
536
+ }
537
+ if (map.Emissive !== undefined) {
538
+ emissive = that.toVec3(scope, map.Emissive);
539
+ }
540
+ if (map.Normal !== undefined) {
541
+ normal = that.toVec3(scope, map.Normal);
542
+ }
543
+ if (map.Tangent !== undefined) {
544
+ tangent = that.toVec3(scope, map.Tangent);
545
+ }
546
+ if (map.Opacity !== undefined) {
547
+ opacity = map.Opacity;
548
+ }
549
+ if (map.SpecularWeight !== undefined) {
550
+ specularWeight = map.SpecularWeight;
551
+ }
552
+ if (map.AO !== undefined) {
553
+ ao = map.AO;
554
+ }
555
+ }
556
+ this.$return(outputsStruct(baseColor, metallic, roughness, specular, emissive, normal, tangent, opacity, specularWeight, ao));
557
+ });
558
+ }
559
+ const result = pb.getGlobalScope()[funcName](this.getBlueprintWorldPos(scope), this.getBlueprintFragmentVertexColor(scope), this.getBlueprintFragmentVertexUV(scope), this.getBlueprintFragmentVertexNormal(scope), this.getBlueprintFragmentVertexTangent(scope), this.getBlueprintFragmentVertexBinormal(scope));
560
+ const map = {};
561
+ for (const name of DEFAULT_OUTPUT_NAMES){
562
+ map[name] = result[name];
563
+ }
564
+ return map;
565
+ }
566
+ getBlueprintWorldPos(scope) {
567
+ const pb = scope.$builder;
568
+ return scope.$inputs.worldPos ?? scope.zWorldPos ?? pb.vec3(0);
569
+ }
570
+ getBlueprintFragmentVertexColor(scope) {
571
+ const pb = scope.$builder;
572
+ return scope.$inputs.zOutDiffuse ?? scope.zVertexColor ?? pb.vec4(1);
573
+ }
574
+ getBlueprintFragmentVertexUV(scope) {
575
+ const pb = scope.$builder;
576
+ return scope.$inputs.zVertexUV ?? scope.zVertexUV ?? pb.vec2(0);
577
+ }
578
+ getBlueprintFragmentVertexNormal(scope) {
579
+ const pb = scope.$builder;
580
+ return scope.$inputs.wNorm ?? scope.zVertexNormal ?? pb.vec3(0, 0, 1);
581
+ }
582
+ getBlueprintFragmentVertexTangent(scope) {
583
+ const pb = scope.$builder;
584
+ return scope.$inputs.wTangent ?? scope.zVertexTangent ?? pb.vec3(1, 0, 0);
585
+ }
586
+ getBlueprintFragmentVertexBinormal(scope) {
587
+ const pb = scope.$builder;
588
+ return scope.$inputs.wBinormal ?? scope.zVertexBinormal ?? pb.vec3(0, 1, 0);
589
+ }
590
+ toOutputMap(outputs) {
591
+ const map = {};
592
+ for (const output of outputs){
593
+ map[output.name] = output.exp;
594
+ }
595
+ return map;
596
+ }
597
+ toVertexOutputMap(outputs) {
598
+ const map = {};
599
+ for (const output of outputs){
600
+ map[output.name] = output.exp;
601
+ }
602
+ return map;
603
+ }
604
+ toFloat(scope, value) {
605
+ const pb = scope.$builder;
606
+ if (typeof value === 'number') {
607
+ return pb.float(value);
608
+ }
609
+ const exp = value;
610
+ return exp.getTypeName() === 'float' ? exp : exp.x;
611
+ }
612
+ toVec2(scope, value) {
613
+ const pb = scope.$builder;
614
+ if (typeof value === 'number') {
615
+ return pb.vec2(value);
616
+ }
617
+ const exp = value;
618
+ const type = exp.getTypeName();
619
+ if (type === 'float') {
620
+ return pb.vec2(exp);
621
+ }
622
+ if (type === 'vec3' || type === 'vec4') {
623
+ return exp.xy;
624
+ }
625
+ return exp;
626
+ }
627
+ toVec3(scope, value) {
628
+ const pb = scope.$builder;
629
+ if (typeof value === 'number') {
630
+ return pb.vec3(value);
631
+ }
632
+ const exp = value;
633
+ const type = exp.getTypeName();
634
+ if (type === 'float') {
635
+ return pb.vec3(exp);
636
+ }
637
+ if (type === 'vec2') {
638
+ return pb.vec3(exp, 0);
639
+ }
640
+ if (type === 'vec4') {
641
+ return exp.xyz;
642
+ }
643
+ return exp;
495
644
  }
496
- applySubsurfaceScattering(scope, litColor, albedoRGB, normal, viewVec) {
645
+ toVec4(scope, value, alpha) {
497
646
  const pb = scope.$builder;
498
- scope.$l.NoV = pb.clamp(pb.dot(normal, viewVec), 0, 1);
499
- scope.$l.wrapNdotV = pb.clamp(pb.div(pb.add(pb.sub(1, scope.NoV), scope.zSubsurfaceScale), pb.add(1, scope.zSubsurfaceScale)), 0, 1);
500
- scope.$l.sssFactor = pb.mul(pb.pow(scope.wrapNdotV, scope.zSubsurfacePower), scope.zSubsurfaceIntensity);
501
- return pb.vec4(pb.add(litColor.rgb, pb.mul(albedoRGB, scope.zSubsurfaceColor, scope.sssFactor)), litColor.a);
647
+ if (typeof value === 'number') {
648
+ return pb.vec4(value, value, value, alpha);
649
+ }
650
+ const exp = value;
651
+ const type = exp.getTypeName();
652
+ if (type === 'float') {
653
+ return pb.vec4(pb.vec3(exp), alpha);
654
+ }
655
+ if (type === 'vec2') {
656
+ return pb.vec4(exp, 0, alpha);
657
+ }
658
+ if (type === 'vec3') {
659
+ return pb.vec4(exp, alpha);
660
+ }
661
+ return exp;
662
+ }
663
+ _connectedOutputHash() {
664
+ return DEFAULT_OUTPUT_NAMES.map((name)=>this._connectedOutputs.has(name) ? '1' : '0').join('');
502
665
  }
503
666
  }
504
667