@bornengine/engine 0.4.16

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 (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/native/android/Cargo.lock +1848 -0
  4. package/native/android/Cargo.toml +24 -0
  5. package/native/android/src/lib.rs +702 -0
  6. package/native/ios/Cargo.lock +1690 -0
  7. package/native/ios/Cargo.toml +32 -0
  8. package/native/ios/src/lib.rs +1267 -0
  9. package/native/linux/Cargo.lock +3279 -0
  10. package/native/linux/Cargo.toml +29 -0
  11. package/native/linux/src/lib.rs +1331 -0
  12. package/native/macos/Cargo.lock +3310 -0
  13. package/native/macos/Cargo.toml +46 -0
  14. package/native/macos/src/lib.rs +1302 -0
  15. package/native/shared/Cargo.lock +1899 -0
  16. package/native/shared/Cargo.toml +62 -0
  17. package/native/shared/assets/default_font.ttf +0 -0
  18. package/native/shared/build.rs +270 -0
  19. package/native/shared/shaders/common/clouds.wgsl +122 -0
  20. package/native/shared/shaders/common/fog.wgsl +16 -0
  21. package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
  22. package/native/shared/shaders/common/imposter.wgsl +112 -0
  23. package/native/shared/shaders/common/pbr.wgsl +186 -0
  24. package/native/shared/shaders/common/shadows.wgsl +186 -0
  25. package/native/shared/shaders/common/sky.wgsl +8 -0
  26. package/native/shared/shaders/common/tonemap.wgsl +25 -0
  27. package/native/shared/shaders/impulse_field.wgsl +57 -0
  28. package/native/shared/shaders/material_abi.wgsl +383 -0
  29. package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
  30. package/native/shared/src/anim_mixer.rs +61 -0
  31. package/native/shared/src/attach.rs +263 -0
  32. package/native/shared/src/audio/decode.rs +123 -0
  33. package/native/shared/src/audio/mod.rs +863 -0
  34. package/native/shared/src/audio/render.rs +892 -0
  35. package/native/shared/src/audio/spsc.rs +156 -0
  36. package/native/shared/src/audio/stream.rs +226 -0
  37. package/native/shared/src/custom_shaders.rs +104 -0
  38. package/native/shared/src/decals.rs +245 -0
  39. package/native/shared/src/drs.rs +211 -0
  40. package/native/shared/src/engine.rs +261 -0
  41. package/native/shared/src/ffi.rs +116 -0
  42. package/native/shared/src/ffi_core/assets.rs +388 -0
  43. package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
  44. package/native/shared/src/ffi_core/draw.rs +334 -0
  45. package/native/shared/src/ffi_core/game_loop.rs +577 -0
  46. package/native/shared/src/ffi_core/input.rs +234 -0
  47. package/native/shared/src/ffi_core/mod.rs +127 -0
  48. package/native/shared/src/ffi_core/models.rs +1154 -0
  49. package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
  50. package/native/shared/src/ffi_core/scene.rs +626 -0
  51. package/native/shared/src/ffi_core/vfx.rs +212 -0
  52. package/native/shared/src/ffi_core/visual.rs +691 -0
  53. package/native/shared/src/frame_callbacks.rs +122 -0
  54. package/native/shared/src/geometry.rs +236 -0
  55. package/native/shared/src/handles.rs +182 -0
  56. package/native/shared/src/input.rs +448 -0
  57. package/native/shared/src/jolt_sys.rs +822 -0
  58. package/native/shared/src/lib.rs +55 -0
  59. package/native/shared/src/models.rs +1093 -0
  60. package/native/shared/src/models_gltf.rs +1280 -0
  61. package/native/shared/src/particles.rs +391 -0
  62. package/native/shared/src/physics_jolt.rs +1908 -0
  63. package/native/shared/src/picking.rs +298 -0
  64. package/native/shared/src/postfx.rs +345 -0
  65. package/native/shared/src/profiler.rs +492 -0
  66. package/native/shared/src/ragdoll.rs +474 -0
  67. package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
  68. package/native/shared/src/renderer/brdf_lut.rs +154 -0
  69. package/native/shared/src/renderer/draw2d.rs +143 -0
  70. package/native/shared/src/renderer/formats.rs +822 -0
  71. package/native/shared/src/renderer/froxel.rs +421 -0
  72. package/native/shared/src/renderer/gi_bake.rs +653 -0
  73. package/native/shared/src/renderer/graph.rs +462 -0
  74. package/native/shared/src/renderer/hiz.rs +269 -0
  75. package/native/shared/src/renderer/hot_reload.rs +390 -0
  76. package/native/shared/src/renderer/impulse_field.rs +456 -0
  77. package/native/shared/src/renderer/lighting.rs +154 -0
  78. package/native/shared/src/renderer/material_instancing.rs +171 -0
  79. package/native/shared/src/renderer/material_pipeline.rs +700 -0
  80. package/native/shared/src/renderer/material_system.rs +1996 -0
  81. package/native/shared/src/renderer/material_system_tests.rs +601 -0
  82. package/native/shared/src/renderer/material_system_wasm.rs +41 -0
  83. package/native/shared/src/renderer/mod.rs +12556 -0
  84. package/native/shared/src/renderer/model_draw.rs +641 -0
  85. package/native/shared/src/renderer/occlusion.rs +429 -0
  86. package/native/shared/src/renderer/planar_pass.rs +593 -0
  87. package/native/shared/src/renderer/planar_reflection.rs +499 -0
  88. package/native/shared/src/renderer/post_pass.rs +249 -0
  89. package/native/shared/src/renderer/postfx_chain.rs +728 -0
  90. package/native/shared/src/renderer/pt_pass.rs +577 -0
  91. package/native/shared/src/renderer/scene_pass.rs +607 -0
  92. package/native/shared/src/renderer/shader_include.rs +205 -0
  93. package/native/shared/src/renderer/shader_library.rs +135 -0
  94. package/native/shared/src/renderer/shaders/ao.rs +570 -0
  95. package/native/shared/src/renderer/shaders/core.rs +1243 -0
  96. package/native/shared/src/renderer/shaders/env.rs +907 -0
  97. package/native/shared/src/renderer/shaders/gi.rs +810 -0
  98. package/native/shared/src/renderer/shaders/mod.rs +19 -0
  99. package/native/shared/src/renderer/shaders/post.rs +1558 -0
  100. package/native/shared/src/renderer/shaders/pt.rs +1859 -0
  101. package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
  102. package/native/shared/src/renderer/shadow_pass.rs +731 -0
  103. package/native/shared/src/renderer/ssgi_pass.rs +392 -0
  104. package/native/shared/src/renderer/ssr_pass.rs +188 -0
  105. package/native/shared/src/renderer/texture_store.rs +473 -0
  106. package/native/shared/src/renderer/transient.rs +591 -0
  107. package/native/shared/src/renderer/types.rs +941 -0
  108. package/native/shared/src/renderer/util.rs +152 -0
  109. package/native/shared/src/scene.rs +1362 -0
  110. package/native/shared/src/sdf_cache.rs +274 -0
  111. package/native/shared/src/shadows.rs +1036 -0
  112. package/native/shared/src/staging.rs +102 -0
  113. package/native/shared/src/string_header.rs +266 -0
  114. package/native/shared/src/text_renderer.rs +502 -0
  115. package/native/shared/src/textures.rs +197 -0
  116. package/native/tvos/Cargo.lock +1693 -0
  117. package/native/tvos/Cargo.toml +36 -0
  118. package/native/tvos/metal-patched/Cargo.toml +178 -0
  119. package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
  120. package/native/tvos/metal-patched/LICENSE-MIT +25 -0
  121. package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
  122. package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
  123. package/native/tvos/metal-patched/src/argument.rs +366 -0
  124. package/native/tvos/metal-patched/src/blitpass.rs +102 -0
  125. package/native/tvos/metal-patched/src/buffer.rs +71 -0
  126. package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
  127. package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
  128. package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
  129. package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
  130. package/native/tvos/metal-patched/src/computepass.rs +107 -0
  131. package/native/tvos/metal-patched/src/constants.rs +152 -0
  132. package/native/tvos/metal-patched/src/counters.rs +119 -0
  133. package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
  134. package/native/tvos/metal-patched/src/device.rs +2134 -0
  135. package/native/tvos/metal-patched/src/drawable.rs +39 -0
  136. package/native/tvos/metal-patched/src/encoder.rs +2041 -0
  137. package/native/tvos/metal-patched/src/heap.rs +281 -0
  138. package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
  139. package/native/tvos/metal-patched/src/lib.rs +657 -0
  140. package/native/tvos/metal-patched/src/library.rs +902 -0
  141. package/native/tvos/metal-patched/src/mps.rs +575 -0
  142. package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
  143. package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
  144. package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
  145. package/native/tvos/metal-patched/src/renderpass.rs +443 -0
  146. package/native/tvos/metal-patched/src/resource.rs +182 -0
  147. package/native/tvos/metal-patched/src/sampler.rs +165 -0
  148. package/native/tvos/metal-patched/src/sync.rs +178 -0
  149. package/native/tvos/metal-patched/src/texture.rs +352 -0
  150. package/native/tvos/metal-patched/src/types.rs +90 -0
  151. package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
  152. package/native/tvos/src/audio_backend.rs +197 -0
  153. package/native/tvos/src/lib.rs +1891 -0
  154. package/native/visionos/Cargo.lock +1693 -0
  155. package/native/visionos/Cargo.toml +40 -0
  156. package/native/visionos/src/audio_backend.rs +197 -0
  157. package/native/visionos/src/lib.rs +1887 -0
  158. package/native/watchos/Cargo.lock +16 -0
  159. package/native/watchos/Cargo.toml +19 -0
  160. package/native/watchos/shaders/bloom_postfx.metal +99 -0
  161. package/native/watchos/src/BloomWatchApp.swift +1267 -0
  162. package/native/watchos/src/BloomWatchAudio.swift +179 -0
  163. package/native/watchos/src/audio.rs +55 -0
  164. package/native/watchos/src/draw_list.rs +229 -0
  165. package/native/watchos/src/ffi_stubs.rs +915 -0
  166. package/native/watchos/src/ffi_stubs_manual.rs +35 -0
  167. package/native/watchos/src/lib.rs +1124 -0
  168. package/native/watchos/src/models.rs +746 -0
  169. package/native/watchos/src/postfx.rs +95 -0
  170. package/native/watchos/src/scene.rs +534 -0
  171. package/native/watchos/src/textures.rs +184 -0
  172. package/native/web/Cargo.lock +1657 -0
  173. package/native/web/Cargo.toml +43 -0
  174. package/native/web/bloom_glue.js +695 -0
  175. package/native/web/build.sh +131 -0
  176. package/native/web/index.html +35 -0
  177. package/native/web/jolt_bridge.js +1519 -0
  178. package/native/web/src/input_ffi.rs +286 -0
  179. package/native/web/src/lib.rs +1796 -0
  180. package/native/web/src/material_ffi.rs +710 -0
  181. package/native/web/src/parity_ffi.rs +343 -0
  182. package/native/web/src/physics_ffi.rs +643 -0
  183. package/native/web/src/ragdoll_ffi.rs +250 -0
  184. package/native/web/src/render_settings.rs +98 -0
  185. package/native/windows/Cargo.lock +1815 -0
  186. package/native/windows/Cargo.toml +68 -0
  187. package/native/windows/src/lib.rs +1486 -0
  188. package/package.json +4279 -0
  189. package/src/audio/index.ts +315 -0
  190. package/src/core/colors.ts +63 -0
  191. package/src/core/index.ts +1206 -0
  192. package/src/core/keys.ts +63 -0
  193. package/src/core/types.ts +104 -0
  194. package/src/index.ts +171 -0
  195. package/src/math/index.ts +516 -0
  196. package/src/mobile/index.ts +294 -0
  197. package/src/models/index.ts +1258 -0
  198. package/src/physics/index.ts +1134 -0
  199. package/src/scene/index.ts +698 -0
  200. package/src/shapes/index.ts +120 -0
  201. package/src/text/index.ts +48 -0
  202. package/src/textures/index.ts +187 -0
  203. package/src/vfx/index.ts +191 -0
  204. package/src/world/index.ts +24 -0
  205. package/src/world/loader.ts +423 -0
  206. package/src/world/prefab.ts +217 -0
  207. package/src/world/render.ts +172 -0
  208. package/src/world/saver.ts +108 -0
  209. package/src/world/serialize.ts +301 -0
  210. package/src/world/terrain.ts +355 -0
  211. package/src/world/types.ts +160 -0
  212. package/src/world/validate.ts +319 -0
  213. package/src/world/version.ts +114 -0
@@ -0,0 +1,383 @@
1
+ // Bloom shader ABI header — see docs/rfc/0001-material-render-graph.md.
2
+ //
3
+ // ABI-VERSION: 3
4
+ //
5
+ // Included by every 3D shader (built-in PBR, sky, custom user materials).
6
+ // Defines the five bind groups and the vertex attribute layout that any
7
+ // Bloom-compatible 3D pipeline targets. Groups 0, 1, 3 are required;
8
+ // group 2 is required for PBR materials and optional otherwise; group 4
9
+ // is opt-in via the material descriptor's `reads_scene = true`.
10
+ //
11
+ // Bumping the version means every consumer has to be updated in the same
12
+ // PR. Version-check happens in engine/renderer/shaders.rs at pipeline
13
+ // creation time: mismatched headers are a hard error, not a warning.
14
+
15
+ // =====================================================================
16
+ // Group 0 — PerFrame (written once per frame, read by every 3D draw)
17
+ // =====================================================================
18
+
19
+ struct PerFrame {
20
+ // Clock / counters
21
+ time: f32, // seconds since process start, wraps every ~2²³ s
22
+ delta_time: f32, // seconds since previous frame
23
+ frame_index: u32, // monotonic, wraps at u32::MAX
24
+ _pad0: u32,
25
+
26
+ // Resolutions
27
+ screen_resolution: vec2<f32>, // physical pixels (matches swapchain)
28
+ render_resolution: vec2<f32>, // may be smaller than screen when TSR < 1
29
+
30
+ // TAA / TSR jitter applied to the projection this frame
31
+ taa_jitter: vec2<f32>,
32
+ _pad1: vec2<f32>,
33
+
34
+ // Global wind field. xy = direction in the XZ plane (need not be
35
+ // normalised; magnitude scales effective amplitude); z = amplitude
36
+ // (~0.1 m typical for grass); w = frequency in Hz (~1.0 typical).
37
+ // Foliage / cloth materials sample this in their vertex stage.
38
+ wind: vec4<f32>,
39
+
40
+ // Cloud deck. x = shadow strength (0 = the world ignores the clouds),
41
+ // y = deck height in metres, z = feature scale, w = drift speed in m/s.
42
+ // Feed it to `cloud_shadow_at` from common/clouds.wgsl and multiply the
43
+ // result into DIRECT sunlight only — this is the same deck the sky pass
44
+ // draws, so a shadow here has a cloud above it.
45
+ cloud: vec4<f32>,
46
+ };
47
+
48
+ @group(0) @binding(0) var<uniform> frame: PerFrame;
49
+
50
+ // =====================================================================
51
+ // Group 1 — PerView (camera + lighting + env maps + shadows)
52
+ // =====================================================================
53
+
54
+ struct DirLight {
55
+ direction: vec4<f32>, // xyz + intensity
56
+ color: vec4<f32>, // rgb + _pad
57
+ };
58
+
59
+ struct PointLight {
60
+ position: vec4<f32>, // xyz + range
61
+ color: vec4<f32>, // rgb + intensity
62
+ };
63
+
64
+ struct PerView {
65
+ // View matrices
66
+ view: mat4x4<f32>,
67
+ proj: mat4x4<f32>,
68
+ view_proj: mat4x4<f32>,
69
+ prev_view_proj: mat4x4<f32>, // for motion vectors / TAA reprojection
70
+ inv_proj: mat4x4<f32>,
71
+
72
+ // Camera state
73
+ camera_pos: vec4<f32>, // xyz=world pos, w=env_intensity
74
+ camera_dir: vec4<f32>, // xyz=forward, w=fovy_rad
75
+
76
+ // Basic scene lighting
77
+ ambient: vec4<f32>, // rgb + intensity
78
+ fog: vec4<f32>, // rgb + density
79
+
80
+ // Legacy single-sun shortcut (also in dir_lights[0])
81
+ sun_dir: vec4<f32>, // xyz + intensity
82
+ sun_color: vec4<f32>, // rgb + 0
83
+
84
+ // Arrayed lights
85
+ dir_light_count: vec4<f32>, // x = count, y = shadows-enabled flag, zw = 0
86
+ dir_lights: array<DirLight, 8>,
87
+ point_light_count: vec4<f32>,
88
+ point_lights: array<PointLight, 256>,
89
+
90
+ // Shadow data — three cascades, split by shadow_splits.xyz
91
+ shadow_splits: vec4<f32>,
92
+ shadow_view: mat4x4<f32>, // orientation for cascade compute
93
+ shadow_cascades: array<mat4x4<f32>, 3>, // light-space view-proj per cascade
94
+ };
95
+
96
+ @group(1) @binding(0) var<uniform> view: PerView;
97
+
98
+ // Environment maps — pre-filtered at load time.
99
+ @group(1) @binding(1) var env_tex: texture_2d<f32>;
100
+ @group(1) @binding(2) var env_samp: sampler;
101
+ @group(1) @binding(3) var env_diffuse_tex: texture_2d<f32>;
102
+
103
+ // BRDF LUT for IBL specular (split-sum approximation).
104
+ @group(1) @binding(4) var brdf_lut_tex: texture_2d<f32>;
105
+ @group(1) @binding(5) var brdf_lut_samp: sampler;
106
+
107
+ // Shadow depth textures, one per cascade.
108
+ @group(1) @binding(6) var shadow_tex_0: texture_depth_2d;
109
+ @group(1) @binding(7) var shadow_tex_1: texture_depth_2d;
110
+ @group(1) @binding(8) var shadow_tex_2: texture_depth_2d;
111
+ @group(1) @binding(9) var shadow_samp: sampler_comparison;
112
+
113
+ // =====================================================================
114
+ // Group 2 — PerMaterial (PBR textures + factors + user params)
115
+ // =====================================================================
116
+ //
117
+ // All texture bindings have a 1×1 white default bound when a material
118
+ // doesn't provide its own — shaders can unconditionally sample.
119
+
120
+ struct MaterialFactors {
121
+ metal_rough: vec4<f32>, // x=metallic, y=roughness, z=has_mr_tex, w=alpha_cutoff
122
+ emissive: vec4<f32>, // rgb + 0
123
+ base_color: vec4<f32>, // rgba tint multiplier
124
+ // EN-012 — shading-model selector + foliage transmission RGB.
125
+ // x = shading_model (0 = default lit, 1 = foliage,
126
+ // 2 = subsurface — V2 stub),
127
+ // yzw = transmission_color (rgb tint for back-lit foliage; ignored
128
+ // for default lit). Default (1, 1, 1) when not set.
129
+ shading_model: vec4<f32>,
130
+ // EN-012 — foliage shading parameters. Read by `shade_foliage`
131
+ // (in common/pbr.wgsl) when shading_model.x == 1.0.
132
+ // x = transmission_amount (0..1, how much sun-behind-leaf bleeds
133
+ // through into the camera),
134
+ // y = wrap_factor (0..1, how much wrap-lambert wraps the diffuse
135
+ // term — 0 is standard lambert, 1 is light fully
136
+ // wrapping around to the back face),
137
+ // zw = reserved.
138
+ foliage_params: vec4<f32>,
139
+ };
140
+
141
+ @group(2) @binding(0) var base_color_tex: texture_2d<f32>;
142
+ @group(2) @binding(1) var base_color_samp: sampler;
143
+ @group(2) @binding(2) var normal_tex: texture_2d<f32>;
144
+ @group(2) @binding(3) var normal_samp: sampler;
145
+ @group(2) @binding(4) var mr_tex: texture_2d<f32>;
146
+ @group(2) @binding(5) var mr_samp: sampler;
147
+ @group(2) @binding(6) var em_tex: texture_2d<f32>;
148
+ @group(2) @binding(7) var em_samp: sampler;
149
+ @group(2) @binding(8) var occ_tex: texture_2d<f32>;
150
+ @group(2) @binding(9) var occ_samp: sampler;
151
+ @group(2) @binding(10) var<uniform> material: MaterialFactors;
152
+
153
+ // Binding 11 is reserved for per-material user params. Shaders that
154
+ // declare user params do so via:
155
+ //
156
+ // struct UserParams { … up to 256 bytes … };
157
+ // @group(2) @binding(11) var<uniform> user: UserParams;
158
+ //
159
+ // Materials that don't use it leave binding 11 unclaimed; the engine's
160
+ // pipeline layout allocates a 16-byte stub UBO there so the bind group
161
+ // always matches the layout.
162
+
163
+ // EN-011 — Planar reflection RT. Bound by the engine for materials
164
+ // linked to a reflection probe via `setMaterialReflectionProbe`.
165
+ // Materials without a probe get a 1×1 black default texture so
166
+ // unconditional sampling is safe.
167
+ //
168
+ // The RT is `Rgba16Float` HDR (matches the engine's pipeline) and
169
+ // holds the previous frame's mirror-camera render of the world
170
+ // minus excluded materials. Sample with screen-space UV — the
171
+ // reflection texture is camera-aligned. Typical water shader pattern:
172
+ //
173
+ // let screen_uv = clip_position.xy / vec2<f32>(frame.screen_resolution);
174
+ // let perturb = wave_normal.xz * 0.05; // wobble strength
175
+ // let refl = textureSample(planar_reflection_tex,
176
+ // planar_reflection_samp,
177
+ // screen_uv + perturb).rgb;
178
+ //
179
+ // Reads are NaN-safe: the default 1×1 black texture means a material
180
+ // can sample even when the game forgot to bind a probe.
181
+ @group(2) @binding(12) var planar_reflection_tex: texture_2d<f32>;
182
+ @group(2) @binding(13) var planar_reflection_samp: sampler;
183
+
184
+ // =====================================================================
185
+ // EN-014 — Texture-array slots for splat-mapped terrain.
186
+ // =====================================================================
187
+ //
188
+ // Three optional `texture_2d_array<f32>` slots. Materials that don't
189
+ // bind a texture array see a 1×1×1 stub array (single transparent
190
+ // black layer) so unconditional sampling is safe.
191
+ //
192
+ // Layer count is capped at 16 layers per array in V1. Games address
193
+ // individual layers via a fragment-shader argument:
194
+ // let albedo = textureSample(albedo_array, albedo_array_samp,
195
+ // uv_world_xz, layer_idx);
196
+ //
197
+ // Pair these with a splat-weight texture (RGBA8 with 4 weight channels)
198
+ // in user_params for terrain blending. Single sampler is reused across
199
+ // all 3 arrays — terrain layers share UV / filtering, so a per-array
200
+ // sampler would just waste binding slots.
201
+ @group(2) @binding(14) var albedo_array: texture_2d_array<f32>;
202
+ @group(2) @binding(15) var normal_array: texture_2d_array<f32>;
203
+ @group(2) @binding(16) var mr_array: texture_2d_array<f32>;
204
+ @group(2) @binding(17) var albedo_array_samp: sampler;
205
+
206
+ // =====================================================================
207
+ // Group 3 — PerDraw (transform + skinning)
208
+ // =====================================================================
209
+
210
+ struct PerDraw {
211
+ mvp: mat4x4<f32>,
212
+ model: mat4x4<f32>,
213
+ prev_mvp: mat4x4<f32>, // for motion vectors
214
+ model_tint: vec4<f32>,
215
+ skin_info: vec4<u32>, // x=joint_offset into global buffer, y=joint_count, zw=0
216
+ };
217
+
218
+ struct JointMatrices {
219
+ matrices: array<mat4x4<f32>, 1024>, // global joint buffer; draws read slices by offset
220
+ };
221
+
222
+ @group(3) @binding(0) var<uniform> draw: PerDraw;
223
+ @group(3) @binding(1) var<uniform> joints: JointMatrices;
224
+
225
+ // EN-022 — standard motion vector from current/previous clip positions.
226
+ // Matches the core path's convention exactly (NDC delta × 0.5). Vertex
227
+ // shaders pass their clip position plus a previous-frame clip position:
228
+ // static geometry uses `draw.prev_mvp * local` (or
229
+ // `view.prev_view_proj * world` — for static models the two agree);
230
+ // procedurally displaced geometry (wind sway) re-evaluates the
231
+ // displacement at `frame.time - frame.delta_time` and projects with
232
+ // `view.prev_view_proj`. Write the result to OpaqueOut.velocity — a
233
+ // zero write disables TAA's motion-adaptive clamp for that surface
234
+ // (the round-2 audit's primary TSR-shimmer mechanism, F8).
235
+ fn abi_motion_vector(curr_clip: vec4<f32>, prev_clip: vec4<f32>) -> vec2<f32> {
236
+ let c = curr_clip.xy / curr_clip.w;
237
+ let p = prev_clip.xy / prev_clip.w;
238
+ return (c - p) * 0.5;
239
+ }
240
+
241
+ // =====================================================================
242
+ // Group 4 — SceneInputs (optional; `reads_scene = true` materials only)
243
+ // =====================================================================
244
+ //
245
+ // The render graph synthesises a SceneColor snapshot pass before the
246
+ // first consumer runs. Materials that don't declare reads_scene never
247
+ // receive this group and pay no cost for the copy.
248
+
249
+ @group(4) @binding(0) var scene_color_tex: texture_2d<f32>;
250
+ @group(4) @binding(1) var scene_color_samp: sampler;
251
+ @group(4) @binding(2) var scene_depth_tex: texture_depth_2d;
252
+ @group(4) @binding(3) var scene_depth_samp: sampler;
253
+ @group(4) @binding(4) var impulse_tex: texture_2d<f32>; // world-space decals / splashes
254
+ @group(4) @binding(5) var impulse_samp: sampler;
255
+ @group(4) @binding(6) var motion_vectors: texture_2d<f32>;
256
+
257
+ // =====================================================================
258
+ // Vertex attribute layout (matches renderer::types::Vertex3D)
259
+ // =====================================================================
260
+
261
+ struct VertexInput {
262
+ @location(0) position: vec3<f32>,
263
+ @location(1) normal: vec3<f32>,
264
+ @location(2) color: vec4<f32>, // per-vertex tint multiplier
265
+ @location(3) uv: vec2<f32>,
266
+ @location(4) joints: vec4<f32>, // bone indices as floats (engine convention)
267
+ @location(5) weights: vec4<f32>, // sum to 1.0 for skinned, 0 for unskinned
268
+ @location(6) tangent: vec4<f32>, // xyz=tangent, w=bitangent sign (+1 / -1)
269
+ };
270
+
271
+ // =====================================================================
272
+ // Fragment output profiles
273
+ // =====================================================================
274
+
275
+ // Opaque profile — writes the full forward G-buffer. Materials that use
276
+ // this must live in `Bucket::Opaque` and render in the main_hdr pass.
277
+ struct OpaqueOut {
278
+ @location(0) hdr: vec4<f32>, // HDR colour, pre-bloom
279
+ @location(1) material: vec2<f32>, // metallic, roughness for SSR / SSGI
280
+ @location(2) velocity: vec2<f32>, // NDC-space motion, for TAA reprojection
281
+ @location(3) albedo: vec4<f32>, // for SSGI bounce colour
282
+ };
283
+
284
+ // Translucent profile — writes HDR only, alpha blended. Materials in
285
+ // `Bucket::Transparent`, `Refractive`, or `Additive` use this. The main
286
+ // HDR pass' G-buffer targets are load-op'd in the translucent sub-pass;
287
+ // material/velocity/albedo remain whatever the opaque pass left them.
288
+ struct TranslucentOut {
289
+ @location(0) hdr: vec4<f32>,
290
+ };
291
+
292
+ // =====================================================================
293
+ // Standard helpers
294
+ // =====================================================================
295
+
296
+ // Skinning — blends a world-space position through the joint matrices,
297
+ // using the per-draw joint_offset so multiple skinned models share the
298
+ // global joint buffer.
299
+ fn world_position_from_skinned(
300
+ local_pos: vec3<f32>, vertex: VertexInput, include_non_skinned: bool,
301
+ ) -> vec4<f32> {
302
+ let total_weight = vertex.weights.x + vertex.weights.y
303
+ + vertex.weights.z + vertex.weights.w;
304
+ var pos = vec4<f32>(local_pos, 1.0);
305
+ if (total_weight > 0.01) {
306
+ let off = draw.skin_info.x;
307
+ let j0 = off + u32(vertex.joints.x);
308
+ let j1 = off + u32(vertex.joints.y);
309
+ let j2 = off + u32(vertex.joints.z);
310
+ let j3 = off + u32(vertex.joints.w);
311
+ pos = joints.matrices[j0] * pos * vertex.weights.x
312
+ + joints.matrices[j1] * pos * vertex.weights.y
313
+ + joints.matrices[j2] * pos * vertex.weights.z
314
+ + joints.matrices[j3] * pos * vertex.weights.w;
315
+ } else if (include_non_skinned) {
316
+ pos = draw.model * pos;
317
+ }
318
+ return pos;
319
+ }
320
+
321
+ // Standard NDC-space motion vector computation. Pass the current and
322
+ // previous clip positions, get back a vec2 suitable for the
323
+ // velocity G-buffer target.
324
+ fn compute_motion_vector(curr_clip: vec4<f32>, prev_clip: vec4<f32>) -> vec2<f32> {
325
+ let curr_ndc = curr_clip.xy / curr_clip.w;
326
+ let prev_ndc = prev_clip.xy / prev_clip.w;
327
+ return (curr_ndc - prev_ndc) * 0.5;
328
+ }
329
+
330
+ // Linearise a hardware depth value given the bound projection. Useful
331
+ // for materials that read `scene_depth_tex` (group 4 binding 2).
332
+ fn linearize_depth(z_ndc: f32) -> f32 {
333
+ // Standard reverse-depth linearisation: the engine uses a standard
334
+ // depth range (0..1) with proj.z mapping near→0, far→1.
335
+ // Derived from the inv_proj mat4; this cheap form works for our
336
+ // perspective projections. Override in shaders that don't match.
337
+ let z_clip = z_ndc * 2.0 - 1.0;
338
+ let a = view.proj[2][2];
339
+ let b = view.proj[3][2];
340
+ // z_eye = b / (z_clip + a). Result is negative (camera looks down -Z).
341
+ return -b / (z_clip + a);
342
+ }
343
+
344
+ // =====================================================================
345
+ // EN-001 — Instance attribute layout (wants_instancing pipelines)
346
+ // =====================================================================
347
+ //
348
+ // Materials compiled via `compile_material_instanced` get a second
349
+ // vertex buffer at slot 1, step_mode = Instance. Game shaders that opt
350
+ // into instancing extend their VertexInput with these locations IN
351
+ // ADDITION to the standard 0..6 above:
352
+ //
353
+ // struct InstancedVertexInput {
354
+ // @location(0) position: vec3<f32>,
355
+ // @location(1) normal: vec3<f32>,
356
+ // @location(2) color: vec4<f32>,
357
+ // @location(3) uv: vec2<f32>,
358
+ // @location(4) joints: vec4<f32>,
359
+ // @location(5) weights: vec4<f32>,
360
+ // @location(6) tangent: vec4<f32>,
361
+ // @location(7) instance_pos: vec3<f32>,
362
+ // @location(8) instance_rot_y: f32,
363
+ // @location(9) instance_scale: f32,
364
+ // @location(10) instance_tint: vec4<f32>,
365
+ // };
366
+ //
367
+ // Typical vertex-shader pattern:
368
+ //
369
+ // let cy = cos(in.instance_rot_y);
370
+ // let sy = sin(in.instance_rot_y);
371
+ // let local = in.position * in.instance_scale;
372
+ // let world = vec3<f32>(
373
+ // cy * local.x + sy * local.z,
374
+ // local.y,
375
+ // -sy * local.x + cy * local.z,
376
+ // ) + in.instance_pos;
377
+ // let world_pos = vec4<f32>(world, 1.0);
378
+ // out.clip_position = view.view_proj * world_pos;
379
+ //
380
+ // The Rust-side `InstanceData3D` struct mirrors this layout and is
381
+ // the canonical CPU representation. Per-draw `model_tint` is
382
+ // multiplied against `instance_tint` in the fragment shader so games
383
+ // can dim/colour an entire instanced batch without re-uploading.
@@ -0,0 +1,42 @@
1
+ // Minimal ABI-compliant test material (opaque profile).
2
+ //
3
+ // Reads PerFrame.time and PerDraw.mvp to render the bound geometry
4
+ // with a solid time-pulsed colour. Writes the full 4-MRT G-buffer so
5
+ // the main HDR pass accepts it without a translucent sub-pass (that's
6
+ // phase 2's job). Proves the material pipeline + submission plumbing
7
+ // works end-to-end.
8
+
9
+ #include "material_abi.wgsl"
10
+
11
+ struct VsOut {
12
+ @builtin(position) clip_position: vec4<f32>,
13
+ @location(0) world_normal: vec3<f32>,
14
+ @location(1) local_color: vec3<f32>,
15
+ };
16
+
17
+ @vertex
18
+ fn vs_main(in: VertexInput) -> VsOut {
19
+ var out: VsOut;
20
+ out.clip_position = draw.mvp * vec4<f32>(in.position, 1.0);
21
+ out.world_normal = normalize((draw.model * vec4<f32>(in.normal, 0.0)).xyz);
22
+
23
+ // Pulse colour with PerFrame.time to demonstrate the binding.
24
+ let pulse = 0.5 + 0.5 * sin(frame.time * 1.5);
25
+ out.local_color = vec3<f32>(pulse, 0.4, 1.0 - pulse) * draw.model_tint.rgb;
26
+ return out;
27
+ }
28
+
29
+ @fragment
30
+ fn fs_main(in: VsOut) -> OpaqueOut {
31
+ var out: OpaqueOut;
32
+ // Simple directional-ish shading so the cube reads as 3D.
33
+ let n = normalize(in.world_normal);
34
+ let light_dir = normalize(vec3<f32>(0.3, 0.7, 0.4));
35
+ let diffuse = max(dot(n, light_dir), 0.0);
36
+ let lit = in.local_color * (0.3 + 0.7 * diffuse);
37
+ out.hdr = vec4<f32>(lit, 1.0);
38
+ out.material = vec2<f32>(0.0, 0.9); // non-metal, rough
39
+ out.velocity = vec2<f32>(0.0, 0.0);
40
+ out.albedo = vec4<f32>(in.local_color, 1.0);
41
+ return out;
42
+ }
@@ -0,0 +1,61 @@
1
+ //! EN-028 — per-model animation mixer state.
2
+ //!
3
+ //! Split out of `models.rs` (EN-052 line budget) when EN-055 landed: the
4
+ //! mixer is exactly the state that is PER-INSTANCE under animation
5
+ //! instancing, so it earns its own module. Re-exported from `models`, so
6
+ //! `crate::models::AnimMixer` paths are unchanged.
7
+ //!
8
+ //! Three things a single-clip sampler cannot do, all of which read as
9
+ //! "cheap" on screen: transitions pop, an attacking character has to stop
10
+ //! walking, and authored locomotion arcs (a pounce, a lunge) get replaced
11
+ //! by hand-tuned kinematics because the root is nailed to the rest pose.
12
+ //!
13
+ //! Layout: a base track that crossfades from `prev` to `cur` over
14
+ //! `fade_dur`, plus one optional additive-by-mask layer (an attack driving
15
+ //! the spine-up while the legs keep walking). All clocks advance in
16
+ //! `advance_animation`, so the game hands over a dt and never tracks clip
17
+ //! time itself.
18
+
19
+ #[derive(Clone)]
20
+ pub struct AnimMixer {
21
+ pub cur_clip: usize,
22
+ pub cur_time: f32,
23
+ pub cur_speed: f32,
24
+ pub cur_loop: bool,
25
+ /// Clip we are fading *out* of. `fade_dur <= 0` means no fade in flight.
26
+ pub prev_clip: usize,
27
+ pub prev_time: f32,
28
+ pub prev_speed: f32,
29
+ pub prev_loop: bool,
30
+ pub fade_t: f32,
31
+ pub fade_dur: f32,
32
+ /// Masked layer. `layer_clip < 0` = inactive.
33
+ pub layer_clip: i32,
34
+ pub layer_time: f32,
35
+ pub layer_speed: f32,
36
+ pub layer_loop: bool,
37
+ pub layer_weight: f32,
38
+ /// Root joint of the masked subtree (e.g. the spine). Every joint at or
39
+ /// below it takes the layer pose; everything else keeps the base pose.
40
+ pub layer_mask_root: i32,
41
+ /// Opt-in root motion. Off by default so existing games are unchanged.
42
+ pub root_motion: bool,
43
+ pub root_delta: [f32; 3],
44
+ /// True once a non-looping `cur_clip` has played past its duration.
45
+ pub finished: bool,
46
+ pub started: bool,
47
+ }
48
+
49
+ impl Default for AnimMixer {
50
+ fn default() -> Self {
51
+ Self {
52
+ cur_clip: 0, cur_time: 0.0, cur_speed: 1.0, cur_loop: true,
53
+ prev_clip: 0, prev_time: 0.0, prev_speed: 1.0, prev_loop: true,
54
+ fade_t: 0.0, fade_dur: 0.0,
55
+ layer_clip: -1, layer_time: 0.0, layer_speed: 1.0, layer_loop: false,
56
+ layer_weight: 0.0, layer_mask_root: -1,
57
+ root_motion: false, root_delta: [0.0; 3],
58
+ finished: false, started: false,
59
+ }
60
+ }
61
+ }