#include "nw4r/g3d.h" // IWYU pragma: export #include "nw4r/math.h" // IWYU pragma: export #include "rvl/GX.h" // IWYU pragma: export #include "rvl/MTX.h" // IWYU pragma: export namespace nw4r { namespace g3d { Camera::Camera(CameraData *pData) : ResCommon(pData) {} void Camera::Init() { const GXRenderModeObj *pObj = G3DState::GetRenderModeObj(); Init(pObj->fbWidth, pObj->efbHeight, pObj->fbWidth, pObj->xfbHeight, pObj->viWidth, pObj->viHeight); } void Camera::Init(u16 efbWidth, u16 efbHeight, u16 xfbWidth, u16 xfbHeight, u16 viWidth, u16 viHeight) { if (!IsValid()) { return; } CameraData &r = ref(); r.flags = CameraData::FLAG_CAM_LOOKAT | CameraData::FLAG_PROJ_PERSP; r.cameraPos.x = 0.0f; r.cameraPos.y = 0.0f; r.cameraPos.z = 15.0f; r.cameraUp.x = 0.0f; r.cameraUp.y = 1.0f; r.cameraUp.z = 0.0f; r.cameraTarget.x = 0.0f; r.cameraTarget.y = 0.0f; r.cameraTarget.z = 0.0f; r.cameraRotate.x = 0.0f; r.cameraRotate.y = 0.0f; r.cameraRotate.z = 0.0f; r.cameraTwist = 0.0f; r.projType = GX_PERSPECTIVE; r.projFovy = 60.0f; r.projAspect = 4.0f / 3.0f; r.projNear = 0.1f; r.projFar = 1000.f; r.projTop = 0.0f; r.projBottom = static_cast(viHeight); r.projLeft = 0.0f; r.projRight = static_cast(viWidth); r.lightScaleS = 0.5f; r.lightScaleT = 0.5f; r.lightTransS = 0.5f; r.lightTransT = 0.5f; r.viewportOrigin.x = 0.0f; r.viewportOrigin.y = 0.0f; r.viewportSize.x = static_cast(xfbWidth); r.viewportSize.y = static_cast(xfbHeight); r.viewportNear = 0.0f; r.viewportFar = 1.0f; r.scissorX = 0; r.scissorY = 0; r.scissorWidth = efbWidth; r.scissorHeight = efbHeight; r.scissorOffsetX = 0; r.scissorOffsetY = 0; } void Camera::SetPosition(f32 x, f32 y, f32 z) { if (!IsValid()) { return; } CameraData &r = ref(); r.cameraPos.x = x; r.cameraPos.y = y; r.cameraPos.z = z; r.flags &= ~CameraData::FLAG_CAM_MTX_READY; } void Camera::SetPosition(const math::VEC3 &rPos) { if (!IsValid()) { return; } CameraData &r = ref(); r.cameraPos = rPos; r.flags &= ~CameraData::FLAG_CAM_MTX_READY; } void Camera::SetPosture(const PostureInfo &rInfo) { if (!IsValid()) { return; } CameraData &r = ref(); switch (rInfo.tp) { case POSTURE_LOOKAT: { if (r.flags & CameraData::FLAG_CAM_LOOKAT) { if (!(rInfo.cameraUp != r.cameraUp) && !(rInfo.cameraTarget != r.cameraTarget)) { return; } } r.flags &= ~(CameraData::FLAG_CAM_LOOKAT | CameraData::FLAG_CAM_ROTATE | CameraData::FLAG_CAM_AIM); r.flags |= CameraData::FLAG_CAM_LOOKAT; r.cameraUp = rInfo.cameraUp; r.cameraTarget = rInfo.cameraTarget; r.flags &= ~CameraData::FLAG_CAM_MTX_READY; break; } case POSTURE_ROTATE: { if (r.flags & CameraData::FLAG_CAM_ROTATE) { if (!(rInfo.cameraRotate != r.cameraRotate)) { return; } } r.flags &= ~(CameraData::FLAG_CAM_LOOKAT | CameraData::FLAG_CAM_ROTATE | CameraData::FLAG_CAM_AIM); r.flags |= CameraData::FLAG_CAM_ROTATE; r.cameraRotate = rInfo.cameraRotate; r.flags &= ~CameraData::FLAG_CAM_MTX_READY; break; } case POSTURE_AIM: { if (r.flags & CameraData::FLAG_CAM_AIM) { if (!(rInfo.cameraTarget != r.cameraTarget) && rInfo.cameraTwist == r.cameraTwist) { return; } } r.flags &= ~(CameraData::FLAG_CAM_LOOKAT | CameraData::FLAG_CAM_ROTATE | CameraData::FLAG_CAM_AIM); r.flags |= CameraData::FLAG_CAM_AIM; r.cameraTarget = rInfo.cameraTarget; r.cameraTwist = rInfo.cameraTwist; r.flags &= ~CameraData::FLAG_CAM_MTX_READY; break; } default: { break; } } } void Camera::SetCameraMtxDirectly(const math::MTX34 &rMtx) { if (!IsValid()) { return; } CameraData &r = ref(); math::MTX34Copy(&r.cameraMtx, &rMtx); r.flags |= CameraData::FLAG_CAM_MTX_READY; } void Camera::SetPerspective(f32 fovy, f32 aspect, f32 near, f32 far) { if (!IsValid()) { return; } CameraData &r = ref(); r.projType = GX_PERSPECTIVE; r.projFovy = fovy; r.projAspect = aspect; r.projNear = near; r.projFar = far; r.flags &= ~(CameraData::FLAG_PROJ_FRUSTUM | CameraData::FLAG_PROJ_PERSP | CameraData::FLAG_PROJ_ORTHO | CameraData::FLAG_PROJ_MTX_READY); r.flags |= CameraData::FLAG_PROJ_PERSP; } void Camera::SetOrtho(f32 top, f32 bottom, f32 left, f32 right, f32 near, f32 far) { if (!IsValid()) { return; } CameraData &r = ref(); r.projType = GX_ORTHOGRAPHIC; r.projTop = top; r.projBottom = bottom; r.projLeft = left; r.projRight = right; r.projNear = near; r.projFar = far; r.flags &= ~(CameraData::FLAG_PROJ_FRUSTUM | CameraData::FLAG_PROJ_PERSP | CameraData::FLAG_PROJ_ORTHO | CameraData::FLAG_PROJ_MTX_READY); r.flags |= CameraData::FLAG_PROJ_ORTHO; } void Camera::SetProjectionMtxDirectly(const math::MTX44 *pMtx) { if (pMtx != NULL && IsValid()) { CameraData &r = ref(); math::MTX44Copy(&r.projMtx, pMtx); r.flags |= CameraData::FLAG_PROJ_MTX_READY; } } void Camera::SetTexMtxParam(f32 lightScaleS, f32 lightScaleT, f32 lightTransS, f32 lightTransT) { if (IsValid()) { CameraData &r = ref(); r.lightScaleS = lightScaleS; r.lightScaleT = lightScaleT; r.lightTransS = lightTransS; r.lightTransT = lightTransT; } } void Camera::SetScissor(u32 x, u32 y, u32 width, u32 height) { if (!IsValid()) { return; } CameraData &r = ref(); r.scissorX = x; r.scissorY = y; r.scissorWidth = width; r.scissorHeight = height; } void Camera::SetScissorBoxOffset(s32 ox, s32 oy) { if (!IsValid()) { return; } CameraData &r = ref(); r.scissorOffsetX = ox; r.scissorOffsetY = oy; } void Camera::SetViewport(f32 x, f32 y, f32 width, f32 height) { if (!IsValid()) { return; } CameraData &r = ref(); r.viewportOrigin.x = x; r.viewportOrigin.y = y; r.viewportSize.x = width; r.viewportSize.y = height; SetScissor(static_cast(x), static_cast(y), static_cast(width), static_cast(height)); } void Camera::SetViewportZRange(f32 near, f32 far) { if (!IsValid()) { return; } CameraData &r = ref(); r.viewportNear = near; r.viewportFar = far; } void Camera::GetViewport(f32 *pX, f32 *pY, f32 *pWidth, f32 *pHeight, f32 *pNear, f32 *pFar) const { if (!IsValid()) { return; } const CameraData &r = ref(); if (pX != NULL) { *pX = r.viewportOrigin.x; } if (pY != NULL) { *pY = r.viewportOrigin.y; } if (pWidth != NULL) { *pWidth = r.viewportSize.x; } if (pHeight != NULL) { *pHeight = r.viewportSize.y; } if (pNear != NULL) { *pNear = r.viewportNear; } if (pFar != NULL) { *pFar = r.viewportFar; } } void Camera::Project(math::VEC3 *pSrcPos, const math::VEC3 &worldPos) const { if (pSrcPos == NULL) { return; } math::MTX34 cam; math::MTX44 proj; f32 vp[GX_VIEWPORT_SZ]; f32 p[GX_PROJECTION_SZ]; GetCameraMtx(&cam); GetProjectionMtx(&proj); p[0] = GetProjectionType(); p[1] = proj._00; p[3] = proj._11; p[5] = proj._22; p[6] = proj._23; if (p[0] == 1.0f) { p[2] = proj._03; p[4] = proj._13; } else { p[2] = proj._02; p[4] = proj._12; } GetViewport(&vp[0], &vp[1], &vp[2], &vp[3], &vp[4], &vp[5]); ::GXProject(worldPos.x, worldPos.y, worldPos.z, cam, p, vp, &pSrcPos->x, &pSrcPos->y, &pSrcPos->z); } void Camera::GetCameraMtx(math::MTX34 *pMtx) const { if (pMtx && IsValid()) { const CameraData &r = ref(); if (!(r.flags & CameraData::FLAG_CAM_MTX_READY)) { UpdateCameraMtx(); } math::MTX34Copy(pMtx, &r.cameraMtx); } } void Camera::GetProjectionMtx(math::MTX44 *pMtx) const { if (pMtx && IsValid()) { const CameraData &r = ref(); if (!(r.flags & CameraData::FLAG_PROJ_MTX_READY)) { UpdateProjectionMtx(); } math::MTX44Copy(pMtx, &r.projMtx); } } void Camera::GetProjectionTexMtx(math::MTX34 *pMtx) const { if (pMtx != NULL && IsValid()) { const CameraData &r = ref(); if (r.flags & CameraData::FLAG_PROJ_ORTHO) { C_MTXLightOrtho( *pMtx, r.projTop, r.projBottom, r.projLeft, r.projRight, r.lightScaleS, -r.lightScaleT, r.lightTransS, r.lightTransT ); } else if (r.flags & CameraData::FLAG_PROJ_FRUSTUM) { C_MTXLightFrustum( *pMtx, r.projTop, r.projBottom, r.projLeft, r.projRight, r.projNear, r.lightScaleS, -r.lightScaleT, r.lightTransS, r.lightTransT ); } else /* FLAG_PROJ_PERSP */ { C_MTXLightPerspective( *pMtx, r.projFovy, r.projAspect, r.lightScaleS, -r.lightScaleT, r.lightTransS, r.lightTransT ); } } } void Camera::GetEnvironmentTexMtx(math::MTX34 *pMtx) const { if (pMtx != NULL && IsValid()) { const CameraData &r = ref(); math::MTX34Identity(pMtx); pMtx->_00 = r.lightScaleS; pMtx->_03 = r.lightTransS; pMtx->_11 = -r.lightScaleT; pMtx->_13 = r.lightTransT; pMtx->_22 = 0.0f; pMtx->_23 = 1.0f; } } void Camera::GXSetViewport() const { if (!IsValid()) { return; } const CameraData &r = ref(); const GXRenderModeObj *pObj = G3DState::GetRenderModeObj(); if (pObj->field_rendering) { ::GXSetViewportJitter( r.viewportOrigin.x, r.viewportOrigin.y, r.viewportSize.x, r.viewportSize.y, r.viewportNear, r.viewportFar, r.flags & CameraData::FLAG_VI_ODD_FIELD ? GX_FIELD_ODD : GX_FIELD_EVEN ); } else { ::GXSetViewport( r.viewportOrigin.x, r.viewportOrigin.y, r.viewportSize.x, r.viewportSize.y, r.viewportNear, r.viewportFar ); } } void Camera::GXSetProjection() const { if (!IsValid()) { return; } const CameraData &r = ref(); if (!(r.flags & CameraData::FLAG_PROJ_MTX_READY)) { UpdateProjectionMtx(); } ::GXSetProjection(r.projMtx, r.projType); } void Camera::GXSetScissor() const { if (!IsValid()) { return; } const CameraData &r = ref(); ::GXSetScissor(r.scissorX, r.scissorY, r.scissorWidth, r.scissorHeight); } void Camera::GXSetScissorBoxOffset() const { if (!IsValid()) { return; } const CameraData &r = ref(); ::GXSetScissorBoxOffset(r.scissorOffsetX, r.scissorOffsetY); } void Camera::UpdateCameraMtx() const { CameraData &r = const_cast(ref()); if (r.flags & CameraData::FLAG_CAM_LOOKAT) { C_MTXLookAt(r.cameraMtx, r.cameraPos, r.cameraUp, r.cameraTarget); } else if (r.flags & CameraData::FLAG_CAM_AIM) { math::MTX34 &rMtx = r.cameraMtx; math::VEC3 &rPos = r.cameraPos; math::VEC3 &rTarget = r.cameraTarget; math::VEC3 back(rPos.x - rTarget.x, rPos.y - rTarget.y, rPos.z - rTarget.z); if (back.x == 0.0f && back.z == 0.0f) { rMtx._00 = 1.0f; rMtx._01 = 0.0f; rMtx._02 = 0.0f; rMtx._03 = -rPos.x; rMtx._10 = 0.0f; rMtx._11 = 0.0f; rMtx._20 = 0.0f; rMtx._22 = 0.0f; if (back.y <= 0.0f) { rMtx._12 = 1.0f; rMtx._13 = -rPos.z; rMtx._21 = -1.0f; rMtx._23 = rPos.y; } else { rMtx._12 = -1.0f; rMtx._13 = rPos.z; rMtx._21 = 1.0f; rMtx._23 = -rPos.y; } } else { math::VEC3 _r(back.z, 0.0f, -back.x); math::VEC3 u; math::VEC3Normalize(&back, &back); math::VEC3Normalize(&_r, &_r); math::VEC3Cross(&u, &back, &_r); f32 st, ct; math::SinCosDeg(&st, &ct, r.cameraTwist); math::VEC3 right, up; right.x = st * u.x + ct * _r.x; right.y = st * u.y; right.z = st * u.z + ct * _r.z; up.x = ct * u.x - st * _r.x; up.y = ct * u.y; up.z = ct * u.z - st * _r.z; rMtx._00 = right.x; rMtx._01 = right.y; rMtx._02 = right.z; rMtx._03 = -math::VEC3Dot(&rPos, &right); rMtx._10 = up.x; rMtx._11 = up.y; rMtx._12 = up.z; rMtx._13 = -math::VEC3Dot(&rPos, &up); rMtx._20 = back.x; rMtx._21 = back.y; rMtx._22 = back.z; rMtx._23 = -math::VEC3Dot(&rPos, &back); } } else /* FLAG_CAM_ROTATE */ { math::MTX34 &rMtx = r.cameraMtx; math::VEC3 &rPos = r.cameraPos; f32 sx, sy, sz, cx, cy, cz; math::SinCosDeg(&sx, &cx, r.cameraRotate.x); math::SinCosDeg(&sy, &cy, r.cameraRotate.y); math::SinCosDeg(&sz, &cz, r.cameraRotate.z); math::VEC3 right, up, back; right.x = sx * sy * sz + cy * cz; right.y = cx * sz; right.z = sx * cy * sz - sy * cz; up.x = sx * sy * cz - cy * sz; up.y = cx * cz; up.z = sx * cy * cz + sy * sz; back.x = cx * sy; back.y = -sx; back.z = cx * cy; rMtx._00 = right.x; rMtx._01 = right.y; rMtx._02 = right.z; rMtx._03 = -math::VEC3Dot(&rPos, &right); rMtx._10 = up.x; rMtx._11 = up.y; rMtx._12 = up.z; rMtx._13 = -math::VEC3Dot(&rPos, &up); rMtx._20 = back.x; rMtx._21 = back.y; rMtx._22 = back.z; rMtx._23 = -math::VEC3Dot(&rPos, &back); } r.flags |= CameraData::FLAG_CAM_MTX_READY; } void Camera::UpdateProjectionMtx() const { CameraData &r = const_cast(ref()); if (r.flags & CameraData::FLAG_PROJ_ORTHO) { C_MTXOrtho(r.projMtx, r.projTop, r.projBottom, r.projLeft, r.projRight, r.projNear, r.projFar); } else if (r.flags & CameraData::FLAG_PROJ_FRUSTUM) { C_MTXFrustum(r.projMtx, r.projTop, r.projBottom, r.projLeft, r.projRight, r.projNear, r.projFar); } else /* FLAG_PROJ_PERSP */ { C_MTXPerspective(r.projMtx, r.projFovy, r.projAspect, r.projNear, r.projFar); } r.flags |= CameraData::FLAG_PROJ_MTX_READY; } } // namespace g3d } // namespace nw4r