39 halp_meta(name,
"Camera Switch")
40 halp_meta(category,
"Visuals/3D/Scene")
41 halp_meta(c_name,
"camera_switch")
42 halp_meta(description,
"Select or blend up to four scene cameras, including their transforms and projection parameters.")
43 halp_meta(authors,
"ossia team")
46 "https://ossia.io/score-docs/processes/camera-switch.html")
47 halp_meta(uuid,
"d1e8c4b7-6a32-4f9e-b5d8-2c4f3a1e8b6d")
53 halp_meta(name,
"Camera 0");
54 ossia::scene_spec scene;
59 halp_meta(name,
"Camera 1");
60 ossia::scene_spec scene;
65 halp_meta(name,
"Camera 2");
66 ossia::scene_spec scene;
71 halp_meta(name,
"Camera 3");
72 ossia::scene_spec scene;
83 struct Mode : halp::enum_t<CameraMode, "Mode">
87 std::string_view values[2]{
"Select",
"Blend"};
88 CameraMode init{Select};
93 struct : halp::spinbox_i32<
"Index", halp::irange{0, 3, 0}>
97 struct : halp::xyzw_spinboxes_f32<
"Weights", halp::range{-10000., 10000., 0.}>
98 {
void update(CameraSwitch& n) { n.rebuild(); } } weights;
105 halp_meta(name,
"Scene Out");
106 ossia::scene_spec scene;
115 ossia::scene_node_id m_id{};
116 std::shared_ptr<ossia::scene_state> m_state;
117 int64_t m_version{0};
118 uint8_t m_pending_dirty{ossia::scene_port::dirty_transform};
121 const ossia::scene_state* m_cached_cam_state[4]{};
122 int64_t m_cached_cam_ver[4]{-1, -1, -1, -1};
126 static bool extractCameraPose(
127 const ossia::scene_spec& in, ossia::scene_transform& xform,
128 ossia::camera_component& cam)
130 if(!in.state || !in.state->roots || in.state->roots->empty())
132 const auto& root = (*in.state->roots)[0];
133 if(!root || !root->children)
136 bool gotXform =
false;
138 for(
const auto& child : *root->children)
140 if(
auto* t = ossia::get_if<ossia::scene_transform>(&child))
145 else if(
auto* c = ossia::get_if<ossia::camera_component_ptr>(&child))
154 return gotXform && gotCam;
159 const int mode = inputs.mode.value;
160 if(mode == ins::CameraMode::Select)
165 m_pending_dirty = 0xFF;
171 inputs.weights.value.x, inputs.weights.value.y,
172 inputs.weights.value.z, inputs.weights.value.w};
173 for(
float& x : w) x = x > 0.f ? x : 0.f;
175 const ossia::scene_spec* inputsArr[4]{
176 &inputs.cam0.scene, &inputs.cam1.scene,
177 &inputs.cam2.scene, &inputs.cam3.scene};
180 ossia::scene_transform xforms[4]{};
181 ossia::camera_component cams[4]{};
182 float effWeights[4]{};
184 for(
int i = 0; i < 4; ++i)
186 if(w[i] <= 0.f)
continue;
187 if(!extractCameraPose(*inputsArr[i], xforms[i], cams[i]))
189 effWeights[i] = w[i];
198 m_state->roots.reset();
199 m_state->active_camera_id = {};
201 m_state->version = m_version;
204 m_pending_dirty = 0xFF;
207 for(
float& x : effWeights) x /= wsum;
214 ossia::scene_transform outX{};
215 outX.scale[0] = outX.scale[1] = outX.scale[2] = 0.f;
216 QQuaternion qSum(0, 0, 0, 0);
217 for(
int i = 0; i < 4; ++i)
219 if(effWeights[i] <= 0.f)
continue;
220 const float wi = effWeights[i];
221 outX.translation[0] += xforms[i].translation[0] * wi;
222 outX.translation[1] += xforms[i].translation[1] * wi;
223 outX.translation[2] += xforms[i].translation[2] * wi;
224 outX.scale[0] += xforms[i].scale[0] * wi;
225 outX.scale[1] += xforms[i].scale[1] * wi;
226 outX.scale[2] += xforms[i].scale[2] * wi;
232 xforms[i].rotation[3], xforms[i].rotation[0],
233 xforms[i].rotation[1], xforms[i].rotation[2]);
234 if(QQuaternion::dotProduct(qSum, qi) < 0.f)
239 outX.rotation[0] = qSum.x();
240 outX.rotation[1] = qSum.y();
241 outX.rotation[2] = qSum.z();
242 outX.rotation[3] = qSum.scalar();
247 ossia::camera_component outCam{};
249 outCam.aspect_ratio = 0.f;
254 outCam.physical.focal_length = 0.f;
255 outCam.physical.focus_distance = 0.f;
256 outCam.physical.fstop = 0.f;
265 for(
int i = 0; i < 4; ++i)
267 if(effWeights[i] > best)
269 best = effWeights[i];
273 outCam.projection = cams[dominant].projection;
275 for(
int i = 0; i < 4; ++i)
277 if(effWeights[i] <= 0.f)
continue;
278 const float wi = effWeights[i];
279 outCam.yfov += cams[i].yfov * wi;
280 outCam.aspect_ratio += cams[i].aspect_ratio * wi;
281 outCam.xmag += cams[i].xmag * wi;
282 outCam.ymag += cams[i].ymag * wi;
283 outCam.znear += cams[i].znear * wi;
284 outCam.zfar += cams[i].zfar * wi;
285 outCam.physical.focal_length += cams[i].physical.focal_length * wi;
286 outCam.physical.focus_distance += cams[i].physical.focus_distance * wi;
287 outCam.physical.fstop += cams[i].physical.fstop * wi;
293 m_state = std::make_shared<ossia::scene_state>();
294 m_id.value =
reinterpret_cast<std::uintptr_t
>(
this) | 0x1u;
297 auto camPtr = std::make_shared<ossia::camera_component>(std::move(outCam));
298 auto children = std::make_shared<std::vector<ossia::scene_payload>>();
299 children->push_back(outX);
300 children->push_back(ossia::camera_component_ptr(std::move(camPtr)));
302 auto node = std::make_shared<ossia::scene_node>();
304 node->children = std::move(children);
306 auto roots = std::make_shared<std::vector<ossia::scene_node_ptr>>();
307 roots->push_back(std::move(node));
309 m_state->roots = std::move(roots);
310 m_state->active_camera_id = m_id;
312 m_state->version = m_version;
313 m_pending_dirty = ossia::scene_port::dirty_transform;
322 const ossia::scene_spec* cams[4]{
323 &inputs.cam0.scene, &inputs.cam1.scene,
324 &inputs.cam2.scene, &inputs.cam3.scene};
325 bool upstream_changed =
false;
326 for(
int i = 0; i < 4; ++i)
328 const auto* s = cams[i]->state.get();
329 const int64_t v = s ? s->version : -1;
330 if(m_cached_cam_state[i] != s || m_cached_cam_ver[i] != v)
332 upstream_changed =
true;
333 m_cached_cam_state[i] = s;
334 m_cached_cam_ver[i] = v;
338 if(inputs.mode.value == ins::CameraMode::Select)
343 const int idx = inputs.index.value;
344 const ossia::scene_spec* picked =
nullptr;
347 case 0: picked = &inputs.cam0.scene;
break;
348 case 1: picked = &inputs.cam1.scene;
break;
349 case 2: picked = &inputs.cam2.scene;
break;
350 case 3: picked = &inputs.cam3.scene;
break;
351 default: picked = &inputs.cam0.scene;
break;
353 outputs.scene_out.scene.state = picked->state;
354 outputs.scene_out.dirty
355 = (upstream_changed && picked->state) ? 0xFF : 0;
360 if(!m_state || upstream_changed)
362 outputs.scene_out.scene.state = m_state;
363 outputs.scene_out.dirty = m_pending_dirty;