@luma.gl/engine 9.2.6 → 9.3.0-alpha.11

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 (198) hide show
  1. package/dist/animation-loop/animation-loop.d.ts +11 -5
  2. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  3. package/dist/animation-loop/animation-loop.js +83 -47
  4. package/dist/animation-loop/animation-loop.js.map +1 -1
  5. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
  6. package/dist/animation-loop/make-animation-loop.js +8 -1
  7. package/dist/animation-loop/make-animation-loop.js.map +1 -1
  8. package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
  9. package/dist/animation-loop/request-animation-frame.js +23 -6
  10. package/dist/animation-loop/request-animation-frame.js.map +1 -1
  11. package/dist/compute/computation.d.ts +3 -7
  12. package/dist/compute/computation.d.ts.map +1 -1
  13. package/dist/compute/computation.js +16 -13
  14. package/dist/compute/computation.js.map +1 -1
  15. package/dist/compute/swap.d.ts +2 -0
  16. package/dist/compute/swap.d.ts.map +1 -1
  17. package/dist/compute/swap.js +10 -5
  18. package/dist/compute/swap.js.map +1 -1
  19. package/dist/debug/debug-framebuffer.d.ts +9 -4
  20. package/dist/debug/debug-framebuffer.d.ts.map +1 -1
  21. package/dist/debug/debug-framebuffer.js +91 -45
  22. package/dist/debug/debug-framebuffer.js.map +1 -1
  23. package/dist/dist.dev.js +2767 -1344
  24. package/dist/dist.min.js +326 -211
  25. package/dist/dynamic-texture/dynamic-texture.d.ts +102 -0
  26. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
  27. package/dist/dynamic-texture/dynamic-texture.js +558 -0
  28. package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
  29. package/dist/dynamic-texture/texture-data.d.ts +144 -0
  30. package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
  31. package/dist/dynamic-texture/texture-data.js +208 -0
  32. package/dist/dynamic-texture/texture-data.js.map +1 -0
  33. package/dist/geometries/cone-geometry.d.ts +3 -1
  34. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  35. package/dist/geometries/cone-geometry.js.map +1 -1
  36. package/dist/geometries/cube-geometry.js +7 -7
  37. package/dist/geometries/cube-geometry.js.map +1 -1
  38. package/dist/geometries/cylinder-geometry.d.ts +2 -1
  39. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  40. package/dist/geometries/cylinder-geometry.js.map +1 -1
  41. package/dist/geometries/ico-sphere-geometry.js +3 -1
  42. package/dist/geometries/ico-sphere-geometry.js.map +1 -1
  43. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  44. package/dist/geometry/gpu-geometry.js +11 -3
  45. package/dist/geometry/gpu-geometry.js.map +1 -1
  46. package/dist/index.cjs +2620 -1267
  47. package/dist/index.cjs.map +4 -4
  48. package/dist/index.d.ts +20 -6
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +12 -4
  51. package/dist/index.js.map +1 -1
  52. package/dist/material/material-factory.d.ts +73 -0
  53. package/dist/material/material-factory.d.ts.map +1 -0
  54. package/dist/material/material-factory.js +111 -0
  55. package/dist/material/material-factory.js.map +1 -0
  56. package/dist/material/material.d.ts +84 -0
  57. package/dist/material/material.d.ts.map +1 -0
  58. package/dist/material/material.js +176 -0
  59. package/dist/material/material.js.map +1 -0
  60. package/dist/model/model.d.ts +47 -16
  61. package/dist/model/model.d.ts.map +1 -1
  62. package/dist/model/model.js +148 -71
  63. package/dist/model/model.js.map +1 -1
  64. package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
  65. package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
  66. package/dist/model/split-uniforms-and-bindings.js +2 -2
  67. package/dist/model/split-uniforms-and-bindings.js.map +1 -1
  68. package/dist/models/billboard-texture-model.d.ts +8 -5
  69. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  70. package/dist/models/billboard-texture-model.js +79 -25
  71. package/dist/models/billboard-texture-model.js.map +1 -1
  72. package/dist/models/billboard-texture-module.d.ts +1 -1
  73. package/dist/models/billboard-texture-module.js +1 -1
  74. package/dist/models/clip-space.js +7 -7
  75. package/dist/models/directional-light-model.d.ts +7 -0
  76. package/dist/models/directional-light-model.d.ts.map +1 -0
  77. package/dist/models/directional-light-model.js +23 -0
  78. package/dist/models/directional-light-model.js.map +1 -0
  79. package/dist/models/light-model-utils.d.ts +69 -0
  80. package/dist/models/light-model-utils.d.ts.map +1 -0
  81. package/dist/models/light-model-utils.js +395 -0
  82. package/dist/models/light-model-utils.js.map +1 -0
  83. package/dist/models/point-light-model.d.ts +7 -0
  84. package/dist/models/point-light-model.d.ts.map +1 -0
  85. package/dist/models/point-light-model.js +22 -0
  86. package/dist/models/point-light-model.js.map +1 -0
  87. package/dist/models/spot-light-model.d.ts +7 -0
  88. package/dist/models/spot-light-model.d.ts.map +1 -0
  89. package/dist/models/spot-light-model.js +23 -0
  90. package/dist/models/spot-light-model.js.map +1 -0
  91. package/dist/modules/picking/color-picking.d.ts +5 -9
  92. package/dist/modules/picking/color-picking.d.ts.map +1 -1
  93. package/dist/modules/picking/color-picking.js +122 -115
  94. package/dist/modules/picking/color-picking.js.map +1 -1
  95. package/dist/modules/picking/index-picking.d.ts +4 -4
  96. package/dist/modules/picking/index-picking.d.ts.map +1 -1
  97. package/dist/modules/picking/index-picking.js +36 -16
  98. package/dist/modules/picking/index-picking.js.map +1 -1
  99. package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
  100. package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
  101. package/dist/modules/picking/legacy-color-picking.js +7 -0
  102. package/dist/modules/picking/legacy-color-picking.js.map +1 -0
  103. package/dist/modules/picking/picking-manager.d.ts +29 -3
  104. package/dist/modules/picking/picking-manager.d.ts.map +1 -1
  105. package/dist/modules/picking/picking-manager.js +188 -41
  106. package/dist/modules/picking/picking-manager.js.map +1 -1
  107. package/dist/modules/picking/picking-uniforms.d.ts +13 -12
  108. package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
  109. package/dist/modules/picking/picking-uniforms.js +27 -14
  110. package/dist/modules/picking/picking-uniforms.js.map +1 -1
  111. package/dist/modules/picking/picking.d.ts +25 -0
  112. package/dist/modules/picking/picking.d.ts.map +1 -0
  113. package/dist/modules/picking/picking.js +18 -0
  114. package/dist/modules/picking/picking.js.map +1 -0
  115. package/dist/passes/get-fragment-shader.js +12 -27
  116. package/dist/passes/get-fragment-shader.js.map +1 -1
  117. package/dist/passes/shader-pass-renderer.d.ts +5 -7
  118. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  119. package/dist/passes/shader-pass-renderer.js +16 -42
  120. package/dist/passes/shader-pass-renderer.js.map +1 -1
  121. package/dist/scenegraph/group-node.d.ts +5 -0
  122. package/dist/scenegraph/group-node.d.ts.map +1 -1
  123. package/dist/scenegraph/group-node.js +12 -0
  124. package/dist/scenegraph/group-node.js.map +1 -1
  125. package/dist/scenegraph/model-node.d.ts +2 -2
  126. package/dist/scenegraph/model-node.d.ts.map +1 -1
  127. package/dist/scenegraph/model-node.js.map +1 -1
  128. package/dist/scenegraph/scenegraph-node.d.ts +1 -1
  129. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  130. package/dist/scenegraph/scenegraph-node.js +23 -15
  131. package/dist/scenegraph/scenegraph-node.js.map +1 -1
  132. package/dist/shader-inputs.d.ts +9 -7
  133. package/dist/shader-inputs.d.ts.map +1 -1
  134. package/dist/shader-inputs.js +90 -13
  135. package/dist/shader-inputs.js.map +1 -1
  136. package/dist/utils/buffer-layout-order.d.ts.map +1 -1
  137. package/dist/utils/buffer-layout-order.js +12 -2
  138. package/dist/utils/buffer-layout-order.js.map +1 -1
  139. package/dist/utils/shader-module-utils.d.ts +7 -0
  140. package/dist/utils/shader-module-utils.d.ts.map +1 -0
  141. package/dist/utils/shader-module-utils.js +46 -0
  142. package/dist/utils/shader-module-utils.js.map +1 -0
  143. package/package.json +6 -6
  144. package/src/animation-loop/animation-loop.ts +89 -50
  145. package/src/animation-loop/make-animation-loop.ts +14 -5
  146. package/src/animation-loop/request-animation-frame.ts +32 -6
  147. package/src/compute/computation.ts +32 -17
  148. package/src/compute/swap.ts +13 -7
  149. package/src/debug/debug-framebuffer.ts +139 -61
  150. package/src/dynamic-texture/dynamic-texture.ts +730 -0
  151. package/src/dynamic-texture/texture-data.ts +336 -0
  152. package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +1 -1
  153. package/src/geometries/cone-geometry.ts +6 -1
  154. package/src/geometries/cube-geometry.ts +7 -7
  155. package/src/geometries/cylinder-geometry.ts +5 -1
  156. package/src/geometries/ico-sphere-geometry.ts +3 -1
  157. package/src/geometry/gpu-geometry.ts +11 -3
  158. package/src/index.ts +38 -8
  159. package/src/material/material-factory.ts +157 -0
  160. package/src/material/material.ts +254 -0
  161. package/src/model/model.ts +196 -93
  162. package/src/model/split-uniforms-and-bindings.ts +8 -6
  163. package/src/models/billboard-texture-model.ts +90 -29
  164. package/src/models/billboard-texture-module.ts +1 -1
  165. package/src/models/clip-space.ts +7 -7
  166. package/src/models/directional-light-model.ts +32 -0
  167. package/src/models/light-model-utils.ts +587 -0
  168. package/src/models/point-light-model.ts +31 -0
  169. package/src/models/spot-light-model.ts +32 -0
  170. package/src/modules/picking/color-picking.ts +123 -122
  171. package/src/modules/picking/index-picking.ts +36 -16
  172. package/src/modules/picking/legacy-color-picking.ts +8 -0
  173. package/src/modules/picking/picking-manager.ts +252 -50
  174. package/src/modules/picking/picking-uniforms.ts +39 -24
  175. package/src/modules/picking/picking.ts +22 -0
  176. package/src/passes/get-fragment-shader.ts +12 -27
  177. package/src/passes/shader-pass-renderer.ts +25 -48
  178. package/src/scenegraph/group-node.ts +16 -0
  179. package/src/scenegraph/model-node.ts +2 -2
  180. package/src/scenegraph/scenegraph-node.ts +27 -16
  181. package/src/shader-inputs.ts +167 -26
  182. package/src/utils/buffer-layout-order.ts +18 -2
  183. package/src/utils/shader-module-utils.ts +65 -0
  184. package/dist/async-texture/async-texture.d.ts +0 -166
  185. package/dist/async-texture/async-texture.d.ts.map +0 -1
  186. package/dist/async-texture/async-texture.js +0 -386
  187. package/dist/async-texture/async-texture.js.map +0 -1
  188. package/dist/factories/pipeline-factory.d.ts +0 -37
  189. package/dist/factories/pipeline-factory.d.ts.map +0 -1
  190. package/dist/factories/pipeline-factory.js +0 -181
  191. package/dist/factories/pipeline-factory.js.map +0 -1
  192. package/dist/factories/shader-factory.d.ts +0 -22
  193. package/dist/factories/shader-factory.d.ts.map +0 -1
  194. package/dist/factories/shader-factory.js +0 -88
  195. package/dist/factories/shader-factory.js.map +0 -1
  196. package/src/async-texture/async-texture.ts +0 -551
  197. package/src/factories/pipeline-factory.ts +0 -224
  198. package/src/factories/shader-factory.ts +0 -103
@@ -0,0 +1,157 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {Binding, BindingsByGroup, Device} from '@luma.gl/core';
6
+ import type {ShaderModule} from '@luma.gl/shadertools';
7
+ import {ShaderInputs} from '../shader-inputs';
8
+ import type {Material, MaterialProps} from './material';
9
+ import {Material as MaterialClass} from './material';
10
+
11
+ type MaterialModuleProps = Partial<Record<string, Record<string, unknown>>>;
12
+ type MaterialBindings = Record<
13
+ string,
14
+ Binding | import('../dynamic-texture/dynamic-texture').DynamicTexture
15
+ >;
16
+
17
+ /** Logical bind-group slot reserved for material-owned bindings. */
18
+ export const MATERIAL_BIND_GROUP = 3;
19
+
20
+ /** Construction props for {@link MaterialFactory}. */
21
+ export type MaterialFactoryProps = {
22
+ /** Shader modules that define the material schema for bind group `3`. */
23
+ modules?: ShaderModule[];
24
+ };
25
+
26
+ /**
27
+ * Creates typed {@link Material} instances for a stable material binding schema.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const pbrFactory = new MaterialFactory<
32
+ * {pbrMaterial: PBRMaterialUniforms},
33
+ * PBRMaterialBindings
34
+ * >(device, {modules: [pbrMaterial]});
35
+ * const pbr = pbrFactory.createMaterial();
36
+ * pbr.setProps({pbrMaterial: {baseColorFactor: [1, 0, 0, 1]}});
37
+ * const pbrVariant = pbr.clone({bindings: {pbr_baseColorSampler: texture}});
38
+ * ```
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const phongFactory = new MaterialFactory<
43
+ * {phongMaterial: PhongMaterialProps},
44
+ * {}
45
+ * >(device, {modules: [phongMaterial]});
46
+ * const phong = phongFactory.createMaterial();
47
+ * phong.setProps({phongMaterial: {ambient: 0.4, diffuse: 0.7}});
48
+ * const phongVariant = phong.clone({moduleProps: {phongMaterial: {shininess: 64}}});
49
+ * ```
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const gouraudFactory = new MaterialFactory<
54
+ * {gouraudMaterial: GouraudMaterialProps},
55
+ * {}
56
+ * >(device, {modules: [gouraudMaterial]});
57
+ * const gouraud = gouraudFactory.createMaterial();
58
+ * gouraud.setProps({gouraudMaterial: {ambient: 0.25}});
59
+ * const gouraudVariant = gouraud.clone({moduleProps: {gouraudMaterial: {diffuse: 0.8}}});
60
+ * ```
61
+ */
62
+ export class MaterialFactory<
63
+ TModuleProps extends MaterialModuleProps = MaterialModuleProps,
64
+ TBindings extends MaterialBindings = MaterialBindings
65
+ > {
66
+ /** Device that creates materials for this schema. */
67
+ readonly device: Device;
68
+ /** Shader modules that define the material schema. */
69
+ readonly modules: ShaderModule[];
70
+
71
+ private _materialBindingNames: Set<string>;
72
+ private _materialModuleNames: Set<string>;
73
+
74
+ constructor(device: Device, props: MaterialFactoryProps = {}) {
75
+ this.device = device;
76
+ this.modules = props.modules || [];
77
+
78
+ const shaderInputs = new ShaderInputs(
79
+ Object.fromEntries(this.modules.map(module => [module.name, module]))
80
+ );
81
+ this._materialBindingNames = getMaterialBindingNames(shaderInputs);
82
+ this._materialModuleNames = getMaterialModuleNames(shaderInputs);
83
+ }
84
+
85
+ /** Creates one typed material instance for this factory's schema. */
86
+ createMaterial(
87
+ props: Omit<MaterialProps<TModuleProps, TBindings>, 'factory' | 'modules'> = {}
88
+ ): Material<TModuleProps, TBindings> {
89
+ return new MaterialClass<TModuleProps, TBindings>(this.device, {
90
+ ...props,
91
+ factory: this
92
+ });
93
+ }
94
+
95
+ /** Returns the logical material-owned resource binding names. */
96
+ getBindingNames(): string[] {
97
+ return Array.from(this._materialBindingNames);
98
+ }
99
+
100
+ /** Returns `true` when the supplied binding belongs to this material schema. */
101
+ ownsBinding(bindingName: string): boolean {
102
+ if (this._materialBindingNames.has(bindingName)) {
103
+ return true;
104
+ }
105
+
106
+ const aliasedModuleName = getModuleNameFromUniformBinding(bindingName);
107
+ return aliasedModuleName ? this._materialModuleNames.has(aliasedModuleName) : false;
108
+ }
109
+
110
+ /** Returns `true` when the supplied shader module is owned by this material schema. */
111
+ ownsModule(moduleName: string): boolean {
112
+ return this._materialModuleNames.has(moduleName);
113
+ }
114
+
115
+ /** Packages resolved material bindings into bind group `3`. */
116
+ getBindingsByGroup(
117
+ bindings: Partial<{[K in keyof TBindings]: Binding}> & Record<string, Binding>
118
+ ): BindingsByGroup {
119
+ return Object.keys(bindings).length > 0 ? {[MATERIAL_BIND_GROUP]: bindings} : {};
120
+ }
121
+ }
122
+
123
+ /** Returns the module name corresponding to an auto-generated `*Uniforms` binding. */
124
+ export function getModuleNameFromUniformBinding(bindingName: string): string | null {
125
+ return bindingName.endsWith('Uniforms') ? bindingName.slice(0, -'Uniforms'.length) : null;
126
+ }
127
+
128
+ function getMaterialBindingNames(shaderInputs: ShaderInputs): Set<string> {
129
+ const bindingNames = new Set<string>();
130
+
131
+ for (const module of Object.values(shaderInputs.modules)) {
132
+ for (const binding of module.bindingLayout || []) {
133
+ if (binding.group === MATERIAL_BIND_GROUP) {
134
+ bindingNames.add(binding.name);
135
+ }
136
+ }
137
+ }
138
+
139
+ return bindingNames;
140
+ }
141
+
142
+ function getMaterialModuleNames(shaderInputs: ShaderInputs): Set<string> {
143
+ const moduleNames = new Set<string>();
144
+
145
+ for (const module of Object.values(shaderInputs.modules)) {
146
+ if (
147
+ module.name &&
148
+ module.bindingLayout?.some(
149
+ binding => binding.group === MATERIAL_BIND_GROUP && binding.name === module.name
150
+ )
151
+ ) {
152
+ moduleNames.add(module.name);
153
+ }
154
+ }
155
+
156
+ return moduleNames;
157
+ }
@@ -0,0 +1,254 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {Binding, BindingsByGroup, Device} from '@luma.gl/core';
6
+ import {Buffer, Sampler, Texture, TextureView, UniformStore} from '@luma.gl/core';
7
+ import type {ShaderModule} from '@luma.gl/shadertools';
8
+ import {DynamicTexture} from '../dynamic-texture/dynamic-texture';
9
+ import {ShaderInputs} from '../shader-inputs';
10
+ import {shaderModuleHasUniforms} from '../utils/shader-module-utils';
11
+ import {uid} from '../utils/uid';
12
+ import {
13
+ getModuleNameFromUniformBinding,
14
+ MATERIAL_BIND_GROUP,
15
+ MaterialFactory
16
+ } from './material-factory';
17
+
18
+ type MaterialModuleProps = Partial<Record<string, Record<string, unknown>>>;
19
+ type MaterialBindings = Record<string, Binding | DynamicTexture>;
20
+ type MaterialPropsUpdate<TModuleProps extends MaterialModuleProps> = Partial<{
21
+ [P in keyof TModuleProps]?: Partial<TModuleProps[P]>;
22
+ }>;
23
+
24
+ /** Construction props for one typed {@link Material}. */
25
+ export type MaterialProps<
26
+ TModuleProps extends MaterialModuleProps = MaterialModuleProps,
27
+ TBindings extends MaterialBindings = MaterialBindings
28
+ > = {
29
+ /** Optional application-provided identifier. */
30
+ id?: string;
31
+ /** Factory that owns the material schema. */
32
+ factory?: MaterialFactory<TModuleProps, TBindings>;
33
+ /** Optional pre-created shader inputs for the material modules. */
34
+ shaderInputs?: ShaderInputs<TModuleProps>;
35
+ /** Shader modules used when a factory is not supplied. */
36
+ modules?: ShaderModule[];
37
+ /** Initial material-owned resource bindings. */
38
+ bindings?: Partial<TBindings>;
39
+ };
40
+
41
+ /** Structural overrides applied when cloning a {@link Material}. */
42
+ export type MaterialCloneProps<
43
+ TModuleProps extends MaterialModuleProps = MaterialModuleProps,
44
+ TBindings extends MaterialBindings = MaterialBindings
45
+ > = {
46
+ /** Optional identifier for the cloned material. */
47
+ id?: string;
48
+ /** Replacement material-owned resource bindings. */
49
+ bindings?: Partial<TBindings>;
50
+ /** Additional uniform/module props applied to the clone. */
51
+ moduleProps?: MaterialPropsUpdate<TModuleProps>;
52
+ /** Optional full replacement shader-input store. */
53
+ shaderInputs?: ShaderInputs<TModuleProps>;
54
+ };
55
+
56
+ /**
57
+ * Material owns bind group `3` resources and uniforms for one material instance.
58
+ *
59
+ * `setProps()` mutates uniform values in place. Structural resource changes are
60
+ * expressed through `clone({...})`, which creates a new material identity.
61
+ */
62
+ export class Material<
63
+ TModuleProps extends MaterialModuleProps = MaterialModuleProps,
64
+ TBindings extends MaterialBindings = MaterialBindings
65
+ > {
66
+ /** Application-provided identifier. */
67
+ readonly id: string;
68
+ /** Device that owns the material resources. */
69
+ readonly device: Device;
70
+ /** Factory that defines the material schema. */
71
+ readonly factory: MaterialFactory<TModuleProps, TBindings>;
72
+ /** Shader inputs for the material-owned modules. */
73
+ readonly shaderInputs: ShaderInputs<TModuleProps>;
74
+ /** Internal binding store including uniform buffers and resource bindings. */
75
+ readonly bindings: Record<string, Binding | DynamicTexture> = {};
76
+
77
+ private _uniformStore: UniformStore;
78
+ private _bindGroupCacheToken: object = {};
79
+
80
+ constructor(device: Device, props: MaterialProps<TModuleProps, TBindings> = {}) {
81
+ this.id = props.id || uid('material');
82
+ this.device = device;
83
+
84
+ this.factory =
85
+ props.factory ||
86
+ new MaterialFactory<TModuleProps, TBindings>(device, {
87
+ modules: props.modules || props.shaderInputs?.getModules() || []
88
+ });
89
+
90
+ const moduleMap = Object.fromEntries(
91
+ (props.shaderInputs?.getModules() || this.factory.modules).map(module => [
92
+ module.name,
93
+ module
94
+ ])
95
+ ) as {[P in keyof TModuleProps]?: ShaderModule[] extends never ? never : any};
96
+ this.shaderInputs = props.shaderInputs || new ShaderInputs<TModuleProps>(moduleMap);
97
+ this._uniformStore = new UniformStore(this.device, this.shaderInputs.modules);
98
+
99
+ for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
100
+ if (this.ownsModule(moduleName) && shaderModuleHasUniforms(module)) {
101
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(moduleName);
102
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
103
+ }
104
+ }
105
+
106
+ this.updateShaderInputs();
107
+ if (props.bindings) {
108
+ this._replaceOwnedBindings(props.bindings);
109
+ }
110
+ }
111
+
112
+ /** Destroys managed uniform-buffer resources owned by this material. */
113
+ destroy(): void {
114
+ this._uniformStore.destroy();
115
+ }
116
+
117
+ /** Creates a new material variant with optional structural and uniform overrides. */
118
+ clone(
119
+ props: MaterialCloneProps<TModuleProps, TBindings> = {}
120
+ ): Material<TModuleProps, TBindings> {
121
+ const material = this.factory.createMaterial({
122
+ id: props.id,
123
+ shaderInputs: props.shaderInputs,
124
+ bindings: {
125
+ ...this.getResourceBindings(),
126
+ ...props.bindings
127
+ }
128
+ });
129
+
130
+ if (!props.shaderInputs) {
131
+ material.setProps(this.shaderInputs.getUniformValues() as MaterialPropsUpdate<TModuleProps>);
132
+ }
133
+ if (props.moduleProps) {
134
+ material.setProps(props.moduleProps);
135
+ }
136
+ return material;
137
+ }
138
+
139
+ /** Returns `true` if this material owns the supplied binding name. */
140
+ ownsBinding(bindingName: string): boolean {
141
+ return this.factory.ownsBinding(bindingName);
142
+ }
143
+
144
+ /** Returns `true` if this material owns the supplied shader module. */
145
+ ownsModule(moduleName: string): boolean {
146
+ return this.factory.ownsModule(moduleName);
147
+ }
148
+
149
+ /** Updates material uniform/module props in place without changing material identity. */
150
+ setProps(props: MaterialPropsUpdate<TModuleProps>): void {
151
+ this.shaderInputs.setProps(props);
152
+ this.updateShaderInputs();
153
+ }
154
+
155
+ /** Updates managed uniform buffers and shader-input-owned bindings. */
156
+ updateShaderInputs(): void {
157
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
158
+ const didChange = this._setOwnedBindings(this.shaderInputs.getBindingValues());
159
+ if (didChange) {
160
+ this._bindGroupCacheToken = {};
161
+ }
162
+ }
163
+
164
+ /** Returns the material-owned resource bindings without internal uniform buffers. */
165
+ getResourceBindings(): Partial<TBindings> {
166
+ const resourceBindings = {} as Partial<TBindings>;
167
+
168
+ for (const [name, binding] of Object.entries(this.bindings)) {
169
+ if (!getModuleNameFromUniformBinding(name)) {
170
+ (resourceBindings as Record<string, Binding | DynamicTexture>)[name] = binding;
171
+ }
172
+ }
173
+
174
+ return resourceBindings;
175
+ }
176
+
177
+ /** Returns the resolved bindings, including internal uniform buffers and ready textures. */
178
+ getBindings(): Partial<{[K in keyof TBindings]: Binding}> & Record<string, Binding> {
179
+ const validBindings = {} as Partial<{[K in keyof TBindings]: Binding}> &
180
+ Record<string, Binding>;
181
+ const validBindingsMap = validBindings as Record<string, Binding>;
182
+
183
+ for (const [name, binding] of Object.entries(this.bindings)) {
184
+ if (binding instanceof DynamicTexture) {
185
+ if (binding.isReady) {
186
+ validBindingsMap[name] = binding.texture;
187
+ }
188
+ } else {
189
+ validBindingsMap[name] = binding;
190
+ }
191
+ }
192
+
193
+ return validBindings;
194
+ }
195
+
196
+ /** Packages resolved material bindings into logical bind group `3`. */
197
+ getBindingsByGroup(): BindingsByGroup {
198
+ return this.factory.getBindingsByGroup(this.getBindings());
199
+ }
200
+
201
+ /** Returns the stable bind-group cache token for the requested bind group. */
202
+ getBindGroupCacheKey(group: number): object | null {
203
+ return group === MATERIAL_BIND_GROUP ? this._bindGroupCacheToken : null;
204
+ }
205
+
206
+ /** Returns the latest update timestamp across material-owned resources. */
207
+ getBindingsUpdateTimestamp(): number {
208
+ let timestamp = 0;
209
+ for (const binding of Object.values(this.bindings)) {
210
+ if (binding instanceof TextureView) {
211
+ timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
212
+ } else if (binding instanceof Buffer || binding instanceof Texture) {
213
+ timestamp = Math.max(timestamp, binding.updateTimestamp);
214
+ } else if (binding instanceof DynamicTexture) {
215
+ timestamp = binding.texture
216
+ ? Math.max(timestamp, binding.texture.updateTimestamp)
217
+ : Infinity;
218
+ } else if (!(binding instanceof Sampler)) {
219
+ timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
220
+ }
221
+ }
222
+ return timestamp;
223
+ }
224
+
225
+ /** Replaces owned resource bindings and invalidates the material cache identity when needed. */
226
+ private _replaceOwnedBindings(bindings: Partial<TBindings>): void {
227
+ const didChange = this._setOwnedBindings(bindings);
228
+ if (didChange) {
229
+ this._bindGroupCacheToken = {};
230
+ }
231
+ }
232
+
233
+ private _setOwnedBindings(
234
+ bindings: Partial<TBindings> | Record<string, Binding | DynamicTexture>
235
+ ): boolean {
236
+ let didChange = false;
237
+
238
+ for (const [name, binding] of Object.entries(bindings)) {
239
+ if (binding === undefined) {
240
+ continue;
241
+ }
242
+ if (!this.ownsBinding(name)) {
243
+ continue;
244
+ }
245
+
246
+ if (this.bindings[name] !== binding) {
247
+ this.bindings[name] = binding;
248
+ didChange = true;
249
+ }
250
+ }
251
+
252
+ return didChange;
253
+ }
254
+ }