#include "nw4r/g3d.h" // IWYU pragma: export #include "nw4r/ut.h" // IWYU pragma: export #include "rvl/GX.h" // IWYU pragma: export namespace nw4r { namespace g3d { NW4R_G3D_RTTI_DEF(AnmObjTexPat); NW4R_G3D_RTTI_DEF(AnmObjTexPatNode); NW4R_G3D_RTTI_DEF(AnmObjTexPatOverride); NW4R_G3D_RTTI_DEF(AnmObjTexPatRes); /****************************************************************************** * * AnmObjTexPat * ******************************************************************************/ AnmObjTexPat::AnmObjTexPat(MEMAllocator *pAllocator, u16 *pBindingBuf, int numBinding) : AnmObj(pAllocator, NULL), mNumBinding(numBinding), mpBinding(pBindingBuf) { Release(); } bool AnmObjTexPat::TestExistence(u32 idx) const { return !(mpBinding[idx] & (BINDING_UNDEFINED | BINDING_INVALID)); } bool AnmObjTexPat::TestDefined(u32 idx) const { return !(mpBinding[idx] & BINDING_UNDEFINED); } void AnmObjTexPat::Release() { for (int i = 0; i < mNumBinding; i++) { mpBinding[i] = BINDING_UNDEFINED; } SetAnmFlag(FLAG_ANM_BOUND, false); } AnmObjTexPatRes *AnmObjTexPat::Attach(int idx, AnmObjTexPatRes *pRes) { #pragma unused(idx) #pragma unused(pRes) return NULL; } AnmObjTexPatRes *AnmObjTexPat::Detach(int idx) { #pragma unused(idx) return NULL; } void AnmObjTexPat::DetachAll() {} /****************************************************************************** * * AnmObjTexPatNode * ******************************************************************************/ AnmObjTexPatNode::AnmObjTexPatNode( MEMAllocator *pAllocator, u16 *pBindingBuf, int numBinding, AnmObjTexPatRes **ppChildrenBuf, int numChildren ) : AnmObjTexPat(pAllocator, pBindingBuf, numBinding), mChildrenArraySize(numChildren), mpChildrenArray(ppChildrenBuf) { for (int i = 0; i < mChildrenArraySize; i++) { mpChildrenArray[i] = NULL; } } AnmObjTexPatNode::~AnmObjTexPatNode() { DetachAll(); } AnmObjTexPatRes *AnmObjTexPatNode::Attach(int idx, AnmObjTexPatRes *pRes) { AnmObjTexPatRes *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; } AnmObjTexPatRes *AnmObjTexPatNode::Detach(int idx) { AnmObjTexPatRes *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++) { AnmObjTexPatRes *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 AnmObjTexPatNode::DetachAll() { for (int i = 0; i < mChildrenArraySize; i++) { Detach(i); } } void AnmObjTexPatNode::UpdateFrame() { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->UpdateFrame(); } } } void AnmObjTexPatNode::SetFrame(f32 frame) { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->SetFrame(frame); } } } f32 AnmObjTexPatNode::GetFrame() const { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { return mpChildrenArray[i]->GetFrame(); } } return 0.0f; } void AnmObjTexPatNode::SetUpdateRate(f32 rate) { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->SetUpdateRate(rate); } } } f32 AnmObjTexPatNode::GetUpdateRate() const { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { return mpChildrenArray[i]->GetUpdateRate(); } } return 1.0f; } bool AnmObjTexPatNode::Bind(const ResMdl mdl) { bool success = false; for (int i = 0; i < mChildrenArraySize; i++) { AnmObjTexPatRes *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 AnmObjTexPatNode::Release() { for (int i = 0; i < mChildrenArraySize; i++) { if (mpChildrenArray[i] != NULL) { mpChildrenArray[i]->Release(); } } AnmObjTexPat::Release(); } void AnmObjTexPatNode::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; } } } /****************************************************************************** * * AnmObjTexPatOverride * ******************************************************************************/ AnmObjTexPatOverride * AnmObjTexPatOverride::Construct(MEMAllocator *pAllocator, u32 *pSize, ResMdl mdl, int numChildren) { if (!mdl.IsValid()) { return NULL; } int numAnim = mdl.GetResMatNumEntries(); int bindNum = numAnim; u32 objSize = sizeof(AnmObjTexPatOverride); u32 bindSize = bindNum * sizeof(u16); u32 childrenSize = numChildren * sizeof(AnmObjTexPatRes *); 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) AnmObjTexPatOverride( pAllocator, reinterpret_cast(pBuffer + bindOfs), bindNum, reinterpret_cast(pBuffer + childrenOfs), numChildren); // clang-format on } const TexPatAnmResult *AnmObjTexPatOverride::GetResult(TexPatAnmResult *pResult, u32 idx) { for (int i = mChildrenArraySize - 1; i >= 0; i--) { AnmObjTexPatRes *pChild = mpChildrenArray[i]; if (pChild == NULL || !pChild->TestExistence(idx)) { continue; } const TexPatAnmResult *pChildResult = pChild->GetResult(pResult, idx); if (pChildResult->bTexExist != 0 || pChildResult->bPlttExist) { return pChildResult; } } pResult->bTexExist = 0; pResult->bPlttExist = 0; return pResult; } /****************************************************************************** * * AnmObjTexPatRes * ******************************************************************************/ AnmObjTexPatRes * AnmObjTexPatRes::Construct(MEMAllocator *pAllocator, u32 *pSize, ResAnmTexPat pat, ResMdl mdl, bool cache) { if (!pat.IsValid() || !mdl.IsValid()) { return NULL; } int numAnim = pat.GetNumMaterial(); int numMat = mdl.GetResMatNumEntries(); int bindNum = numMat; int cacheNum = cache ? numAnim : 0; u32 bindSize = bindNum * sizeof(u16); u32 cacheSize = cacheNum * sizeof(TexPatAnmResult); u32 size = bindSize + cacheSize + sizeof(AnmObjTexPatRes); if (pSize != NULL) { *pSize = size; } if (pAllocator == NULL) { return NULL; } u8 *pBuffer = reinterpret_cast(Alloc(pAllocator, size)); if (pBuffer == NULL) { return NULL; } TexPatAnmResult *pCacheBuf = cache ? reinterpret_cast(pBuffer + sizeof(AnmObjTexPatRes)) : NULL; u16 *pBindingBuf = reinterpret_cast(cacheSize + (pBuffer + sizeof(AnmObjTexPatRes))); return new (pBuffer) AnmObjTexPatRes(pAllocator, pat, pBindingBuf, bindNum, pCacheBuf); } AnmObjTexPatRes::AnmObjTexPatRes( MEMAllocator *pAllocator, ResAnmTexPat pat, u16 *pBindingBuf, int numBinding, TexPatAnmResult *pCacheBuf ) : AnmObjTexPat(pAllocator, pBindingBuf, numBinding), FrameCtrl(0.0f, pat.GetNumFrame(), GetAnmPlayPolicy(pat.GetAnmPolicy())), mRes(pat), mpResultCache(pCacheBuf) { if (mpResultCache != NULL) { UpdateCache(); } } void AnmObjTexPatRes::SetFrame(f32 frame) { SetFrm(frame); if (mpResultCache != NULL) { UpdateCache(); } } f32 AnmObjTexPatRes::GetFrame() const { return GetFrm(); } void AnmObjTexPatRes::SetUpdateRate(f32 rate) { SetRate(rate); if (rate == 0.f && mpResultCache != NULL) { UpdateCache(); } } f32 AnmObjTexPatRes::GetUpdateRate() const { return GetRate(); } void AnmObjTexPatRes::UpdateFrame() { if (GetRate() != 0.f) { UpdateFrm(); if (mpResultCache != NULL) { UpdateCache(); } } } bool AnmObjTexPatRes::Bind(const ResMdl mdl) { int numAnim = mRes.GetNumMaterial(); bool success = false; for (u16 i = 0; i < numAnim; i++) { const ResAnmTexPatMatData *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 TexPatAnmResult *AnmObjTexPatRes::GetResult(TexPatAnmResult *pResult, u32 i) { u32 id = mpBinding[i]; if (id & (BINDING_UNDEFINED | BINDING_INVALID)) { pResult->bTexExist = 0; pResult->bPlttExist = 0; return pResult; } if (mpResultCache != NULL) { return &mpResultCache[id]; } mRes.GetAnmResult(pResult, id, GetFrm()); return pResult; } void AnmObjTexPatRes::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 AnmObjTexPatRes::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; } } } /****************************************************************************** * * ApplyTexPatAnmResult * ******************************************************************************/ void ApplyTexPatAnmResult(ResTexObj texObj, ResTlutObj tlutObj, const TexPatAnmResult *pResult) { u32 texExist = pResult->bTexExist; u32 i; for (i = 0; texExist != 0; texExist >>= 1, i++) { if (!(texExist & 1)) { continue; } ResTex tex = pResult->tex[i]; GXTexObj *pGXObj = texObj.GetTexObj(static_cast(i)); GXTexFilter minFilt, magFilt; f32 minLod, maxLod, lodBias; GXBool biasClamp, edgeLod, mipmap; GXAnisotropy maxAniso; void *pTexData; u16 width, height; GXTexFmt fmt; GXCITexFmt cifmt; GXGetTexObjLODAll(pGXObj, &minFilt, &magFilt, &minLod, &maxLod, &lodBias, &biasClamp, &edgeLod, &maxAniso); GXTexWrapMode wrapS = GXGetTexObjWrapS(pGXObj); GXTexWrapMode wrapT = GXGetTexObjWrapT(pGXObj); if (tex.IsCIFmt()) { tex.GetTexObjCIParam(&pTexData, &width, &height, &cifmt, &minLod, &maxLod, &mipmap); GXInitTexObjCI(pGXObj, pTexData, width, height, cifmt, wrapS, wrapT, mipmap, static_cast(i)); } else { tex.GetTexObjParam(&pTexData, &width, &height, &fmt, &minLod, &maxLod, &mipmap); GXInitTexObj(pGXObj, pTexData, width, height, fmt, wrapS, wrapT, mipmap); } GXInitTexObjLOD(pGXObj, minFilt, magFilt, minLod, maxLod, lodBias, biasClamp, edgeLod, maxAniso); } u32 plttExist = pResult->bPlttExist; for (i = 0; plttExist != 0; plttExist >>= 1, i++) { if (!(plttExist & 1)) { continue; } ResPltt pltt = pResult->pltt[i]; void *pLUT = pltt.GetPlttData(); GXTlutFmt fmt = pltt.GetFmt(); u16 numEntries = pltt.GetNumEntries(); GXTlutObj *pGXObj = tlutObj.GetTlut(static_cast(i)); GXInitTlutObj(pGXObj, pLUT, fmt, numEntries); } } } // namespace g3d } // namespace nw4r