#include "nw4r/g3d.h" // IWYU pragma: export #include "nw4r/ut.h" // IWYU pragma: export #include "rvl/BASE.h" // IWYU pragma: export #include namespace nw4r { namespace g3d { namespace detail { G3DState::IndMtxOp *GetIndMtxOp(ResMat mat, ResNode node, ResShp shp) { if (!mat.IsValid() || !shp.IsValid()) { return NULL; } ResMatMisc misc = mat.GetResMatMisc(); ResMatMiscData::IndirectMethod method[GX_ITM_2 - GX_ITM_0 + 1]; s8 lightRef[GX_ITM_2 - GX_ITM_0 + 1]; misc.GetIndirectTexMtxCalcMethod(GX_ITM_0, &method[GX_ITM_0 - 1], &lightRef[GX_ITM_0 - 1]); misc.GetIndirectTexMtxCalcMethod(GX_ITM_1, &method[GX_ITM_1 - 1], &lightRef[GX_ITM_1 - 1]); misc.GetIndirectTexMtxCalcMethod(GX_ITM_2, &method[GX_ITM_2 - 1], &lightRef[GX_ITM_2 - 1]); if (method[GX_ITM_0 - 1] == ResMatMiscData::WARP && method[GX_ITM_1 - 1] == ResMatMiscData::WARP && method[GX_ITM_2 - 1] == ResMatMiscData::WARP) { return NULL; } bool nrmMtxInit = false; math::MTX34 nrmMtx; u32 i; G3DState::IndMtxOp &rOp = *G3DState::GetIndMtxOp(); rOp.Reset(); for (i = 0; i < GX_ITM_2 - GX_ITM_0 + 1; i++) { GXIndTexMtxID id = static_cast(i + 1); if (method[i] == ResMatMiscData::WARP) { continue; } if (!nrmMtxInit) { nrmMtxInit = true; if (shp.ptr()->curMtxIdx >= 0) { if (shp.ptr()->curMtxIdx == node.GetMtxID()) { const math::MTX33 *pViewNrm = G3DState::GetViewNrmMtxPtr(shp.ptr()->curMtxIdx); nrmMtx._00 = pViewNrm->_00; nrmMtx._01 = pViewNrm->_01; nrmMtx._02 = pViewNrm->_02; nrmMtx._10 = pViewNrm->_10; nrmMtx._11 = pViewNrm->_11; nrmMtx._12 = pViewNrm->_12; nrmMtx._20 = pViewNrm->_20; nrmMtx._21 = pViewNrm->_21; nrmMtx._22 = pViewNrm->_22; } else { ResMdl mdl = mat.GetParent(); int shpNodeID = mdl.GetResMdlInfo().GetNodeIDFromMtxID(shp.ptr()->curMtxIdx); ResNode shpNode = mdl.GetResNode(shpNodeID); math::MTX34Mult( &nrmMtx, static_cast(&shpNode.ref().invModelMtx), static_cast(&node.ref().modelMtx) ); const math::MTX33 *pViewNrm = G3DState::GetViewNrmMtxPtr(shp.ptr()->curMtxIdx); // clang-format off math::MTX34 viewNrm34( pViewNrm->_00, pViewNrm->_01, pViewNrm->_02, 0.0f, pViewNrm->_10, pViewNrm->_11, pViewNrm->_12, 0.0f, pViewNrm->_20, pViewNrm->_21, pViewNrm->_22, 0.0f); // clang-format on math::MTX34Mult(&nrmMtx, &viewNrm34, &nrmMtx); } nrmMtx._03 = nrmMtx._13 = nrmMtx._23 = 0.0f; math::VEC3 col0(nrmMtx._00, nrmMtx._10, nrmMtx._20); math::VEC3Normalize(&col0, &col0); nrmMtx._00 = col0.x; nrmMtx._10 = col0.y; nrmMtx._20 = col0.z; math::VEC3 col1(nrmMtx._01, nrmMtx._11, nrmMtx._21); math::VEC3Normalize(&col1, &col1); nrmMtx._01 = col1.x; nrmMtx._11 = col1.y; nrmMtx._21 = col1.z; math::VEC3 col2(nrmMtx._02, nrmMtx._12, nrmMtx._22); math::VEC3Normalize(&col2, &col2); nrmMtx._02 = col2.x; nrmMtx._12 = col2.y; nrmMtx._22 = col2.z; } else { math::MTX34Copy(&nrmMtx, G3DState::GetCameraMtxPtr()); } } const LightObj *pLight = G3DState::GetLightObj(lightRef[i]); if (pLight != NULL && pLight->IsEnable()) { math::VEC3 lightVec; if (pLight->IsSpotLight()) { pLight->GetLightDir(&lightVec); if (lightVec.x == 0.0f && lightVec.y == 0.0f && lightVec.z == 0.0f) { pLight->GetLightPos(&lightVec); lightVec = -lightVec; math::VEC3Normalize(&lightVec, &lightVec); } } else if (pLight->IsDiffuseLight()) { pLight->GetLightPos(&lightVec); lightVec = -lightVec; math::VEC3Normalize(&lightVec, &lightVec); } else { math::VEC3 H; pLight->GetLightDir(&H); lightVec.x = -2.0f * H.z * H.x; lightVec.y = -2.0f * H.z * H.y; lightVec.z = -2.0f * H.z * H.z + 1.0f; math::VEC3Normalize(&lightVec, &lightVec); } rOp.SetNrmMapMtx(id, &lightVec, &nrmMtx, method[i]); } else { rOp.SetNrmMapMtx(id, NULL, &nrmMtx, method[i]); } } return &rOp; } } // namespace detail namespace { void SetupDraw1Mat1ShpSwap(Draw1Mat1ShpSwap *pSwap, DrawResMdlReplacement *pReplacement, u32 id) { if (pReplacement->texObjDataArray != NULL) { pSwap->texObj = ResTexObj(&pReplacement->texObjDataArray[id]); } else { pSwap->texObj = ResTexObj(NULL); } if (pReplacement->tlutObjDataArray != NULL) { pSwap->tlutObj = ResTlutObj(&pReplacement->tlutObjDataArray[id]); } else { pSwap->tlutObj = ResTlutObj(NULL); } if (pReplacement->texSrtDataArray != NULL) { pSwap->texSrt = ResTexSrt(&pReplacement->texSrtDataArray[id]); } else { pSwap->texSrt = ResTexSrt(NULL); } if (pReplacement->chanDataArray != NULL) { pSwap->chan = ResMatChan(&pReplacement->chanDataArray[id]); } else { pSwap->chan = ResMatChan(NULL); } if (pReplacement->genModeDataArray != NULL) { pSwap->genMode = ResGenMode(&pReplacement->genModeDataArray[id]); } else { pSwap->genMode = ResGenMode(NULL); } if (pReplacement->matMiscDataArray != NULL) { pSwap->misc = ResMatMisc(&pReplacement->matMiscDataArray[id]); } else { pSwap->misc = ResMatMisc(NULL); } if (pReplacement->pixDLArray != NULL) { pSwap->pix = ResMatPix(&pReplacement->pixDLArray[id]); } else { pSwap->pix = ResMatPix(NULL); } if (pReplacement->tevColorDLArray != NULL) { pSwap->tevColor = ResMatTevColor(&pReplacement->tevColorDLArray[id]); } else { pSwap->tevColor = ResMatTevColor(NULL); } if (pReplacement->indMtxAndScaleDLArray != NULL) { pSwap->indMtxAndScale = ResMatIndMtxAndScale(&pReplacement->indMtxAndScaleDLArray[id]); } else { pSwap->indMtxAndScale = ResMatIndMtxAndScale(NULL); } if (pReplacement->texCoordGenDLArray != NULL) { pSwap->texCoordGen = ResMatTexCoordGen(&pReplacement->texCoordGenDLArray[id]); } else { pSwap->texCoordGen = ResMatTexCoordGen(NULL); } if (pReplacement->tevDataArray != NULL) { pSwap->tev = ResTev(&pReplacement->tevDataArray[id]); } else { pSwap->tev = ResTev(NULL); } if (pReplacement->flag & 1) { pSwap->vtxPosTable = NULL; pSwap->vtxNrmTable = NULL; pSwap->vtxClrTable = NULL; } else { pSwap->vtxPosTable = pReplacement->vtxPosTable; pSwap->vtxNrmTable = pReplacement->vtxNrmTable; pSwap->vtxClrTable = pReplacement->vtxClrTable; } } inline bool FrontToBack(const detail::workmem::MdlZ &rLhs, const detail::workmem::MdlZ &rRhs) { if (rLhs.priority < rRhs.priority) { return true; } if (rLhs.priority > rRhs.priority) { return false; } if (rLhs.Z > rRhs.Z) { return true; } return false; } inline bool BackToFront(const detail::workmem::MdlZ &rLhs, const detail::workmem::MdlZ &rRhs) { if (rLhs.priority < rRhs.priority) { return true; } if (rLhs.priority > rRhs.priority) { return false; } if (rLhs.Z < rRhs.Z) { return true; } if (rLhs.Z == rRhs.Z && rLhs.matID < rRhs.matID) { return true; } return false; } void DrawResMdlLoop(const ResMdl mdl, const u8 *pByteCode, u32 drawMode) { #define pDrawCmd reinterpret_cast(pByteCode) u8 c; ResMat mat; ResMat prevMat; ResShp shp; ResNode node; u32 ctrl = DRAW1MAT1SHP_CTRL_NOPPCSYNC; if (drawMode & RESMDL_DRAWMODE_FORCE_LIGHTOFF) { ctrl |= DRAW1MAT1SHP_CTRL_FORCE_LIGHTOFF; } if (drawMode & RESMDL_DRAWMODE_IGNORE_MATERIAL) { ctrl |= RESMDL_DRAWMODE_NOPPCSYNC; } for (; (c = *pByteCode) != ResByteCodeData::END; pByteCode += sizeof(ResByteCodeData::DrawParams)) { node = mdl.GetResNode(pDrawCmd->nodeIdHi << 8 | pDrawCmd->nodeIdLo); shp = mdl.GetResShp(pDrawCmd->shpIdHi << 8 | pDrawCmd->shpIdLo); if (node.IsVisible() && shp.IsVisible()) { mat = mdl.GetResMat(pDrawCmd->matIdHi << 8 | pDrawCmd->matIdLo); Draw1Mat1ShpDirectly( mat, shp, NULL, NULL, mat == prevMat ? ctrl | RESMDL_DRAWMODE_NOPPCSYNC : ctrl, NULL, detail::GetIndMtxOp(mat, node, shp) ); prevMat = mat; } } #undef pDrawCmd } void DrawResMdlLoop(const ResMdl mdl, const u8 *pByteCode, DrawResMdlReplacement *pReplacement, u32 drawMode) { #define pDrawCmd reinterpret_cast(pByteCode) u8 c; ResMat mat; ResMat prevMat; ResShp shp; ResNode node; Draw1Mat1ShpSwap swap; u32 ctrl = DRAW1MAT1SHP_CTRL_NOPPCSYNC; if (drawMode & RESMDL_DRAWMODE_FORCE_LIGHTOFF) { ctrl |= DRAW1MAT1SHP_CTRL_FORCE_LIGHTOFF; } if (drawMode & RESMDL_DRAWMODE_IGNORE_MATERIAL) { ctrl |= RESMDL_DRAWMODE_NOPPCSYNC; } for (; (c = *pByteCode) != ResByteCodeData::END; pByteCode += sizeof(ResByteCodeData::DrawParams)) { node = mdl.GetResNode(pDrawCmd->nodeIdHi << 8 | pDrawCmd->nodeIdLo); shp = mdl.GetResShp(pDrawCmd->shpIdHi << 8 | pDrawCmd->shpIdLo); bool visible; // @bug Replacement pointer not validated if (pReplacement->visArray != NULL) { visible = pReplacement->visArray[node.GetID()] != 0; } else { visible = node.IsVisible(); } if (visible && shp.IsVisible()) { mat = mdl.GetResMat(pDrawCmd->matIdHi << 8 | pDrawCmd->matIdLo); SetupDraw1Mat1ShpSwap(&swap, pReplacement, mat.GetID()); Draw1Mat1ShpDirectly( mat, shp, NULL, NULL, mat == prevMat ? ctrl | RESMDL_DRAWMODE_NOPPCSYNC : ctrl, &swap, detail::GetIndMtxOp(mat, node, shp) ); prevMat = mat; } } #undef pDrawCmd } void DrawResMdlLoop(const ResMdl mdl, const detail::workmem::MdlZ *pMdlZArray, u32 numMdlZ, u32 drawMode) { u32 i; ResMat mat; ResMat prevMat; ResShp shp; ResNode node; u32 ctrl = DRAW1MAT1SHP_CTRL_NOPPCSYNC; if (drawMode & RESMDL_DRAWMODE_FORCE_LIGHTOFF) { ctrl |= DRAW1MAT1SHP_CTRL_FORCE_LIGHTOFF; } if (drawMode & RESMDL_DRAWMODE_IGNORE_MATERIAL) { ctrl |= RESMDL_DRAWMODE_NOPPCSYNC; } for (i = 0; i < numMdlZ; i++) { const detail::workmem::MdlZ &rMdlZ = pMdlZArray[i]; shp = mdl.GetResShp(rMdlZ.shpID); if (shp.IsVisible()) { mat = mdl.GetResMat(rMdlZ.matID); node = mdl.GetResNode(rMdlZ.nodeID); Draw1Mat1ShpDirectly( mat, shp, NULL, NULL, mat == prevMat ? ctrl | RESMDL_DRAWMODE_NOPPCSYNC : ctrl, NULL, detail::GetIndMtxOp(mat, node, shp) ); prevMat = mat; } } } void DrawResMdlLoop( const ResMdl mdl, const detail::workmem::MdlZ *pMdlZArray, u32 numMdlZ, DrawResMdlReplacement *pReplacement, u32 drawMode ) { u32 i; ResMat mat; ResMat prevMat; ResShp shp; ResNode node; Draw1Mat1ShpSwap swap; u32 ctrl = DRAW1MAT1SHP_CTRL_NOPPCSYNC; if (drawMode & RESMDL_DRAWMODE_FORCE_LIGHTOFF) { ctrl |= DRAW1MAT1SHP_CTRL_FORCE_LIGHTOFF; } if (drawMode & RESMDL_DRAWMODE_IGNORE_MATERIAL) { ctrl |= RESMDL_DRAWMODE_NOPPCSYNC; } for (i = 0; i < numMdlZ; i++) { const detail::workmem::MdlZ &rMdlZ = pMdlZArray[i]; shp = mdl.GetResShp(rMdlZ.shpID); if (shp.IsVisible()) { mat = mdl.GetResMat(rMdlZ.matID); node = mdl.GetResNode(rMdlZ.nodeID); SetupDraw1Mat1ShpSwap(&swap, pReplacement, mat.GetID()); Draw1Mat1ShpDirectly( mat, shp, NULL, NULL, mat == prevMat ? ctrl | RESMDL_DRAWMODE_NOPPCSYNC : ctrl, &swap, detail::GetIndMtxOp(mat, node, shp) ); prevMat = mat; } } } detail::workmem::MdlZ *SetUpMdlZ( u32 *pNumMdlZ, const ResMdl mdl, const math::MTX34 *pViewPosMtxArray, const u8 *pByteCode, DrawResMdlReplacement *pReplacement ) { #define pDrawCmd reinterpret_cast(pByteCode) u16 nodeID; u8 c; u32 mdlZNum = 0; ResNode node; u32 mtxID; u32 viewMtxNum = mdl.GetResMdlInfo().GetNumViewMtx(); detail::workmem::MdlZ *pMdlZArray = detail::workmem::GetMdlZTemporary(); for (; (c = *pByteCode) != ResByteCodeData::END; pByteCode += sizeof(ResByteCodeData::DrawParams)) { nodeID = pDrawCmd->nodeIdHi << 8 | pDrawCmd->nodeIdLo; node = mdl.GetResNode(nodeID); bool visible; if (pReplacement == NULL || pReplacement->visArray == NULL) { visible = node.IsVisible(); } else { visible = pReplacement->visArray[node.GetID()] != 0; } if (!visible) { continue; } detail::workmem::MdlZ &rMdlZ = pMdlZArray[mdlZNum]; rMdlZ.nodeID = nodeID; rMdlZ.matID = pDrawCmd->matIdHi << 8 | pDrawCmd->matIdLo; rMdlZ.shpID = pDrawCmd->shpIdHi << 8 | pDrawCmd->shpIdLo; mtxID = node.GetMtxID(); if (mtxID < viewMtxNum) { rMdlZ.Z = pViewPosMtxArray[mtxID]._23; } else { rMdlZ.Z = 0.f; } rMdlZ.priority = pDrawCmd->priority; mdlZNum++; } *pNumMdlZ = mdlZNum; return pMdlZArray; #undef pDrawCmd } } // namespace void DrawResMdlDirectly( const ResMdl mdl, const math::MTX34 *pViewPosMtxArray, const math::MTX33 *pViewNrmMtxArray, const math::MTX34 *pViewEnvMtxArray, const u8 *pByteCodeOpa, const u8 *pByteCodeXlu, DrawResMdlReplacement *pReplacement, u32 drawMode ) { G3DState::SetViewPosNrmMtxArray(pViewPosMtxArray, pViewNrmMtxArray, pViewEnvMtxArray); if (!(drawMode & RESMDL_DRAWMODE_NOPPCSYNC)) { ut::LC::QueueWait(0); PPCSync(); } if (pByteCodeOpa != NULL) { if (drawMode & RESMDL_DRAWMODE_SORT_OPA_Z) { u32 numMdlZ; detail::workmem::MdlZ *pMdlZArray = SetUpMdlZ(&numMdlZ, mdl, pViewPosMtxArray, pByteCodeOpa, pReplacement); std::sort(pMdlZArray, pMdlZArray + numMdlZ, FrontToBack); if (pReplacement != NULL) { DrawResMdlLoop(mdl, pMdlZArray, numMdlZ, pReplacement, drawMode); } else { DrawResMdlLoop(mdl, pMdlZArray, numMdlZ, drawMode); } } else if (pReplacement != NULL) { DrawResMdlLoop(mdl, pByteCodeOpa, pReplacement, drawMode); } else { DrawResMdlLoop(mdl, pByteCodeOpa, drawMode); } } if (pByteCodeXlu != NULL) { if (drawMode & RESMDL_DRAWMODE_SORT_XLU_Z) { u32 numMdlZ; detail::workmem::MdlZ *pMdlZArray = SetUpMdlZ(&numMdlZ, mdl, pViewPosMtxArray, pByteCodeXlu, pReplacement); std::sort(pMdlZArray, pMdlZArray + numMdlZ, BackToFront); if (pReplacement != NULL) { DrawResMdlLoop(mdl, pMdlZArray, numMdlZ, pReplacement, drawMode); } else { DrawResMdlLoop(mdl, pMdlZArray, numMdlZ, drawMode); } } else if (pReplacement != NULL) { DrawResMdlLoop(mdl, pByteCodeXlu, pReplacement, drawMode); } else { DrawResMdlLoop(mdl, pByteCodeXlu, drawMode); } } G3DState::SetViewPosNrmMtxArray(NULL, NULL, NULL); } } // namespace g3d } // namespace nw4r