mirror of
https://github.com/zeldaret/ss
synced 2026-06-11 21:18:40 -04:00
401 lines
11 KiB
C++
401 lines
11 KiB
C++
#include "egg/gfx/eggLightObject.h"
|
|
|
|
#include "common.h"
|
|
#include "egg/gfx/eggDrawGX.h"
|
|
#include "egg/gfx/eggLightTexture.h"
|
|
#include "nw4r/g3d/g3d_light.h"
|
|
#include "nw4r/g3d/res/g3d_resanmlight.h"
|
|
#include "nw4r/math/math_arithmetic.h"
|
|
#include "nw4r/math/math_types.h"
|
|
#include "rvl/GX/GXLight.h"
|
|
#include "rvl/GX/GXTypes.h"
|
|
#include "rvl/MTX/mtxvec.h"
|
|
#include "rvl/MTX/vec.h"
|
|
|
|
// TODO: This whole file does weird things with vectors!
|
|
// NSMBW symbol names indicate that functions use nw4r::math::VEC3
|
|
// arguments and we have some nw4r PS inlines, but
|
|
// a fair number of vector assignments happen in a weird order
|
|
// which might indicate EGG::Vector3f?
|
|
|
|
namespace EGG {
|
|
|
|
template <>
|
|
const char *IBinary<LightObject>::GetBinaryType() {
|
|
return "LOBJ";
|
|
}
|
|
|
|
template <>
|
|
int IBinary<LightObject>::GetVersion() {
|
|
return 2;
|
|
}
|
|
|
|
// why
|
|
namespace {
|
|
|
|
static const f32 ZERO_VEC[3] = {0.0f, 0.0f, 0.0f};
|
|
}
|
|
|
|
LightObject::LightObject()
|
|
: mIndex(0),
|
|
field_0x06(0),
|
|
mAt(ZERO_VEC),
|
|
mPos(-10000.0f, 10000.0f, 10000.0f),
|
|
mDir(0.0f, -1.0f, 0.0f),
|
|
mWhite(DrawGX::WHITE),
|
|
field_0x30(1.0f),
|
|
mDist(1.0f),
|
|
mBlack(DrawGX::BLACK),
|
|
field_0x3C(0),
|
|
field_0x3D(1),
|
|
mSpotFn(GX_SP_OFF),
|
|
mDistAttnFn(GX_DA_OFF),
|
|
mCutoff(90.0f),
|
|
mRefDist(0.5f),
|
|
mRefBrightness(0.5f),
|
|
mShininess(16.0f),
|
|
mFlags(0x661) {}
|
|
|
|
void LightObject::Reset() {
|
|
SetPosAt(nw4r::math::VEC3(-10000.0f, 10000.0f, 10000.0f), ZERO_VEC);
|
|
mFlags = 0x661;
|
|
mWhite = DrawGX::WHITE;
|
|
mBlack = DrawGX::BLACK;
|
|
field_0x30 = 1.0;
|
|
field_0x3C = 0;
|
|
field_0x3D = 1;
|
|
mCutoff = 90.0;
|
|
mRefDist = 0.5;
|
|
mRefBrightness = 0.5;
|
|
mSpotFn = GX_SP_OFF;
|
|
mDistAttnFn = GX_DA_OFF;
|
|
mShininess = 16.0;
|
|
|
|
field_0x4C.x = field_0x58.x = 1.0f;
|
|
field_0x4C.z = field_0x58.z = 0.0f;
|
|
field_0x4C.y = field_0x58.y = 0.0f;
|
|
}
|
|
|
|
void LightObject::Calc() {
|
|
field_0x6C = mDir;
|
|
mLightColor = mWhite;
|
|
}
|
|
|
|
void LightObject::CalcView(nw4r::math::MTX34 const &viewMtx) {
|
|
if ((mFlags & 1) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (getField0x3C() < 0 || getField0x3C() > 1) {
|
|
switch (getField0x3C()) {
|
|
case 3: {
|
|
field_0x94.x = mViewAt.x = 0.0f;
|
|
field_0x94.y = mViewAt.y = 0.0f;
|
|
field_0x94.z = mViewAt.z = -1.0f;
|
|
mViewAt.z *= mDist;
|
|
mViewPos.x = mViewPos.y = mViewPos.z = 0.0f;
|
|
break;
|
|
}
|
|
case 4: {
|
|
field_0x94.x = mViewPos.x = 0.0f;
|
|
field_0x94.y = mViewPos.y = 0.0f;
|
|
field_0x94.z = mViewPos.z = 1.0f;
|
|
mViewPos.z *= mDist;
|
|
mViewAt.x = mViewAt.y = mViewAt.z = 0.0f;
|
|
break;
|
|
}
|
|
case 2:
|
|
field_0x94 = mDir;
|
|
mViewAt = mAt;
|
|
mViewPos = mPos;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
if (getField0x3C() == 1) {
|
|
nw4r::math::VEC3 tmp(-viewMtx._20, -viewMtx._21, -viewMtx._22);
|
|
if (PSVECMag(tmp) > 0.0f) {
|
|
nw4r::math::VEC3 angle;
|
|
C_VECHalfAngle(mDir, tmp, angle);
|
|
nw4r::math::VEC3Scale(&field_0x6C, &angle, -1.0f);
|
|
PSVECNormalize(field_0x6C, field_0x6C);
|
|
} else {
|
|
field_0x6C.x = field_0x6C.y = 0.0f;
|
|
field_0x6C.z = 1.0f;
|
|
}
|
|
}
|
|
PSMTXMultVec(viewMtx, mPos, mViewPos);
|
|
PSMTXMultVec(viewMtx, mAt, mViewAt);
|
|
nw4r::math::VEC3TransformNormal(&field_0x94, &viewMtx, &field_0x6C);
|
|
}
|
|
}
|
|
|
|
void LightObject::fn_804A9C30(const LightTexture *tex, nw4r::math::VEC3 *pVec, GXColor *color) const {
|
|
*pVec = field_0x94;
|
|
*color = mLightColor;
|
|
f32 f = tex->getFloat(mIndex);
|
|
if (f < 1.0f) {
|
|
color->r = color->r * f;
|
|
color->g = color->g * f;
|
|
color->b = color->b * f;
|
|
color->a = color->a * f;
|
|
} else {
|
|
// Yes
|
|
*color = *color;
|
|
}
|
|
}
|
|
|
|
void LightObject::InitGX(GXLightObj *obj) const {
|
|
if ((mFlags & 1) != 0 && ((mFlags >> 6) & 1) != 0) {
|
|
GXInitLightColor(obj, mLightColor);
|
|
switch (field_0x3D) {
|
|
case 1: {
|
|
nw4r::math::VEC3 tmp = field_0x94 * -1e10f;
|
|
GXInitLightPos(obj, tmp.x, tmp.y, tmp.z);
|
|
GXInitLightDir(obj, ZERO_VEC[0], ZERO_VEC[1], ZERO_VEC[2]);
|
|
break;
|
|
}
|
|
case 0: {
|
|
GXInitLightPos(obj, mViewPos.x, mViewPos.y, mViewPos.z);
|
|
GXInitLightDir(obj, ZERO_VEC[0], ZERO_VEC[1], ZERO_VEC[2]);
|
|
break;
|
|
}
|
|
case 2: {
|
|
GXInitLightPos(obj, mViewPos.x, mViewPos.y, mViewPos.z);
|
|
GXInitLightDir(obj, field_0x94.x, field_0x94.y, field_0x94.z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((mFlags & 0x80) != 0) {
|
|
GXInitLightAttnA(obj, field_0x4C.x, field_0x4C.y, field_0x4C.z);
|
|
} else {
|
|
GXInitLightSpot(obj, mCutoff > 0.0f ? mCutoff : 0.0001f, getSpotFn());
|
|
}
|
|
|
|
if ((mFlags & 0x100) != 0) {
|
|
GXInitLightAttnK(obj, field_0x58.x, field_0x58.y, field_0x58.z);
|
|
} else {
|
|
GXInitLightDistAttn(obj, mRefDist * getDistance(), mRefBrightness, getDistAttnFn());
|
|
}
|
|
|
|
if ((mFlags & 0x800) != 0) {
|
|
f32 ka = mShininess / 2.0f;
|
|
GXInitLightAttn(obj, 0.0f, 0.0f, 1.0f, ka, 0.0f, 1.0f - ka);
|
|
}
|
|
|
|
} else {
|
|
GXInitLightColor(obj, DrawGX::BLACK);
|
|
}
|
|
}
|
|
|
|
void LightObject::CopyToG3D_World(nw4r::g3d::LightObj &g3dObj) const {
|
|
if ((mFlags & 1) != 0 && (mFlags & 0x40) != 0) {
|
|
g3dObj.Clear();
|
|
if ((mFlags & 0x200) == 0) {
|
|
g3dObj.DisableColor();
|
|
}
|
|
if ((mFlags & 0x400) == 0) {
|
|
g3dObj.DisableAlpha();
|
|
}
|
|
|
|
g3dObj.Enable();
|
|
g3dObj.InitLightColor(mLightColor);
|
|
|
|
if ((mFlags & 0x80) != 0) {
|
|
g3dObj.InitLightAttnA(field_0x4C.x, field_0x4C.y, field_0x4C.z);
|
|
} else {
|
|
g3dObj.InitLightSpot(mCutoff > 0.0f ? mCutoff : 0.0001f, getSpotFn());
|
|
}
|
|
|
|
if ((mFlags & 0x100) != 0) {
|
|
g3dObj.InitLightAttnK(field_0x58.x, field_0x58.y, field_0x58.z);
|
|
} else {
|
|
g3dObj.InitLightDistAttn(mRefDist * getDistance(), mRefBrightness, getDistAttnFn());
|
|
}
|
|
|
|
if ((mFlags & 0x800) != 0) {
|
|
g3dObj.InitLightShininess(mShininess);
|
|
}
|
|
} else {
|
|
g3dObj.Disable();
|
|
}
|
|
}
|
|
|
|
void LightObject::CopyToG3D_View(nw4r::g3d::LightObj &g3dObj, const nw4r::math::MTX34 &viewMtx) const {
|
|
if (!g3dObj.IsEnable()) {
|
|
return;
|
|
}
|
|
|
|
if (!((mFlags >> 11) & 1)) {
|
|
nw4r::math::VEC3 dir;
|
|
nw4r::math::VEC3 pos;
|
|
switch (field_0x3D) {
|
|
case 1: {
|
|
nw4r::math::VEC3TransformNormal(&dir, &viewMtx, &field_0x94);
|
|
nw4r::math::VEC3Scale(&pos, &dir, -1e10f);
|
|
dir.x = dir.y = dir.z = 0.0f;
|
|
break;
|
|
}
|
|
case 0: {
|
|
PSMTXMultVec(viewMtx, mViewPos, pos);
|
|
dir.x = dir.y = dir.z = 0.0f;
|
|
break;
|
|
}
|
|
case 2: {
|
|
PSMTXMultVec(viewMtx, mViewPos, pos);
|
|
nw4r::math::VEC3TransformNormal(&dir, &viewMtx, &field_0x94);
|
|
break;
|
|
}
|
|
}
|
|
g3dObj.InitLightPos(pos.x, pos.y, pos.z);
|
|
g3dObj.InitLightDir(dir.x, dir.y, dir.z);
|
|
} else {
|
|
g3dObj.InitSpecularDir(mDir.x, mDir.y, mDir.z);
|
|
}
|
|
}
|
|
|
|
void LightObject::CopyFromG3D(
|
|
const nw4r::g3d::ResAnmLight &g3dLight, f32 frame, EGG::LightObject *optObj, bool skipDoingSomething
|
|
) {
|
|
nw4r::g3d::LightAnmResult result;
|
|
g3dLight.GetAnmResult(&result, frame);
|
|
if (!skipDoingSomething || (result.flags & nw4r::g3d::LightAnmResult::FLAG_LIGHT_ENABLE) != 0) {
|
|
if ((result.flags & nw4r::g3d::LightAnmResult::FLAG_LIGHT_ENABLE) != 0) {
|
|
ImportAnmG3D_Diff(result);
|
|
if (optObj != nullptr && optObj->ImportAnmG3D_Spec(result)) {
|
|
optObj->field_0x06 = mIndex;
|
|
optObj->mFlags |= 2;
|
|
}
|
|
} else {
|
|
ClearFlag2();
|
|
if (optObj != nullptr) {
|
|
optObj->ClearFlag2();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LightObject::ImportAnmG3D(const nw4r::g3d::LightAnmResult &res) {
|
|
// TODO
|
|
mFlags = mFlags & 0xF07E;
|
|
mCutoff = 90.0f;
|
|
mRefDist = 0.5f;
|
|
mRefBrightness = 0.5f;
|
|
mSpotFn = GX_SP_OFF;
|
|
mDistAttnFn = GX_DA_OFF;
|
|
mShininess = 16.0f;
|
|
|
|
field_0x4C.x = field_0x58.x = 1.0f;
|
|
field_0x4C.z = field_0x58.z = 0.0f;
|
|
field_0x4C.y = field_0x58.y = 0.0f;
|
|
|
|
if ((res.flags & nw4r::g3d::LightAnmResult::FLAG_COLOR_ENABLE) != 0) {
|
|
mFlags |= 0x200;
|
|
}
|
|
if ((res.flags & nw4r::g3d::LightAnmResult::FLAG_ALPHA_ENABLE) != 0) {
|
|
mFlags |= 0x400;
|
|
}
|
|
if ((res.flags & nw4r::g3d::LightAnmResult::FLAG_LIGHT_ENABLE) != 0) {
|
|
mFlags |= 1;
|
|
}
|
|
}
|
|
|
|
void LightObject::ImportAnmG3D_Diff(const nw4r::g3d::LightAnmResult &res) {
|
|
ImportAnmG3D(res);
|
|
switch ((int)(res.flags & nw4r::g3d::LightAnmResult::FLAG_LIGHT_TYPE_MASK)) {
|
|
case 0: {
|
|
field_0x3D = 0;
|
|
mWhite = res.color;
|
|
SetPosAt(res.pos, res.pos);
|
|
mCutoff = 0.0f;
|
|
mSpotFn = GX_SP_OFF;
|
|
mRefDist = res.refDistance;
|
|
mRefBrightness = res.refBrightness;
|
|
mDistAttnFn = res.distFunc;
|
|
break;
|
|
}
|
|
case 1: {
|
|
field_0x3D = 1;
|
|
mWhite = res.color;
|
|
SetPosAt(res.pos, res.aim);
|
|
mSpotFn = GX_SP_OFF;
|
|
mDistAttnFn = GX_AF_SPEC;
|
|
break;
|
|
}
|
|
case 2: {
|
|
field_0x3D = 2;
|
|
mWhite = res.color;
|
|
SetPosAt(res.pos, res.aim);
|
|
mSpotFn = res.spotFunc;
|
|
mCutoff = res.cutoff;
|
|
mDistAttnFn = res.distFunc;
|
|
mRefDist = res.refDistance;
|
|
mRefBrightness = res.refBrightness;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LightObject::ImportAnmG3D_Spec(const nw4r::g3d::LightAnmResult &res) {
|
|
if ((res.flags & nw4r::g3d::LightAnmResult::FLAG_SPECULAR_ENABLE) != 0) {
|
|
ImportAnmG3D(res);
|
|
field_0x3D = 1;
|
|
mWhite = res.specColor;
|
|
SetPosAt(res.pos, res.aim);
|
|
mFlags |= 0x800;
|
|
mShininess = res.shininess;
|
|
return true;
|
|
} else {
|
|
ClearFlag2();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void LightObject::CalcDirDist() {
|
|
mDir.x = mAt.x - mPos.x;
|
|
mDir.y = mAt.y - mPos.y;
|
|
mDir.z = mAt.z - mPos.z;
|
|
mDist = nw4r::math::VEC3LenSq(&mDir);
|
|
if (mDist > 0.0f) {
|
|
nw4r::math::VEC3Scale(&mDir, &mDir, 1.0f / nw4r::math::FSqrt(mDist));
|
|
} else {
|
|
mDir.x = mDir.y = mDir.z = 0.0f;
|
|
}
|
|
}
|
|
|
|
void LightObject::CalcAt() {
|
|
mAt.x = mPos.x + mDir.x * mDist;
|
|
mAt.y = mPos.y + mDir.y * mDist;
|
|
mAt.z = mPos.z + mDir.z * mDist;
|
|
}
|
|
|
|
void LightObject::SetPos(const nw4r::math::VEC3 &pos) {
|
|
mPos = pos;
|
|
CalcDirDist();
|
|
}
|
|
|
|
void LightObject::SetAt(const nw4r::math::VEC3 &at) {
|
|
mAt = at;
|
|
CalcDirDist();
|
|
}
|
|
|
|
void LightObject::SetDist(f32 dist) {
|
|
mDist = dist;
|
|
CalcAt();
|
|
}
|
|
|
|
void LightObject::SetPosAt(nw4r::math::VEC3 const &pos, nw4r::math::VEC3 const &at) {
|
|
mPos = pos;
|
|
mAt = at;
|
|
CalcDirDist();
|
|
}
|
|
|
|
void LightObject::SetBinaryInner(const Bin &bin) {}
|
|
|
|
void LightObject::GetBinaryInner(Bin *bin) const {}
|
|
|
|
void LightObject::SetBinaryInner(const Bin &bin1, const Bin &bin2, f32 blend) {}
|
|
|
|
} // namespace EGG
|