39 halp_meta(name,
"Camera Switch")
40 halp_meta(category,
"Visuals/3D/Scene")
41 halp_meta(c_name,
"camera_switch")
42 halp_meta(authors,
"ossia team")
45 "https://ossia.io/score-docs/processes/camera-switch.html")
46 halp_meta(uuid,
"d1e8c4b7-6a32-4f9e-b5d8-2c4f3a1e8b6d")
52 halp_meta(name,
"Camera 0");
53 ossia::scene_spec scene;
58 halp_meta(name,
"Camera 1");
59 ossia::scene_spec scene;
64 halp_meta(name,
"Camera 2");
65 ossia::scene_spec scene;
70 halp_meta(name,
"Camera 3");
71 ossia::scene_spec scene;
82 struct Mode : halp::enum_t<CameraMode, "Mode">
86 std::string_view values[2]{
"Select",
"Blend"};
87 CameraMode init{Select};
92 struct : halp::spinbox_i32<
"Index", halp::irange{0, 3, 0}>
96 struct : halp::xyzw_spinboxes_f32<
"Weights", halp::range{-10000., 10000., 0.}>
97 {
void update(CameraSwitch& n) { n.rebuild(); } } weights;
104 halp_meta(name,
"Scene Out");
105 ossia::scene_spec scene;
114 ossia::scene_node_id m_id{};
115 std::shared_ptr<ossia::scene_state> m_state;
116 int64_t m_version{0};
117 uint8_t m_pending_dirty{ossia::scene_port::dirty_transform};
120 const ossia::scene_state* m_cached_cam_state[4]{};
121 int64_t m_cached_cam_ver[4]{-1, -1, -1, -1};
125 static bool extractCameraPose(
126 const ossia::scene_spec& in, ossia::scene_transform& xform,
127 ossia::camera_component& cam)
129 if(!in.state || !in.state->roots || in.state->roots->empty())
131 const auto& root = (*in.state->roots)[0];
132 if(!root || !root->children)
135 bool gotXform =
false;
137 for(
const auto& child : *root->children)
139 if(
auto* t = ossia::get_if<ossia::scene_transform>(&child))
144 else if(
auto* c = ossia::get_if<ossia::camera_component_ptr>(&child))
153 return gotXform && gotCam;
158 const int mode = inputs.mode.value;
159 if(mode == ins::CameraMode::Select)
164 m_pending_dirty = 0xFF;
170 inputs.weights.value.x, inputs.weights.value.y,
171 inputs.weights.value.z, inputs.weights.value.w};
172 for(
float& x : w) x = x > 0.f ? x : 0.f;
174 const ossia::scene_spec* inputsArr[4]{
175 &inputs.cam0.scene, &inputs.cam1.scene,
176 &inputs.cam2.scene, &inputs.cam3.scene};
179 ossia::scene_transform xforms[4]{};
180 ossia::camera_component cams[4]{};
181 float effWeights[4]{};
183 for(
int i = 0; i < 4; ++i)
185 if(w[i] <= 0.f)
continue;
186 if(!extractCameraPose(*inputsArr[i], xforms[i], cams[i]))
188 effWeights[i] = w[i];
197 m_state->roots.reset();
198 m_state->active_camera_id = {};
200 m_state->version = m_version;
203 m_pending_dirty = 0xFF;
206 for(
float& x : effWeights) x /= wsum;
213 ossia::scene_transform outX{};
214 outX.scale[0] = outX.scale[1] = outX.scale[2] = 0.f;
215 QQuaternion qSum(0, 0, 0, 0);
216 for(
int i = 0; i < 4; ++i)
218 if(effWeights[i] <= 0.f)
continue;
219 const float wi = effWeights[i];
220 outX.translation[0] += xforms[i].translation[0] * wi;
221 outX.translation[1] += xforms[i].translation[1] * wi;
222 outX.translation[2] += xforms[i].translation[2] * wi;
223 outX.scale[0] += xforms[i].scale[0] * wi;
224 outX.scale[1] += xforms[i].scale[1] * wi;
225 outX.scale[2] += xforms[i].scale[2] * wi;
231 xforms[i].rotation[3], xforms[i].rotation[0],
232 xforms[i].rotation[1], xforms[i].rotation[2]);
233 if(QQuaternion::dotProduct(qSum, qi) < 0.f)
238 outX.rotation[0] = qSum.x();
239 outX.rotation[1] = qSum.y();
240 outX.rotation[2] = qSum.z();
241 outX.rotation[3] = qSum.scalar();
246 ossia::camera_component outCam{};
248 outCam.aspect_ratio = 0.f;
253 outCam.physical.focal_length = 0.f;
254 outCam.physical.focus_distance = 0.f;
255 outCam.physical.fstop = 0.f;
264 for(
int i = 0; i < 4; ++i)
266 if(effWeights[i] > best)
268 best = effWeights[i];
272 outCam.projection = cams[dominant].projection;
274 for(
int i = 0; i < 4; ++i)
276 if(effWeights[i] <= 0.f)
continue;
277 const float wi = effWeights[i];
278 outCam.yfov += cams[i].yfov * wi;
279 outCam.aspect_ratio += cams[i].aspect_ratio * wi;
280 outCam.xmag += cams[i].xmag * wi;
281 outCam.ymag += cams[i].ymag * wi;
282 outCam.znear += cams[i].znear * wi;
283 outCam.zfar += cams[i].zfar * wi;
284 outCam.physical.focal_length += cams[i].physical.focal_length * wi;
285 outCam.physical.focus_distance += cams[i].physical.focus_distance * wi;
286 outCam.physical.fstop += cams[i].physical.fstop * wi;
292 m_state = std::make_shared<ossia::scene_state>();
293 m_id.value =
reinterpret_cast<std::uintptr_t
>(
this) | 0x1u;
296 auto camPtr = std::make_shared<ossia::camera_component>(std::move(outCam));
297 auto children = std::make_shared<std::vector<ossia::scene_payload>>();
298 children->push_back(outX);
299 children->push_back(ossia::camera_component_ptr(std::move(camPtr)));
301 auto node = std::make_shared<ossia::scene_node>();
303 node->children = std::move(children);
305 auto roots = std::make_shared<std::vector<ossia::scene_node_ptr>>();
306 roots->push_back(std::move(node));
308 m_state->roots = std::move(roots);
309 m_state->active_camera_id = m_id;
311 m_state->version = m_version;
312 m_pending_dirty = ossia::scene_port::dirty_transform;
321 const ossia::scene_spec* cams[4]{
322 &inputs.cam0.scene, &inputs.cam1.scene,
323 &inputs.cam2.scene, &inputs.cam3.scene};
324 bool upstream_changed =
false;
325 for(
int i = 0; i < 4; ++i)
327 const auto* s = cams[i]->state.get();
328 const int64_t v = s ? s->version : -1;
329 if(m_cached_cam_state[i] != s || m_cached_cam_ver[i] != v)
331 upstream_changed =
true;
332 m_cached_cam_state[i] = s;
333 m_cached_cam_ver[i] = v;
337 if(inputs.mode.value == ins::CameraMode::Select)
342 const int idx = inputs.index.value;
343 const ossia::scene_spec* picked =
nullptr;
346 case 0: picked = &inputs.cam0.scene;
break;
347 case 1: picked = &inputs.cam1.scene;
break;
348 case 2: picked = &inputs.cam2.scene;
break;
349 case 3: picked = &inputs.cam3.scene;
break;
350 default: picked = &inputs.cam0.scene;
break;
352 outputs.scene_out.scene.state = picked->state;
353 outputs.scene_out.dirty
354 = (upstream_changed && picked->state) ? 0xFF : 0;
359 if(!m_state || upstream_changed)
361 outputs.scene_out.scene.state = m_state;
362 outputs.scene_out.dirty = m_pending_dirty;