Loading...
Searching...
No Matches
SceneGPUState.hpp
1#pragma once
2#include <score_plugin_gfx_export.h>
3#include <ossia/dataflow/geometry_port.hpp>
4
5#include <QMatrix4x4>
6
7#include <cstdint>
8#include <vector>
9
10namespace score::gfx
11{
12class GpuResourceRegistry;
13
14// GPU-friendly structures for packing scene data into UBOs/SSBOs. All matrices
15// are column-major.
16//
17// Raw* structs are written by source nodes (Camera, Light, Transform3D,
18// EnvironmentLoader) into their own GpuResourceRegistry arena slot at their
19// operator()() time: view-independent, no aspect-ratio math or scene-graph
20// composition. The cooked structs (CameraUBOData, LightGPU, MaterialGPU,
21// PerDrawGPU, WorldTransformMat4) are produced by ScenePreprocessor from those
22// arenas plus render-target state, and are what consumer shaders bind.
23//
24// Materials and env are composition-independent, so Raw == Cooked for them and
25// MaterialGPU / EnvParamsUBO are used directly from source nodes.
26
27#pragma pack(push, 1)
28
29// Consumer shaders read RawLightData directly from the RawLight arena
30// and compose world-space direction via world_transforms[transform_slot].
31
32// Scene-level UBO: camera + global scene data.
34{
35 float view[16]{};
36 float projection[16]{};
37 float viewProjection[16]{};
38 float cameraPosition[4]{}; // xyz = position, w = padding
39 float time{};
40 int32_t lightCount{};
41 int32_t materialCount{};
42 float padding0{};
43 float ambientColor[4]{0.03f, 0.03f, 0.03f, 1.f};
44};
45
46// Per-mesh UBO: model transform for the current draw call.
47struct MeshUBO
48{
49 float model[16]{};
50 float modelViewProjection[16]{};
51 float normalMatrix[12]{}; // mat3 in std140 = 3 × vec4 (48 bytes)
52 int32_t materialIndex{};
53 float padding[3]{};
54};
55
56// Packed 32-bit texture reference stored in MaterialGPU::textureRefs[]:
57// bits 31..30 : source (0 = NONE, 1 = STATIC pool, 2 = DYNAMIC pool)
58// bits 29..23 : bucket index within the selected pool
59// bits 22.. 0 : layer index within the bucket's texture array
60//
61// 0xFFFFFFFF is the "no texture" sentinel: the shader falls back to the
62// constant baseColor factor, metallic_factor, and so on.
63inline constexpr uint32_t tex_ref_none() { return 0xFFFFFFFFu; }
64inline constexpr uint32_t tex_ref_static(uint32_t bucket, uint32_t layer)
65{
66 // Packed layout: source:2 | bucket:7 | layer:23. The 7-bit bucket field gives
67 // headroom for 128 buckets against a runtime cap of kMaxBuckets = 8 in
68 // GpuResourceRegistry.hpp; raising the cap only needs larger shader sampler
69 // arrays, not a new encoding. Shader-side decode is `(ref >> 23) & 0x7Fu` for
70 // the bucket and `ref & 0x007FFFFFu` for the layer.
71 return (1u << 30) | ((bucket & 0x7Fu) << 23) | (layer & 0x007FFFFFu);
72}
73// Dynamic texture slot: source=2, bucket unused, low 24 bits hold the
74// per-channel slot index. Consumer shaders branch on the source bits and sample
75// one of a fixed set of direct sampler2D uniforms named <channel>Dyn0 and so on,
76// forwarding the upstream handle as-is. For large runtime textures (8K video,
77// HDR shader outputs) that do not fit the scaled-and-uploaded array path.
78inline constexpr uint32_t tex_ref_dynamic(uint32_t slot)
79{
80 return (2u << 30) | (slot & 0x00FFFFFFu);
81}
82
83// Per-material data for the material SSBO, 80 bytes (5 x vec4). The 16 B of
84// runtime metadata leaves headroom for future fields (animation ID, LOD hint,
85// shader permutation hash) without an ABI break.
87{
88 float baseColor[4]{1.f, 1.f, 1.f, 1.f};
89 // x = metallic, y = roughness, z = occlusion, w = unlit flag
90 float metallicRoughnessOcclusionUnlit[4]{0.f, 0.5f, 1.f, 0.f};
91 // xyz = emissive, w = emissive strength
92 float emissive_strength[4]{0.f, 0.f, 0.f, 1.f};
93 // Packed texture refs: [0] = base color, [1..3] reserved for MR, normal,
94 // emissive. See tex_ref_* helpers for encoding.
95 uint32_t textureRefs[4]{
96 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu};
97
98 // --- Runtime metadata (16 B) ----------------------------------------
99 // Producer-derived bitmask of "which BRDF lobes / features are active"
100 // for this material. Used as:
101 // - Coherence key for NVIDIA Shader Execution Reordering
102 // (`reorderThread(feature_mask)` before closest-hit shading) so
103 // threads in the same warp converge on the same shading path.
104 // - Shader-side specialization in the main closest-hit / fragment
105 // body: `if(fm & HAS_TRANSMISSION) { ... }`.
106 // Bit layout:
107 // bit 0 : has_base_color_texture
108 // bit 1 : has_metal_rough_texture
109 // bit 2 : has_normal_texture
110 // bit 3 : has_emissive_texture
111 // bit 4 : unlit
112 // bit 5 : alpha_non_opaque (mask OR blend)
113 // bit 6 : has_clearcoat (KHR_materials_clearcoat)
114 // bit 7 : has_sheen (KHR_materials_sheen)
115 // bit 8 : has_transmission (KHR_materials_transmission)
116 // bit 9 : has_volume (KHR_materials_volume)
117 // bit 10 : has_specular (KHR_materials_specular)
118 // bit 11 : has_iridescence (KHR_materials_iridescence)
119 // bit 12 : has_anisotropy
120 // bit 13 : has_diffuse_transmission
121 // bit 14 : has_subsurface
122 // bit 15 : thin_walled
123 // bit 16 : alpha_mask (glTF alphaMode = MASK)
124 // bit 17 : alpha_blend (glTF alphaMode = BLEND)
125 // bit 18 : double_sided (glTF doubleSided)
126 // bit 19 : has_separate_occlusion (occlusion ≠ MR source)
127 // bits 20-21 : BC texcoord_set (0 or 1, glTF TEXCOORD_0/1)
128 // bits 22-23 : MR texcoord_set
129 // bits 24-25 : Normal texcoord_set
130 // bits 26-27 : Emissive texcoord_set
131 // bits 28-29 : Occlusion texcoord_set
132 // bit 30 : shadow_caster_disabled (material.shadow_caster == false)
133 // bit 31 : reflection_caster_disabled (material.reflection_caster == false)
134 uint32_t feature_mask{0u};
135
136 // Shader binding table hit-group index for ray tracing pipelines.
137 // Producers with a pre-computed hit-group mapping stamp this at
138 // material-authoring time; 0 means "default lit material" and is the
139 // safe fallback for renderers that haven't computed the mapping yet.
140 uint32_t hit_group_id{0u};
141
142 // 5th texture channel (occlusion). glTF separates occlusionTexture
143 // from metallicRoughnessTexture; conventionally both are sometimes
144 // packed into the same image (occlusion in R, roughness in G,
145 // metallic in B). When they're distinct sources, this slot points
146 // at the occlusion array layer; when they're the same, this stays
147 // at tex_ref_none() and the shader uses MR.r * occlusion_factor.
148 uint32_t occlusion_textureRef{0xFFFFFFFFu};
149
150 // glTF alphaMode = MASK cutoff. Shader does `if(alpha < cutoff)
151 // discard;` when the `alpha_mask` feature_mask bit is set.
152 // Default 0.5 matches the glTF spec default.
153 float alpha_cutoff{0.5f};
154};
155static_assert(sizeof(MaterialGPU) == 80, "MaterialGPU layout must match shader");
156
157// Feature-mask bit flags. Producers OR these together to derive the
158// per-material feature_mask; hit-group shaders branch on them to
159// select the relevant BRDF lobe code path.
160namespace material_feature
161{
162inline constexpr uint32_t has_base_color_texture = 1u << 0;
163inline constexpr uint32_t has_metal_rough_texture = 1u << 1;
164inline constexpr uint32_t has_normal_texture = 1u << 2;
165inline constexpr uint32_t has_emissive_texture = 1u << 3;
166inline constexpr uint32_t unlit = 1u << 4;
167inline constexpr uint32_t alpha_non_opaque = 1u << 5;
168inline constexpr uint32_t has_clearcoat = 1u << 6;
169inline constexpr uint32_t has_sheen = 1u << 7;
170inline constexpr uint32_t has_transmission = 1u << 8;
171inline constexpr uint32_t has_volume = 1u << 9;
172inline constexpr uint32_t has_specular = 1u << 10;
173inline constexpr uint32_t has_iridescence = 1u << 11;
174inline constexpr uint32_t has_anisotropy = 1u << 12;
175inline constexpr uint32_t has_diffuse_transmission = 1u << 13;
176inline constexpr uint32_t has_subsurface = 1u << 14;
177inline constexpr uint32_t thin_walled = 1u << 15;
178// glTF alpha mode (parsed from material.alphaMode). MASK → shader
179// discards fragments with alpha < alpha_cutoff. BLEND → shader emits
180// translucent alpha (caller handles depth/sort separately).
181inline constexpr uint32_t alpha_mask = 1u << 16;
182inline constexpr uint32_t alpha_blend = 1u << 17;
183// glTF doubleSided. When set, shader flips the surface normal for
184// back-facing fragments (so lighting works on both sides). When unset
185// AND the pipeline cull mode is `none` (MDI default), shader discards
186// back-facing fragments to mimic single-sided culling.
187inline constexpr uint32_t double_sided = 1u << 18;
188// Separate occlusion texture present (independent from MR texture).
189// Shader samples mat.occlusion_textureRef instead of using mr.r.
190inline constexpr uint32_t has_separate_occlusion = 1u << 19;
191// Scene-filter opt-outs. "Disabled" semantics (default 0 = participates
192// in the pass) so the common case stays bit-clear. Packed at bits
193// 30/31 — CSF filter shaders test these to drop draws from auxiliary
194// passes (shadow-map, reflection capture).
195inline constexpr uint32_t shadow_caster_disabled = 1u << 30;
196inline constexpr uint32_t reflection_caster_disabled = 1u << 31;
197}
198
199// Per-material EXTENSION data: a parallel SSBO indexed by the same
200// material_index as MaterialGPU. Shaders needing only the 80-byte base material
201// ignore it; OpenPBR-grade shaders declare `scene_materials_ext`.
202//
203// std430-friendly: every member starts on a 16-byte boundary. Field names track
204// OpenPBR_ResolvedInputs / glTF KHR extension names so the shader-side
205// translation is a 1:1 copy.
206//
207// textureRefs[16] uses the same tex_ref_static / tex_ref_dynamic / tex_ref_none
208// encoding as MaterialGPU.textureRefs. The slot ordering documented below MUST
209// match what packMaterialExtensions writes and what the consumer shader's
210// Material_Ext struct reads.
212{
213 // --- Coat / clearcoat (KHR_materials_clearcoat) ---------------------
214 // x = coat_weight, y = coat_roughness, z = coat_ior, w = coat_darkening
215 float coat[4]{0.f, 0.f, 1.5f, 0.f};
216 // x = roughness_anisotropy, y = rotation_cos, z = rotation_sin,
217 // w = clearcoatNormalTexture.scale
218 float coat_anisotropy[4]{0.f, 1.f, 0.f, 1.f};
219
220 // --- Fuzz / sheen (KHR_materials_sheen) -----------------------------
221 // xyz = color, w = roughness
222 float fuzz_color[4]{0.f, 0.f, 0.f, 0.f};
223
224 // --- Transmission + volume (KHR_materials_transmission + _volume) ---
225 // x = transmission_weight, y = transmission_depth,
226 // z = dispersion_scale, w = dispersion_abbe_number
227 float transmission[4]{0.f, 0.f, 0.f, 20.f};
228 // xyz = transmission_color, w = scatter_anisotropy
229 float transmission_color[4]{1.f, 1.f, 1.f, 0.f};
230 // xyz = transmission_scatter (vec3), w = _pad
231 float transmission_scatter[4]{0.f, 0.f, 0.f, 0.f};
232
233 // --- Specular (KHR_materials_specular) + base specular anisotropy ---
234 // x = specular_weight, yzw = specular_color
235 float specular_weight_color[4]{1.f, 1.f, 1.f, 1.f};
236 // x = specular_ior, y = roughness_anisotropy,
237 // z = rotation_cos, w = rotation_sin
238 float specular_ior_anisotropy[4]{1.5f, 0.f, 1.f, 0.f};
239
240 // --- Thin-film iridescence (KHR_materials_iridescence) --------------
241 // x = thin_film_weight (iridescence factor),
242 // y = thin_film_thickness (glTF average of min/max),
243 // z = thin_film_ior, w = _pad
244 float thin_film[4]{0.f, 400.f, 1.3f, 0.f};
245
246 // --- Diffuse transmission (KHR_materials_diffuse_transmission) ------
247 // x = factor, yzw = color
248 float diffuse_transmission[4]{0.f, 1.f, 1.f, 1.f};
249
250 // --- Subsurface (OpenPBR subsurface; not present in stock glTF) -----
251 // x = weight, yzw = color
252 float subsurface_weight_color[4]{0.f, 0.8f, 0.8f, 0.8f};
253 // x = radius, yzw = radius_scale
254 float subsurface_radius_scale[4]{1.f, 1.f, 0.5f, 0.25f};
255
256 // --- Misc scalars + flags -------------------------------------------
257 // x = base_diffuse_roughness (OpenPBR Oren-Nayar knob),
258 // y = thin_walled (bool-as-float 0/1),
259 // z = _pad, w = _pad
260 float flags[4]{0.f, 0.f, 0.f, 0.f};
261
262 // --- Texture refs ---------------------------------------------------
263 // Slot layout:
264 // 0 = coat factor
265 // 1 = coat roughness
266 // 2 = coat normal
267 // 3 = fuzz color (sheen)
268 // 4 = fuzz roughness
269 // 5 = transmission
270 // 6 = specular factor
271 // 7 = specular color
272 // 8 = iridescence (thin-film)
273 // 9 = iridescence thickness
274 // 10 = anisotropy
275 // 11 = diffuse transmission
276 // 12 = diffuse transmission color
277 // 13 = subsurface factor
278 // 14 = subsurface color
279 // 15 = reserved
280 uint32_t textureRefs[16]{
281 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
282 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
283 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
284 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu};
285};
286
287// ─── Raw layouts (source-owned arena slots) ────────────────────────────
288//
289// Written by source halp nodes directly into their GpuResourceRegistry arena
290// slot. ScenePreprocessor reads these, applies aspect-ratio and scene-graph
291// composition, and writes the cooked equivalents consumer shaders bind.
292
293// Camera parameters before matrix composition. No aspect ratio, no
294// view / projection matrices — the preprocessor builds those per render
295// target.
297{
298 float eye[4]{0.f, 0.f, 3.f, 0.f}; // xyz = world-space eye, w = pad
299 float target[4]{0.f, 0.f, 0.f, 0.f}; // xyz = look-at target, w = pad
300 float up[4]{0.f, 1.f, 0.f, 0.f}; // xyz = up, w = pad
301 float yfov{60.f * 3.14159265f / 180.f}; // vertical FOV, radians
302 float znear{0.1f};
303 float zfar{1000.f};
304 uint32_t projection{0}; // 0 = perspective, 1 = orthographic
305};
306
307// Light parameters in local frame. The final world-space direction
308// depends on the node's world transform (composed by the preprocessor
309// from its scene-node parent chain); this struct stores only what the
310// node itself knows.
312{
313 float color[4]{1.f, 1.f, 1.f, 1.f}; // xyz = color, w = intensity
314 float local_direction[4]{0.f, 0.f, -1.f, 0.f}; // xyz = dir (local),
315 // w = type enum:
316 // 0 = directional
317 // 1 = point
318 // 2 = spot
319 // (area / dome modes
320 // collapse to point /
321 // directional; dome
322 // lights are served by
323 // the scene-global env
324 // path, see EnvParamsUBO.)
325 float range_cone[4]{ // x = range (point/spot;
326 0.f, 1.f, 0.7071f, 0.005f}; // 0 = infinite)
327 // y = inner cone cos
328 // z = outer cone cos
329 // w = shadow depth bias
330 // Shadow gate: shadow-receiving shaders MUST multiply the computed shadow term
331 // by `shadow_enabled != 0`. Separate from the per-material
332 // shadow_caster_disabled bit, which controls whether a draw participates in the
333 // depth-only cast pass.
334 uint32_t shadow_enabled{0};
335 uint32_t decay_mode{2}; // 0=const 1=lin 2=quad 3=cubic
336 // RawTransform arena slot index for this light's scene_transform.
337 // Consumer shader reads world_transforms.data[transform_slot] to
338 // get the world matrix, composes world-space direction / position
339 // from local_direction on the fly.
340 uint32_t transform_slot{0};
341 // Receiver-plane / slope-scaled bias for shadow sampling, exposed in the UI
342 // as Light::inputs.shadow_normal_bias. PCF shaders add
343 // `normal_bias * (1 - max(dot(N, Ldir), 0))` to the receiver depth before the
344 // comparison, to kill shadow acne on grazing surfaces.
345 float normal_bias{0.01f};
346};
347static_assert(sizeof(RawLightData) == 64, "RawLightData must stay 64 B");
348
349// Local TRS for a scene_transform, stamped by the producer and uploaded into a
350// RawTransform arena slot. Parent-chain composition stays on the CPU inside
351// ScenePreprocessor's FlattenVisitor; the composed world matrix ends up in the
352// WorldTransform arena at the same offset the RawTransform slot occupies.
354{
355 float translation[4]{0.f, 0.f, 0.f, 0.f}; // xyz + pad
356 float rotation[4]{0.f, 0.f, 0.f, 1.f}; // quaternion xyzw
357 float scale[4]{1.f, 1.f, 1.f, 0.f}; // xyz + pad
358 float _pad[4]{}; // std430 alignment
359};
360
361// Environment parameters (ambient, fog, exposure, gamma). Already
362// view-independent — this is both Raw (source-written) and Cooked
363// (shader-bound) in one struct. Published here so EnvironmentLoader
364// can write its own slot bytes matching what ScenePreprocessor expects
365// on the other end.
367{
368 float ambient[4]{0.f, 0.f, 0.f, 1.f}; // xyz = color, w = intensity
369 float fog_color_density[4]{0.8f, 0.8f, 0.8f, 0.f}; // xyz = color, w = density
370 float fog_range[4]{10.f, 100.f, 0.f, 0.f}; // x = start, y = end,
371 // z = mode, w = enabled (0/1)
372 float exposure_gamma[4]{1.f, 2.2f, 0.f, 0.f}; // x = exposure (linear),
373 // y = gamma, zw = pad
374};
375
376// World-space mat4 emitted by ScenePreprocessor's FlattenVisitor from
377// the scene_node tree (CPU walk with parent-chain accumulation). One
378// entry per producer-authored scene_transform, laid out at the same
379// byte offset as the producer's RawTransform slot so shaders can
380// address either side by `scene_transform::raw_slot.offset`.
382{
383 float m[16]{1.f, 0.f, 0.f, 0.f,
384 0.f, 1.f, 0.f, 0.f,
385 0.f, 0.f, 1.f, 0.f,
386 0.f, 0.f, 0.f, 1.f};
387};
388
389// Shadow cascades UBO, scene-wide, published by ScenePreprocessor as the
390// `shadow_cascades` aux on the output geometry. Shading shaders read it to pick
391// the right cascade per fragment; the depth-only pass reads light_view_proj from
392// it to transform vertices into cascade clip-space, with its per-invocation
393// cascade_index in a separate `shadow_draw_cfg` UBO.
394//
395// std140, 560 B. Fields mirror ossia::shadow_cascades_info in geometry_port.hpp:
396// light_view_proj[8] world -> cascade clip-space per cascade
397// cascade_split_distances[8] view-space far-plane Z of cascade k; slots
398// >= cascade_count read as 0
399// cascade_count how many entries are live (0..8)
400// light_slot RawLight arena slot of the light the cascades
401// belong to, 0xFFFFFFFF for none
403{
404 float light_view_proj[8][16]{};
405 // 8 split distances symmetric with light_view_proj[8].
406 // std140: two consecutive vec4 rows (32 B total).
407 float cascade_split_distances[8]{};
408 uint32_t cascade_count{0};
409 uint32_t light_slot{0xFFFFFFFFu};
410 uint32_t _pad1{};
411 uint32_t _pad2{};
412};
413static_assert(sizeof(ShadowCascadesUBO) == 560,
414 "ShadowCascadesUBO size = mat4[8] (512) + float[8] (32) + 4×uint (16) = 560 B");
415
416#pragma pack(pop)
417
418// CPU-side flattened scene representation.
420{
421 // Points at either a mesh from geometry_ref (legacy_geometry path) OR at
422 // owned_mesh (mesh_primitive path). `mesh` is always non-null for a valid
423 // draw; one of geometry_ref or owned_mesh keeps the target alive.
424 const ossia::geometry* mesh{};
425 ossia::geometry_spec geometry_ref; // Legacy path: keeps source alive.
426 std::shared_ptr<ossia::geometry> owned_mesh; // Primitive path: built from mesh_primitive.
427
428 // Stable cross-frame identity of the source mesh primitive: the mesh_primitive's
429 // stable_id, or its raw pointer bits for a legacy producer that stamps none.
430 // `mesh` cannot serve -- on the primitive path it points into a freshly
431 // allocated wrapper with a different address every flatten. ScenePreprocessor
432 // uses this to skip vertex/index re-uploads.
433 uint64_t stable_id{};
434
435 QMatrix4x4 worldTransform;
436
437 // Direct shared_ptr to the material — null means "no material / use
438 // the renderer's default factors". Carries the material's gpu_slot_ref
439 // for GPU-side lookup without any scene-wide index array.
440 ossia::material_component_ptr material;
441
442 // Direct shared_ptr to the skin — null means "no skinning". When
443 // present, the ScenePreprocessor attaches a `joint_matrices` auxiliary
444 // buffer to this draw's output geometry; a downstream skinning compute
445 // pass (or user shader) deforms positions/normals using
446 // joints0/weights0 vertex attributes.
447 ossia::skeleton_component_ptr skin;
448
449 // Index into FlatScene::materials after the flatten pass has
450 // deduplicated the material pointers into its flat materials array.
451 // -1 means "material was null / default factors only". Set by
452 // flattenScene after collecting all draws.
453 int materialIndex{-1};
454
455 // Index into FlatScene::skins after dedup. -1 = no skinning.
456 int skinIndex{-1};
457
458 // Local-space AABB of the source mesh_primitive. Copied by the
459 // FlattenVisitor from mesh_primitive::bounds. Empty (inverted) if the
460 // source didn't compute bounds — downstream per_draw_bounds emitter
461 // writes an infinite AABB in that case so GPU culling shaders never
462 // cull the draw.
463 ossia::aabb local_bounds{};
464
465 // RawTransform arena slot of the nearest producer-authored
466 // scene_transform on this draw's walk path (0xFFFFFFFF = none). Stamped
467 // into PerDrawGPU.transform_slot so shaders can look up
468 // world_transforms_prev[slot] for motion vectors / TAA / reprojection.
469 std::uint32_t transform_slot{0xFFFFFFFFu};
470};
471
472// Per-skeleton packed joint matrices: joint_matrix[i] = world_joint × inverse_bind.
473// One std::vector<QMatrix4x4> per skeleton index (parallel to scene_state.skeletons).
475{
476 std::vector<QMatrix4x4> joint_matrices;
477};
478
479// One entry per producer-authored scene_transform seen during the walk. The
480// preprocessor's private world-transforms SSBO is a packed array indexed by the
481// scene_transform's raw_slot.internal_index, and consumer shaders read
482// world_transforms.data[transform_slot].
483//
484// Each preprocessor owns its own buffer, so two preprocessors with different
485// filtered views of one scene compute different world matrices for the same
486// scene_transform without stomping each other.
488{
489 QMatrix4x4 world;
490 uint32_t transform_slot; // RawTransform arena slot index
491};
492
494{
495 std::vector<DrawCall> draws;
496 // RawLight arena slot index per light the walk encountered.
497 // 0xFFFFFFFF for producer-less lights until the ScenePreprocessor gives
498 // them a slot (see loaderLights); still 0xFFFFFFFF entries are filtered
499 // out when building scene_light_indices, the shader-facing compact list.
500 std::vector<uint32_t> lightArenaSlots;
501
502 // Lights without a RawLight slot of their own, e.g. glTF
503 // KHR_lights_punctual or FBX lights: the ScenePreprocessor writes their
504 // RawLightData and world matrix into slots it owns on their behalf, and
505 // patches lightArenaSlots[index].
507 {
508 ossia::light_component_ptr light;
509 QMatrix4x4 worldTransform;
510 std::size_t index{};
511 };
512 std::vector<LoaderLight> loaderLights;
513 std::vector<MaterialGPU> materials;
514 // Parallel to `materials`: same size, same indexing, zeroed to the OpenPBR spec
515 // defaults for materials that set no extension fields. Consumer shaders either
516 // ignore it or bind it as `scene_materials_ext`.
517 std::vector<MaterialExtensionsGPU> material_extensions;
518 // scene_state.skeletons in order, then the skins meshes reference that it
519 // does not list.
520 std::vector<SkeletonGPU> skins;
521
522 // World matrices to upload into the WorldTransform arena, one per
523 // producer-authored scene_transform encountered in the walk whose
524 // raw_slot is valid. Sparse: the arena is indexed by offset, not
525 // by position in this vector.
526 std::vector<WorldTransformEmit> worldTransforms;
527
528 // Loader-emitted scene_data payloads, collected during the walk.
529 // ScenePreprocessor forwards each entry as an auxiliary_buffer on every output
530 // geometry (by name). Lifetime held via shared_ptr.
531 std::vector<ossia::scene_data_ptr> scene_data;
532
533 // Instance components encountered during the walk. Each pair is a
534 // (worldTransform, instance_component_ptr) that the ScenePreprocessor emits as
535 // a dedicated instanced DrawCall with per-instance auxiliaries.
537 {
538 ossia::instance_component_ptr instance;
539 QMatrix4x4 worldTransform;
540 };
541 std::vector<InstanceDraw> instances;
542
543 // Primitive cloud (splat / point-cloud) entries: format-agnostic payloads whose
544 // schema is described by their CSF chain. ScenePreprocessor buckets them by
545 // format_id, one indirect-draw geometry per bucket; an empty format_id is
546 // bucketed individually on the stable id.
548 {
549 ossia::primitive_cloud_component_ptr cloud;
550 QMatrix4x4 worldTransform;
551 // RawTransform arena slot index, or 0xFFFFFFFFu if no producer
552 // transform was on the walk path. Mirrors PerDrawGPU.transform_slot.
553 uint32_t transform_slot{0xFFFFFFFFu};
554 };
555 std::vector<PrimitiveCloudDraw> primitive_clouds;
556
557 // Cameras collected from the scene tree. Each entry keeps its source
558 // camera_component alive, its accumulated world transform (column 3 =
559 // eye position, inverse = view matrix), and the scene_node_id of the
560 // node it was attached to so consumers can resolve `active_camera_id`.
562 {
563 ossia::camera_component_ptr component;
564 QMatrix4x4 worldTransform;
565 ossia::scene_node_id node_id{};
566 };
567 std::vector<CameraEntry> cameras;
568
569 // Index into `cameras` of the currently-active camera. -1 when the scene
570 // has no cameras; in that case downstream falls back to a default eye
571 // placement (see the legacy single-camera fields below, populated from
572 // this slot if valid or from a default otherwise).
573 int activeCameraIndex{-1};
574
575 // Camera (from scene or override) — legacy mirror fields. Kept populated
576 // for consumers that haven't migrated to `cameras[activeCameraIndex]`
577 // yet. Resolved by flattenScene() after the tree walk:
578 // - cameras empty → sensible default (eye at (0,1,3))
579 // - cameras nonempty → copied from cameras[activeCameraIndex]
580 QMatrix4x4 viewMatrix;
581 QMatrix4x4 projectionMatrix;
582 QVector3D cameraPosition;
583 float cameraFov{60.f};
584 float cameraNear{0.1f};
585 float cameraFar{1000.f};
586
587 bool hasCamera{false};
588
589 void clear()
590 {
591 draws.clear();
592 lightArenaSlots.clear();
593 loaderLights.clear();
594 materials.clear();
595 material_extensions.clear();
596 skins.clear();
597 scene_data.clear();
598 instances.clear();
599 primitive_clouds.clear();
600 cameras.clear();
601 worldTransforms.clear();
602 activeCameraIndex = -1;
603 hasCamera = false;
604 // The legacy single-camera mirror is part of the state too: leaving it
605 // populated means a reused FlatScene reports the previous scene's camera
606 // to any consumer that reads it without checking hasCamera.
607 viewMatrix.setToIdentity();
608 projectionMatrix.setToIdentity();
609 cameraPosition = {};
610 cameraFov = 60.f;
611 cameraNear = 0.1f;
612 cameraFar = 1000.f;
613 }
614};
615
616// Flatten a scene_spec into a FlatScene for GPU consumption.
617// With a registry, a transform or light raw_slot that is not live in its arena
618// is treated as unstamped.
619SCORE_PLUGIN_GFX_EXPORT
620void flattenScene(
621 const ossia::scene_spec& scene,
622 FlatScene& out,
623 float aspectRatio,
624 const GpuResourceRegistry* registry = nullptr);
625
626// Build a transient ossia::geometry that wraps a mesh_primitive's buffers
627// and attributes. The result is heap-allocated and owned by shared_ptr so
628// callers can keep it alive beyond the flatten pass (see DrawCall::owned_mesh).
629SCORE_PLUGIN_GFX_EXPORT
630std::shared_ptr<ossia::geometry>
631primitiveToGeometry(const ossia::mesh_primitive& prim);
632
633SCORE_PLUGIN_GFX_EXPORT
634MaterialGPU packMaterial(const ossia::material_component& mc);
635MaterialExtensionsGPU packMaterialExtensions(const ossia::material_component& mc);
636}
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition SceneGPUState.hpp:420
Definition SceneGPUState.hpp:367
Definition SceneGPUState.hpp:562
Definition SceneGPUState.hpp:537
Definition SceneGPUState.hpp:507
Definition SceneGPUState.hpp:548
Definition SceneGPUState.hpp:494
Definition SceneGPUState.hpp:212
Definition SceneGPUState.hpp:87
Definition SceneGPUState.hpp:48
Definition SceneGPUState.hpp:297
Definition SceneGPUState.hpp:312
Definition SceneGPUState.hpp:354
Definition SceneGPUState.hpp:34
Definition SceneGPUState.hpp:403
Definition SceneGPUState.hpp:475
Definition SceneGPUState.hpp:488
Definition SceneGPUState.hpp:382