16using namespace Tempest;
19 a = std::fmod(a,360.f);
34float Camera::maxDist = 150;
35float Camera::baseSpeeed = 200;
36float Camera::offsetAngleMul = 0.1f;
37const float Camera::minLength = 0.0001f;
47 const auto& def = cameraDef();
48 dst.range = userRange*(def.max_range-def.min_range)+def.min_range;
51 dst.spin.x = def.best_elevation;
52 dst.spin.y = pl ? pl->
rotation() : 0;
56 calcControlPoints(-1.f);
60 s.
write(src.range, src.target, src.spin,
61 dst.range, dst.target, dst.spin);
62 s.
write(cameraPos,origin,rotOffset);
67 s.
read(src.range, src.target, src.spin,
68 dst.range, dst.target, dst.spin);
69 s.
read(cameraPos,origin,rotOffset);
76 userRange-=0.02f;
else
78 userRange = std::max(0.f,std::min(userRange,1.f));
83 proj.perspective(fov,
float(w)/
float(h),
zNear(),
zFar());
86 static float l = 0.951978f, r = 1;
87 auto il =
reinterpret_cast<uint32_t&
>(l);
88 auto ir =
reinterpret_cast<uint32_t&
>(r);
98 static float near = 10.f;
103 static float far = 100000.0f;
108 implMove(KeyEvent::K_Q,dt);
112 implMove(KeyEvent::K_E,dt);
116 implMove(KeyEvent::K_W,dt);
120 implMove(KeyEvent::K_S,dt);
124 implMove(KeyEvent::K_A,dt);
128 implMove(KeyEvent::K_D,dt);
148 dst.spin.y = pl->rotation();
153 if(camMarvinMod==nextMod)
159 float range = src.range*100.f;
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;
172 const auto& def = cameraDef();
173 auto offset = origin;
174 Matrix4x4 rotMat = pl->cameraMatrix(
false);
177 rotMat.project(offset);
179 pin.spin.x = src.spin.x - def.best_elevation;
180 pin.spin.y = src.spin.y - (pl ? pl->rotation() : 0);
183 camMarvinMod = nextMod;
191 return camMarvinMod==
M_Free;
215 return inertiaTarget;
238 dst.spin = Vec3(p.x,p.y,0);
245 dst.spin = Vec3(p.x,p.y,0);
255 dst.spin.x += dpos.x;
256 dst.spin.y += dpos.y;
262 w->globalFx()->morph(ret);
268 float rotation = (180+src.spin.y-rotOffset.y);
271 return mkViewShadow(cameraPos-origin,rotation,vp,lightDir,layer);
275 return mkViewShadowVsm(cameraPos,ldir);
279 return mkViewShadowVsm(cameraPos-origin,ldir);
282Matrix4x4 Camera::mkViewShadowVsm(
const Vec3& cameraPos,
const Vec3& ldir)
const {
283 float smWidth = 1024;
284 float smDepth = 10*5120;
286 float smWidthInv = 1.f/smWidth;
287 float zScale = 1.f/smDepth;
289 auto up = std::abs(ldir.z)<0.999 ? Vec3(0,0,1) : Vec3(1,0,0);
291 auto x = Vec3::normalize(Vec3::crossProduct(z, up));
292 auto y = Vec3::crossProduct(x, z);
294 auto view = Matrix4x4 {
304 scale.scale(smWidthInv, smWidthInv, zScale);
308 view.translate(-cameraPos);
313 proj.translate(0.f, 0.f, 0.5f);
323 float rotation = (180+src.spin.y-rotOffset.y);
326 return mkViewShadow(cameraPos,rotation,vp,lightDir,layer);
329Matrix4x4 Camera::mkViewShadow(
const Vec3& cameraPos,
float rotation,
const Tempest::Matrix4x4& viewProj,
const Vec3& lightDir,
size_t layer)
const {
330 Vec3 ldir = lightDir;
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;
340 Vec3 center = cameraPos;
345 Vec3 l = {-1,center.y,center.z}, r = {1,center.y,center.z};
350 float smDepth = 5120*5;
353 smWidth = (r-l).length();
354 smWidth = std::max(smWidth,1024.f);
363 float smWidthInv = 1.f/smWidth;
364 float zScale = 1.f/smDepth;
368 view.rotate(-90, 1, 0, 0);
369 view.rotate(rotation, 0, 1, 0);
370 view.scale(smWidthInv, zScale, smWidthInv);
371 view.translate(cameraPos);
372 view.scale(-1,-1,-1);
376 float lx = ldir.x/std::abs(ldir.y);
377 float lz = ldir.z/std::abs(ldir.y);
379 const float ang = -rotation*float(M_PI)/180.f;
380 const float c = std::cos(ang), s = std::sin(ang);
382 float dx = lx*c-lz*s;
383 float dz = lx*s+lz*c;
385 view.set(1,0, dx*smWidthInv);
386 view.set(1,1, dz*smWidthInv);
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);
396 float s = std::abs(ldir.y);
399 proj.rotate( r, 0, 0, 1);
400 proj.translate(-0.5f,0,0);
403 proj.translate(0.5f,0,0);
404 proj.rotate(-r, 0, 0, 1);
411 Tempest::Matrix4x4 proj;
414 static float k = -0.4f;
424 view.translate(mid-cameraPos);
431 proj.translate(0.f, 0.8f, 0.5f);
434 proj.translate(0.f, 0.5f, 0.5f);
451 pos.
up = Vec3(0,1,0);
452 pos.
front = Vec3(0,0,1);
453 pos.
pos = Vec3(0,0,0);
455 vp.project(pos.
front);
458 pos.
up = Vec3::normalize(pos.
up - pos.
pos);
463const zenkit::ICamera& Camera::cameraDef()
const {
466 return camd.dialogCam();
468 return camd.backCam();
472 return camd.stdCam();
475 return camd.inventoryCam();
477 return camd.meleeCam();
479 return camd.rangeCam();
481 return camd.mageCam();
483 return camd.swimCam();
485 return camd.diveCam();
487 return camd.fallCam();
489 std::string_view tag =
"", pos =
"";
491 if(
auto inter = pl->interactive()) {
492 tag = inter->schemeName();
493 pos = inter->posSchemeName();
495 return camd.mobsiCam(tag,pos);
498 return camd.deathCam();
499 return camd.stdCam();
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;
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);
521 if(key==KeyEvent::K_A) {
522 dst.target.x += dpos*c;
523 dst.target.z += dpos*s;
525 if(key==KeyEvent::K_D) {
526 dst.target.x -= dpos*c;
527 dst.target.z -= dpos*s;
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;
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;
539 if(key==KeyEvent::K_Q)
541 if(key==KeyEvent::K_E)
547 src.target = dst.target;
548 cameraPos = dst.target;
560void Camera::followPos(Vec3& pos, Vec3 dest,
float dtF) {
561 const auto& def = cameraDef();
563 auto dp = (dest-pos);
564 auto len = dp.length();
575 static float mul = 2.1f;
576 static float mul2 = 10.f;
577 targetVelo = targetVelo + (len-targetVelo)*std::min(1.f,dtF*mul2);
580 veloTrans = std::min(def.velo_trans*100, targetVelo*mul);
582 veloTrans = def.velo_trans*100;
585 float tr = std::min(veloTrans*dtF,len);
602void Camera::followCamera(Vec3& pos, Vec3 dest,
float dtF) {
603 const auto& def = cameraDef();
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);
615void Camera::followAng(
float& ang,
float dest,
float speed,
float dtF) {
617 float shift = da*speed*std::min(1.f, dtF);
618 if(std::abs(da)<=0.0001f || dtF<0.f) {
623 static const float min=-45, max=45;
637 const float dtF = float(dt)/1000.f;
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);
648 calcControlPoints(dtF);
653 auto& physic = *world->physic();
655 if(pl!=
nullptr && !pl->isInWater()) {
656 inWater = physic.cameraRay(src.target, origin).waterCol % 2;
659 inWater = inWater ^ (physic.cameraRay(prev, origin).waterCol % 2);
664void Camera::calcControlPoints(
float dtF) {
665 const auto& def = cameraDef();
666 auto targetOffset = Vec3(def.target_offset_x,
668 def.target_offset_z);
669 auto rotOffsetDef = Vec3(def.rot_offset_x,
672 auto rotBest = Vec3(0,def.best_azimuth,0);
674 clampRotation(dst.spin);
676 float range = src.range*100.f;
681 src.target = dst.target;
682 cameraPos = src.target;
684 rotOffsetDef = Vec3();
689 rotOffset = rotOffsetDef;
693 followAng(src.spin, dst.spin+rotBest, dtF);
695 followAng(rotOffset, rotOffsetDef, dtF);
697 Matrix4x4 rotOffsetMat;
698 rotOffsetMat.identity();
699 rotOffsetMat.rotateOY(180-src.spin.y);
700 rotOffsetMat.rotateOX(src.spin.x);
701 rotOffsetMat.project(targetOffset);
704 rotOffsetMat.project(dir);
706 auto target = dst.target + targetOffset;
707 followPos(src.target,target,dtF);
709 auto camTg = src.target;
710 followCamera(cameraPos,src.target,dtF);
712 origin = cameraPos - dir*range;
720 auto offset = pin.origin;
721 rotMat.project(offset);
723 src.target = dst.target;
724 src.spin = dst.spin + pin.spin;
729 if(def.collision!=0) {
732 origin = calcCameraColision(camTg,origin,src.spin+offsetAng,range);
733 range = (origin - camTg).length();
736 auto baseOrigin = target - dir*range;
738 offsetAng = Vec3();
else
739 offsetAng = calcOffsetAngles(origin,baseOrigin,dst.target);
741 if(fpEnable && camMarvinMod==
M_Normal) {
745 Vec3 offset = {0,0,20};
746 Matrix4x4 rotOffsetMat;
747 rotOffsetMat.identity();
748 rotOffsetMat.rotateOY(180-src.spin.y);
749 rotOffsetMat.project(offset);
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);
759 return Vec3(x0,-y0,0);
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;
766 auto dot = Vec3::dotProduct(src,dst);
768 if(dst.length()>minLength) {
769 k = dot/dst.length();
770 k = std::max(0.f,std::min(k/100.f,1.f));
773 auto a0 = calcOffsetAngles(srcOrigin,target);
774 auto a1 = calcOffsetAngles(dstOrigin,target);
776 return da*k*offsetAngleMul;
779Vec3 Camera::calcCameraColision(
const Vec3& target,
const Vec3& origin,
const Vec3& rotSpin,
float dist)
const {
788 static float padding = 50;
789 static int n = 1, nn=1;
792 vinv.mul(mkView(origin,rotSpin));
795 auto& physic = *world->physic();
796 auto dview = (origin - target);
800 for(
int i=-n;i<=n;++i)
801 for(
int r=-n;r<=n;++r) {
803 float u = float(i)/float(nn),v = float(r)/float(nn);
804 Tempest::Vec3 r1 = {u,v,depthNear};
806 auto dr = (r1 - target);
807 dr = dr * (dist+padding) / (dr.length()+0.00001f);
809 auto rc = physic.ray(target, target+dr);
813 auto tr = (rc.v - target);
814 float dist1 = Vec3::dotProduct(dview,tr)/dist;
816 dist1 = std::max<float>(dist1-padding, 0);
821 auto dp = Vec3::normalize(origin-target)*distM;
822 static float dd = 100.f;
823 if(dp.y>0 && dp.y<dd && camMod!=
Dialog) {
833Matrix4x4 Camera::mkView(
const Vec3& pos,
const Vec3& spin)
const {
836 view.scale(-1,-1,-1);
840 view.translate(-pos);
845Matrix4x4 Camera::mkRotation(
const Vec3& spin)
const {
854void Camera::resetDst() {
857 const auto& def = cameraDef();
858 dst.spin.x = def.best_elevation;
859 dst.range = def.best_range;
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);
877 int y = 300+fnt.pixelSize();
880 p.
drawText(8,y,buf); y += fnt.pixelSize();
882 buf =
string_frm(
"PlayerPos : ",dst.target.x,
' ', dst.target.y,
' ', dst.target.z);
883 p.
drawText(8,y,buf); y += fnt.pixelSize();
886 p.
drawText(8,y,buf); y += fnt.pixelSize()*4;
888 buf =
string_frm(
"Range To Player : ", (dst.target-origin).length());
889 p.
drawText(8,y,buf); y += fnt.pixelSize();
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();
898 return PointF(src.spin.x,src.spin.y);
902 return PointF(dst.spin.x,dst.spin.y);
916 auto spin = src.spin+offsetAng;
917 return mkView(origin,
spin);
921 auto spin = src.spin+offsetAng;
922 return mkView(Vec3(0),
spin);
static float angleMod(float a)
void setMarvinMode(MarvinMode m)
Tempest::PointF spin() const
bool isToggleEnabled() const
Tempest::Matrix4x4 viewShadowLwc(const Tempest::Vec3 &ldir, size_t layer) const
void moveForward(uint64_t dt)
void setPosition(const Tempest::Vec3 &pos)
void setViewport(uint32_t w, uint32_t h)
Tempest::Matrix4x4 view() const
void setSpin(const Tempest::PointF &p)
void moveRight(uint64_t dt)
void moveLeft(uint64_t dt)
void debugDraw(DbgPainter &p)
void rotateLeft(uint64_t dt)
Tempest::Matrix4x4 projective() const
void setFirstPerson(bool fp)
void setToggleEnable(bool e)
void onRotateMouse(const Tempest::PointF &dpos)
void moveBack(uint64_t dt)
void setDialogDistance(float d)
void setDestPosition(const Tempest::Vec3 &pos)
void setDestSpin(const Tempest::PointF &p)
void setLookBack(bool lb)
bool isInertiaTargetEnabled() const
Tempest::Matrix4x4 viewProjLwc() const
Tempest::Vec3 destPosition() const
void rotateRight(uint64_t dt)
void setInertiaTargetEnable(bool e)
Tempest::Matrix4x4 viewLwc() const
Tempest::Matrix4x4 viewShadowVsm(const Tempest::Vec3 &ldir) const
ListenerPos listenerPosition() const
bool isFirstPerson() const
Tempest::Matrix4x4 viewShadow(const Tempest::Vec3 &ldir, size_t layer) const
Tempest::PointF destSpin() const
void changeZoom(int delta)
Tempest::Matrix4x4 viewShadowVsmLwc(const Tempest::Vec3 &ldir) const
Tempest::Matrix4x4 viewProj() const
void load(Serialize &s, Npc *pl)
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 &
const World * world() const
static const CameraDefinitions & cameraDef()
auto cameraBone(bool isFirstPerson=false) const -> Tempest::Vec3
auto cameraMatrix(bool isFirstPerson=false) const -> Tempest::Matrix4x4
static const GthFont & font(const float scale)
void write(const Arg &... a)