Loading...
Searching...
No Matches
Instancer.hpp
1#pragma once
2#include "TransformHelper.hpp"
3
4#include <Threedim/TinyObj.hpp>
5#include <halp/buffer.hpp>
6#include <halp/controls.hpp>
7#include <halp/geometry.hpp>
8#include <halp/meta.hpp>
9
10#include <ossia/dataflow/geometry_port.hpp>
11
12#include <Gfx/Graph/GpuResourceRegistry.hpp>
13
14#include <cstdint>
15#include <memory>
16
17class QMatrix4x4;
18class QRhi;
19class QRhiBuffer;
20class QRhiCommandBuffer;
21class QRhiComputePipeline;
22class QRhiResourceUpdateBatch;
23class QRhiShaderResourceBindings;
24
25namespace score::gfx
26{
27class RenderList;
28struct Edge;
29}
30
31namespace Threedim
32{
33
34// GPU-instancing authoring node. Takes a scene containing a mesh and a GPU buffer
35// of per-instance transforms, plus optional colors and custom data, and emits a
36// scene_spec wrapping an instance_component that ScenePreprocessor forwards
37// downstream as the standard instance_transforms / instance_colors /
38// instance_custom auxiliary buffers.
39//
40// Consumer shaders read the per-instance attributes through the VERTEX_INPUTS
41// location 3..5 convention already in GeometryToBufferStrategies.hpp:
42// 3 = translation / rotation / transform_matrix, 4 = color0, 5 = scale/custom.
43//
44// Transform formats, packed floats per instance:
45// mat4 16 floats, column-major
46// trs 10 floats: 3 translation + 4 quaternion + 3 scale
47// translation 3 floats, rotation and scale identity
49{
50public:
51 halp_meta(name, "Instancer")
52 halp_meta(category, "Visuals/3D/Scene")
53 halp_meta(c_name, "instancer")
54 halp_meta(description, "Create GPU instances of a scene mesh using per-instance transforms, colors and custom data.")
55 halp_meta(authors, "ossia team")
56 halp_meta(
57 manual_url,
58 "https://ossia.io/score-docs/processes/instancer.html")
59 halp_meta(uuid, "5e8a2c7f-9b4d-4e3a-a1c6-2d7f0b3e8c4a")
60
61 enum TransformFormat
62 {
63 Mat4,
64 TRS,
65 Translation
66 };
67
68 enum InstanceTransformMode
69 {
70 FullMatrix,
71 TranslationOnly
72 };
73
74 struct ins
75 {
76 struct
77 {
78 halp_meta(name, "Scene In");
79 ossia::scene_spec scene;
80 uint8_t dirty{0};
81 } scene_in;
82
83 halp::gpu_buffer_input<"Transforms"> transforms;
84 halp::gpu_buffer_input<"Colors"> colors;
85 halp::gpu_buffer_input<"Custom"> custom;
86
87 // Optional point-cloud geometry input. When wired, its semantic attributes
88 // override the raw buffer inputs above: translation / position and
89 // transform_matrix feed the Transforms buffer in Translation and Mat4 mode,
90 // color0 feeds the Colors buffer. The `count` inlet is then overridden by the
91 // geometry's vertex_count, so shaderlib presets can feed Instancer directly
92 // without a glue repack.
93 struct
94 {
95 halp_meta(name, "Points");
96 halp::dynamic_gpu_geometry mesh;
97 float transform[16]{};
98 bool dirty_mesh = false;
99 bool dirty_transform = false;
100 } points;
101
102 // Port-driven rebuild: scalar controls trigger Instancer::rebuild().
103 // Upstream scene_in / buffer handles are detected in operator()()
104 // because they can change without a port-update event.
105 struct : halp::combobox_t<"Format", TransformFormat>
106 {
107 struct range
108 {
109 std::string_view values[3]{"mat4", "trs", "translation"};
110 int init{0};
111 };
112 void update(Instancer& n) { n.rebuild(); }
113 } format;
114
115 struct : halp::spinbox_i32<"Count", halp::irange{1, 1000000, 1}>
116 { void update(Instancer& n) { n.rebuild(); } } count;
117
118 // Optional TRS applied to the prototype before instancing — lets
119 // the node place the instanced cloud without a separate
120 // Transform3D upstream.
121 struct : PositionControl
122 { void update(Instancer& n) { n.rebuild(); } } position;
123 struct : RotationControl
124 { void update(Instancer& n) { n.rebuild(); } } rotation;
125 struct : ScaleControl
126 { void update(Instancer& n) { n.rebuild(); } } scale;
127
128 struct : halp::combobox_t<"Per-instance transform", InstanceTransformMode>
129 {
130 struct range
131 {
132 std::string_view values[2]{"Full matrix", "Translation only"};
133 int init{0};
134 };
135 void update(Instancer& n) { n.rebuild(); }
136 } transform_mode;
137 } inputs;
138
139 struct outs
140 {
141 struct
142 {
143 halp_meta(name, "Scene Out");
144 ossia::scene_spec scene;
145 uint8_t dirty{0};
146 } scene_out;
147 } outputs;
148
149 void rebuild();
150 void operator()();
151
152 void init(score::gfx::RenderList& r, QRhiResourceUpdateBatch& res);
153 void update(
154 score::gfx::RenderList& r, QRhiResourceUpdateBatch& res,
156 void runInitialPasses(
157 score::gfx::RenderList& r, QRhiCommandBuffer& cb,
158 QRhiResourceUpdateBatch*& res, score::gfx::Edge& e);
159 void release(score::gfx::RenderList& r);
160
161 enum BakeSourceIndex
162 {
163 BakeTransforms,
164 BakeRotations,
165 BakeScales,
166 BakeColors,
167 BakeCustom,
168 BakeSourceCount
169 };
171 {
172 QRhiBuffer* buffer{};
173 uint32_t offset{};
174 uint32_t stride{};
175 uint32_t components{};
176 };
177 struct Bake
178 {
179 BakeSource sources[BakeSourceCount]{};
180 uint32_t transform_kind{};
181 uint32_t count{};
182 bool full{};
183 float proto[16]{};
184 float proto_inverse_linear[16]{};
185 };
186
187 // Cache so we republish a stable shared_ptr when inputs haven't
188 // changed — ScenePreprocessor's identity caches stay warm.
189 std::shared_ptr<ossia::scene_state> m_wrapped_state;
190 uint8_t m_pending_dirty{0xFF};
191 CachedTRS m_cachedTRS{};
192 // Track input identity to detect when a rebuild is needed without
193 // relying on buffer-contents equality.
194 const ossia::scene_state* m_cached_in_state{};
195 // The VIEW, not just the handle. A producer that re-points a buffer at a
196 // different offset, or shrinks it to a smaller window of the same
197 // allocation, keeps the handle -- and the instance-count capacity clamp
198 // below depends on the size. Comparing handles alone would let a shrink go
199 // unnoticed and the cloud would draw past the end of its transforms.
201 {
202 void* handle{};
203 int64_t byte_offset{};
204 int64_t byte_size{};
205 friend bool operator==(const CachedView&, const CachedView&) = default;
206 };
207 static CachedView viewOf(const halp::gpu_buffer& b) noexcept
208 {
209 return {b.handle, (int64_t)b.byte_offset, (int64_t)b.byte_size};
210 }
211 CachedView m_cached_transforms{};
212 CachedView m_cached_colors{};
213 CachedView m_cached_custom{};
214 int32_t m_cached_count{-1};
215 int m_cached_format{-1};
216 // For the point-cloud input: cache the primary-buffer identity so we
217 // detect upstream handle replacements without poking every buffer
218 // every frame.
219 void* m_cached_points_buf{};
220 int64_t m_cached_points_vertices{-1};
221 // Fingerprint of ALL Points buffer handles (not just buffers[0]), so a
222 // reallocated secondary attribute buffer (transform_matrix / color0)
223 // that leaves the primary buffer + vertex count untouched still forces a
224 // rebuild instead of republishing a dangling handle.
225 uintptr_t m_cached_points_fingerprint{};
226 int64_t m_version_counter{0};
227
228 score::gfx::GpuResourceRegistry::Slot raw_transform_slot;
229 ossia::gpu_slot_ref m_xform_ref{};
230
231 bool refresh();
232 bool ensureBakeTarget(QRhiBuffer*& buf, quint32 bytes, const char* name);
233 bool prepareBake(
234 const BakeSource (&sources)[BakeSourceCount], uint32_t transform_kind,
235 uint32_t& count, bool full, const QMatrix4x4& proto);
236
237 QRhi* m_rhi{};
238 Bake m_bake{};
239 bool m_baking{};
240 QRhiBuffer* m_bakedTransforms{};
241 QRhiBuffer* m_bakedColors{};
242 QRhiBuffer* m_bakedCustom{};
243 QRhiBuffer* m_bakeParams{};
244 QRhiBuffer* m_bakeDummy{};
245 QRhiShaderResourceBindings* m_bakeSrb{};
246 QRhiComputePipeline* m_bakePipeline{};
247 bool m_bakeSrbDirty{true};
248 bool m_warnedBakeFallback{};
249};
250
251}
Definition Instancer.hpp:49
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 TransformHelper.hpp:26
Definition Instancer.hpp:178
Definition Instancer.hpp:171
Definition Instancer.hpp:201
Definition Instancer.hpp:75
Definition Instancer.hpp:140
Definition TinyObj.hpp:98
Definition TinyObj.hpp:103
Definition TinyObj.hpp:107
Definition TinyObj.hpp:27
Definition MIDISync.hpp:128
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:236
Definition GpuResourceRegistry.hpp:84