OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
camera.cpp
Go to the documentation of this file.
1#include "camera.h"
2
3#include <Tempest/Log>
4
5#include "world/objects/npc.h"
7#include "world/world.h"
9#include "game/serialize.h"
10#include "utils/gthfont.h"
11#include "utils/dbgpainter.h"
12#include "gothic.h"
13
14#include "utils/string_frm.h"
15
16using namespace Tempest;
17
18static float angleMod(float a) {
19 a = std::fmod(a,360.f);
20 if(a<-180.f)
21 a+=360.f;
22 if(a>180.f)
23 a-=360.f;
24 return a;
25 }
26
27static Vec3 angleMod(Vec3 a) {
28 a.x = angleMod(a.x);
29 a.y = angleMod(a.y);
30 a.z = angleMod(a.z);
31 return a;
32 }
33
34float Camera::maxDist = 150;
35float Camera::baseSpeeed = 200;
36float Camera::offsetAngleMul = 0.1f;
37const float Camera::minLength = 0.0001f;
38
41
43 reset(Gothic::inst().player());
44 }
45
46void Camera::reset(const Npc* pl) {
47 const auto& def = cameraDef();
48 dst.range = userRange*(def.max_range-def.min_range)+def.min_range;
49 dst.target = pl ? pl->cameraBone() : Vec3();
50
51 dst.spin.x = def.best_elevation;
52 dst.spin.y = pl ? pl->rotation() : 0;
53
54 src.spin = dst.spin;
55
56 calcControlPoints(-1.f);
57 }
58
60 s.write(src.range, src.target, src.spin,
61 dst.range, dst.target, dst.spin);
62 s.write(cameraPos,origin,rotOffset);
63 }
64
65void Camera::load(Serialize &s, Npc* pl) {
66 reset(pl);
67 s.read(src.range, src.target, src.spin,
68 dst.range, dst.target, dst.spin);
69 s.read(cameraPos,origin,rotOffset);
70 }
71
72void Camera::changeZoom(int delta) {
73 if(fpEnable)
74 return;
75 if(delta>0)
76 userRange-=0.02f; else
77 userRange+=0.02f;
78 userRange = std::max(0.f,std::min(userRange,1.f));
79 }
80
81void Camera::setViewport(uint32_t w, uint32_t h) {
82 const float fov = Gothic::options().cameraFov;
83 proj.perspective(fov, float(w)/float(h), zNear(), zFar());
84
85 // NOTE: usually depth bouds are from 0.95 to 1.0, resilting into ~805675 discrete values
86 static float l = 0.951978f, r = 1;
87 auto il = reinterpret_cast<uint32_t&>(l);
88 auto ir = reinterpret_cast<uint32_t&>(r);
89 auto diff = ir-il;
90 (void)diff;
91
92 vpWidth = w;
93 vpHeight = h;
94 depthNear = 0;
95 }
96
97float Camera::zNear() const {
98 static float near = 10.f;
99 return near;
100 }
101
102float Camera::zFar() const {
103 static float far = 100000.0f;
104 return far;
105 }
106
107void Camera::rotateLeft(uint64_t dt) {
108 implMove(KeyEvent::K_Q,dt);
109 }
110
111void Camera::rotateRight(uint64_t dt) {
112 implMove(KeyEvent::K_E,dt);
113 }
114
115void Camera::moveForward(uint64_t dt) {
116 implMove(KeyEvent::K_W,dt);
117 }
118
119void Camera::moveBack(uint64_t dt) {
120 implMove(KeyEvent::K_S,dt);
121 }
122
123void Camera::moveLeft(uint64_t dt) {
124 implMove(KeyEvent::K_A,dt);
125 }
126
127void Camera::moveRight(uint64_t dt) {
128 implMove(KeyEvent::K_D,dt);
129 }
130
132 if(camMod==m)
133 return;
134
135 const bool reset = (m==Inventory || camMod==Inventory || camMod==Dialog || camMod==Dive || m==Fall || camMod==Fall);
136 camMod = m;
137
138 if(camMarvinMod==M_Freeze)
139 return;
140
141 if(reset) {
142 resetDst();
143 // auto ang = calcOffsetAngles(src.target,dst.target);
144 // dst.spin.x = ang.x;
145 }
146
147 if(auto pl = Gothic::inst().player()) {
148 dst.spin.y = pl->rotation();
149 }
150 }
151
153 if(camMarvinMod==nextMod)
154 return;
155
156 if(auto pl = Gothic::inst().player()) {
157 if(camMarvinMod==M_Pinned) {
158 src.spin = dst.spin;
159 float range = src.range*100.f;
160 Vec3 dir = {0,0,1};
161 Matrix4x4 rotOffsetMat;
162 rotOffsetMat.identity();
163 rotOffsetMat.rotateOY(180-src.spin.y);
164 rotOffsetMat.rotateOX(src.spin.x);
165 rotOffsetMat.project(dir);
166 dst.target = origin +dir*range;
167 src.target = dst.target;
168 cameraPos = src.target;
169 rotOffset.y = 0;
170 }
171 if(nextMod==M_Pinned) {
172 const auto& def = cameraDef();
173 auto offset = origin;
174 Matrix4x4 rotMat = pl->cameraMatrix(false);
175
176 rotMat.inverse();
177 rotMat.project(offset);
178 pin.origin = offset;
179 pin.spin.x = src.spin.x - def.best_elevation;
180 pin.spin.y = src.spin.y - (pl ? pl->rotation() : 0);
181 }
182 }
183 camMarvinMod = nextMod;
184 }
185
186bool Camera::isMarvin() const {
187 return camMarvinMod!=M_Normal;
188 }
189
190bool Camera::isFree() const {
191 return camMarvinMod==M_Free;
192 }
193
194bool Camera::isInWater() const {
195 return inWater;
196 }
197
198bool Camera::isCutscene() const {
199 return camMod==Camera::Mode::Cutscene;
200 }
201
203 tgEnable = e;
204 }
205
207 return tgEnable;
208 }
209
211 inertiaTarget = e;
212 }
213
215 return inertiaTarget;
216 }
217
219 fpEnable = fp;
220 }
221
223 return fpEnable;
224 }
225
226void Camera::setLookBack(bool lb) {
227 if(lbEnable==lb)
228 return;
229 lbEnable = lb;
230 resetDst();
231 }
232
234 dbg = !dbg;
235 }
236
237void Camera::setSpin(const PointF &p) {
238 dst.spin = Vec3(p.x,p.y,0);
239 src.spin = dst.spin;
240 }
241
242void Camera::setDestSpin(const PointF& p) {
243 if(camMarvinMod==M_Free || camMarvinMod==M_Freeze)
244 return;
245 dst.spin = Vec3(p.x,p.y,0);
246 if(dst.spin.x<-90)
247 dst.spin.x = -90;
248 if(dst.spin.x>90)
249 dst.spin.x = 90;
250 }
251
252void Camera::onRotateMouse(const PointF& dpos) {
253 if(camMarvinMod==M_Freeze)
254 return;
255 dst.spin.x += dpos.x;
256 dst.spin.y += dpos.y;
257 }
258
259Matrix4x4 Camera::projective() const {
260 auto ret = proj;
261 if(auto w = Gothic::inst().world())
262 w->globalFx()->morph(ret);
263 return ret;
264 }
265
266Matrix4x4 Camera::viewShadowLwc(const Tempest::Vec3& lightDir, size_t layer) const {
267 auto vp = viewProjLwc();
268 float rotation = (180+src.spin.y-rotOffset.y);
269 // if(layer==0)
270 // return viewShadowVsm(cameraPos-origin,rotation,vp,lightDir);
271 return mkViewShadow(cameraPos-origin,rotation,vp,lightDir,layer);
272 }
273
274Matrix4x4 Camera::viewShadowVsm(const Tempest::Vec3& ldir) const {
275 return mkViewShadowVsm(cameraPos,ldir);
276 }
277
278Matrix4x4 Camera::viewShadowVsmLwc(const Tempest::Vec3& ldir) const {
279 return mkViewShadowVsm(cameraPos-origin,ldir);
280 }
281
282Matrix4x4 Camera::mkViewShadowVsm(const Vec3& cameraPos, const Vec3& ldir) const {
283 float smWidth = 1024; // ~4 pixels per santimeter
284 float smDepth = 10*5120;
285
286 float smWidthInv = 1.f/smWidth;
287 float zScale = 1.f/smDepth;
288
289 auto up = std::abs(ldir.z)<0.999 ? Vec3(0,0,1) : Vec3(1,0,0);
290 auto z = ldir;
291 auto x = Vec3::normalize(Vec3::crossProduct(z, up));
292 auto y = Vec3::crossProduct(x, z);
293
294 auto view = Matrix4x4 {
295 x.x, y.x, z.x, 0,
296 x.y, y.y, z.y, 0,
297 x.z, y.z, z.z, 0,
298 0, 0, 0, 1
299 };
300 view.transpose();
301 {
302 Matrix4x4 scale;
303 scale.identity();
304 scale.scale(smWidthInv, smWidthInv, zScale);
305 scale.mul(view);
306 view = scale;
307 }
308 view.translate(-cameraPos);
309
310 {
311 Matrix4x4 proj;
312 proj.identity();
313 proj.translate(0.f, 0.f, 0.5f);
314 proj.mul(view);
315 view = proj;
316 }
317
318 return view;
319 }
320
321Matrix4x4 Camera::viewShadow(const Vec3& lightDir, size_t layer) const {
322 auto vp = viewProj();
323 float rotation = (180+src.spin.y-rotOffset.y);
324 // if(layer==0)
325 // return viewShadowVsm(cameraPos,rotation,vp,lightDir);
326 return mkViewShadow(cameraPos,rotation,vp,lightDir,layer);
327 }
328
329Matrix4x4 Camera::mkViewShadow(const Vec3& cameraPos, float rotation, const Tempest::Matrix4x4& viewProj, const Vec3& lightDir, size_t layer) const {
330 Vec3 ldir = lightDir;
331 float eps = 0.005f;
332
333 if(std::abs(ldir.y)<eps) {
334 float k = (1.f-eps*eps)/std::sqrt(ldir.x*ldir.x + ldir.z*ldir.z);
335 ldir.y = (ldir.y<0) ? -eps : eps;
336 ldir.x *= k;
337 ldir.z *= k;
338 }
339
340 Vec3 center = cameraPos;
341 auto vp = viewProj;
342 vp.project(center);
343
344 vp.inverse();
345 Vec3 l = {-1,center.y,center.z}, r = {1,center.y,center.z};
346 vp.project(l);
347 vp.project(r);
348
349 float smWidth = 0;
350 float smDepth = 5120*5;
351 switch(layer) {
352 case 0:
353 smWidth = (r-l).length();
354 smWidth = std::max(smWidth,1024.f); // ~4 pixels per santimeter
355 smDepth = 5120;
356 break;
357 case 1:
358 smWidth = 5120;
359 smDepth = 5120*5;
360 break;
361 };
362
363 float smWidthInv = 1.f/smWidth;
364 float zScale = 1.f/smDepth;
365
366 Matrix4x4 view;
367 view.identity();
368 view.rotate(-90, 1, 0, 0); // +Y -> -Z
369 view.rotate(rotation, 0, 1, 0);
370 view.scale(smWidthInv, zScale, smWidthInv);
371 view.translate(cameraPos);
372 view.scale(-1,-1,-1);
373
374 // sun direction
375 if(ldir.y!=0.f) {
376 float lx = ldir.x/std::abs(ldir.y);
377 float lz = ldir.z/std::abs(ldir.y);
378
379 const float ang = -rotation*float(M_PI)/180.f;
380 const float c = std::cos(ang), s = std::sin(ang);
381
382 float dx = lx*c-lz*s;
383 float dz = lx*s+lz*c;
384
385 view.set(1,0, dx*smWidthInv);
386 view.set(1,1, dz*smWidthInv);
387 }
388
389 // stretch shadowmap on light dir
390 if(ldir.y!=0.f) {
391 // stretch view to camera
392 float r0 = std::fmod(rotation, 360.f);
393 float r = std::fmod(std::atan2(ldir.z,ldir.x)*180.f/float(M_PI), 360.f);
394 r -= r0;
395
396 float s = std::abs(ldir.y);
397 Matrix4x4 proj;
398 proj.identity();
399 proj.rotate( r, 0, 0, 1);
400 proj.translate(-0.5f,0,0);
401 proj.scale(s, 1, 1);
402 //proj.scale(0.1f, 1, 1);
403 proj.translate(0.5f,0,0);
404 proj.rotate(-r, 0, 0, 1);
405 proj.mul(view);
406 view = proj;
407 }
408
409 if(layer>0) {
410 // projective shadowmap
411 Tempest::Matrix4x4 proj;
412 proj.identity();
413
414 static float k = -0.4f;
415 proj.set(1,3, k);
416 proj.mul(view);
417 view = proj;
418 }
419
420 auto inv = view;
421 inv.inverse();
422 Vec3 mid = {};
423 inv.project(mid);
424 view.translate(mid-cameraPos);
425
426 {
427 Matrix4x4 proj;
428 proj.identity();
429 switch(layer) {
430 case 0:
431 proj.translate(0.f, 0.8f, 0.5f);
432 break;
433 case 1: {
434 proj.translate(0.f, 0.5f, 0.5f);
435 break;
436 }
437 }
438
439 proj.mul(view);
440 view = proj;
441 }
442
443 return view;
444 }
445
447 auto vp = viewProj();
448 vp.inverse();
449
450 ListenerPos pos;
451 pos.up = Vec3(0,1,0);
452 pos.front = Vec3(0,0,1);
453 pos.pos = Vec3(0,0,0);
454 vp.project(pos.up);
455 vp.project(pos.front);
456 vp.project(pos.pos);
457
458 pos.up = Vec3::normalize(pos.up - pos.pos);
459 pos.front = Vec3::normalize(pos.front - pos.pos);
460 return pos;
461 }
462
463const zenkit::ICamera& Camera::cameraDef() const {
464 auto& camd = Gothic::cameraDef();
465 if(camMod==Dialog)
466 return camd.dialogCam();
467 if(lbEnable)
468 return camd.backCam();
469 if(fpEnable && (camMod==Normal || camMod==Melee))
470 return camd.fpCam();
471 if(camMod==Normal) {
472 return camd.stdCam();
473 }
474 if(camMod==Inventory)
475 return camd.inventoryCam();
476 if(camMod==Melee)
477 return camd.meleeCam();
478 if(camMod==Ranged)
479 return camd.rangeCam();
480 if(camMod==Magic)
481 return camd.mageCam();
482 if(camMod==Swim)
483 return camd.swimCam();
484 if(camMod==Dive)
485 return camd.diveCam();
486 if(camMod==Fall)
487 return camd.fallCam();
488 if(camMod==Mobsi) {
489 std::string_view tag = "", pos = "";
490 if(auto pl = Gothic::inst().player())
491 if(auto inter = pl->interactive()) {
492 tag = inter->schemeName();
493 pos = inter->posSchemeName();
494 }
495 return camd.mobsiCam(tag,pos);
496 }
497 if(camMod==Death)
498 return camd.deathCam();
499 return camd.stdCam();
500 }
501
502void Camera::clampRotation(Tempest::Vec3& spin) {
503 const auto& def = cameraDef();
504 float maxElev = isMarvin() ? 90 : def.max_elevation;
505 float minElev = isMarvin() ? -90 : def.min_elevation;
506 if(spin.x>maxElev)
507 spin.x = maxElev;
508 if(spin.x<minElev)
509 ;//spin.x = def.minElevation;
510 }
511
512void Camera::implMove(Tempest::Event::KeyType key, uint64_t dt) {
513 float dpos = float(dt);
514 float dRot = dpos/15.f;
515 float k = float(M_PI/180.0);
516 float s = std::sin(dst.spin.y*k), c=std::cos(dst.spin.y*k);
517 const auto& def = cameraDef();
518 float sx = std::sin((dst.spin.x-def.best_elevation)*k);
519 float cx = std::cos((dst.spin.x-def.best_elevation)*k);
520
521 if(key==KeyEvent::K_A) {
522 dst.target.x += dpos*c;
523 dst.target.z += dpos*s;
524 }
525 if(key==KeyEvent::K_D) {
526 dst.target.x -= dpos*c;
527 dst.target.z -= dpos*s;
528 }
529 if(key==KeyEvent::K_W) {
530 dst.target.x += dpos*s*cx;
531 dst.target.z -= dpos*c*cx;
532 dst.target.y -= dpos*sx;
533 }
534 if(key==KeyEvent::K_S) {
535 dst.target.x -= dpos*s*cx;
536 dst.target.z += dpos*c*cx;
537 dst.target.y += dpos*sx;
538 }
539 if(key==KeyEvent::K_Q)
540 dst.spin.y += dRot;
541 if(key==KeyEvent::K_E)
542 dst.spin.y -= dRot;
543 }
544
545void Camera::setPosition(const Tempest::Vec3& pos) {
546 dst.target = pos;
547 src.target = dst.target;
548 cameraPos = dst.target;
549 }
550
551void Camera::setDestPosition(const Tempest::Vec3& pos) {
552 if(camMarvinMod!=M_Free && (camMarvinMod!=M_Freeze || camMod==Dialog))
553 dst.target = pos;
554 }
555
557 dlgDist = d;
558 }
559
560void Camera::followPos(Vec3& pos, Vec3 dest, float dtF) {
561 const auto& def = cameraDef();
562
563 auto dp = (dest-pos);
564 auto len = dp.length();
565
566 if(dtF<=0.f) {
567 pos = dest;
568 return;
569 }
570
571 if(len<=minLength) {
572 return;
573 }
574
575 static float mul = 2.1f;
576 static float mul2 = 10.f;
577 targetVelo = targetVelo + (len-targetVelo)*std::min(1.f,dtF*mul2);
578
579 if(inertiaTarget) {
580 veloTrans = std::min(def.velo_trans*100, targetVelo*mul);
581 } else {
582 veloTrans = def.velo_trans*100;
583 }
584
585 float tr = std::min(veloTrans*dtF,len);
586 float k = tr/len;
587 pos += dp*k;
588
589 /*
590 {
591 auto diff = dp*k;
592 float speed = diff.length()/dtF;
593 static float prevSpeed = 0;
594
595 if(false && speed > 1.f)
596 Log::i("speed: ", speed, "delta: ", std::abs(prevSpeed-speed));
597 prevSpeed = speed;
598 }
599 */
600 }
601
602void Camera::followCamera(Vec3& pos, Vec3 dest, float dtF) {
603 const auto& def = cameraDef();
604 if(!def.translate)
605 return;
606 pos = dest;
607 }
608
609void Camera::followAng(Vec3& spin, Vec3 dest, float dtF) {
610 const auto& def = cameraDef();
611 followAng(spin.x,dest.x,def.velo_rot,dtF);
612 followAng(spin.y,dest.y,def.velo_rot,dtF);
613 }
614
615void Camera::followAng(float& ang, float dest, float speed, float dtF) {
616 float da = angleMod(dest-ang);
617 float shift = da*speed*std::min(1.f, dtF);
618 if(std::abs(da)<=0.0001f || dtF<0.f) {
619 ang = dest;
620 return;
621 }
622
623 static const float min=-45, max=45;
624 if(da>max+1.f) {
625 shift = (da-max);
626 }
627 if(da<min-1.f) {
628 shift = (da-min);
629 }
630 ang += shift;
631 }
632
633void Camera::tick(uint64_t dt) {
634 if(Gothic::inst().isPause() || (camMarvinMod==M_Freeze && camMod!=Dialog))
635 return;
636
637 const float dtF = float(dt)/1000.f;
638
639 {
640 const auto& def = cameraDef();
641 dst.range = def.min_range + (def.max_range-def.min_range)*userRange;
642 const float zSpeed = 5.f;
643 const float dz = dst.range-src.range;
644 src.range+=dz*std::min(1.f,2.f*zSpeed*dtF);
645 }
646
647 auto prev = origin;
648 calcControlPoints(dtF);
649
650 auto world = Gothic::inst().world();
651 if(world!=nullptr) {
652 auto pl = isFree() ? nullptr : world->player();
653 auto& physic = *world->physic();
654
655 if(pl!=nullptr && !pl->isInWater()) {
656 inWater = physic.cameraRay(src.target, origin).waterCol % 2;
657 } else {
658 // NOTE: find a way to avoid persistent tracking
659 inWater = inWater ^ (physic.cameraRay(prev, origin).waterCol % 2);
660 }
661 }
662 }
663
664void Camera::calcControlPoints(float dtF) {
665 const auto& def = cameraDef();
666 auto targetOffset = Vec3(def.target_offset_x,
667 def.target_offset_y,
668 def.target_offset_z);
669 auto rotOffsetDef = Vec3(def.rot_offset_x,
670 def.rot_offset_y,
671 def.rot_offset_z);
672 auto rotBest = Vec3(0,def.best_azimuth,0);
673
674 clampRotation(dst.spin);
675
676 float range = src.range*100.f;
677 if(camMod==Dialog) {
678 // TODO: DialogCams.zen
679 range = dlgDist;
680 src.spin = dst.spin;
681 src.target = dst.target;
682 cameraPos = src.target;
683 rotOffset = Vec3();
684 rotOffsetDef = Vec3();
685 rotBest = Vec3();
686 //spin.y += def.bestAzimuth;
687 }
688 if(isCutscene()) {
689 rotOffset = rotOffsetDef;
690 range = 0;
691 }
692
693 followAng(src.spin, dst.spin+rotBest, dtF);
694 if(!isMarvin())
695 followAng(rotOffset, rotOffsetDef, dtF);
696
697 Matrix4x4 rotOffsetMat;
698 rotOffsetMat.identity();
699 rotOffsetMat.rotateOY(180-src.spin.y);
700 rotOffsetMat.rotateOX(src.spin.x);
701 rotOffsetMat.project(targetOffset);
702
703 Vec3 dir = {0,0,1};
704 rotOffsetMat.project(dir);
705
706 auto target = dst.target + targetOffset;
707 followPos(src.target,target,dtF);
708
709 auto camTg = src.target;//clampPos(src.target,target);
710 followCamera(cameraPos,src.target,dtF);
711
712 origin = cameraPos - dir*range;
713 if(camMarvinMod==M_Free || isCutscene()) {
714 return;
715 }
716
717 const auto pl = Gothic::inst().player();
718 if(camMarvinMod==M_Pinned && camMod!=Dialog && pl!=nullptr) {
719 auto rotMat = pl->cameraMatrix(false);
720 auto offset = pin.origin;
721 rotMat.project(offset);
722 origin = offset;
723 src.target = dst.target;
724 src.spin = dst.spin + pin.spin;
725 offsetAng = Vec3();
726 return;
727 }
728
729 if(def.collision!=0) {
730 // range = calcCameraColision(camTg,origin,src.spin,range);
731 // origin = cameraPos - dir*range;
732 origin = calcCameraColision(camTg,origin,src.spin+offsetAng,range);
733 range = (origin - camTg).length();
734 }
735
736 auto baseOrigin = target - dir*range;
737 if(camMod==Dialog)
738 offsetAng = Vec3(); else
739 offsetAng = calcOffsetAngles(origin,baseOrigin,dst.target);
740
741 if(fpEnable && camMarvinMod==M_Normal) {
742 origin = dst.target;
743 offsetAng = Vec3();
744
745 Vec3 offset = {0,0,20};
746 Matrix4x4 rotOffsetMat;
747 rotOffsetMat.identity();
748 rotOffsetMat.rotateOY(180-src.spin.y);
749 rotOffsetMat.project(offset);
750 origin += offset;
751 }
752 }
753
754Vec3 Camera::calcOffsetAngles(const Vec3& origin, const Vec3& target) const {
755 auto sXZ = origin-target;
756 float y0 = std::atan2(sXZ.x,sXZ.z)*180.f/float(M_PI);
757 float x0 = std::atan2(sXZ.y,Vec2(sXZ.x,sXZ.z).length())*180.f/float(M_PI);
758
759 return Vec3(x0,-y0,0);
760 }
761
762Vec3 Camera::calcOffsetAngles(Vec3 srcOrigin, Vec3 dstOrigin, Vec3 target) const {
763 auto src = srcOrigin-target; src.y = 0;
764 auto dst = dstOrigin-target; dst.y = 0;
765
766 auto dot = Vec3::dotProduct(src,dst);
767 float k = 0;
768 if(dst.length()>minLength) {
769 k = dot/dst.length();
770 k = std::max(0.f,std::min(k/100.f,1.f));
771 }
772
773 auto a0 = calcOffsetAngles(srcOrigin,target);
774 auto a1 = calcOffsetAngles(dstOrigin,target);
775 auto da = angleMod(a1-a0);
776 return da*k*offsetAngleMul;
777 }
778
779Vec3 Camera::calcCameraColision(const Vec3& target, const Vec3& origin, const Vec3& rotSpin, float dist) const {
780 if(camMod==Dialog)
781 dist = dlgDist;
782
783 auto world = Gothic::inst().world();
784 if(world==nullptr)
785 return origin;
786
787 //static float minDist = 20;
788 static float padding = 50;
789 static int n = 1, nn=1;
790
791 Matrix4x4 vinv=projective();
792 vinv.mul(mkView(origin,rotSpin));
793 vinv.inverse();
794
795 auto& physic = *world->physic();
796 auto dview = (origin - target);
797
798 raysCasted = 0;
799 float distM = dist;
800 for(int i=-n;i<=n;++i)
801 for(int r=-n;r<=n;++r) {
802 raysCasted++;
803 float u = float(i)/float(nn),v = float(r)/float(nn);
804 Tempest::Vec3 r1 = {u,v,depthNear};
805 vinv.project(r1);
806 auto dr = (r1 - target);
807 dr = dr * (dist+padding) / (dr.length()+0.00001f);
808
809 auto rc = physic.ray(target, target+dr);
810 if(!rc.hasCol)
811 continue;
812
813 auto tr = (rc.v - target);
814 float dist1 = Vec3::dotProduct(dview,tr)/dist;
815
816 dist1 = std::max<float>(dist1-padding, 0);
817 if(dist1<distM)
818 distM = dist1;
819 }
820
821 auto dp = Vec3::normalize(origin-target)*distM;
822 static float dd = 100.f;
823 if(dp.y>0 && dp.y<dd && camMod!=Dialog) {
824 // pin to hero head
825 // dp.y = dd;
826 }
827 return target + dp;
828
829 // distM = std::max(minDist,distM);
830 // return target + Vec3::normalize(origin-target)*distM;
831 }
832
833Matrix4x4 Camera::mkView(const Vec3& pos, const Vec3& spin) const {
834 Matrix4x4 view;
835 view.identity();
836 view.scale(-1,-1,-1);
837
838 // view.translate(0,0,-zNear());
839 view.mul(mkRotation(spin));
840 view.translate(-pos);
841
842 return view;
843 }
844
845Matrix4x4 Camera::mkRotation(const Vec3& spin) const {
846 Matrix4x4 view;
847 view.identity();
848 view.rotateOX(spin.x-rotOffset.x);
849 view.rotateOY(spin.y-rotOffset.y);
850 view.rotateOZ(spin.z-rotOffset.z);
851 return view;
852 }
853
854void Camera::resetDst() {
855 if(isMarvin())
856 return;
857 const auto& def = cameraDef();
858 dst.spin.x = def.best_elevation;
859 dst.range = def.best_range;
860 }
861
863 if(!dbg)
864 return;
865
866 p.setPen(Color(0,1,0));
867 p.drawLine(dst.target, src.target);
868
869 if(auto pl = Gothic::inst().player()) {
870 float a = pl->rotationRad();
871 float c = std::cos(a), s = std::sin(a);
872 auto ln = Vec3(c,0,s)*25.f;
873 p.drawLine(src.target-ln, src.target+ln);
874 }
875
876 auto& fnt = Resources::font(1.0);
877 int y = 300+fnt.pixelSize();
878
879 string_frm buf("RaysCasted: ",raysCasted);
880 p.drawText(8,y,buf); y += fnt.pixelSize();
881
882 buf = string_frm("PlayerPos : ",dst.target.x, ' ', dst.target.y, ' ', dst.target.z);
883 p.drawText(8,y,buf); y += fnt.pixelSize();
884
885 buf = string_frm("targetVelo : ",targetVelo);
886 p.drawText(8,y,buf); y += fnt.pixelSize()*4;
887
888 buf = string_frm("Range To Player : ", (dst.target-origin).length());
889 p.drawText(8,y,buf); y += fnt.pixelSize();
890
891 buf = string_frm("Azimuth : ", angleMod(dst.spin.y-src.spin.y));
892 p.drawText(8,y,buf); y += fnt.pixelSize();
893 buf = string_frm("Elevation : ", rotOffset.x-src.spin.x);
894 p.drawText(8,y,buf); y += fnt.pixelSize();
895 }
896
897PointF Camera::spin() const {
898 return PointF(src.spin.x,src.spin.y);
899 }
900
901PointF Camera::destSpin() const {
902 return PointF(dst.spin.x,dst.spin.y);
903 }
904
906 return dst.target;
907 }
908
909Matrix4x4 Camera::viewProj() const {
910 Matrix4x4 ret=projective();
911 ret.mul(view());
912 return ret;
913 }
914
915Matrix4x4 Camera::view() const {
916 auto spin = src.spin+offsetAng;
917 return mkView(origin,spin);
918 }
919
920Matrix4x4 Camera::viewLwc() const {
921 auto spin = src.spin+offsetAng;
922 return mkView(Vec3(0),spin);
923 }
924
925Matrix4x4 Camera::viewProjLwc() const {
926 Matrix4x4 ret=projective();
927 ret.mul(viewLwc());
928 return ret;
929 }
static float angleMod(float a)
Definition camera.cpp:18
Camera()
Definition camera.cpp:39
void setMarvinMode(MarvinMode m)
Definition camera.cpp:152
void reset()
Definition camera.cpp:42
Tempest::PointF spin() const
Definition camera.cpp:897
bool isToggleEnabled() const
Definition camera.cpp:206
bool isMarvin() const
Definition camera.cpp:186
MarvinMode
Definition camera.h:35
@ M_Free
Definition camera.h:38
@ M_Normal
Definition camera.h:36
@ M_Freeze
Definition camera.h:37
@ M_Pinned
Definition camera.h:39
Tempest::Matrix4x4 viewShadowLwc(const Tempest::Vec3 &ldir, size_t layer) const
Definition camera.cpp:266
void moveForward(uint64_t dt)
Definition camera.cpp:115
void setPosition(const Tempest::Vec3 &pos)
Definition camera.cpp:545
void toggleDebug()
Definition camera.cpp:233
void setViewport(uint32_t w, uint32_t h)
Definition camera.cpp:81
Tempest::Matrix4x4 view() const
Definition camera.cpp:915
float zFar() const
Definition camera.cpp:102
void setSpin(const Tempest::PointF &p)
Definition camera.cpp:237
void moveRight(uint64_t dt)
Definition camera.cpp:127
void moveLeft(uint64_t dt)
Definition camera.cpp:123
void debugDraw(DbgPainter &p)
Definition camera.cpp:862
void tick(uint64_t dt)
Definition camera.cpp:633
void rotateLeft(uint64_t dt)
Definition camera.cpp:107
Tempest::Matrix4x4 projective() const
Definition camera.cpp:259
bool isCutscene() const
Definition camera.cpp:198
void setFirstPerson(bool fp)
Definition camera.cpp:218
void setToggleEnable(bool e)
Definition camera.cpp:202
void onRotateMouse(const Tempest::PointF &dpos)
Definition camera.cpp:252
void moveBack(uint64_t dt)
Definition camera.cpp:119
void setDialogDistance(float d)
Definition camera.cpp:556
bool isFree() const
Definition camera.cpp:190
void setDestPosition(const Tempest::Vec3 &pos)
Definition camera.cpp:551
void setDestSpin(const Tempest::PointF &p)
Definition camera.cpp:242
void setLookBack(bool lb)
Definition camera.cpp:226
void setMode(Mode m)
Definition camera.cpp:131
void save(Serialize &s)
Definition camera.cpp:59
bool isInWater() const
Definition camera.cpp:194
bool isInertiaTargetEnabled() const
Definition camera.cpp:214
float zNear() const
Definition camera.cpp:97
Tempest::Matrix4x4 viewProjLwc() const
Definition camera.cpp:925
Tempest::Vec3 destPosition() const
Definition camera.cpp:905
void rotateRight(uint64_t dt)
Definition camera.cpp:111
void setInertiaTargetEnable(bool e)
Definition camera.cpp:210
Tempest::Matrix4x4 viewLwc() const
Definition camera.cpp:920
Tempest::Matrix4x4 viewShadowVsm(const Tempest::Vec3 &ldir) const
Definition camera.cpp:274
ListenerPos listenerPosition() const
Definition camera.cpp:446
bool isFirstPerson() const
Definition camera.cpp:222
Tempest::Matrix4x4 viewShadow(const Tempest::Vec3 &ldir, size_t layer) const
Definition camera.cpp:321
Tempest::PointF destSpin() const
Definition camera.cpp:901
void changeZoom(int delta)
Definition camera.cpp:72
Tempest::Matrix4x4 viewShadowVsmLwc(const Tempest::Vec3 &ldir) const
Definition camera.cpp:278
Tempest::Matrix4x4 viewProj() const
Definition camera.cpp:909
@ Ranged
Definition camera.h:25
@ Death
Definition camera.h:28
@ Cutscene
Definition camera.h:32
@ Normal
Definition camera.h:22
@ Swim
Definition camera.h:29
@ Dive
Definition camera.h:30
@ Dialog
Definition camera.h:21
@ Melee
Definition camera.h:24
@ Fall
Definition camera.h:31
@ Mobsi
Definition camera.h:27
@ Magic
Definition camera.h:26
void load(Serialize &s, Npc *pl)
Definition camera.cpp:65
void drawText(int x, int y, std::string_view txt)
void drawLine(const Tempest::Vec3 &a, const Tempest::Vec3 &b)
void setPen(const Tempest::Pen &pen)
static auto options() -> const Options &
Definition gothic.cpp:496
const World * world() const
Definition gothic.cpp:278
static Gothic & inst()
Definition gothic.cpp:249
static const CameraDefinitions & cameraDef()
Definition gothic.cpp:663
Npc * player()
Definition gothic.cpp:313
Definition npc.h:25
auto cameraBone(bool isFirstPerson=false) const -> Tempest::Vec3
Definition npc.cpp:636
float rotation() const
Definition npc.cpp:658
auto cameraMatrix(bool isFirstPerson=false) const -> Tempest::Matrix4x4
Definition npc.cpp:650
static const GthFont & font(const float scale)
void write(const Arg &... a)
Definition serialize.h:76
void read(Arg &... a)
Definition serialize.h:81
Npc * player() const
Definition world.h:111
Tempest::Vec3 front
Definition camera.h:44
Tempest::Vec3 up
Definition camera.h:43
Tempest::Vec3 pos
Definition camera.h:45