#include "nw4r/g3d.h" // IWYU pragma: export #include "nw4r/ut.h" // IWYU pragma: export #include "rvl/GX.h" // IWYU pragma: export #include namespace nw4r { namespace g3d { NW4R_G3D_RTTI_DEF(AnmObjTexSrt); NW4R_G3D_RTTI_DEF(AnmObjTexSrtNode); NW4R_G3D_RTTI_DEF(AnmObjTexSrtOverride); NW4R_G3D_RTTI_DEF(AnmObjTexSrtRes); /****************************************************************************** * * AnmObjTexSrt * ******************************************************************************/ AnmObjTexSrt::AnmObjTexSrt(MEMAllocator *pAllocator, u16 *pBindingBuf, int numBinding) : AnmObj(pAllocator, NULL), mNumBinding(numBinding), mpBinding(pBindingBuf) { Release(); } bool AnmObjTexSrt::TestExistence(u32 idx) const { return !(mpBinding[idx] & (BINDING_UNDEFINED | BINDING_INVALID)); } bool AnmObjTexSrt::TestDefined(u32 idx) const { return !(mpBinding[idx] & BINDING_UNDEFINED); } void AnmObjTexSrt::Release() { for (int i = 0; i < mNumBinding; i++) { mpBinding[i] = BINDING_UNDEFINED; } SetAnmFlag(FLAG_ANM_BOUND, false); } AnmObjTexSrtRes *AnmObjTexSrt::Attach(int idx, AnmObjTexSrtRes *pRes) { #pragma unused(idx) #pragma unused(pRes) return NULL; } AnmObjTexSrtRes *AnmObjTexSrt::Detach(int idx) { #pragma unused(idx) return NULL; } void AnmObjTexSrt::DetachAll() {} /****************************************************************************** * * AnmObjTexSrtNode * ******************************************************************************/ AnmObjTexSrtNode::AnmObjTexSrtNode( MEMAllocator *pAllocator, u16 *pBindingBuf, int numBinding, AnmObjTexSrtRes **ppChildrenBuf, int numChildren ) : AnmObjTexSrt(pAllocator, pBindingBuf, numBinding), mChildrenArraySize(numChildren), mpChildrenArray(ppChildrenBuf) { for (int i = 0; i < mChildrenArraySize; i++) { mpChildrenArray[i] = NULL; } } AnmObjTexSrtNode::~AnmObjTexSrtNode() { DetachAll(); } AnmObjTexSrtRes *AnmObjTexSrtNode::Attach(int idx, AnmObjTexSrtRes *pRes) { AnmObjTexSrtRes *pOld = Detach(idx); bool hasAnm = false; for (u32 i = 0; i < mNumBinding; i++) { if (!pRes->TestDefined(i)) { continue; } hasAnm = true; mpBinding[i] = 0; } if (hasAnm) { SetAnmFlag(FLAG_ANM_BOUND, true); } mpChildrenArray[idx] = pRes; pRes->G3dProc(G3DPROC_ATTACH_PARENT, 0, this); return pOld; } AnmObjTexSrtRes *AnmObjTexSrtNode::Detach(int idx) { AnmObjTexSrtRes *pOld = mpChildrenArray[idx]; if (pOld != NULL) { pOld->G3dProc(G3DPROC_DETACH_PARENT, 0, this); mpChildrenArray[idx] = NULL; bool hasAnm = false; for (u32 i = 0; i < mNumBinding; i++) { u16 binding = BINDING_UNDEFINED; for (int j = 0; j < mChildrenArraySize; j++) { AnmObjTexSrtRes *pChild = mpChildrenArray[j]; if (pChild == NULL || !pChild->TestDefined(i)) { continue; } hasAnm = true; binding = 0; break; } mpBinding[i] = binding; } if (!hasAnm) { SetAnmFlag(FLAG_ANM_BOUND, false); } } return pOld; } void AnmObjTexSrtNode::DetachAll() { for (int i = 0; i < mChildrenArraySize; i++) { Detach(i); } } void AnmObjTexSrtNode::UpdateFrame() { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->UpdateFrame(); } } } void AnmObjTexSrtNode::SetFrame(f32 frame) { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->SetFrame(frame); } } } f32 AnmObjTexSrtNode::GetFrame() const { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { return mpChildrenArray[i]->GetFrame(); } } return 0.0f; } void AnmObjTexSrtNode::SetUpdateRate(f32 rate) { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->SetUpdateRate(rate); } } } f32 AnmObjTexSrtNode::GetUpdateRate() const { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { return mpChildrenArray[i]->GetUpdateRate(); } } return 1.0f; } bool AnmObjTexSrtNode::Bind(const ResMdl mdl) { bool success = false; for (int i = 0; i < mChildrenArraySize; i++) { AnmObjTexSrtRes *pChild = mpChildrenArray[i]; if (pChild == NULL) { continue; } bool childSuccess = pChild->Bind(mdl); success = success || childSuccess; for (u32 j = 0; j < mNumBinding; j++) { if (pChild->TestDefined(j)) { mpBinding[j] = 0; } } } SetAnmFlag(FLAG_ANM_BOUND, true); return success; } void AnmObjTexSrtNode::Release() { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->Release(); } } AnmObjTexSrt::Release(); } void AnmObjTexSrtNode::G3dProc(u32 task, u32 param, void *pInfo) { #pragma unused(param) switch (task) { case G3DPROC_CHILD_DETACHED: { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] == pInfo) { Detach(i); return; } } break; } case G3DPROC_DETACH_PARENT: { SetParent(NULL); break; } case G3DPROC_ATTACH_PARENT: { SetParent(static_cast(pInfo)); break; } } } /****************************************************************************** * * AnmObjTexSrtOverride * ******************************************************************************/ AnmObjTexSrtOverride * AnmObjTexSrtOverride::Construct(MEMAllocator *pAllocator, u32 *pSize, ResMdl mdl, int numChildren) { if (!mdl.IsValid()) { return NULL; } int numAnim = mdl.GetResMatNumEntries(); int bindNum = numAnim; u32 objSize = sizeof(AnmObjTexSrtOverride); u32 bindSize = bindNum * sizeof(u16); u32 childrenSize = numChildren * sizeof(AnmObjTexSrtRes *); u32 bindOfs = align4(objSize); u32 childrenOfs = align4(bindOfs + bindSize); u32 size = align4(childrenOfs + childrenSize); if (pSize != NULL) { *pSize = size; } if (pAllocator == NULL) { return NULL; } u8 *pBuffer = reinterpret_cast(Alloc(pAllocator, size)); if (pBuffer == NULL) { return NULL; } // clang-format off return new (pBuffer) AnmObjTexSrtOverride( pAllocator, reinterpret_cast(pBuffer + bindOfs), bindNum, reinterpret_cast(pBuffer + childrenOfs), numChildren); // clang-format on } const TexSrtAnmResult *AnmObjTexSrtOverride::GetResult(TexSrtAnmResult *pResult, u32 idx) { for (int i = mChildrenArraySize - 1; i >= 0; i--) { AnmObjTexSrtRes *pChild = mpChildrenArray[i]; if (pChild == NULL || !pChild->TestExistence(idx)) { continue; } const TexSrtAnmResult *pChildResult = pChild->GetResult(pResult, idx); if (pChildResult->flags != 0 || pChildResult->indFlags != 0) { return pChildResult; } } pResult->flags = 0; pResult->indFlags = 0; return pResult; } /****************************************************************************** * * AnmObjTexSrtRes * ******************************************************************************/ AnmObjTexSrtRes * AnmObjTexSrtRes::Construct(MEMAllocator *pAllocator, u32 *pSize, ResAnmTexSrt srt, ResMdl mdl, bool cache) { if (!srt.IsValid() || !mdl.IsValid()) { return NULL; } int numAnim = srt.GetNumMaterial(); int numMat = mdl.GetResMatNumEntries(); int bindNum = numMat; int cacheNum = cache ? numAnim : 0; u32 bindSize = bindNum * sizeof(u16); u32 cacheSize = cacheNum * sizeof(TexSrtAnmResult); u32 size = bindSize + cacheSize + sizeof(AnmObjTexSrtRes); if (pSize != NULL) { *pSize = size; } if (pAllocator == NULL) { return NULL; } u8 *pBuffer = reinterpret_cast(Alloc(pAllocator, size)); if (pBuffer == NULL) { return NULL; } TexSrtAnmResult *pCacheBuf = cache ? reinterpret_cast(pBuffer + sizeof(AnmObjTexSrtRes)) : NULL; u16 *pBindingBuf = reinterpret_cast(cacheSize + (pBuffer + sizeof(AnmObjTexSrtRes))); return new (pBuffer) AnmObjTexSrtRes(pAllocator, srt, pBindingBuf, bindNum, pCacheBuf); } AnmObjTexSrtRes::AnmObjTexSrtRes( MEMAllocator *pAllocator, ResAnmTexSrt srt, u16 *pBindingBuf, int numBinding, TexSrtAnmResult *pCacheBuf ) : AnmObjTexSrt(pAllocator, pBindingBuf, numBinding), FrameCtrl(0.0f, srt.GetNumFrame(), GetAnmPlayPolicy(srt.GetAnmPolicy())), mRes(srt), mpResultCache(pCacheBuf) { if (mpResultCache != NULL) { UpdateCache(); } } void AnmObjTexSrtRes::SetFrame(f32 frame) { SetFrm(frame); if (mpResultCache != NULL) { UpdateCache(); } } f32 AnmObjTexSrtRes::GetFrame() const { return GetFrm(); } void AnmObjTexSrtRes::SetUpdateRate(f32 rate) { SetRate(rate); if (rate == 0.f && mpResultCache != NULL) { UpdateCache(); } } f32 AnmObjTexSrtRes::GetUpdateRate() const { return GetRate(); } void AnmObjTexSrtRes::UpdateFrame() { if (GetRate() != 0.f) { UpdateFrm(); if (mpResultCache != NULL) { UpdateCache(); } } } bool AnmObjTexSrtRes::Bind(const ResMdl mdl) { int numAnim = mRes.GetNumMaterial(); bool success = false; for (u16 i = 0; i < numAnim; i++) { const ResAnmTexSrtMatData *pData = mRes.GetMatAnm(i); // Seek back from name string to start of ResName ResName name(ut::AddOffsetToPtr(pData, pData->name - 4)); ResMat mat = mdl.GetResMat(name); if (!mat.IsValid()) { continue; } mpBinding[mat.GetID()] = i; success = true; } SetAnmFlag(FLAG_ANM_BOUND, true); return success; } const TexSrtAnmResult *AnmObjTexSrtRes::GetResult(TexSrtAnmResult *pResult, u32 idx) { u32 id = mpBinding[idx]; if (id & (BINDING_UNDEFINED | BINDING_INVALID)) { pResult->flags = 0; pResult->indFlags = 0; return pResult; } if (mpResultCache != NULL) { return &mpResultCache[id]; } mRes.GetAnmResult(pResult, id, GetFrm()); return pResult; } void AnmObjTexSrtRes::UpdateCache() { f32 frame = GetFrm(); for (u32 i = 0; i < mNumBinding; i++) { u16 bind = mpBinding[i]; if (!(bind & BINDING_UNDEFINED)) { u32 id = bind & BINDING_ID_MASK; mRes.GetAnmResult(&mpResultCache[id], id, frame); } } } void AnmObjTexSrtRes::G3dProc(u32 task, u32 param, void *pInfo) { #pragma unused(param) switch (task) { case G3DPROC_UPDATEFRAME: { UpdateFrame(); break; } case G3DPROC_DETACH_PARENT: { SetParent(NULL); break; } case G3DPROC_ATTACH_PARENT: { SetParent(static_cast(pInfo)); break; } } } /****************************************************************************** * * ApplyTexSrtAnmResult * ******************************************************************************/ void ApplyTexSrtAnmResult(ResTexSrt srt, const TexSrtAnmResult *pResult) { ResTexSrtData &r = srt.ref(); u32 mdlFlags = r.flag; u32 anmFlags = pResult->flags; u32 mask = 0x0F; for (int i = 0; mdlFlags != 0 && anmFlags != 0; mdlFlags >>= TexSrt::NUM_OF_FLAGS, anmFlags >>= TexSrt::NUM_OF_FLAGS, mask <<= 4, i++) { if (!(mdlFlags & TexSrt::FLAG_ANM_EXISTS) || !(anmFlags & TexSrt::FLAG_ANM_EXISTS)) { continue; } r.texMtxMode = pResult->texMtxMode; r.texSrt[i] = pResult->srt[i]; r.flag = (r.flag & ~mask) | (pResult->flags & mask); } } void ApplyTexSrtAnmResult(ResTexSrt srt, ResMatIndMtxAndScale ind, const TexSrtAnmResult *pResult) { // I need this to not inline :( ApplyTexSrtAnmResult(srt, pResult); u32 flags = pResult->indFlags; for (int i = TexSrtAnmResult::NUM_OF_MAT_TEX_MTX; flags != 0; i++, flags >>= TexSrt::NUM_OF_FLAGS) { if (!(flags & 1)) { continue; } math::MTX34 mtx; CalcTexMtx(&mtx, true, pResult->srt[i], static_cast(flags & TexSrt::FLAGSET_IDENTITY)); s8 scaleExp; f32 maxElem = 0.000000000000000001f; maxElem = ut::Max(maxElem, ut::Abs(mtx._00)); maxElem = ut::Max(maxElem, ut::Abs(mtx._01)); maxElem = ut::Max(maxElem, ut::Abs(mtx._03)); maxElem = ut::Max(maxElem, ut::Abs(mtx._10)); maxElem = ut::Max(maxElem, ut::Abs(mtx._11)); maxElem = ut::Max(maxElem, ut::Abs(mtx._13)); scaleExp = static_cast(math::FGetExpPart(maxElem) + 1); f32 invScale = std::ldexpf(1.0f, -scaleExp); mtx._00 *= invScale; mtx._01 *= invScale; mtx._02 = mtx._03 * invScale; mtx._10 *= invScale; mtx._11 *= invScale; mtx._12 = mtx._13 * invScale; GXIndTexMtxID id = static_cast(i - TexSrtAnmResult::NUM_OF_MAT_TEX_MTX + 1); ind.GXSetIndTexMtx(id, mtx, scaleExp); } } } // namespace g3d } // namespace nw4r