Loading...
Searching...
No Matches
Light.hpp
1#pragma once
2#include <Threedim/TinyObj.hpp>
3#include <halp/controls.hpp>
4#include <halp/meta.hpp>
5
6#include <ossia/dataflow/geometry_port.hpp>
7
8#include <Gfx/Graph/GpuResourceRegistry.hpp>
9
10#include <QMatrix4x4>
11#include <QQuaternion>
12#include <QVector3D>
13
14#include <cstdint>
15#include <memory>
16
17class QRhiResourceUpdateBatch;
18
19namespace score::gfx
20{
21class RenderList;
22struct Edge;
23}
24
25namespace Threedim
26{
27
28// Unified light producer. One node with a mode combobox covers every
29// punctual / area light type ossia::light_component defines —
30// directional, point, spot, rect, disk, sphere, cylinder, dome —
31// mirroring UsdLux's RectLight/DiskLight/SphereLight and glTF
32// KHR_lights_punctual.
33//
34// Emits an ossia::scene_spec containing one scene_node with:
35// - child[0] = scene_transform (position + rotation, no scale)
36// - child[1] = light_component_ptr
37// ScenePreprocessor packs it into the scene-wide `scene_lights` SSBO via
38// packLight(). Current consumer shaders (`classic_pbr_*.frag`) only
39// sample the common fields (position/direction/color/intensity/range +
40// spot cone angles) — area-light shapes pass through correctly but
41// are rendered as point-light approximations until shaders add the
42// Rect/Disk/Sphere sampling math.
43class Light
44{
45public:
46 halp_meta(name, "Light")
47 halp_meta(category, "Visuals/3D/Scene")
48 halp_meta(c_name, "light")
49 halp_meta(description, "Add a configurable light and transform to a 3D scene.")
50 halp_meta(authors, "ossia team")
51 halp_meta(
52 manual_url,
53 "https://ossia.io/score-docs/processes/light.html")
54 halp_meta(uuid, "9f3c1a5e-4b7d-4e2a-8c5f-1d6e0b9a3c7f")
55
56 enum Mode
57 {
58 Directional,
59 Point,
60 Spot,
61 Rect,
62 Disk,
63 Sphere,
64 Dome
65 };
66
67 enum Decay
68 {
69 DecayNone,
70 DecayLinear,
71 DecayQuadratic, // physically correct
72 DecayCubic
73 };
74
75 struct ins
76 {
77 // Port-driven rebuild: each control's update() callback triggers
78 // Light::rebuild() on user change. operator()() just republishes.
79 struct : halp::combobox_t<"Mode", Mode>
80 {
81 struct range
82 {
83 std::string_view values[7]{
84 "Directional", "Point", "Spot",
85 "Rect", "Disk", "Sphere", "Dome"};
86 int init{0};
87 };
88 void update(Light& n) { n.rebuild(); }
89 } mode;
90
91 // Common — always applies
92 struct : halp::color_chooser<"Color">
93 { void update(Light& n) { n.rebuild(); } } color;
94 struct : halp::hslider_f32<"Intensity", halp::range{0., 100., 1.}>
95 { void update(Light& n) { n.rebuild(); } } intensity;
96 // range=0 → infinite falloff (directional / dome ignore this field)
97 struct : halp::hslider_f32<"Range", halp::range{0., 1000., 0.}>
98 { void update(Light& n) { n.rebuild(); } } range;
99
100 struct : halp::combobox_t<"Falloff", Decay>
101 {
102 struct range
103 {
104 std::string_view values[4]{
105 "None", "Linear", "Quadratic (physical)", "Cubic"};
106 int init{2};
107 };
108 void update(Light& n) { n.rebuild(); }
109 } decay;
110
111 // Spot cone (radians via hsliders taking degrees; converted in cpp)
112 struct : halp::hslider_f32<"Inner cone °", halp::range{0., 90., 0.}>
113 { void update(Light& n) { n.rebuild(); } } inner_cone;
114 struct : halp::hslider_f32<"Outer cone °", halp::range{0., 90., 45.}>
115 { void update(Light& n) { n.rebuild(); } } outer_cone;
116
117 // Area shapes
118 struct : halp::hslider_f32<"Width", halp::range{0.01, 100., 1.}>
119 { void update(Light& n) { n.rebuild(); } } width;
120 struct : halp::hslider_f32<"Height", halp::range{0.01, 100., 1.}>
121 { void update(Light& n) { n.rebuild(); } } height;
122 struct : halp::hslider_f32<"Radius", halp::range{0.01, 100., 0.5}>
123 { void update(Light& n) { n.rebuild(); } } radius;
124
125 // Shadow settings
126 struct : halp::toggle<"Cast shadow">
127 { void update(Light& n) { n.rebuild(); } } cast_shadow;
128 struct : halp::hslider_f32<"Shadow bias", halp::range{0., 0.1, 0.001}>
129 { void update(Light& n) { n.rebuild(); } } shadow_bias;
130 struct : halp::hslider_f32<"Shadow normal bias", halp::range{0., 0.1, 0.01}>
131 { void update(Light& n) { n.rebuild(); } } shadow_normal_bias;
132
133 // Transform: position for positional lights, rotation encodes the
134 // direction used by Directional / Spot (local -Z mapped to the
135 // light direction, glTF / Vulkan convention).
136 struct : PositionControl
137 { void update(Light& n) { n.rebuild(); } } position;
138 struct : RotationControl
139 { void update(Light& n) { n.rebuild(); } } rotation;
140 } inputs;
141
142 struct outs
143 {
144 struct
145 {
146 halp_meta(name, "Scene");
147 ossia::scene_spec scene;
148 uint8_t dirty{0};
149 } scene_out;
150 } outputs;
151
152 // Built once from control values whenever a port's update() fires.
153 // operator()() just republishes m_state.
154 void rebuild();
155 void operator()();
156
157 // Render-thread hooks. init claims one RawLight slot; update packs
158 // color / intensity / type / local-direction / range / cone angles /
159 // decay / shadow into a RawLightData and uploads; release returns
160 // the slot. Final world-direction composition happens inside the
161 // preprocessor (parent-chain world matrix), so this slot carries
162 // only the node-local fields.
163 void init(score::gfx::RenderList& r, QRhiResourceUpdateBatch& res);
164 void update(
165 score::gfx::RenderList& r, QRhiResourceUpdateBatch& res,
167 void release(score::gfx::RenderList& r);
168
169 std::shared_ptr<ossia::scene_state> m_state;
170 int64_t m_version{0};
171 uint8_t m_pending_dirty{ossia::scene_port::dirty_lights};
172 // Stable id for the single light_component this node emits. Minted
173 // lazily on first rebuild() and reused across all subsequent rebuilds
174 // so downstream caches (preprocessor fingerprint, SER coherence key)
175 // stay keyed on identity, not pointer.
176 uint64_t m_light_stable_id{};
177 uint64_t m_xform_stable_id{};
178
180 score::gfx::GpuResourceRegistry::Slot raw_transform_slot;
181
182 // Ossia-facing snapshots. Written once in init() on the render
183 // thread; copied onto each emitted light_component / scene_transform
184 // raw_slot in operator()() on the execution thread.
185 ossia::gpu_slot_ref m_light_ref{};
186 ossia::gpu_slot_ref m_xform_ref{};
187};
188
189}
Definition Light.hpp:44
List of nodes to be rendered to an output.
Definition RenderList.hpp:33
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition Light.hpp:76
Definition Light.hpp:143
Definition TinyObj.hpp:98
Definition TinyObj.hpp:103
Definition Primitive.hpp:101
Definition MIDISync.hpp:128
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:236
Definition GpuResourceRegistry.hpp:84