OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
cscamera.cpp
Go to the documentation of this file.
1#include "cscamera.h"
2
3#include <Tempest/Log>
4#include <cassert>
6#include "utils/dbgpainter.h"
7#include "gothic.h"
8
9using namespace Tempest;
10
11Vec3 CsCamera::KeyFrame::position(float u) const {
12 return ((c[0]*u + c[1])*u + c[2])*u + c[3];
13 }
14
15float CsCamera::KeyFrame::arcLength() const {
16 float len = 0;
17 Vec3 prev = c[3];
18 for(int i=1; i<=100; ++i) {
19 Vec3 at = position(float(i)/100.f);
20 len += (at-prev).length();
21 prev = at;
22 }
23 return len;
24 }
25
26
27CsCamera::KbSpline::KbSpline(const std::vector<std::shared_ptr<zenkit::VCameraTrajectoryFrame>>& frames, const float duration, std::string_view vobName) {
28 keyframe.reserve(frames.size());
29 for(auto& f : frames) {
30 KeyFrame kF;
31 kF.c[3] = Vec3(f->original_pose[3][0],f->original_pose[3][1],f->original_pose[3][2]);
32 kF.time = f->time;
33 kF.motionType = f->motion_type;
34 if(kF.time<0) {
35 Log::e("CsCamera: \"", vobName, "\" - negative frame duration");
36 }
37 keyframe.push_back(kF);
38 }
39
40 const size_t size = keyframe.size();
41 for(size_t i=0; i+1<size; ++i) {
42 auto& kF0 = i==0 ? keyframe[i+0] : keyframe[i-1];
43 auto& kF1 = keyframe[i+0];
44 auto& kF2 = keyframe[i+1];
45 auto& kF3 = i+2==size ? kF2 : keyframe[i+2];
46
47 auto& p0 = kF0.c[3];
48 auto& p1 = kF1.c[3];
49 auto& p2 = kF2.c[3];
50 auto& p3 = kF3.c[3];
51
52 float dt = kF2.time-kF1.time;
53 Vec3 dd = (p2-p0) * dt/(kF2.time-kF0.time);
54 Vec3 sd = (p3-p1) * dt/(kF3.time-kF1.time);
55
56 auto& kF = keyframe[i];
57 kF.c[0] = 2 * p1 - 2*p2 + dd + sd;
58 kF.c[1] = -3 * p1 + 3*p2 - 2*dd - sd;
59 kF.c[2] = std::move(dd);
60 }
61
62 if(size<2)
63 return;
64
65 if(keyframe.front().time!=0) {
66 Log::e("CsCamera: \"", vobName, "\" - invalid first frame");
67 }
68 }
69
70Vec3 CsCamera::KbSpline::position(const uint64_t time) const {
71 const float t = float(time)/1000.f;
72
73 if(t>=keyframe.back().time) {
74 // duration may be longer then keyframe sequence
75 return keyframe.back().position(1.f);
76 }
77
78 //TODO: lower bound
79 uint32_t n = 0;
80 while(n+1<keyframe.size() && t>=keyframe[n+1].time) {
81 ++n;
82 }
83
84 auto& kF0 = keyframe[n];
85 auto& kF1 = keyframe[n+1];
86 float u = (t - kF0.time) / (kF1.time - kF0.time);
87
88 const float k = 1.005f;
89 if(kF0.motionType==zenkit::CameraMotion::SLOW && kF1.motionType!=zenkit::CameraMotion::SLOW) {
90 // slow -> non-slow = accelerate
91 u = std::pow(u, k);
92 }
93 else if(kF0.motionType!=zenkit::CameraMotion::SLOW && kF1.motionType==zenkit::CameraMotion::SLOW) {
94 // non-slow -> slow = deccelerate
95 u = std::pow(u, 1.f/k);
96 }
97
98 assert(0<=u && u<=1);
99 return keyframe[n].position(u);
100 }
101
102
103CsCamera::CsCamera(Vob* parent, World& world, const zenkit::VCutsceneCamera& cam, Flags flags)
104 :AbstractTrigger(parent,world,cam,flags) {
105 if(cam.position_count<1 || cam.total_duration<=0)
106 return;
107
108 if(cam.trajectory_for==zenkit::CameraCoordinateReference::OBJECT || cam.target_trajectory_for==zenkit::CameraCoordinateReference::OBJECT) {
109 Log::d("Object camera not implemented, \"", name() , "\"");
110 return;
111 }
112
113 duration = uint64_t(cam.total_duration * 1000.f);
114 delay = uint64_t(cam.auto_untrigger_last_delay * 1000.f);
115 autoUntrigger = cam.auto_untrigger_last;
116 playerMovable = cam.auto_player_movable;
117
118 posSpline = KbSpline(cam.trajectory_frames, cam.total_duration, cam.vob_name);
119 targetSpline = KbSpline(cam.target_frames, cam.total_duration, cam.vob_name);
120 }
121
123 return playerMovable;
124 }
125
127 static bool dbg = false;
128 if(!dbg)
129 return;
130
131 if(posSpline.size()<=1)
132 return;
133
134 auto at = posSpline.keyframe[0].position(0);
135 for(size_t i=1; i<posSpline.size(); ++i) {
136 auto& k = posSpline.keyframe[i];
137 p.setPen(Color(0,0,1));
138 for(size_t r=0; r<100; ++r) {
139 auto pos = k.position(float(r)/100.f);
140 p.drawLine(at, pos);
141 at = pos;
142 }
143
144 auto pt = k.position(0);
145 float l = 30;
146 p.setPen(Color(1,0,1));
147 p.drawLine(pt-Vec3(l,0,0),pt+Vec3(l,0,0));
148 p.drawLine(pt-Vec3(0,l,0),pt+Vec3(0,l,0));
149 p.drawLine(pt-Vec3(0,0,l),pt+Vec3(0,0,l));
150 }
151 }
152
153void CsCamera::onTrigger(const TriggerEvent& evt) {
154 if(hasTicksEnabled() || posSpline.size()==0)
155 return;
156
157 if(auto cs = world.currentCs())
158 cs->onUntrigger(evt);
159
160 timeTrigger = world.tickCount();
161 world.setCurrentCs(this);
162 enableTicks();
163
164 auto& camera = world.gameSession().camera();
165 if(!camera.isCutscene()) {
166 auto cPos = position();
167 camera.setMode(Camera::Mode::Cutscene);
168 camera.setPosition(cPos);
169 camera.setSpin(spin(cPos));
170 }
171 }
172
173void CsCamera::onUntrigger(const TriggerEvent& evt) {
174 if(!hasTicksEnabled())
175 return;
176 disableTicks();
177
178 if(world.currentCs()!=this)
179 return;
180
181 world.setCurrentCs(nullptr);
182
183 auto& camera = world.gameSession().camera();
185 if(auto pl = Gothic::inst().player()) {
186 camera.reset(pl);
187 } else {
188 camera.setMarvinMode(Camera::M_Free);
189 }
190
191 if(Gothic::inst().isBenchmarkMode())
193 }
194
195void CsCamera::tick(uint64_t /*dt*/) {
196 const uint64_t time = world.tickCount() - timeTrigger;
197 if(time>duration+delay && (autoUntrigger || vobName=="TIMEDEMO")) {
199 onUntrigger(e);
200 return;
201 }
202
203 if(time>duration)
204 return;
205
206 auto& camera = world.gameSession().camera();
207 if(camera.isCutscene()) {
208 auto cPos = position();
209 camera.setPosition(cPos);
210 camera.setSpin(spin(cPos));
211 }
212 }
213
214Vec3 CsCamera::position() {
215 Vec3 pos;
216 if(posSpline.size()==1) {
217 pos = posSpline.keyframe[0].c[3];
218 } else {
219 const uint64_t time = std::min(world.tickCount() - timeTrigger, duration);
220 pos = posSpline.position(time);
221 }
222 return pos;
223 }
224
225PointF CsCamera::spin(Tempest::Vec3& d) {
226 if(targetSpline.size()==0)
227 d = d - Gothic::inst().camera()->destPosition();
228 else if(targetSpline.size()==1)
229 d = targetSpline.keyframe[0].c[3] - d;
230 else if(targetSpline.size()>1) {
231 const uint64_t time = std::min(world.tickCount() - timeTrigger, duration);
232 d = targetSpline.position(time) - d;
233 }
234
235 float k = 180.f/float(M_PI);
236 float spinX = k * std::asin(d.y/d.length());
237 float spinY = -90;
238 if(d.x!=0.f || d.z!=0.f)
239 spinY = 90 + k * std::atan2(d.z,d.x);
240
241 auto& def = Gothic::cameraDef().stdCam();
242 return {-spinX + def.rot_offset_x, spinY + def.rot_offset_y};
243 }
std::string vobName
bool hasTicksEnabled() const
std::string_view name() const
const zenkit::ICamera & stdCam() const
@ M_Free
Definition camera.h:38
void setMode(Mode m)
Definition camera.cpp:131
Tempest::Vec3 destPosition() const
Definition camera.cpp:905
@ Cutscene
Definition camera.h:32
@ Normal
Definition camera.h:22
void debugDraw(DbgPainter &p) const
Definition cscamera.cpp:126
CsCamera(Vob *parent, World &world, const zenkit::VCutsceneCamera &data, Flags flags)
Definition cscamera.cpp:103
bool isPlayerMovable() const
Definition cscamera.cpp:122
void drawLine(const Tempest::Vec3 &a, const Tempest::Vec3 &b)
void setPen(const Tempest::Pen &pen)
Camera & camera()
Definition gamesession.h:48
Camera * camera()
Definition gothic.cpp:319
static Gothic & inst()
Definition gothic.cpp:249
static const CameraDefinitions & cameraDef()
Definition gothic.cpp:663
Tempest::Signal< void()> onBenchmarkFinished
Definition gothic.h:184
Definition vob.h:11
Flags
Definition vob.h:13
World & world
Definition vob.h:45
Definition world.h:31
CsCamera * currentCs() const
Definition world.cpp:536
uint64_t tickCount() const
Definition world.cpp:387
GameSession & gameSession() const
Definition world.h:85
void setCurrentCs(CsCamera *cs)
Definition world.cpp:532