载入中...
搜索中...
未找到
CameraController.cpp
浏览该文件的文档.
4
6#include "common/Capability.h"
7#include "common/Json.h"
11#include "scene/SceneNodeRef.h"
12
13#include <simplesquirrel/simplesquirrel.hpp>
14
15#include <algorithm>
16#include <cmath>
17#include <limits>
18#include <sstream>
19
20namespace eve::camera {
21
22namespace {
23
24std::string jsonString(const std::string& value) {
25 std::ostringstream out;
26 out << '"';
27 for (const unsigned char c : value) {
28 switch (c) {
29 case '"': out << "\\\""; break;
30 case '\\': out << "\\\\"; break;
31 case '\b': out << "\\b"; break;
32 case '\f': out << "\\f"; break;
33 case '\n': out << "\\n"; break;
34 case '\r': out << "\\r"; break;
35 case '\t': out << "\\t"; break;
36 default:
37 if (c < 0x20)
38 out << "\\u00" << "0123456789abcdef"[c >> 4] << "0123456789abcdef"[c & 0xf];
39 else
40 out << static_cast<char>(c);
41 }
42 }
43 out << '"';
44 return out.str();
45}
46
47glm::vec3 jsonVec3(eve::json::Value value, glm::vec3 fallback) {
48 if (!value.isArray() || value.size() != 3) return fallback;
49 return {value.at(0).asFloat(fallback.x), value.at(1).asFloat(fallback.y),
50 value.at(2).asFloat(fallback.z)};
51}
52
53} // namespace
54
56
58
60 cap::removeListener<action::IActionCameraCueSink>(this);
61 if (photoModeAuthority_) cap::removeListener<IPhotoModeFieldSink>(this);
62}
63
65 for (std::size_t index = 0; index < cap::listenerCount<action::IActionCameraCueSink>(); ++index)
66 if (cap::listenerAt<action::IActionCameraCueSink>(index) == this) return true;
67 return false;
68}
69
71 const bool current = getActionCuesEnabled();
72 if (enabled && !current)
73 cap::addListener<action::IActionCameraCueSink>(this);
74 else if (!enabled && current)
75 cap::removeListener<action::IActionCameraCueSink>(this);
76}
77
78bool CameraController::supports(const LogicalId&) const noexcept { return getActionCuesEnabled(); }
79
82 addImpulse(static_cast<float>(binding.positionAmplitude), static_cast<float>(binding.rotationAmplitude),
83 static_cast<float>(binding.duration.seconds()), binding.seed);
84 addFovImpulse(static_cast<float>(binding.fovAmplitude), static_cast<float>(binding.duration.seconds()));
86}
87
89 if(enabled==photoModeAuthority_)return;
90 if(enabled)cap::addListener<IPhotoModeFieldSink>(this);else cap::removeListener<IPhotoModeFieldSink>(this);
91 photoModeAuthority_=enabled;
92}
98 "camera roll must be finite","camera.photoMode.roll"));
99 cameraRollDeg_=degrees;
100 if(cam_){
101 glm::vec3 eye(cam_->getEyeX(),cam_->getEyeY(),cam_->getEyeZ());
102 glm::vec3 target(cam_->getTargetX(),cam_->getTargetY(),cam_->getTargetZ());
103 glm::vec3 forward=target-eye;const float length=glm::length(forward);
104 if(length>1e-6f){forward/=length;glm::vec3 up(0,1,0);if(std::abs(glm::dot(forward,up))>0.999f)up={0,0,-1};
105 glm::vec3 right=glm::normalize(glm::cross(forward,up));up=glm::normalize(glm::cross(right,forward));
106 const float roll=glm::radians(cameraRollDeg_);up=up*std::cos(roll)+right*std::sin(roll);cam_->setUp(up.x,up.y,up.z);}
107 }
108 return Result<void>::success();
109}
111 auto bad=[&](const std::string&m){return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,m,a.field,{},"camera.photoMode"));};
112 if(!cam_)return bad("photo-mode camera authority requires a bound Camera3D");
113 if(a.domain==PhotoModeDomain::PostFx)return applyPcgPostFx(a);
114 if(a.domain!=PhotoModeDomain::Camera)return bad("unsupported photo-mode camera domain");
115 auto number=std::get_if<float>(&a.value);if(!number||!std::isfinite(*number))return bad("camera field requires a finite float");
116 if(a.field=="m_fieldOfView"){if(*number<=0||*number>=180)return bad("field of view must be in (0,180)");fovDeg_=*number;cam_->setFov(*number);}
117 else if(a.field=="m_pcgCullinDistance"){pcgCullingDistance_=*number;const float distance=std::max(0.f,cam_->getFarClip()+*number);for(int layer=0;layer<32;++layer)cam_->setLayerCullDistance(layer,distance);}
118 else if(a.field=="m_cameraAperture"){if(*number<=0)return bad("camera aperture must be positive");cam_->setPhysicalLens(*number,cam_->getFocalLength());}
119 else if(a.field=="m_cameraFocalLength"){if(*number<=0)return bad("camera focal length must be positive");cam_->setPhysicalLens(cam_->getAperture(),*number);}
120 else if(a.field=="m_cameraRoll")return setCameraRoll(*number);
121 else if(a.field=="m_farClipPlane"){if(*number<=cam_->getNearClip())return bad("far clip must exceed near clip");cam_->setClipPlanes(cam_->getNearClip(),*number);const float distance=std::max(0.f,*number+pcgCullingDistance_);for(int layer=0;layer<32;++layer)cam_->setLayerCullDistance(layer,distance);}
122 else return bad("unsupported photo-mode camera field");
123 return Result<void>::success();
124}
125
126Result<void> CameraController::applyPcgPostFx(const PhotoModeAssignment&a){
127 auto bad=[&](const std::string&m){return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,m,a.field,{},"camera.photoMode.postFx"));};
128 PcgPhotoModePostFxState next=pcgPostFxState_;
129 auto number=std::get_if<float>(&a.value);auto integer=std::get_if<int64_t>(&a.value);auto flag=std::get_if<bool>(&a.value);
130 auto finitePositive=[&](float* target){if(!number||!std::isfinite(*number)||*number<=0.f)return false;*target=*number;return true;};
131 auto finiteNonNegative=[&](float* target){if(!number||!std::isfinite(*number)||*number<0.f)return false;*target=*number;return true;};
132 if(a.field=="m_postFXExposure"){if(!number||!std::isfinite(*number))return bad("exposure requires a finite float");next.postFxExposure=*number;}
133 else if(a.field=="m_postFXExposureMode"){if(!integer||*integer<0)return bad("exposure mode requires a non-negative integer");next.postFxExposureMode=*integer;}
134 else if(a.field=="m_dofActive"){if(!flag)return bad("depth-of-field active requires a bool");next.depthOfFieldActive=*flag;}
135 else if(a.field=="m_autoDOFFocus"){if(!flag)return bad("automatic focus requires a bool");next.autoDepthOfFieldFocus=*flag;}
136 else if(a.field=="m_dofFocusDistance"){if(!finitePositive(&next.focusDistance))return bad("focus distance must be positive and finite");}
137 else if(a.field=="m_dofAperture"){if(!finitePositive(&next.aperture))return bad("aperture must be positive and finite");}
138 else if(a.field=="m_dofFocalLength"){if(!finitePositive(&next.focalLength))return bad("focal length must be positive and finite");}
139 else if(a.field=="m_dofKernelSize"){if(!integer||*integer<0||*integer>3)return bad("kernel size must be in [0,3]");next.kernelSize=*integer;}
140 else if(a.field=="m_savedDofFocusMode"){if(!integer||*integer<0)return bad("saved focus mode requires a non-negative integer");next.savedFocusMode=*integer;}
141 else if(a.field=="m_dofFocusModeHDRP"){if(!integer||*integer<0)return bad("HDRP focus mode requires a non-negative integer");next.focusModeHdrp=*integer;}
142 else if(a.field=="m_dofFocusModeURP"){if(!integer||*integer<0)return bad("URP focus mode requires a non-negative integer");next.focusModeUrp=*integer;}
143 else if(a.field=="m_dofQualityHDRP"){if(!integer||*integer<0)return bad("HDRP quality requires a non-negative integer");next.qualityHdrp=*integer;}
144 else if(a.field=="m_dofNearBlurStart"){if(!finiteNonNegative(&next.nearBlurStart))return bad("near blur start must be finite and non-negative");}
145 else if(a.field=="m_dofNearBlurEnd"){if(!finiteNonNegative(&next.nearBlurEnd))return bad("near blur end must be finite and non-negative");}
146 else if(a.field=="m_dofFarBlurStart"){if(!finiteNonNegative(&next.farBlurStart))return bad("far blur start must be finite and non-negative");}
147 else if(a.field=="m_dofFarBlurEnd"){if(!finiteNonNegative(&next.farBlurEnd))return bad("far blur end must be finite and non-negative");}
148 else if(a.field=="m_dofStartBlurURP"){if(!finiteNonNegative(&next.blurStartUrp))return bad("URP blur start must be finite and non-negative");}
149 else if(a.field=="m_dofEndBlurURP"){if(!finiteNonNegative(&next.blurEndUrp))return bad("URP blur end must be finite and non-negative");}
150 else if(a.field=="m_dofMaxRadiusBlur"){if(!finiteNonNegative(&next.maximumBlurRadius))return bad("maximum blur radius must be finite and non-negative");}
151 else if(a.field=="m_dofHighQualityURP"){if(!flag)return bad("URP high quality requires a bool");next.highQualityUrp=*flag;}
152 else return bad("unsupported photo-mode post-processing field");
153 pcgPostFxState_=next;projectPcgPostFx();return Result<void>::success();
154}
155
156void CameraController::projectPcgPostFx(){
157 if(!cam_)return;
158 const auto&s=pcgPostFxState_;
159 cam_->setExposure(s.postFxExposure);
160 cam_->setAutoExposure(s.postFxExposureMode!=0);
161 cam_->setPhysicalLens(s.aperture,s.focalLength);
162 if(!s.depthOfFieldActive){cam_->clearDepthOfField();return;}
163 const float urpRange=std::max(0.01f,s.blurEndUrp-s.blurStartUrp);
164 const float hdrpRange=std::max(0.01f,s.farBlurStart-s.nearBlurEnd);
165 const float focusRange=s.focusModeUrp==0?urpRange:hdrpRange;
166 const float qualityScale=(s.highQualityUrp?1.5f:1.f)*(1.f+0.25f*float(s.qualityHdrp));
167 const float blurPixels=std::max(0.01f,s.maximumBlurRadius*qualityScale*float(s.kernelSize+1));
168 cam_->setDepthOfField(s.focusDistance,blurPixels,focusRange);
169}
171
173
174void CameraController::setTarget(float x, float y, float z) {
175 targetNodeHost_.clear();
176 targetNodeId_.clear();
177 target_ = glm::vec3(x, y, z);
178}
179
181 targetNodeHost_ = node ? node->getHostName() : std::string();
182 targetNodeId_ = node ? node->getNodeId() : std::string();
183}
184
185float CameraController::getTargetX() const { return target_.x; }
186float CameraController::getTargetY() const { return target_.y; }
187float CameraController::getTargetZ() const { return target_.z; }
188
189void CameraController::setOffset(float x, float y, float z) { offset_ = glm::vec3(x, y, z); }
190
191void CameraController::setLookAhead(float x, float y, float z) { lookAhead_ = glm::vec3(x, y, z); }
192
193void CameraController::setSecondaryTarget(float x, float y, float z) {
194 secondaryTarget_ = glm::vec3(x, y, z);
195 hasSecondaryTarget_ = true;
196}
197
199 secondaryTarget_ = glm::vec3(0.f);
200 hasSecondaryTarget_ = false;
201}
202
203float CameraController::getSecondaryTargetX() const { return secondaryTarget_.x; }
204float CameraController::getSecondaryTargetY() const { return secondaryTarget_.y; }
205float CameraController::getSecondaryTargetZ() const { return secondaryTarget_.z; }
206
207bool CameraController::validMode(const std::string& mode) {
208 return mode == "follow" || mode == "orbit" || mode == "topdown" || mode == "firstperson" || mode == "cinematic" ||
209 mode == "lockon";
210}
211
212void CameraController::setMode(const std::string& mode) {
213 if (validMode(mode)) mode_ = mode;
214}
215
216std::string CameraController::getMode() const { return mode_; }
217
218void CameraController::setRadius(float r) { radius_ = glm::clamp(r, minimumRadius_, maximumRadius_); }
220 if (!std::isfinite(minimum) || !std::isfinite(maximum) || minimum <= 0.f || maximum < minimum)
221 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "camera radius limits are invalid", "camera.radiusLimits"));
222 minimumRadius_ = minimum; maximumRadius_ = maximum; radius_ = glm::clamp(radius_, minimum, maximum);
223 return Result<void>::success();
224}
225void CameraController::setAzimuth(float deg) { azimuthDeg_ = deg; }
226void CameraController::setElevation(float deg) { elevationDeg_ = glm::clamp(deg, -89.f, 89.f); }
227void CameraController::setOrbitSpeed(float degPerSec) { orbitSpeedDeg_ = degPerSec; }
228
229void CameraController::setYaw(float deg) { yawDeg_ = deg; }
230void CameraController::setPitch(float deg) { pitchDeg_ = glm::clamp(deg, -89.f, 89.f); }
231void CameraController::addInput(float yawDeltaDeg, float pitchDeltaDeg, float zoomDelta) {
232 yawDeg_ += yawDeltaDeg;
233 azimuthDeg_ += yawDeltaDeg;
234 setPitch(pitchDeg_ + pitchDeltaDeg);
235 setElevation(elevationDeg_ + pitchDeltaDeg);
236 setRadius(radius_ + zoomDelta);
237}
238
239void CameraController::setComposition(float screenX, float screenY) {
240 composition_ = glm::clamp(glm::vec2(screenX, screenY), glm::vec2(-0.9f), glm::vec2(0.9f));
241}
242
243void CameraController::setDeadZone(float radius) { deadZone_ = std::max(0.f, radius); }
244void CameraController::setFov(float degrees) { fovDeg_ = glm::clamp(degrees, 1.f, 179.f); }
245float CameraController::getFov() const { return fovDeg_; }
246
247void CameraController::setSmooth(float damping) {
248 smooth_ = std::max(0.f, damping);
249 positionSmooth_ = smooth_;
250 targetSmooth_ = smooth_;
251}
252void CameraController::setPositionSmooth(float damping) { positionSmooth_ = std::max(0.f, damping); }
253void CameraController::setTargetSmooth(float damping) { targetSmooth_ = std::max(0.f, damping); }
254
255void CameraController::setMaxSpeed(float unitsPerSec) { maxSpeed_ = std::max(0.f, unitsPerSec); }
256
257void CameraController::setCollisionEnabled(bool enabled) { collisionEnabled_ = enabled; }
258void CameraController::setCollisionRadius(float radius) { collisionRadius_ = std::max(0.f, radius); }
259void CameraController::setCollisionRecovery(float damping) { collisionRecovery_ = std::max(0.f, damping); }
260void CameraController::setCollisionMask(uint64_t maskBits) { collisionMask_ = maskBits; }
261void CameraController::setCollisionIgnoredBody(int bodyId) { collisionIgnoredBody_ = bodyId; }
262void CameraController::clearCollisionBoxes() { collisionBoxes_.clear(); }
263void CameraController::addCollisionBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
264 CollisionBox b;
265 b.min = glm::min(glm::vec3(minX, minY, minZ), glm::vec3(maxX, maxY, maxZ));
266 b.max = glm::max(glm::vec3(minX, minY, minZ), glm::vec3(maxX, maxY, maxZ));
267 collisionBoxes_.push_back(b);
268}
269bool CameraController::isObstructed() const { return obstructed_; }
270int CameraController::getCollisionBodyId() const { return collisionBodyId_; }
271
272bool CameraController::addRig(const std::string& name, const std::string& mode, int priority) {
273 if (name.empty() || !validMode(mode)) return false;
274 for (const auto& rig : rigs_)
275 if (rig.name == name) return false;
276 Rig rig;
277 rig.name = name;
278 rig.mode = mode;
279 rig.priority = priority;
280 rigs_.push_back(rig);
282 directorEnabled_ = true;
283 return true;
284}
285
286bool CameraController::removeRig(const std::string& name) {
287 auto it = std::find_if(rigs_.begin(), rigs_.end(), [&](const Rig& r) { return r.name == name; });
288 if (it == rigs_.end()) return false;
289 rigs_.erase(it);
290 if (activeRig_ == name) activeRig_.clear();
291 return true;
292}
293
294bool CameraController::setRigPriority(const std::string& name, int priority) {
295 for (auto& rig : rigs_)
296 if (rig.name == name) {
297 rig.priority = priority;
298 return true;
299 }
300 return false;
301}
302
303bool CameraController::setRigEnabled(const std::string& name, bool enabled) {
304 for (auto& rig : rigs_)
305 if (rig.name == name) {
306 rig.enabled = enabled;
307 return true;
308 }
309 return false;
310}
311
312bool CameraController::saveRigState(const std::string& name) {
313 for (auto& rig : rigs_) {
314 if (rig.name != name) continue;
315 rig.target = target_;
316 rig.offset = offset_;
317 rig.lookAhead = lookAhead_;
318 rig.composition = composition_;
319 rig.radius = radius_;
320 rig.azimuth = azimuthDeg_;
321 rig.elevation = elevationDeg_;
322 rig.yaw = yawDeg_;
323 rig.pitch = pitchDeg_;
324 rig.fov = fovDeg_;
325 rig.smooth = smooth_;
326 rig.maxSpeed = maxSpeed_;
327 return true;
328 }
329 return false;
330}
331
332bool CameraController::activateRig(const std::string& name, float blendTime) {
333 auto it = std::find_if(rigs_.begin(), rigs_.end(), [&](const Rig& r) { return r.name == name && r.enabled; });
334 if (it == rigs_.end()) return false;
335 const View from = initialized_ ? cur_ : desired();
336 activeRig_ = name;
337 mode_ = it->mode;
338 target_ = it->target;
339 offset_ = it->offset;
340 lookAhead_ = it->lookAhead;
341 composition_ = it->composition;
342 radius_ = it->radius;
343 azimuthDeg_ = it->azimuth;
344 elevationDeg_ = it->elevation;
345 yawDeg_ = it->yaw;
346 pitchDeg_ = it->pitch;
347 fovDeg_ = it->fov;
348 setSmooth(it->smooth);
349 maxSpeed_ = it->maxSpeed;
350 const bool wasBlending = blending_;
351 blending_ = false;
352 const View to = desired();
353 blending_ = wasBlending;
354 if (blendTime > 0.f) {
355 blendFrom_ = from;
356 blendTo_ = to;
357 blendDur_ = blendTime;
358 blendT_ = 0.f;
359 blending_ = true;
360 } else {
361 blending_ = false;
362 }
363 return true;
364}
365
366std::string CameraController::getActiveRig() const { return activeRig_; }
367
368void CameraController::addImpulse(float positionAmplitude, float rotationAmplitude, float duration, unsigned int seed) {
369 if (duration <= 0.f || (positionAmplitude <= 0.f && rotationAmplitude <= 0.f)) return;
370 impulses_.push_back({std::max(0.f, positionAmplitude), std::max(0.f, rotationAmplitude), duration, 0.f, seed, 0.f});
371}
373 if (duration <= 0.f || degrees == 0.f) return;
374 impulses_.push_back({0.f, 0.f, duration, 0.f, 0u, degrees});
375}
376void CameraController::clearImpulses() { impulses_.clear(); }
377
378void CameraController::addView(const std::string& name, float ex, float ey, float ez, float tx, float ty, float tz) {
379 View v;
380 v.name = name;
381 v.eye = glm::vec3(ex, ey, ez);
382 v.target = glm::vec3(tx, ty, tz);
383 v.fov = fovDeg_;
384 views_.push_back(v);
385}
386
387bool CameraController::switchTo(const std::string& name, float blendTime) {
388 int idx = -1;
389 for (int i = 0; i < static_cast<int>(views_.size()); ++i) {
390 if (views_[i].name == name) {
391 idx = i;
392 break;
393 }
394 }
395 if (idx < 0) return false;
396
397 if (viewIndex_ >= 0 && viewIndex_ < static_cast<int>(views_.size())) {
398 blendFrom_ = views_[viewIndex_];
399 } else {
400 blendFrom_ = cur_;
401 }
402 blendTo_ = views_[idx];
403 viewIndex_ = idx;
404
405 if (blendTime > 0.f) {
406 blending_ = true;
407 blendDur_ = blendTime;
408 blendT_ = 0.f;
409 } else {
410 blending_ = false;
411 blendDur_ = 1.f;
412 blendT_ = 0.f;
413 }
414 return true;
415}
416
417void CameraController::playSequence(float stepTime) {
418 playing_ = true;
419 stepTime_ = stepTime > 0.f ? stepTime : 1.f;
420 timer_ = 0.f;
421 if (!blending_ && views_.empty()) return;
422 if (viewIndex_ < 0 || viewIndex_ >= static_cast<int>(views_.size())) {
423 switchTo(views_.front().name, 0.f);
424 }
425}
426
427void CameraController::stopSequence() { playing_ = false; }
428
429bool CameraController::isPlaying() const { return playing_; }
430
432 timelineCuts_.clear();
433 timelineEvents_.clear();
434 timelineFloats_.clear();
435 pendingTimelineEvents_.clear();
436 timelineDuration_ = 0.f;
437 stopTimeline();
438}
439
440bool CameraController::addTimelineFloat(float time, const std::string& property, float value) {
441 if (time < 0.f || (property != "fov" && property != "radius" && property != "smooth" &&
442 property != "compositionX" && property != "compositionY"))
443 return false;
444 timelineFloats_.push_back({time, property, value});
445 std::stable_sort(timelineFloats_.begin(), timelineFloats_.end(), [](const auto& a, const auto& b) {
446 return a.time < b.time;
447 });
448 timelineDuration_ = std::max(timelineDuration_, time);
449 return true;
450}
451
452bool CameraController::addTimelineCut(float time, const std::string& rigName, float blendTime) {
453 if (time < 0.f || blendTime < 0.f) return false;
454 const bool known = std::any_of(rigs_.begin(), rigs_.end(), [&](const Rig& r) { return r.name == rigName; });
455 if (!known) return false;
456 timelineCuts_.push_back({time, rigName, blendTime, false});
457 std::stable_sort(timelineCuts_.begin(), timelineCuts_.end(),
458 [](const TimelineCut& a, const TimelineCut& b) { return a.time < b.time; });
459 timelineDuration_ = std::max(timelineDuration_, time);
460 return true;
461}
462
463bool CameraController::addTimelineEvent(float time, const std::string& name, const std::string& data) {
464 if (time < 0.f || name.empty()) return false;
465 timelineEvents_.push_back({time, name, data, false});
466 std::stable_sort(timelineEvents_.begin(), timelineEvents_.end(),
467 [](const TimelineEvent& a, const TimelineEvent& b) { return a.time < b.time; });
468 timelineDuration_ = std::max(timelineDuration_, time);
469 return true;
470}
471
473
475 timelineLoop_ = loop;
476 timelinePlaying_ = !timelineCuts_.empty() || !timelineEvents_.empty();
477}
478void CameraController::pauseTimeline() { timelinePlaying_ = false; }
480 timelinePlaying_ = false;
481 timelineTime_ = 0.f;
482 for (auto& cut : timelineCuts_) cut.fired = false;
483 for (auto& event : timelineEvents_) event.fired = false;
484}
485void CameraController::seekTimeline(float time, bool fireEvents) {
486 timelineTime_ = glm::clamp(time, 0.f, std::max(0.f, timelineDuration_));
487 for (auto& cut : timelineCuts_) cut.fired = cut.time <= timelineTime_;
488 const TimelineCut* latest = nullptr;
489 for (const auto& cut : timelineCuts_)
490 if (cut.time <= timelineTime_) latest = &cut;
491 if (latest) activateRig(latest->rig, 0.f);
492 for (auto& marker : timelineEvents_) {
493 marker.fired = marker.time <= timelineTime_;
494 if (fireEvents && marker.fired) emitTimelineEvent(marker);
495 }
496 evaluateTimelineFloats();
497}
498bool CameraController::isTimelinePlaying() const { return timelinePlaying_; }
499float CameraController::getTimelineTime() const { return timelineTime_; }
501 if (pendingTimelineEvents_.empty()) return {};
502 auto [out, data] = std::move(pendingTimelineEvents_.front());
503 pendingTimelineEvents_.pop_front();
504 consumedTimelineEventData_ = std::move(data);
505 return out;
506}
507std::string CameraController::getTimelineEventData() const { return consumedTimelineEventData_; }
508int CameraController::getPendingTimelineEventCount() const { return static_cast<int>(pendingTimelineEvents_.size()); }
509float CameraController::getTimelineDuration() const { return timelineDuration_; }
510int CameraController::getRigCount() const { return static_cast<int>(rigs_.size()); }
511
513 std::ostringstream out;
514 out << "{\"version\":1,\"rigs\":[";
515 bool first = true;
516 for (const auto& rig : rigs_) {
517 if (!first) out << ',';
518 first = false;
519 out << "{\"name\":" << jsonString(rig.name) << ",\"mode\":" << jsonString(rig.mode)
520 << ",\"priority\":" << rig.priority << ",\"enabled\":" << (rig.enabled ? "true" : "false")
521 << ",\"target\":[" << rig.target.x << ',' << rig.target.y << ',' << rig.target.z
522 << "],\"offset\":[" << rig.offset.x << ',' << rig.offset.y << ',' << rig.offset.z
523 << "],\"lookAhead\":[" << rig.lookAhead.x << ',' << rig.lookAhead.y << ',' << rig.lookAhead.z
524 << "],\"composition\":[" << rig.composition.x << ',' << rig.composition.y << "]"
525 << ",\"radius\":" << rig.radius << ",\"azimuth\":" << rig.azimuth
526 << ",\"elevation\":" << rig.elevation << ",\"yaw\":" << rig.yaw << ",\"pitch\":" << rig.pitch
527 << ",\"fov\":" << rig.fov << ",\"smooth\":" << rig.smooth << ",\"maxSpeed\":" << rig.maxSpeed << '}';
528 }
529 out << "],\"cuts\":[";
530 first = true;
531 for (const auto& cut : timelineCuts_) {
532 if (!first) out << ',';
533 first = false;
534 out << "{\"time\":" << cut.time << ",\"rig\":" << jsonString(cut.rig) << ",\"blend\":" << cut.blend << '}';
535 }
536 out << "],\"events\":[";
537 first = true;
538 for (const auto& marker : timelineEvents_) {
539 if (!first) out << ',';
540 first = false;
541 out << "{\"time\":" << marker.time << ",\"name\":" << jsonString(marker.name)
542 << ",\"data\":" << jsonString(marker.data) << '}';
543 }
544 out << "],\"floats\":[";
545 first = true;
546 for (const auto& key : timelineFloats_) {
547 if (!first) out << ',';
548 first = false;
549 out << "{\"time\":" << key.time << ",\"property\":" << jsonString(key.property)
550 << ",\"value\":" << key.value << '}';
551 }
552 out << "]}";
553 return out.str();
554}
555
556bool CameraController::deserializeAsset(const std::string& text) {
557 const auto document = eve::json::Document::parse(text);
558 const auto root = document.root();
559 if (!root.isObject() || root.getInt("version", 0) != 1) return false;
560 std::vector<Rig> newRigs;
561 const auto rigs = root.get("rigs");
562 if (rigs && !rigs.isArray()) return false;
563 for (size_t i = 0; i < rigs.size(); ++i) {
564 const auto item = rigs.at(i);
565 if (!item.isObject()) return false;
566 Rig rig;
567 rig.name = item.getString("name");
568 rig.mode = item.getString("mode");
569 if (rig.name.empty() || !validMode(rig.mode)) return false;
570 rig.priority = item.getInt("priority");
571 rig.enabled = item.getBool("enabled", true);
572 rig.target = jsonVec3(item.get("target"), rig.target);
573 rig.offset = jsonVec3(item.get("offset"), rig.offset);
574 rig.lookAhead = jsonVec3(item.get("lookAhead"), rig.lookAhead);
575 const auto composition = item.get("composition");
576 if (composition.isArray() && composition.size() == 2)
577 rig.composition = {composition.at(0).asFloat(), composition.at(1).asFloat()};
578 rig.radius = item.getFloat("radius", rig.radius);
579 rig.azimuth = item.getFloat("azimuth", rig.azimuth);
580 rig.elevation = item.getFloat("elevation", rig.elevation);
581 rig.yaw = item.getFloat("yaw", rig.yaw);
582 rig.pitch = item.getFloat("pitch", rig.pitch);
583 rig.fov = item.getFloat("fov", rig.fov);
584 rig.smooth = item.getFloat("smooth", rig.smooth);
585 rig.maxSpeed = item.getFloat("maxSpeed", rig.maxSpeed);
586 newRigs.push_back(std::move(rig));
587 }
589 rigs_ = std::move(newRigs);
590 activeRig_.clear();
591 directorEnabled_ = !rigs_.empty();
592 const auto cuts = root.get("cuts");
593 if (cuts && !cuts.isArray()) return false;
594 for (size_t i = 0; i < cuts.size(); ++i) {
595 const auto item = cuts.at(i);
596 if (!item.isObject() || !addTimelineCut(item.getFloat("time", -1.f), item.getString("rig"),
597 item.getFloat("blend", 0.f)))
598 return false;
599 }
600 const auto events = root.get("events");
601 if (events && !events.isArray()) return false;
602 for (size_t i = 0; i < events.size(); ++i) {
603 const auto item = events.at(i);
604 if (!item.isObject() || !addTimelineEvent(item.getFloat("time", -1.f), item.getString("name"),
605 item.getString("data")))
606 return false;
607 }
608 const auto floats = root.get("floats");
609 if (floats && !floats.isArray()) return false;
610 for (size_t i = 0; i < floats.size(); ++i) {
611 const auto item = floats.at(i);
612 if (!item.isObject() || !addTimelineFloat(item.getFloat("time", -1.f), item.getString("property"),
613 item.getFloat("value")))
614 return false;
615 }
616 return true;
617}
618
619void CameraController::emitTimelineEvent(const TimelineEvent& marker) {
620 pendingTimelineEvents_.emplace_back(marker.name, marker.data);
621 if (eventSink_) eventSink_->pushData(marker.name, marker.data);
622}
623
624void CameraController::evaluateTimelineFloats() {
625 for (const std::string property : {"fov", "radius", "smooth", "compositionX", "compositionY"}) {
626 const TimelineFloat* before = nullptr;
627 const TimelineFloat* after = nullptr;
628 for (const auto& key : timelineFloats_) {
629 if (key.property != property) continue;
630 if (key.time <= timelineTime_) before = &key;
631 if (key.time >= timelineTime_) {
632 after = &key;
633 break;
634 }
635 }
636 if (!before && !after) continue;
637 if (!before) before = after;
638 if (!after) after = before;
639 const float span = after->time - before->time;
640 const float t = span > 1e-6f ? (timelineTime_ - before->time) / span : 0.f;
641 const float value = glm::mix(before->value, after->value, glm::clamp(t, 0.f, 1.f));
642 if (property == "fov") setFov(value);
643 else if (property == "radius") setRadius(value);
644 else if (property == "smooth") setSmooth(value);
645 else if (property == "compositionX") setComposition(value, composition_.y);
646 else setComposition(composition_.x, value);
647 }
648}
649
650void CameraController::updateTrackedTarget() {
651 if (targetNodeHost_.empty() || targetNodeId_.empty()) return;
652 const scene::SceneNodeRef node(targetNodeHost_, targetNodeId_);
653 if (!node.isValid()) return;
654 const glm::vec3 observed(node.getWorldPositionX(), node.getWorldPositionY(), node.getWorldPositionZ());
655 if (!deadZoneInitialized_ || deadZone_ <= 0.f) {
656 deadZoneTarget_ = observed;
657 deadZoneInitialized_ = true;
658 } else {
659 const glm::vec3 delta = observed - deadZoneTarget_;
660 const float length = glm::length(delta);
661 if (length > deadZone_ && length > 1e-6f) deadZoneTarget_ += delta * ((length - deadZone_) / length);
662 }
663 target_ = deadZoneTarget_;
664}
665
666void CameraController::updateDirector() {
667 if (!directorEnabled_ || timelinePlaying_) return;
668 const Rig* best = nullptr;
669 const Rig* active = nullptr;
670 for (const auto& rig : rigs_) {
671 if (rig.name == activeRig_ && rig.enabled) active = &rig;
672 if (rig.enabled && (!best || rig.priority > best->priority)) best = &rig;
673 }
674 if (active && best && active->priority >= best->priority) best = active;
675 if (best && best->name != activeRig_) activateRig(best->name, initialized_ ? 0.25f : 0.f);
676}
677
678void CameraController::updateTimeline(float dt) {
679 if (!timelinePlaying_) return;
680 float remaining = std::max(0.f, dt);
681 if (timelineDuration_ <= 0.f) remaining = 0.f;
682 timelineTime_ += remaining;
683 for (auto& cut : timelineCuts_) {
684 if (!cut.fired && cut.time <= timelineTime_) {
685 cut.fired = true;
686 activateRig(cut.rig, cut.blend);
687 }
688 }
689 for (auto& marker : timelineEvents_) {
690 if (!marker.fired && marker.time <= timelineTime_) {
691 marker.fired = true;
692 emitTimelineEvent(marker);
693 }
694 }
695 evaluateTimelineFloats();
696 if (timelineTime_ < timelineDuration_) return;
697 if (!timelineLoop_) {
698 timelinePlaying_ = false;
699 return;
700 }
701 timelineTime_ = timelineDuration_ > 0.f ? std::fmod(timelineTime_, timelineDuration_) : 0.f;
702 for (auto& cut : timelineCuts_) cut.fired = false;
703 for (auto& marker : timelineEvents_) marker.fired = false;
704 for (auto& cut : timelineCuts_)
705 if (cut.time <= timelineTime_) {
706 cut.fired = true;
707 activateRig(cut.rig, cut.blend);
708 }
709 for (auto& marker : timelineEvents_)
710 if (marker.time <= timelineTime_) {
711 marker.fired = true;
712 emitTimelineEvent(marker);
713 }
714 evaluateTimelineFloats();
715}
716
717void CameraController::applyCollision(View& v, float dt) {
718 obstructed_ = false;
719 collisionBodyId_ = -1;
720 const glm::vec3 ray = v.eye - v.target;
721 const float desiredDistance = glm::length(ray);
722 if (!collisionEnabled_ || desiredDistance < 1e-5f) {
723 collisionDistance_ = -1.f;
724 return;
725 }
726 const glm::vec3 dir = ray / desiredDistance;
727 float nearest = desiredDistance;
728 for (const auto& box : collisionBoxes_) {
729 const glm::vec3 bmin = box.min - glm::vec3(collisionRadius_);
730 const glm::vec3 bmax = box.max + glm::vec3(collisionRadius_);
731 float tmin = 0.f;
732 float tmax = desiredDistance;
733 bool hit = true;
734 for (int axis = 0; axis < 3; ++axis) {
735 if (std::abs(dir[axis]) < 1e-6f) {
736 if (v.target[axis] < bmin[axis] || v.target[axis] > bmax[axis]) {
737 hit = false;
738 break;
739 }
740 } else {
741 float a = (bmin[axis] - v.target[axis]) / dir[axis];
742 float b = (bmax[axis] - v.target[axis]) / dir[axis];
743 if (a > b) std::swap(a, b);
744 tmin = std::max(tmin, a);
745 tmax = std::min(tmax, b);
746 if (tmin > tmax) {
747 hit = false;
748 break;
749 }
750 }
751 }
752 if (hit && tmin >= 0.f && tmin < nearest) nearest = tmin;
753 }
754 if (auto* query = eve::cap::query<eve::ICameraObstructionQuery>()) {
756 if (query->sphereCast(v.target.x, v.target.y, v.target.z, v.eye.x, v.eye.y, v.eye.z,
757 collisionRadius_, collisionMask_, collisionIgnoredBody_, &hit) &&
758 hit.hit) {
759 const float distance = desiredDistance * glm::clamp(hit.fraction, 0.f, 1.f);
760 if (distance < nearest) {
761 nearest = distance;
762 collisionBodyId_ = hit.bodyId;
763 }
764 }
765 }
766 obstructed_ = nearest < desiredDistance;
767 const float safeDistance = obstructed_ ? std::max(0.05f, nearest - 0.02f) : desiredDistance;
768 if (collisionDistance_ < 0.f || safeDistance < collisionDistance_)
769 collisionDistance_ = safeDistance;
770 else {
771 const float k = 1.f - std::exp(-collisionRecovery_ * std::max(0.f, dt));
772 collisionDistance_ += (safeDistance - collisionDistance_) * k;
773 }
774 collisionDistance_ = std::min(collisionDistance_, desiredDistance);
775 v.eye = v.target + dir * collisionDistance_;
776}
777
778void CameraController::applyModifiers(View& v, float dt) {
779 for (auto& impulse : impulses_) {
780 impulse.age += std::max(0.f, dt);
781 const float life = glm::clamp(1.f - impulse.age / impulse.duration, 0.f, 1.f);
782 const float phase = impulse.age * 37.f + static_cast<float>(impulse.seed % 997u);
783 const glm::vec3 noise(std::sin(phase * 1.13f), std::sin(phase * 1.71f + 2.f), std::sin(phase * 2.07f + 4.f));
784 v.eye += noise * (impulse.positionAmplitude * life);
785 const glm::vec3 forward = glm::normalize(v.target - v.eye);
786 glm::vec3 right = glm::cross(forward, glm::vec3(0.f, 1.f, 0.f));
787 if (glm::length(right) > 1e-5f)
788 right = glm::normalize(right);
789 else
790 right = glm::vec3(1.f, 0.f, 0.f);
791 const glm::vec3 up = glm::normalize(glm::cross(right, forward));
792 const float angular = std::tan(glm::radians(impulse.rotationAmplitude * life));
793 v.target += (right * noise.x + up * noise.y) * angular * glm::length(v.target - v.eye);
794 v.fov = glm::clamp(v.fov + impulse.fovAmplitude * life, 1.f, 179.f);
795 }
796 impulses_.erase(
797 std::remove_if(impulses_.begin(), impulses_.end(), [](const Impulse& i) { return i.age >= i.duration; }),
798 impulses_.end());
799}
800
801CameraController::View CameraController::desired() const {
802 View v;
803 v.fov = fovDeg_;
804 if (blending_) {
805 float t = blendDur_ > 0.f ? glm::clamp(blendT_ / blendDur_, 0.f, 1.f) : 1.f;
806 t = t * t * (3.f - 2.f * t);
807 v.eye = glm::mix(blendFrom_.eye, blendTo_.eye, t);
808 v.target = glm::mix(blendFrom_.target, blendTo_.target, t);
809 v.fov = glm::mix(blendFrom_.fov, blendTo_.fov, t);
810 } else if (mode_ == "orbit") {
811 const float az = glm::radians(azimuthDeg_);
812 const float el = glm::radians(elevationDeg_);
813 glm::vec3 dir(std::cos(el) * std::sin(az), std::sin(el), std::cos(el) * std::cos(az));
814 v.eye = target_ + dir * radius_;
815 v.target = target_;
816 } else if (mode_ == "topdown") {
817 v.eye = target_ + glm::vec3(0.f, radius_, 0.f);
818 v.target = target_;
819 } else if (mode_ == "firstperson") {
820 v.eye = target_;
821 const float yaw = glm::radians(yawDeg_);
822 const float pitch = glm::radians(pitchDeg_);
823 glm::vec3 dir(std::cos(pitch) * std::sin(yaw), std::sin(pitch), std::cos(pitch) * std::cos(yaw));
824 v.target = v.eye + dir * 100.f;
825 } else if (mode_ == "cinematic") {
826 if (viewIndex_ >= 0 && viewIndex_ < static_cast<int>(views_.size())) {
827 v = views_[viewIndex_];
828 } else {
829 v.target = target_ + lookAhead_;
830 v.eye = v.target + offset_;
831 }
832 } else if (mode_ == "lockon") {
833 glm::vec3 back(offset_.x, 0.f, offset_.z);
834 float distance = glm::length(glm::vec2(back.x, back.z));
835 if (distance > 1e-5f)
836 back /= distance;
837 else
838 distance = std::max(radius_, 0.01f);
839 if (glm::length(glm::vec2(back.x, back.z)) < 1e-5f) {
840 const float yaw = glm::radians(yawDeg_);
841 back = glm::vec3(-std::sin(yaw), 0.f, -std::cos(yaw));
842 }
843 glm::vec3 look = target_ + glm::vec3(0.f, lookAhead_.y, 0.f);
844 if (hasSecondaryTarget_) {
845 look = 0.5f * (target_ + secondaryTarget_) + glm::vec3(0.f, lookAhead_.y, 0.f);
846 glm::vec3 toTarget = secondaryTarget_ - target_;
847 toTarget.y = 0.f;
848 const float length = glm::length(toTarget);
849 if (length > 1e-5f) back = -toTarget / length;
850 }
851 v.target = look;
852 v.eye = target_ + glm::vec3(back.x * distance, offset_.y, back.z * distance);
853 } else { // follow (默认)
854 v.target = target_ + lookAhead_;
855 v.eye = v.target + offset_;
856 }
857 const glm::vec3 forward = glm::normalize(v.target - v.eye);
858 glm::vec3 right = glm::cross(forward, glm::vec3(0.f, 1.f, 0.f));
859 if (glm::length(right) < 1e-5f)
860 right = glm::vec3(1.f, 0.f, 0.f);
861 else
862 right = glm::normalize(right);
863 glm::vec3 up = glm::normalize(glm::cross(right, forward));
864 const float distance = glm::length(v.target - v.eye);
865 v.target += (right * composition_.x + up * composition_.y) * distance;
866 return v;
867}
868
869void CameraController::applyView(const View& v) {
870 if (!cam_) return;
871 cam_->setEye(v.eye.x, v.eye.y, v.eye.z);
872 cam_->setTarget(v.target.x, v.target.y, v.target.z);
873 const glm::vec3 forward = glm::normalize(v.target - v.eye);
874 glm::vec3 up(0.f, 1.f, 0.f);
875 if (std::abs(glm::dot(forward, up)) > 0.999f) up = glm::vec3(0.f, 0.f, -1.f);
876 glm::vec3 right=glm::normalize(glm::cross(forward,up));
877 up=glm::normalize(glm::cross(right,forward));
878 const float roll=glm::radians(cameraRollDeg_);
879 up=up*std::cos(roll)+right*std::sin(roll);
880 cam_->setUp(up.x, up.y, up.z);
881 cam_->setFov(v.fov);
882}
883
884void CameraController::easeToward(const View& v, float dt) {
885 const float eyeK = 1.f - std::exp(-positionSmooth_ * dt);
886 const float targetK = 1.f - std::exp(-targetSmooth_ * dt);
887 glm::vec3 deye = (v.eye - cur_.eye) * eyeK;
888 glm::vec3 dtgt = (v.target - cur_.target) * targetK;
889 if (maxSpeed_ > 0.f && dt > 0.f) {
890 const float maxStep = maxSpeed_ * dt;
891 const float le = glm::length(deye);
892 if (le > maxStep && le > 1e-6f) deye *= maxStep / le;
893 const float lt = glm::length(dtgt);
894 if (lt > maxStep && lt > 1e-6f) dtgt *= maxStep / lt;
895 }
896 cur_.eye += deye;
897 cur_.target += dtgt;
898 cur_.fov += (v.fov - cur_.fov) * eyeK;
899}
900
902 cur_ = desired();
903 initialized_ = true;
904 applyView(cur_);
905}
906
908 if (!cam_) return;
909
910 dt = std::max(0.f, dt);
911 updateTrackedTarget();
912 updateTimeline(dt);
913 updateDirector();
914
915 if (mode_ == "cinematic" && playing_ && !views_.empty()) {
916 timer_ += dt;
917 if (!blending_ && timer_ >= stepTime_) {
918 const int next = (viewIndex_ + 1) % static_cast<int>(views_.size());
919 switchTo(views_[next].name, blendDur_);
920 timer_ = 0.f;
921 }
922 }
923
924 if (blending_) {
925 blendT_ += dt;
926 if (blendT_ >= blendDur_) {
927 blending_ = false;
928 blendT_ = blendDur_;
929 }
930 }
931
932 // orbit 自动盘旋:不断累积方位角
933 if (mode_ == "orbit") {
934 azimuthDeg_ += orbitSpeedDeg_ * dt;
935 }
936
937 View d = desired();
938 applyCollision(d, dt);
939 applyModifiers(d, dt);
940
941 if (!initialized_) {
942 cur_ = d;
943 initialized_ = true;
944 }
945 easeToward(d, dt);
946 applyView(cur_);
947}
948
949void Camera::expose(ssq::Class& cls) {
950 // 模块级方法(如需要可在此添加);当前只通过 eve.CameraController 暴露控制器。
951}
952
953void Camera::expose(ssq::Table& table) {
954 auto cls = table.addClass(name, Camera::create, false);
955 expose(cls);
956
957 auto cc = table.addClass<CameraController>(
958 "CameraController", std::function<CameraController*()>([]() { return new CameraController(); }), true);
959
960 cc.addFunc("setCamera", &CameraController::setCamera);
961 cc.addFunc("getCamera", &CameraController::getCamera);
962 cc.addFunc("setActionCuesEnabled", &CameraController::setActionCuesEnabled);
963 cc.addFunc("getActionCuesEnabled", &CameraController::getActionCuesEnabled);
964 cc.addFunc("setPhotoModeAuthority", &CameraController::setPhotoModeAuthority);
965 cc.addFunc("getPhotoModeAuthority", [](const CameraController* self){return self->getPhotoModeAuthority();});
966 cc.addFunc("setCameraRoll", [vm=table.getHandle()](CameraController* self,float value){return eve::script::projectResult(vm,self->setCameraRoll(value));});
967 cc.addFunc("getCameraRoll", [](const CameraController* self){return self->getCameraRoll();});
968 cc.addFunc("getPcgCullingDistance", [](const CameraController* self){return self->getPcgCullingDistance();});
969
970 cc.addFunc("setTarget", &CameraController::setTarget);
971 cc.addFunc("setTargetNode", &CameraController::setTargetNode);
972 cc.addFunc("getTargetX", &CameraController::getTargetX);
973 cc.addFunc("getTargetY", &CameraController::getTargetY);
974 cc.addFunc("getTargetZ", &CameraController::getTargetZ);
975 cc.addFunc("setOffset", &CameraController::setOffset);
976 cc.addFunc("setLookAhead", &CameraController::setLookAhead);
977 cc.addFunc("setSecondaryTarget", &CameraController::setSecondaryTarget);
978 cc.addFunc("clearSecondaryTarget", &CameraController::clearSecondaryTarget);
979 cc.addFunc("hasSecondaryTarget", [](const CameraController* self) { return self->hasSecondaryTarget(); });
980 cc.addFunc("getSecondaryTargetX", &CameraController::getSecondaryTargetX);
981 cc.addFunc("getSecondaryTargetY", &CameraController::getSecondaryTargetY);
982 cc.addFunc("getSecondaryTargetZ", &CameraController::getSecondaryTargetZ);
983
984 cc.addFunc("setMode", &CameraController::setMode);
985 cc.addFunc("getMode", &CameraController::getMode);
986
987 cc.addFunc("setRadius", &CameraController::setRadius);
988 cc.addFunc("getRadius", &CameraController::getRadius);
989 cc.addFunc("setRadiusLimits", [vm=table.getHandle()](CameraController* self,float minimum,float maximum) { return eve::script::projectResult(vm,self->setRadiusLimits(minimum,maximum)); });
990 cc.addFunc("getMinimumRadius", &CameraController::getMinimumRadius);
991 cc.addFunc("getMaximumRadius", &CameraController::getMaximumRadius);
992 cc.addFunc("setAzimuth", &CameraController::setAzimuth);
993 cc.addFunc("setElevation", &CameraController::setElevation);
994 cc.addFunc("setOrbitSpeed", &CameraController::setOrbitSpeed);
995
996 cc.addFunc("setYaw", &CameraController::setYaw);
997 cc.addFunc("setPitch", &CameraController::setPitch);
998 cc.addFunc("addInput", &CameraController::addInput);
999 cc.addFunc("setComposition", &CameraController::setComposition);
1000 cc.addFunc("setDeadZone", &CameraController::setDeadZone);
1001 cc.addFunc("setFov", &CameraController::setFov);
1002 cc.addFunc("getFov", &CameraController::getFov);
1003
1004 cc.addFunc("setSmooth", &CameraController::setSmooth);
1005 cc.addFunc("setPositionSmooth", &CameraController::setPositionSmooth);
1006 cc.addFunc("setTargetSmooth", &CameraController::setTargetSmooth);
1007 cc.addFunc("setMaxSpeed", &CameraController::setMaxSpeed);
1008 cc.addFunc("snap", &CameraController::snap);
1009 cc.addFunc("setCollisionEnabled", &CameraController::setCollisionEnabled);
1010 cc.addFunc("setCollisionRadius", &CameraController::setCollisionRadius);
1011 cc.addFunc("setCollisionRecovery", &CameraController::setCollisionRecovery);
1012 cc.addFunc("setCollisionMask", &CameraController::setCollisionMask);
1013 cc.addFunc("setCollisionIgnoredBody", &CameraController::setCollisionIgnoredBody);
1014 cc.addFunc("clearCollisionBoxes", &CameraController::clearCollisionBoxes);
1015 cc.addFunc("addCollisionBox", &CameraController::addCollisionBox);
1016 cc.addFunc("getCollisionBodyId", &CameraController::getCollisionBodyId);
1017 cc.addFunc("isObstructed", &CameraController::isObstructed);
1018
1019 cc.addFunc("addRig", &CameraController::addRig);
1020 cc.addFunc("removeRig", &CameraController::removeRig);
1021 cc.addFunc("setRigPriority", &CameraController::setRigPriority);
1022 cc.addFunc("setRigEnabled", &CameraController::setRigEnabled);
1023 cc.addFunc("saveRigState", &CameraController::saveRigState);
1024 cc.addFunc("activateRig", &CameraController::activateRig);
1025 cc.addFunc("getActiveRig", &CameraController::getActiveRig);
1026 cc.addFunc("addImpulse", &CameraController::addImpulse);
1027 cc.addFunc("addFovImpulse", &CameraController::addFovImpulse);
1028 cc.addFunc("clearImpulses", &CameraController::clearImpulses);
1029
1030 cc.addFunc("addView", &CameraController::addView);
1031 cc.addFunc("switchTo", &CameraController::switchTo);
1032 cc.addFunc("playSequence", &CameraController::playSequence);
1033 cc.addFunc("stopSequence", &CameraController::stopSequence);
1034 cc.addFunc("isPlaying", &CameraController::isPlaying);
1035
1036 cc.addFunc("clearTimeline", &CameraController::clearTimeline);
1037 cc.addFunc("addTimelineCut", &CameraController::addTimelineCut);
1038 cc.addFunc("addTimelineEvent", &CameraController::addTimelineEvent);
1039 cc.addFunc("addTimelineFloat", &CameraController::addTimelineFloat);
1040 cc.addFunc("setEventSink", &CameraController::setEventSink);
1041 cc.addFunc("playTimeline", &CameraController::playTimeline);
1042 cc.addFunc("pauseTimeline", &CameraController::pauseTimeline);
1043 cc.addFunc("stopTimeline", &CameraController::stopTimeline);
1044 cc.addFunc("seekTimeline", &CameraController::seekTimeline);
1045 cc.addFunc("isTimelinePlaying", &CameraController::isTimelinePlaying);
1046 cc.addFunc("getTimelineTime", &CameraController::getTimelineTime);
1047 cc.addFunc("consumeTimelineEvent", &CameraController::consumeTimelineEvent);
1048 cc.addFunc("getTimelineEventData", &CameraController::getTimelineEventData);
1049 cc.addFunc("getPendingTimelineEventCount", &CameraController::getPendingTimelineEventCount);
1050 cc.addFunc("getTimelineDuration", &CameraController::getTimelineDuration);
1051 cc.addFunc("getRigCount", &CameraController::getRigCount);
1052 cc.addFunc("serializeAsset", &CameraController::serializeAsset);
1053 cc.addFunc("deserializeAsset", &CameraController::deserializeAsset);
1054
1055 cc.addFunc("update", &CameraController::update);
1058}
1059
1060} // namespace eve::camera
LogicalId target
double value
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
int root
Definition AnimSmr.cpp:119
std::string from
const std::string & s
int priority
float degrees
Definition CardTypes.cpp:35
float phase
Definition CaveMesh.cpp:58
float length
Definition CaveMesh.cpp:94
int az
Definition CaveMesh.cpp:113
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
float maximum[3]
float minimum[3]
std::uint32_t key
float u
Definition Grass.cpp:233
double r
float v
HexVec3 up
HexVec3 right
std::int32_t c
std::int32_t first
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::string text
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
eve::action::ActionVfxBinding binding
TileLayer * layer
int idx
float radius
std::uint32_t seed
Definition PointSet.cpp:807
bool hit
float d
float t
glm::vec3 eye
const RoadNode * node
int cuts
Definition RockMesh.cpp:25
double number
double current
The single Squirrel projection for common Result, Status and Value.
std::map< Cell, int > best
Battle::Events events
V3 dir
Definition TreeMesh.cpp:150
uint32_t index
float m[16]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
int getRigCount() const
Returns the rig count.
void setCollisionMask(uint64_t maskBits)
Sets the Box3D category mask used by dynamic obstruction queries.
float getTimelineDuration() const
Returns the timeline duration.
bool deserializeAsset(const std::string &json)
Replaces rigs and timeline data from a versioned JSON asset.
void setYaw(float deg)
Sets the yaw.
bool isObstructed() const
True when obstructed.
void setEventSink(platform_event::PlatformEvent *sink)
Sets the event sink.
int getCollisionBodyId() const
Returns the collision body id.
float getMinimumRadius() const
Return the minimum orbit radius.
bool supports(const LogicalId &cue) const noexcept override
Accept every project-defined camera cue while explicitly enabled.
void setCollisionRadius(float radius)
Sets the collision radius.
void addView(const std::string &name, float ex, float ey, float ez, float tx, float ty, float tz)
Adds view.
Result< void > setCameraRoll(float degrees)
Set finite camera roll in degrees.
CameraController()
Camera controller.
void setTarget(float x, float y, float z)
Sets the target.
void update(float dt)
Updates .
void stopSequence()
Stops sequence.
void setSmooth(float damping)
Sets the smooth.
bool addTimelineCut(float time, const std::string &rigName, float blendTime)
Adds timeline cut.
void setRadius(float r)
Sets the radius.
void setActionCuesEnabled(bool enabled)
Opt this controller into or out of action camera-cue dispatch.
std::string getActiveRig() const
Returns the active rig.
void stopTimeline()
Stops timeline.
float getRadius() const
Return the current orbit radius.
float getTimelineTime() const
Returns the timeline time.
bool setRigEnabled(const std::string &name, bool enabled)
Sets the rig enabled.
std::string serializeAsset() const
Serializes rigs and timeline data as a versioned JSON asset.
void clearTimeline()
Clears timeline.
void setCamera(graphics::Camera3D *cam)
Sets the camera.
bool addTimelineFloat(float time, const std::string &property, float value)
Adds a linearly interpolated camera property key.
void setComposition(float screenX, float screenY)
Screen-space composition offset, expressed as normalized view fractions.
float getSecondaryTargetY() const
Returns the secondary target y.
bool removeRig(const std::string &name)
Removes rig.
Result< void > setRadiusLimits(float minimum, float maximum)
Set inclusive orbit zoom limits atomically.
void playTimeline(bool loop)
Play timeline.
float getMaximumRadius() const
Return the maximum orbit radius.
graphics::Camera3D * getCamera() const
Returns the camera.
Result< void > trigger(const action::ActionCameraCueBinding &binding, const action::ActionNotifyContext &context) override
Convert one action cue into deterministic positional, rotational and FOV impulses.
void setSecondaryTarget(float x, float y, float z)
Store a world-space lock-on look target for mode "lockon".
std::string getMode() const
Returns the mode.
void setPitch(float deg)
Sets the pitch.
float getTargetZ() const
Returns the target z.
float getSecondaryTargetZ() const
Returns the secondary target z.
void seekTimeline(float time, bool fireEvents=false)
Seeks timeline.
void addInput(float yawDeltaDeg, float pitchDeltaDeg, float zoomDelta)
Applies device-independent yaw, pitch and zoom deltas.
float getFov() const
Returns the fov.
void playSequence(float stepTime)
Play sequence.
bool getActionCuesEnabled() const
Return whether this controller is currently registered for action cues.
int getPendingTimelineEventCount() const
Returns the pending timeline event count.
bool addRig(const std::string &name, const std::string &mode, int priority)
Adds rig.
void setCollisionIgnoredBody(int bodyId)
Excludes one Box3D body id, normally the followed player body.
void setCollisionEnabled(bool enabled)
Enables swept-sphere obstruction against boxes registered below.
bool isPlaying() const
True when playing.
void setPhotoModeAuthority(bool enabled)
Register or revoke this controller as the sole photo-mode camera authority.
bool isTimelinePlaying() const
True when timeline playing.
float getTargetX() const
Returns the target x.
std::string consumeTimelineEvent()
Consume timeline event.
void addFovImpulse(float degrees, float duration)
Adds fov impulse.
void addImpulse(float positionAmplitude, float rotationAmplitude, float duration, unsigned int seed=0)
Adds a damped positional/rotational camera impulse.
void setPositionSmooth(float damping)
Sets the position smooth.
Result< void > applyPhotoModeField(const PhotoModeAssignment &assignment) override
Apply one camera assignment atomically to this controller and its bound Camera3D.
std::string getTimelineEventData() const
Returns the timeline event data.
void setLookAhead(float x, float y, float z)
follow:额外的视线前移点(可让镜头看向玩家前方)。
void setMode(const std::string &mode)
Sets the mode.
void clearImpulses()
Clears impulses.
void clearCollisionBoxes()
Clears collision boxes.
void setTargetNode(scene::SceneNodeRef *node)
Follow a stable scene-node handle. nullptr restores explicit coordinates.
void clearSecondaryTarget()
Forget the lock-on look target; lockon then falls back to offset/yaw follow.
void setMaxSpeed(float unitsPerSec)
Sets the max speed.
bool switchTo(const std::string &name, float blendTime)
Switch to.
void setOffset(float x, float y, float z)
follow:摄像机相对 target 的偏移(默认 (0, 2, 6))。
void setFov(float degrees)
Sets the fov.
void pauseTimeline()
Pause timeline.
bool saveRigState(const std::string &name)
Stores the controller's current framing/lens parameters in a rig preset.
void setElevation(float deg)
Sets the elevation.
bool setRigPriority(const std::string &name, int priority)
Sets the rig priority.
PhotoModeFieldAcceptance acceptsPhotoModeField(const PhotoModeAssignment &assignment) const noexcept override
Return whether this controller owns a photo-mode camera assignment.
bool activateRig(const std::string &name, float blendTime)
Activate rig.
void setOrbitSpeed(float degPerSec)
Sets the orbit speed.
~CameraController() override
Camera controller.
void setTargetSmooth(float damping)
Sets the target smooth.
void setDeadZone(float radius)
Dead-zone radius in world units around the tracked target.
void addCollisionBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Adds collision box.
void setAzimuth(float deg)
Sets the azimuth.
float getTargetY() const
Returns the target y.
void setCollisionRecovery(float damping)
Sets the collision recovery.
bool addTimelineEvent(float time, const std::string &name, const std::string &data)
Adds timeline event.
摄像机模块命名空间(eve.Camera)。主要职责是把 CameraController 绑定进脚本。
EVENGINE_API_BACKENDS public API.
float getEyeX()
Camera eye position (world space).
float getNearClip()
Return the positive near clipping distance.
float getAperture()
Return physical-camera aperture.
void setDepthOfField(float focusDistance, float maxBlurPx, float focusRange=8.f)
Configure Gaussian depth-of-field on the final HDR resolve.
float getFocalLength()
Return physical-camera focal length in millimetres.
void setLayerCullDistance(int layer, float distance)
Set the camera cull distance for one render layer; zero disables the override.
void setUp(float x, float y, float z)
void setFov(float fovYDeg)
void setPhysicalLens(float aperture, float focalLength)
Set positive physical-camera aperture and focal length metadata.
void setExposure(float ev)
Set exposure compensation in photographic stops. Positive values brighten.
float getFarClip()
Return the far clipping distance.
void setAutoExposure(bool enabled, float minEV=-8.f, float maxEV=8.f)
Enable log-average automatic exposure and set its EV clamp range.
void setTarget(float x, float y, float z)
void clearDepthOfField()
Disable depth-of-field while retaining the current focus settings.
void setEye(float x, float y, float z)
float getTargetX()
Look-at target (world space).
void setClipPlanes(float nearZ, float farZ)
Set positive near/far clipping distances; far is kept beyond near.
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
EVENGINE_API_FOUNDATION public API.
void pushData(std::string name, std::string data="")
Script helper: pushes an event with an optional string payload.
Script handle to a scene node (hostName + nodeId). Deliberately holds strings rather than arena point...
std::vector< double > forward(const Policy &p, const Observation &o)
Forward.
Definition Learning.h:65
void exposePcgFreeCameraBindings(ssq::Table &t)
Register Pcg free-camera bindings.
void exposePcgCarCameraSetupBindings(ssq::Table &t)
Register Pcg vehicle camera bindings.
I * query()
Queries .
Definition Capability.h:88
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
int axis(int64_t a, size_t rank)
Axis.
PhotoModeFieldAcceptance
Capability implemented by the composition root that routes assignments to domain owners.
bool enabled
Closest hit returned by the optional camera-obstruction provider.
One reversible field assignment in a photo-mode transaction.
Owning validated camera impulse shared by action runtime and camera targets.
Context linking a routed event to its action execution.