Files
ss/src/d/d_camera.cpp
T
robojumper ac48f9877b d_camera OK
2026-03-01 20:59:49 +01:00

593 lines
15 KiB
C++

#include "d/d_camera.h"
#include "c/c_math.h"
#include "common.h"
#include "d/a/d_a_player.h"
#include "d/col/bg/d_bg_s_gnd_chk.h"
#include "d/col/bg/d_bg_s_lin_chk.h"
#include "d/col/bg/d_bg_s_roof_chk.h"
#include "d/d_gfx.h"
#include "d/d_pad.h"
#include "d/d_sc_game.h"
#include "d/d_stage_mgr.h"
#include "d/snd/d_snd_3d_manager.h"
#include "egg/gfx/eggFrustum.h"
#include "egg/math/eggMatrix.h"
#include "f/f_base.h"
#include "f/f_profile.h"
#include "m/m_angle.h"
#include "m/m_mtx.h"
#include "m/m_vec.h"
#include "nw4r/g3d/g3d_camera.h"
#include "toBeSorted/d_camera_base.h"
#include "toBeSorted/d_camera_math.h"
#include "toBeSorted/event_manager.h"
class dCamera_HIO_c {
public:
dCamera_HIO_c();
virtual ~dCamera_HIO_c();
/* 0x04 */ f32 field_0x04;
/* 0x08 */ f32 field_0x08;
/* 0x0C */ f32 field_0x0C;
/* 0x10 */ s16 field_0x10;
/* 0x12 */ s16 field_0x12;
/* 0x14 */ f32 field_0x14;
/* 0x18 */ u16 field_0x18;
/* 0x1C */ f32 field_0x1C;
/* 0x20 */ f32 field_0x20;
/* 0x24 */ s16 field_0x24;
/* 0x28 */ f32 field_0x28;
/* 0x2C */ f32 field_0x2C;
/* 0x30 */ s16 field_0x30;
};
dCamera_HIO_c::~dCamera_HIO_c() {}
dCamera_HIO_c::dCamera_HIO_c() {
field_0x04 = 1.0f;
field_0x08 = 240000.0f;
field_0x14 = 0.1f;
field_0x18 = 0x1555;
field_0x1C = 30.0f;
field_0x20 = 30.0f;
field_0x24 = 8;
field_0x28 = 0.7f;
field_0x2C = 0.2f;
field_0x30 = 0;
field_0x0C = 1.0f;
field_0x10 = 1;
field_0x12 = 1;
}
static dCamera_HIO_c sHio;
SPECIAL_BASE_PROFILE(CAMERA, dCamera_c, fProfile::CAMERA, 0xA, 0x30);
void debugWarn2(const char *fmt, ...);
void debugPrintf7(const char *fmt, ...);
dCamera_c::dCamera_c()
: mMyCameraIndex(0),
mpScreen(dStageMgr_c::GetInstance()->getScreen(0)),
mYAngle(0),
mXZAngle(0),
field_0xD94(0.0f),
field_0x1F8(0),
field_0x1FC(0),
mFlags(0) {
mScreenShaker.init(this);
field_0xDC0.setCamera(this);
field_0x08C = mVec3_c(0.0f, 1.0f, 0.0f);
mActiveCameraIdx = 0;
for (int i = 0; i < CAM_MAX; i++) {
mpCameras[i] = nullptr;
}
}
int dCamera_c::create() {
mFlags = 0;
mMyCameraIndex = getFromParams(0, 0xF);
dScGame_c::setCamera(mMyCameraIndex, nullptr);
mpCameras[0] = &mGameCam1;
mpCameras[1] = &mGameCam2;
mpCameras[2] = &mEventCam;
mpCameras[3] = &mMapCam;
for (int i = 0; i < CAM_MAX; i++) {
if (mpCameras[i] == nullptr) {
continue;
}
if (!mpCameras[i]->doCreate(i)) {
return NOT_READY;
}
}
debugWarn2("u can NOT use debug camera!!"); // aww
mActiveCameraIdx = 0;
mpCameras[0]->activate();
mLetterboxAmount = getEventLetterboxAmount();
field_0x290 = 1.0f;
onFlag(CAM_FLAGS_IN_EVENT);
// TODO maybe struct + inline
mGlobalAlpha = 1.0f;
field_0x298 = field_0x299 = 0;
field_0x29C = 1.0f;
field_0x2A0 = field_0x2A1 = 0;
mView1 = mpCameras[mActiveCameraIdx]->getView();
field_0xDCC = false;
mYAngle = 0;
mXZAngle = 0;
dScGame_c::setCamera(mMyCameraIndex, this);
updateView();
apply();
const STIF *stif = dStageMgr_c::GetInstance()->getStif();
if (stif != nullptr) {
sHio.field_0x04 = stif->field_0x00;
sHio.field_0x08 = stif->field_0x04;
}
field_0x1FC = 0;
if ((dScGame_c::isCurrentStage("F000") && 26 <= dScGame_c::currentSpawnInfo.layer &&
dScGame_c::currentSpawnInfo.layer <= 28) ||
dScGame_c::isInCredits() || dScGame_c::isSeekerStoneStageAndLayer()) {
onFlag(CAM_FLAGS_NO_LETTERBOX_IN_EVENT);
dScGame_c::GetInstance()->setTargetingScreenLetterboxAmount(0.0f);
}
debugPrintf7("create ok!!");
return SUCCEEDED;
}
// Maybe inlines that couldn't be inlined due to variadics...
void debugWarn2(const char *fmt, ...) {
// no-op
}
void debugPrintf7(const char *fmt, ...) {
// no-op
}
int dCamera_c::doDelete() {
debugPrintf7("delete!!");
for (int i = 0; i < CAM_MAX; i++) {
// No null check needed :)
mpCameras[i]->doRemove();
}
dScGame_c::setCamera(mMyCameraIndex, nullptr);
return SUCCEEDED;
}
void dCamera_c::setWorldOffset(f32 x, f32 z) {
mView.mPosition.x += x;
mView.mPosition.z += z;
mView.mTarget.x += x;
mView.mTarget.z += z;
// TODO maybe inline
getGameCam1()->setView(mView);
getGameCam1()->onFlag(0x1);
getGameCam1()->onFlag(0x4000);
updateView();
apply();
}
int dCamera_c::execute() {
dAcPy_c *link = dAcPy_c::GetLinkM();
checkCameraChange();
for (int i = 0; i < CAM_MAX; i++) {
if (mpCameras[i] != nullptr) {
mpCameras[i]->doExecute();
}
}
if (mActiveCameraIdx == CAM_EVENT) {
// hmmmm
static_cast<dCameraGame_c *>(mpCameras[0])->clearCamIds();
}
updateView();
apply();
field_0xDC0.fn_8019E940();
field_0xDCC = false;
if (mActiveCameraIdx == CAM_EVENT) {
onFlag(CAM_FLAGS_IN_EVENT);
}
if (checkFlag(CAM_FLAGS_0x100 | CAM_FLAGS_IN_EVENT)) {
field_0x290 += (1.0f / sHio.field_0x24);
if (field_0x290 > 1.0f) {
field_0x290 = 1.0f;
}
} else {
field_0x290 = field_0x290 - (1.0f / sHio.field_0x24);
if (field_0x290 < 0.0f) {
field_0x290 = 0.0f;
}
}
f32 scale = checkFlag(CAM_FLAGS_IN_EVENT) != 0 ? getEventLetterboxAmount() : sHio.field_0x20;
f32 target = camEaseInOut(field_0x290, 1.0f);
mLetterboxAmount += (target * scale - mLetterboxAmount) * 0.5f;
if (mActiveCameraIdx == CAM_EVENT && checkFlag(CAM_FLAGS_NO_LETTERBOX_IN_EVENT) != 0) {
mLetterboxAmount = 0.0f;
}
dScGame_c::GetInstance()->setTargetingScreenLetterboxAmount(mLetterboxAmount);
offFlag(CAM_FLAGS_0x100 | CAM_FLAGS_IN_EVENT);
if (!link->checkActionFlagsCont(0x400000)) {
field_0x299 = 0;
}
f32 alphaTarget;
f32 alphaUpdateSpeed = sHio.field_0x2C;
if (EventManager::isInEvent()) {
alphaUpdateSpeed = 1.0f;
if (field_0x2A1 != 0) {
alphaTarget = field_0x2A0 ? sHio.field_0x28 : 0.0f;
} else {
alphaTarget = 1.0f;
}
} else {
if (field_0x299 != 0) {
alphaTarget = field_0x298 ? sHio.field_0x28 : 0.0f;
} else {
alphaTarget = 1.0f;
}
}
mGlobalAlpha += alphaUpdateSpeed * (alphaTarget - mGlobalAlpha);
dStageMgr_c::GetInstance()->setGlobalAlpha((u8)(mGlobalAlpha * 255.1f) & 0xFF);
return SUCCEEDED;
}
void dCamera_c::checkCameraChange() {
offFlag(CAM_FLAGS_0x10);
if (checkFlag(CAM_FLAGS_0x4)) {
getGameCam1()->onFlag(0x400);
offFlag(CAM_FLAGS_0x4);
}
if (checkFlag(CAM_FLAGS_MAP) && mActiveCameraIdx == CAM_MAP && !mMapCam.fn_800932E0()) {
offFlag(CAM_FLAGS_MAP);
onFlag(CAM_FLAGS_0x4);
getGameCam1()->onFlag(0x10000);
debugPrintf7("off map");
}
if (EventManager::isInEvent()) {
onFlag(CAM_FLAGS_EVENT);
offFlag(CAM_FLAGS_MAP);
} else {
offFlag(CAM_FLAGS_EVENT);
}
if (checkFlag(CAM_FLAGS_EVENT)) {
if (mActiveCameraIdx != CAM_EVENT) {
setActiveCamera(CAM_EVENT);
}
} else if (checkFlag(CAM_FLAGS_MAP)) {
if (mActiveCameraIdx != CAM_MAP) {
setActiveCamera(CAM_MAP);
}
} else {
if (mActiveCameraIdx != CAM_GAME_0) {
setActiveCamera(CAM_GAME_0);
}
}
}
void dCamera_c::updateView() {
if (field_0xDCC) {
mView = mView1;
field_0xDCC = 0;
} else {
mView = mpCameras[mActiveCameraIdx]->getView();
}
mVec3_c dir = mView.mTarget - mView.mPosition;
mYAngle = cM::atan2s(dir.y, dir.absXZ());
mXZAngle = cM::atan2s(dir.x, dir.z);
}
int dCamera_c::draw() {
if (mpCameras[mActiveCameraIdx] != nullptr) {
mpCameras[mActiveCameraIdx]->doDraw();
}
return SUCCEEDED;
}
bool dCamera_c::isUnderwater() const {
return checkFlag(CAM_FLAGS_UNDERWATER);
}
void dCamera_c::apply() {
updateUnderwaterDepth(mView.mPosition);
if (isUnderwater_()) {
onFlag(CAM_FLAGS_UNDERWATER);
} else {
offFlag(CAM_FLAGS_UNDERWATER);
}
if (mScreenShaker.execute()) {
mView.mPosition += mScreenShaker.getShakeOffset();
mView.mTarget += mScreenShaker.getShakeOffset();
}
mLookAtCamera.mPos = mView.mPosition;
mLookAtCamera.mAt = mView.mTarget;
if (mLookAtCamera.mAt.x == mLookAtCamera.mPos.x && mLookAtCamera.mAt.z == mLookAtCamera.mPos.z) {
mLookAtCamera.mPos.z += 1.0f;
}
mLookAtCamera.mUp = mVec3_c::Ey;
mLookAtCamera.doUpdateMatrix();
mMtx.copyFrom(mLookAtCamera.getViewMatrix());
// TODO: Maybe mutating mtx wasn't intended here but it's probably unproblematic
mMtx.inverse();
mMtxInv = mMtx;
mMtxInv.m[0][3] = 0.0f;
mMtxInv.m[1][3] = 0.0f;
mMtxInv.m[2][3] = 0.0f;
dSnd3DManager_c::GetInstance()->updateFromCamera(mLookAtCamera);
applyTilt();
setFrustum(mView.mFov, sHio.field_0x04, sHio.field_0x08);
nw4r::g3d::Camera cam = dStageMgr_c::GetInstance()->getCamera(mMyCameraIndex);
mpScreen->CopyToG3D(cam);
mLookAtCamera.setG3DCamera(cam);
}
s32 dCamera_c::setActiveCamera(s32 newCamIdx) {
if (mpCameras[newCamIdx] == nullptr) {
return mActiveCameraIdx;
} else if (mActiveCameraIdx == newCamIdx) {
return newCamIdx;
} else {
for (int i = 0; i < CAM_MAX; i++) {
mpCameras[i]->deactivate();
}
mpCameras[newCamIdx]->activate();
debugPrintf7("unit %d -> %d", mActiveCameraIdx, newCamIdx);
mActiveCameraIdx = newCamIdx;
return newCamIdx;
}
}
f32 dCamera_c::getEventLetterboxAmount() {
return sHio.field_0x1C;
}
void dCamera_c::setFrustum(f32 fov, f32 near, f32 far) {
if (dGfx_c::isTvMode4To3()) {
mpScreen->Reset(
0.0f, dGfx_c::getEFBHeightDifferenceF(), dGfx_c::getCurrentScreenWidthF(), dGfx_c::getHeightScaledF(),
mpScreen->GetCanvasMode()
);
} else {
mpScreen->Reset(
0.0f, 0.0f, dGfx_c::getCurrentScreenWidthF(), dGfx_c::getCurrentScreenHeightF(), mpScreen->GetCanvasMode()
);
if (dGfx_c::isTvMode4To3()) {
// Unreachable...
fov *= sHio.field_0x0C;
}
}
mpScreen->SetProjectionType(EGG::Frustum::PROJ_PERSP);
mpScreen->SetNearZ(near);
mpScreen->SetFarZ(far);
mpScreen->SetFovy(fov);
// Possible fakematch: Either the internals of EGG::Matrix34f allow
// copying the data via compiler-generated struct copies (lwz/stw),
// or this is really what they did. NSMBW symbols confirm that this function
// takes mMtx_c while it's clear that EGG cameras return EGG matrices.
mMtx_c mtx = *(mMtx_c *)&mLookAtCamera.getViewMatrix();
mFrustum.set(fov, mpScreen->GetAspect(), near, far, mtx);
}
mAng dCamera_c::getYAngle() const {
return mYAngle;
}
mAng dCamera_c::getYRot() const {
return field_0xDC0.fn_8019E930();
}
mAng dCamera_c::getXZAngle() const {
return mXZAngle;
}
bool dCamera_c::fn_8019E3C0() const {
if (mActiveCameraIdx == CAM_GAME_0) {
return mGameCam1.getField_0x078() == true;
} else if (mActiveCameraIdx == CAM_GAME_1) {
return mGameCam2.getField_0x078() == true;
}
return false;
}
void dCamera_c::enterMap() {
if (!checkFlag(CAM_FLAGS_MAP)) {
onFlag(CAM_FLAGS_MAP);
}
}
void dCamera_c::leaveMap() {
if (checkFlag(CAM_FLAGS_MAP)) {
mMapCam.fn_800932F0();
}
}
void dCamera_c::applyTilt() {
EGG::Matrix34f &mtx = mLookAtCamera.getViewMatrix();
mMtx_c rotMtx;
rotMtx.ZrotS(mAng::fromDeg(-mView.mTilt));
MTXConcat(rotMtx, mtx.m, mtx.m);
}
bool dCamera_c::fn_8019E4D0() const {
dAcPy_c *link = dAcPy_c::GetLinkM();
return !EventManager::isInEvent() && !(link != nullptr && link->checkActionFlagsCont(0x5d7));
}
void dCamera_c::updateUnderwaterDepth(const mVec3_c &pos) {
mVec3_c chkPos = pos;
dBgS_RoofChk roofChk;
roofChk.SetPos(&chkPos);
roofChk.SetUnderwaterRoof();
roofChk.SetField_0x7C(1);
f32 f = dBgS::GetInstance()->RoofChk(&roofChk);
if (f != 1e9f) {
chkPos.y = f;
} else {
chkPos.y += 10000.0f;
}
dBgS_ObjGndChk_Wtr wtrChk;
wtrChk.SetPos(chkPos);
f32 waterHeight = dBgS::GetInstance()->GroundCross(&wtrChk);
if (waterHeight != 1e9f) {
mIsUnderwater = waterHeight > pos.y;
mWaterHeight = waterHeight;
} else {
mIsUnderwater = false;
mWaterHeight = -1e9f;
}
if (mIsUnderwater) {
mUnderwaterDepth = waterHeight - pos.y;
} else {
mUnderwaterDepth = 0.0f;
}
}
bool dCamera_c::isUnderwater_() const {
return mIsUnderwater;
}
f32 dCamera_c::getUnderwaterDepth() const {
return mUnderwaterDepth;
}
bool dCamera_c::screen_shaker::execute() {
mShakeOffset = mVec3_c::Zero;
bool ret = false;
if (mScreenShakeIntensity > 0.001f) {
static s32 upOrDown = 0;
upOrDown = upOrDown ^ 1;
f32 f;
if (upOrDown) {
f = 400.0f;
} else {
f = -400.0f;
}
mVec3_c shakeOffset(cM::rndFX(100.0f), cM::rndF(f), 0.0f);
shakeOffset.normalize();
// Note: the difference in coordinates here is due to the camera view using a different
// coordinate convention than game code.
shakeOffset.rotX(mpCamera->getYAngle());
shakeOffset.rotY(mpCamera->getXZAngle());
mVec3_c chckVector = shakeOffset * (mScreenShakeIntensity + 5.0f);
shakeOffset *= mScreenShakeIntensity;
mVec3_c pos = mpCamera->getPosition();
mVec3_c dest = pos + chckVector;
dBgS_CamLinChk chk;
chk.Set(&pos, &dest, nullptr);
if (!dBgS::GetInstance()->LineCross(&chk)) {
mShakeOffset = shakeOffset;
ret = true;
}
}
mScreenShakeIntensity = 0.0f;
return ret;
}
mVec3_c dCamera_c::screen_shaker::getShakeOffset() const {
return mShakeOffset;
}
void dCamera_c::substruct_1::setCamera(dCamera_c *cam) {
mpCamera = cam;
}
void dCamera_c::substruct_1::fn_8019E890() {
fn_8019E8D0(mpCamera->getXZAngle());
}
void dCamera_c::substruct_1::fn_8019E8D0(const mAng &ang) {
if (!mActive) {
mActive = true;
field_0x04 = ang;
mFSStickAngle = dPad::getFSStickAngle();
}
}
mAng dCamera_c::substruct_1::fn_8019E930() const {
return field_0x04;
}
void dCamera_c::substruct_1::fn_8019E940() {
if (mActive) {
mAng angle = mFSStickAngle - dPad::getFSStickAngle();
f32 distance = dPad::getFSStickDistance();
if (distance < sHio.field_0x14 || mAng::abs(angle) > sHio.field_0x18) {
mActive = false;
}
}
if (!mActive) {
field_0x04 = mpCamera->getXZAngle();
mFSStickAngle = dPad::getFSStickAngle();
}
}
bool dCamera_c::setEventCamView(const mVec3_c &target, const mVec3_c &pos, f32 fov, f32 tilt) {
CamView view(pos, target, fov, tilt);
getEventCam()->setView(view);
return true;
}
bool dCamera_c::fn_8019EA70(bool b) {
CamView view = getEventCam()->getView();
mVec3_c dest;
dest = camGetPointOnLine(view.mTarget, view.mPosition, dAcPy_c::GetLinkM()->mPositionCopy3);
view.mTarget = dest;
// TODO maybe inline
getGameCam1()->setView(view);
getGameCam1()->onFlag(0x1);
if (b) {
getGameCam1()->onFlag(0x4000);
} else {
getGameCam1()->offFlag(0x200);
}
return true;
}
f32 dCamera_c::fn_8019EB90() {
return getGameCam1()->getField_0x0AC();
}