diff --git a/include/Kyoto/Graphics/CColor.hpp b/include/Kyoto/Graphics/CColor.hpp index ed4b728b..a02fe2ba 100644 --- a/include/Kyoto/Graphics/CColor.hpp +++ b/include/Kyoto/Graphics/CColor.hpp @@ -42,6 +42,7 @@ public: uchar GetAlphau8() const { return mA; } ushort ToRGB5A3() const; GXColor ToGX(uint); + uint GetColor_u32() const { return mRgba; } static const CColor& Black(); static const CColor& White(); diff --git a/include/Kyoto/Graphics/CGraphics.hpp b/include/Kyoto/Graphics/CGraphics.hpp index fba249bb..9b5cd4b8 100644 --- a/include/Kyoto/Graphics/CGraphics.hpp +++ b/include/Kyoto/Graphics/CGraphics.hpp @@ -30,13 +30,83 @@ enum ERglTevStage { }; enum ERglPrimitive { - kP_Quads = 0x80, - kP_Triangles = 0x90, - kP_TriangleStrip = 0x98, - kP_TriangleFan = 0xA0, - kP_Lines = 0xA8, - kP_LineStrip = 0xB0, - kP_Points = 0xB8, + kP_Quads = GX_QUADS, + kP_Triangles = GX_TRIANGLES, + kP_TriangleStrip = GX_TRIANGLESTRIP, + kP_TriangleFan = GX_TRIANGLEFAN, + kP_Lines = GX_LINES, + kP_LineStrip = GX_LINESTRIP, + kP_Points = GX_POINTS, +}; + +enum ERglBlendMode { + kBM_None = GX_BM_NONE, + kBM_Blend = GX_BM_BLEND, + kBM_Logic = GX_BM_LOGIC, + kBM_Subtract = GX_BM_SUBTRACT, + kBM_Max = GX_MAX_BLENDMODE, +}; + +enum ERglBlendFactor { + kBF_Zero = GX_BL_ZERO, + kBF_One = GX_BL_ONE, + kBF_SrcColor = GX_BL_SRCCLR, + kBF_InvSrcColor = GX_BL_INVSRCCLR, + kBF_SrcAlpha = GX_BL_SRCALPHA, + kBF_InvSrcAlpha = GX_BL_INVSRCALPHA, + kBF_DstAlpha = GX_BL_DSTALPHA, + kBF_InvDstAlpha = GX_BL_INVDSTALPHA, + kBF_DstColor = GX_BL_DSTCLR, + kBF_InvDstColor = GX_BL_INVDSTCLR, +}; + +enum ERglLogicOp { + kLO_Clear = GX_LO_CLEAR, + kLO_And = GX_LO_AND, + kLO_RevAnd = GX_LO_REVAND, + kLO_Copy = GX_LO_COPY, + kLO_InvAnd = GX_LO_INVAND, + kLO_NoOp = GX_LO_NOOP, + kLO_Xor = GX_LO_XOR, + kLO_Or = GX_LO_OR, + kLO_Nor = GX_LO_NOR, + kLO_Equiv = GX_LO_EQUIV, + kLO_Inv = GX_LO_INV, + kLO_RevOr = GX_LO_REVOR, + kLO_InvCopy = GX_LO_INVCOPY, + kLO_InvOr = GX_LO_INVOR, + kLO_NAnd = GX_LO_NAND, + kLO_Set = GX_LO_SET, +}; + +enum ERglAlphaFunc { + kAF_Never = GX_NEVER, + kAF_Less = GX_LESS, + kAF_Equal = GX_EQUAL, + kAF_LEqual = GX_LEQUAL, + kAF_Greater = GX_GREATER, + kAF_NEqual = GX_NEQUAL, + kAF_GEqual = GX_GEQUAL, + kAF_Always = GX_ALWAYS, +}; + +enum ERglAlphaOp { + kAO_And = GX_AOP_AND, + kAO_Or = GX_AOP_OR, + kAO_Xor = GX_AOP_XOR, + kAO_XNor = GX_AOP_XNOR, + kAO_Max = GX_MAX_ALPHAOP, +}; + +enum ERglEnum { + kE_Never = GX_NEVER, + kE_Less = GX_LESS, + kE_Equal = GX_EQUAL, + kE_LEqual = GX_LEQUAL, + kE_Greater = GX_GREATER, + kE_NEqual = GX_NEQUAL, + kE_GEqual = GX_GEQUAL, + kE_Always = GX_ALWAYS, }; class CTimeProvider; @@ -60,6 +130,23 @@ public: static void SetExternalTimeProvider(CTimeProvider* provider); static void DisableAllLights(); + static void SetModelMatrix(const CTransform4f& xf); + static void SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, + u8 ref1); + static void SetDepthWriteMode(bool test, ERglEnum comp, bool write); + static void SetBlendMode(ERglBlendMode, ERglBlendFactor, ERglBlendFactor, ERglLogicOp); + + static CTevCombiners::CTevPass& kEnvPassThru; + static CTevCombiners::CTevPass kEnvModulateConstColor; + static CTevCombiners::CTevPass kEnvConstColor; + static CTevCombiners::CTevPass kEnvModulate; + static CTevCombiners::CTevPass kEnvDecal; + static CTevCombiners::CTevPass kEnvBlend; + static CTevCombiners::CTevPass kEnvReplace; + static CTevCombiners::CTevPass kEnvModulateAlpha; + static CTevCombiners::CTevPass kEnvModulateColor; + static CTevCombiners::CTevPass kEnvModulateColorByalpha; + private: static CTransform4f mViewMatrix; static CTransform4f mModelMatrix; diff --git a/include/Kyoto/Graphics/CTexture.hpp b/include/Kyoto/Graphics/CTexture.hpp index b6acc87d..06169072 100644 --- a/include/Kyoto/Graphics/CTexture.hpp +++ b/include/Kyoto/Graphics/CTexture.hpp @@ -10,9 +10,9 @@ class CInputStream; class CTexture { public: enum EClampMode { - Clamp, - Repeat, - Mirror, + kCM_Clamp, + kCM_Repeat, + kCM_Mirror, }; enum EAutoMipmap { kAM_Zero, diff --git a/include/Kyoto/Math/CRelAngle.hpp b/include/Kyoto/Math/CRelAngle.hpp index 247a716a..6016072f 100644 --- a/include/Kyoto/Math/CRelAngle.hpp +++ b/include/Kyoto/Math/CRelAngle.hpp @@ -43,6 +43,7 @@ CHECK_SIZEOF(CRelAngle, 0x4) // __mi__FRC9CRelAngleRC9CRelAngle // __pl__FRC9CRelAngleRC9CRelAngle // __dv__FRC9CRelAnglef -// sine__FRC9CRelAngle +static inline float sine(const CRelAngle& angle) { return sin(angle.AsRadians()); } +static inline float cosine(const CRelAngle& angle) { return cos(angle.AsRadians()); } #endif // _CRELANGLE diff --git a/include/Kyoto/Particles/CGenDescription.hpp b/include/Kyoto/Particles/CGenDescription.hpp index 199b5fe3..a5b6970c 100644 --- a/include/Kyoto/Particles/CGenDescription.hpp +++ b/include/Kyoto/Particles/CGenDescription.hpp @@ -9,6 +9,7 @@ #include "rstl/optional_object.hpp" #include "rstl/single_ptr.hpp" +class CModel; class CElectricDescription; class CSwooshDescription; class CSpawnSystemKeyframeData; diff --git a/include/Kyoto/Particles/IElement.hpp b/include/Kyoto/Particles/IElement.hpp index ddf4af43..7598c171 100644 --- a/include/Kyoto/Particles/IElement.hpp +++ b/include/Kyoto/Particles/IElement.hpp @@ -53,6 +53,10 @@ public: struct SUVElementSet { float xMin, yMin, xMax, yMax; + + SUVElementSet() : xMin(0.f), yMin(1.f), xMax(0.f), yMax(1.f) {} + SUVElementSet(float xMin, float yMin, float xMax, float yMax) + : xMin(xMin), yMin(yMin), xMax(xMax), yMax(yMax) {} }; class CUVElement : public IElement { diff --git a/include/Kyoto/TToken.hpp b/include/Kyoto/TToken.hpp index 81b4270a..b8588db4 100644 --- a/include/Kyoto/TToken.hpp +++ b/include/Kyoto/TToken.hpp @@ -55,6 +55,7 @@ public: operator const TToken< T >&() const { return x0_token; } T* operator*() const { return x8_item; } + T* operator->() const { return x8_item; } bool IsLoaded() const { return x0_token.IsLoaded(); } private: diff --git a/include/Weapons/CDecal.hpp b/include/Weapons/CDecal.hpp index cb668c8e..5b445b34 100644 --- a/include/Weapons/CDecal.hpp +++ b/include/Weapons/CDecal.hpp @@ -3,6 +3,7 @@ #include "Kyoto/CRandom16.hpp" #include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Particles/CParticleGlobals.hpp" #include "Kyoto/TToken.hpp" #include "Weapons/CDecalDescription.hpp" @@ -11,7 +12,7 @@ class CDecalDescription; class CDecal { static CRandom16 sDecalRandom; - static bool sMoveRedToAlpha; + static bool sMoveRedToAlphaBuffer; public: class CQuadDecal { @@ -29,7 +30,7 @@ public: inline float GetRotation() const { return x8_rotation; } inline void SetRotation(float rotation) { x8_rotation = rotation; } - private: + // private: bool x0_24_invalid : 1; int x4_lifetime; float x8_rotation; @@ -43,6 +44,8 @@ public: void Render() const; void Update(float dt); + bool IsDone() const { return x5c_flags == 7; } + private: TLockedToken< CDecalDescription > x0_description; CTransform4f xc_transform; @@ -52,5 +55,44 @@ private: int x58_frameIdx; int x5c_flags; CVector3f x60_rotation; + + void InitQuad(CQuadDecal& quad, const CDecalDescription::SQuadDescr& desc, int flag) { + if (!desc.x14_TEX.null()) { + if (!desc.x0_LFT.null()) { + desc.x0_LFT->GetValue(0, quad.x4_lifetime); + } else { + quad.x4_lifetime = 0x7FFFFF; + } + + if (!desc.x8_ROT.null()) { + desc.x8_ROT->GetValue(0, quad.x8_rotation); + quad.x0_24_invalid &= desc.x8_ROT->IsConstant(); + } + + if (!desc.x4_SZE.null()) { + quad.x0_24_invalid &= desc.x4_SZE->IsConstant(); + if (quad.x0_24_invalid) { + float size = 1.f; + desc.x4_SZE->GetValue(0, size); + quad.x0_24_invalid = size <= 1.f; + } + } + + if (!desc.xc_OFF.null()) { + quad.x0_24_invalid &= desc.xc_OFF->IsFastConstant(); + } + } else { + quad.x0_24_invalid = false; + x5c_flags |= flag; + } + } + + void ProcessQuad(CQuadDecal& quad, const CDecalDescription::SQuadDescr& desc, int flag) const { + if (!desc.x14_TEX.null() && (x5c_flags & flag) == 0) { + CParticleGlobals::SetParticleLifetime(quad.x4_lifetime); + CParticleGlobals::UpdateParticleLifetimeTweenValues(x58_frameIdx); + RenderQuad(quad, desc); + } + } }; #endif // _CDECAL diff --git a/include/Weapons/CDecalDescription.hpp b/include/Weapons/CDecalDescription.hpp index 6395ba85..5b85fa80 100644 --- a/include/Weapons/CDecalDescription.hpp +++ b/include/Weapons/CDecalDescription.hpp @@ -1,11 +1,31 @@ #ifndef _CDECALDESCRIPTION #define _CDECALDESCRIPTION +#include "Kyoto/Particles/CGenDescription.hpp" + class CDecalDescription { public: - struct SQuadDescr {}; + struct SQuadDescr { + rstl::single_ptr< CIntElement > x0_LFT; + rstl::single_ptr< CRealElement > x4_SZE; + rstl::single_ptr< CRealElement > x8_ROT; + rstl::single_ptr< CVectorElement > xc_OFF; + rstl::single_ptr< CColorElement > x10_CLR; + rstl::single_ptr< CUVElement > x14_TEX; + bool x18_ADD; + }; -private: +// private: + SQuadDescr x0_quad1; + SQuadDescr x1c_quad2; + CGenDescription::TParticleModel x38_DMDL; + rstl::single_ptr< CIntElement > x48_DLFT; + rstl::single_ptr< CVectorElement > x4c_DMOP; + rstl::single_ptr< CVectorElement > x50_DMRT; + rstl::single_ptr< CVectorElement > x54_DMSC; + rstl::single_ptr< CColorElement > x58_DMCL; + bool x5c_24_DMAB : 1; + bool x5c_25_DMOO : 1; }; #endif // _CDECALDESCRIPTION diff --git a/include/dolphin/gx/GXVert.h b/include/dolphin/gx/GXVert.h index 9579ac32..5af2084a 100644 --- a/include/dolphin/gx/GXVert.h +++ b/include/dolphin/gx/GXVert.h @@ -104,6 +104,10 @@ static inline void GXNormal3f32(const f32 x, const f32 y, const f32 z) { GXWGFifo.f32 = z; } +static inline void GXColor1u32(const u32 v) { + GXWGFifo.u32 = v; +} + static inline void GXColor4u8(const u8 r, const u8 g, const u8 b, const u8 a) { GXWGFifo.u8 = r; GXWGFifo.u8 = g; diff --git a/src/MetroidPrime/CRipple.cpp b/src/MetroidPrime/CRipple.cpp index 70ce7f10..d9ce630f 100644 --- a/src/MetroidPrime/CRipple.cpp +++ b/src/MetroidPrime/CRipple.cpp @@ -8,21 +8,20 @@ #include "rstl/math.hpp" CRipple::CRipple(TUniqueId id, const CVector3f& center, float intensity) - : x0_id(id) - , x4_time(0.f) - , x8_center(center) - , x14_timeFalloff(2.f) - , x18_distFalloff(12.f) - , x1c_frequency(3.f) - , x20_amplitude(0.25f) - , x24_lookupAmplitude(0.00098039221f) - , x28_ooTimeFalloff(0.f) - , x2c_ooDistFalloff(0.f) - , x30_ooPhase(0.f) - , x34_phase(0.f) - , x38_lookupPhase(0.f) - , x3c_(sub_8012f098()) -{ +: x0_id(id) +, x4_time(0.f) +, x8_center(center) +, x14_timeFalloff(2.f) +, x18_distFalloff(12.f) +, x1c_frequency(3.f) +, x20_amplitude(0.25f) +, x24_lookupAmplitude(0.00098039221f) +, x28_ooTimeFalloff(0.f) +, x2c_ooDistFalloff(0.f) +, x30_ooPhase(0.f) +, x34_phase(0.f) +, x38_lookupPhase(0.f) +, x3c_(sub_8012f098()) { if (0.f < intensity && intensity < 1.f) { static CRandom16 sRippleRandom(0xABBA); diff --git a/src/Weapons/CDecal.cpp b/src/Weapons/CDecal.cpp index d7a42a94..1493485c 100644 --- a/src/Weapons/CDecal.cpp +++ b/src/Weapons/CDecal.cpp @@ -1,9 +1,12 @@ #include "Weapons/CDecal.hpp" +#include "Kyoto/Graphics/CGX.hpp" #include "Kyoto/Graphics/CGraphics.hpp" +#include "Kyoto/Graphics/CTexture.hpp" +#include "Kyoto/Math/CRelAngle.hpp" #include "Kyoto/Particles/CParticleGlobals.hpp" CRandom16 CDecal::sDecalRandom(99); -bool CDecal::sMoveRedToAlpha = false; +bool CDecal::sMoveRedToAlphaBuffer = false; void CDecal::SetGlobalSeed(ushort seed) { sDecalRandom.SetSeed(seed); } @@ -15,18 +18,167 @@ CDecal::CDecal(const TToken< CDecalDescription >& desc, const CTransform4f& xf) , x5c_flags(0) , x60_rotation(CVector3f::Zero()) { CGlobalRandom gr(sDecalRandom); + + InitQuad(x3c_quad1, x0_description->x0_quad1, 1); + InitQuad(x48_quad2, x0_description->x1c_quad2, 2); + + if (x0_description->x38_DMDL) { + if (!x0_description->x48_DLFT.null()) { + x0_description->x48_DLFT->GetValue(0, x54_modelLifetime); + } else { + x54_modelLifetime = 0x7FFFFF; + } + + if (!x0_description->x50_DMRT.null()) { + x0_description->x50_DMRT->GetValue(0, x60_rotation); + } + } else { + x5c_flags |= 4; + } +} + +void CDecal::RenderQuad(CQuadDecal& decal, const CDecalDescription::SQuadDescr& desc) const { + CColor color = CColor::White(); + float size = 1.f; + CVector3f offset = CVector3f::Zero(); + if (CColorElement* clr = desc.x10_CLR.get()) { + clr->GetValue(x58_frameIdx, color); + } + if (CRealElement* sze = desc.x4_SZE.get()) { + sze->GetValue(x58_frameIdx, size); + size *= 0.5f; + } + if (CRealElement* rot = desc.x8_ROT.get()) { + rot->GetValue(x58_frameIdx, decal.x8_rotation); + } + if (CVectorElement* off = desc.xc_OFF.get()) { + off->GetValue(x58_frameIdx, offset); + offset.SetY(0.f); + } + + CTransform4f modXf = xc_transform; + modXf.SetTranslation(modXf.GetTranslation() + offset); + CGraphics::SetModelMatrix(modXf); + CGraphics::SetAlphaCompare(kAF_Always, 0, kAO_And, kAF_Always, 0); + + bool redToAlpha = CDecal::sMoveRedToAlphaBuffer && desc.x18_ADD && !desc.x14_TEX.null(); + if (desc.x18_ADD) { + CGraphics::SetDepthWriteMode(true, kE_LEqual, false); + if (redToAlpha) { + CGraphics::SetBlendMode(kBM_Blend, kBF_One, kBF_One, kLO_Clear); + } else { + CGraphics::SetBlendMode(kBM_Blend, kBF_SrcAlpha, kBF_One, kLO_Clear); + } + } else { + CGraphics::SetDepthWriteMode(true, kE_LEqual, false); + CGraphics::SetBlendMode(kBM_Blend, kBF_SrcAlpha, kBF_InvSrcAlpha, kLO_Clear); + } + + SUVElementSet uvSet; + if (!desc.x14_TEX.null()) { + TLockedToken< CTexture > tex = desc.x14_TEX->GetValueTexture(x58_frameIdx); + tex->Load(GX_TEXMAP0, CTexture::kCM_Repeat); + CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvModulate); + desc.x14_TEX->GetValueUV(x58_frameIdx, uvSet); + if (redToAlpha) { + CGX::SetNumTevStages(2); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_APREV, GX_CC_ZERO); + CGX::SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_TEXA, GX_CA_APREV, GX_CA_ZERO); + CGX::SetStandardTevColorAlphaOp(GX_TEVSTAGE1); + CGX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_NEVER, 0); + GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP1); + } else { + CGraphics::SetTevOp(kTS_Stage1, CGraphics::kEnvPassThru); + } + } else { + CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvPassThru); + CGraphics::SetTevOp(kTS_Stage1, CGraphics::kEnvPassThru); + } + + CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + CGX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); + CGX::SetNumTexGens(1); + CGX::SetNumChans(1); + CGX::SetNumIndStages(0); + CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY, false, GX_PTIDENTITY); + CGX::SetChanCtrl(CGX::Channel0, false, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, + GX_AF_NONE); + static const GXVtxDescList vtxDesc[4] = { + {GX_VA_POS, GX_DIRECT}, + {GX_VA_CLR0, GX_DIRECT}, + {GX_VA_TEX0, GX_DIRECT}, + {GX_VA_NULL, GX_NONE}, + }; + CGX::SetVtxDescv(vtxDesc); + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4); + + float y = 0.001f; + if (decal.x8_rotation == 0.f) { + // Vertex 0 + GXPosition3f32(-size, y, size); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMin, uvSet.yMin); + // Vertex 1 + GXPosition3f32(size, y, size); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMax, uvSet.yMin); + // Vertex 2 + GXPosition3f32(-size, y, -size); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMin, uvSet.yMax); + // Vertex 3 + GXPosition3f32(size, y, -size); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMax, uvSet.yMax); + } else { + CRelAngle ang = CRelAngle::FromDegrees(decal.x8_rotation); + float sinSize = sine(ang) * size; + float cosSize = cosine(ang) * size; + // Vertex 0 + GXPosition3f32(sinSize - cosSize, y, cosSize + sinSize); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMin, uvSet.yMin); + // Vertex 1 + GXPosition3f32(cosSize + sinSize, y, cosSize - sinSize); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMax, uvSet.yMin); + // Vertex 2 + GXPosition3f32(-(cosSize + sinSize), y, -(cosSize - sinSize)); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMin, uvSet.yMax); + // Vertex 3 + GXPosition3f32(-sinSize + cosSize, y, -cosSize - sinSize); + GXColor1u32(color.GetColor_u32()); + GXTexCoord2f32(uvSet.xMax, uvSet.yMax); + } + + CGX::End(); + if (redToAlpha) { + GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); + CGX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0); + } } -void CDecal::RenderQuad(CQuadDecal& quad, const CDecalDescription::SQuadDescr& quadDesc) const {} void CDecal::RenderMdl() const {} + void CDecal::Render() const { CGlobalRandom gr(sDecalRandom); - if (x5c_flags == 7) { + if (IsDone()) { return; } + CGraphics::DisableAllLights(); CParticleGlobals::SetEmitterTime(x58_frameIdx); + + ProcessQuad(const_cast< CQuadDecal& >(x3c_quad1), x0_description->x0_quad1, 1); + ProcessQuad(const_cast< CQuadDecal& >(x48_quad2), x0_description->x1c_quad2, 2); + if (x0_description->x38_DMDL && (x5c_flags & 4) == 0) { + CParticleGlobals::SetParticleLifetime(x54_modelLifetime); + CParticleGlobals::UpdateParticleLifetimeTweenValues(x58_frameIdx); + RenderMdl(); + } } + void CDecal::Update(float dt) { if (x58_frameIdx >= x3c_quad1.GetLifetime()) { x5c_flags |= 1;