#include "nw4r/g3d.h" // IWYU pragma: export namespace nw4r { namespace g3d { void WorldMtxManip::SetScale(f32 x, f32 y, f32 z) { mpS->x = x; mpS->y = y; mpS->z = z; if (x == y && x == z) { if (1.f == x) { *mpWMAttr |= detail::WorldMtxAttr::ATTR_S_ONE; } else { *mpWMAttr |= detail::WorldMtxAttr::ATTR_S_UNIFORM; *mpWMAttr &= ~(detail::WorldMtxAttr::ATTR_S_ONE | detail::WorldMtxAttr::ATTR_ALL_S_ONE); } } else { *mpWMAttr &= ~(detail::WorldMtxAttr::ATTR_S_UNIFORM | detail::WorldMtxAttr::ATTR_ALL_S_UNIFORM | detail::WorldMtxAttr::ATTR_S_ONE | detail::WorldMtxAttr::ATTR_ALL_S_ONE); } } void CalcWorld( math::MTX34 *pModelMtxArray, u32 *pModelMtxAttribArray, const u8 *pByteCode, const math::MTX34 *pBaseMtx, ResMdl mdl, AnmObjChr *pAnmChr, FuncObjCalcWorld *pFuncObj, u32 rootAttrib ) { #define pCalcCmd reinterpret_cast(pByteCode) #define pMtxDupCmd reinterpret_cast(pByteCode) u8 c; ChrAnmResult result; const ChrAnmResult *pResult = NULL; if (pByteCode == NULL) { pByteCode = mdl.GetResByteCode("NodeTree"); if (pByteCode == NULL) { return; } } bool xsi = mdl.GetResMdlInfo().GetScalingRule() == SCALINGRULE_SOFTIMAGE; bool ignoreTrans = detail::WorldMtxAttr::IsIgnoreTrans(rootAttrib); math::VEC3 *pScaleArray = detail::workmem::GetScaleTemporary(); pScaleArray[0].x = pScaleArray[0].y = pScaleArray[0].z = 1.0f; math::MTX34Copy(pModelMtxArray, pBaseMtx); u32 *pMtxIDArray = detail::workmem::GetMtxIDTemporary(); u32 numMtxID = 0; pModelMtxAttribArray[0] = rootAttrib; while ((c = *pByteCode) != ResByteCodeData::END) { if (c == ResByteCodeData::CALC) { u32 nodeID = (pCalcCmd->nodeIdHi << 8) + pCalcCmd->nodeIdLo; u32 parentMtxID = (pCalcCmd->parentMtxIdHi << 8) + pCalcCmd->parentMtxIdLo; ResNode node = mdl.GetResNode(nodeID); u32 mtxID = node.GetMtxID(); pMtxIDArray[numMtxID++] = mtxID; math::MTX34 *pModelMtx = &pModelMtxArray[mtxID]; math::VEC3 *pScale = &pScaleArray[mtxID]; math::MTX34 *pParentModelMtx = &pModelMtxArray[parentMtxID]; math::VEC3 *pParentScale = &pScaleArray[parentMtxID]; u32 parentAttr = pModelMtxAttribArray[parentMtxID]; if (pAnmChr != NULL) { pResult = pAnmChr->GetResult(&result, node.GetID()); } if (pAnmChr == NULL || pResult->flags == 0) { node.CalcChrAnmResult(&result); pResult = &result; } if (nodeID != 0 && ignoreTrans) { if (pResult != &result) { result = *pResult; pResult = &result; } result.flags |= ChrAnmResult::FLAG_PATCH_TRANS; node.PatchChrAnmResult(&result); } if (pFuncObj != NULL) { if (pResult != &result) { result = *pResult; pResult = &result; } pFuncObj->CheckCallbackA(nodeID, &result, mdl); } if (xsi) { pModelMtxAttribArray[mtxID] = detail::dcc::CalcWorldMtx_Xsi( pModelMtx, pScale, pParentModelMtx, pParentScale, parentAttr, pResult ); } else if (pResult->flags & ChrAnmResult::FLAG_SSC_APPLY) { pModelMtxAttribArray[mtxID] = detail::dcc::CalcWorldMtx_Maya_SSC_Apply( pModelMtx, pScale, pParentModelMtx, pParentScale, parentAttr, pResult ); } else { pModelMtxAttribArray[mtxID] = detail::dcc::CalcWorldMtx_Basic( pModelMtx, pScale, pParentModelMtx, pParentScale, parentAttr, pResult ); } pModelMtxAttribArray[mtxID] = detail::WorldMtxAttr::SetBillboard(pModelMtxAttribArray[mtxID], node.GetBillboardMode()); if (pFuncObj != NULL) { pFuncObj->CheckCallbackB(nodeID, pModelMtx, pScale, &pModelMtxAttribArray[mtxID], mdl); } pByteCode += sizeof(ResByteCodeData::CalcParams); } else /* Assume MTXDUP */ { u32 toMtxID = (pMtxDupCmd->toMtxIdHi << 8) + pMtxDupCmd->toMtxIdLo; u32 fromMtxID = (pMtxDupCmd->fromMtxIdHi << 8) + pMtxDupCmd->fromMtxIdLo; pMtxIDArray[numMtxID++] = toMtxID; pModelMtxAttribArray[toMtxID] = pModelMtxAttribArray[fromMtxID]; math::MTX34Copy(&pModelMtxArray[toMtxID], &pModelMtxArray[fromMtxID]); pScaleArray[toMtxID] = pScaleArray[fromMtxID]; pByteCode += sizeof(ResByteCodeData::MtxDupParams); } } for (u32 i = 0; i < numMtxID; i++) { u32 mtxID = pMtxIDArray[i]; if (pScaleArray[mtxID].x == 1.0f && pScaleArray[mtxID].y == 1.0f && pScaleArray[mtxID].z == 1.0f) { continue; } math::MTX34Scale(&pModelMtxArray[mtxID], &pModelMtxArray[mtxID], &pScaleArray[mtxID]); } if (pFuncObj != NULL) { pFuncObj->CheckCallbackC(pModelMtxArray, mdl); } #undef pCalcCmd #undef pMtxDupCmd } void CalcWorld( math::MTX34 *pModelMtxArray, u32 *pModelMtxAttribArray, const u8 *pByteCode, const math::MTX34 *pBaseMtx, ResMdl mdl, AnmObjChr *pAnmChr, FuncObjCalcWorld *pFuncObj ) { CalcWorld( pModelMtxArray, pModelMtxAttribArray, pByteCode, pBaseMtx, mdl, pAnmChr, pFuncObj, detail::WorldMtxAttr::GetRootMtxAttr() ); } void CalcSkinning(math::MTX34 *pModelMtxArray, u32 *pModelMtxAttribArray, const ResMdl mdl, const u8 *pByteCode) { // Allows struct offsets inside assembly using nw4r::math::MTX34; #define pWeightCmd reinterpret_cast(pByteCode) #define pWeightEntry reinterpret_cast(pByteCode) #define pEvpMtxCmd reinterpret_cast(pByteCode) u8 c; if (pByteCode == NULL) { pByteCode = mdl.GetResByteCode("NodeMix"); if (pByteCode == NULL) { return; } } math::MTX34 *pSkinMtxArray = detail::workmem::GetSkinningMtxTemporary(); while ((c = *pByteCode) != ResByteCodeData::END) { if (c == ResByteCodeData::WEIGHT) { u32 targetMtxID = (pWeightCmd->tgtIdHi << 8) + pWeightCmd->tgtIdLo; u32 numBlendMtx = pWeightCmd->numBlendMtx; pModelMtxAttribArray[targetMtxID] = detail::WorldMtxAttr::GetRootMtxAttr(); pByteCode += sizeof(ResByteCodeData::WeightParams); register f32 M00, M02; register f32 M10, M12; register f32 M20, M22; M00 = 0.0f; // clang-format off asm { ps_merge00 M00, M00, M00 ps_merge00 M02, M00, M00 ps_merge00 M10, M00, M00 ps_merge00 M12, M00, M00 ps_merge00 M20, M00, M00 ps_merge00 M22, M00, M00 } // clang-format on for (u32 i = 0; i < numBlendMtx; i++) { u32 mtxID = (pWeightEntry->mtxIdHi << 8) + pWeightEntry->mtxIdLo; u32 iraito = pWeightEntry->fWeight0 << 24 | pWeightEntry->fWeight1 << 16 | pWeightEntry->fWeight2 << 8 | pWeightEntry->fWeight3; register f32 R = *reinterpret_cast(&iraito); register f32 T00, T02; register f32 T10, T12; register f32 T20, T22; register math::MTX34 *pT = &pSkinMtxArray[mtxID]; // clang-format off asm { psq_l T00, MTX34._00(pT), 0, 0 psq_l T02, MTX34._02(pT), 0, 0 psq_l T10, MTX34._10(pT), 0, 0 psq_l T12, MTX34._12(pT), 0, 0 psq_l T20, MTX34._20(pT), 0, 0 psq_l T22, MTX34._22(pT), 0, 0 ps_madds0 M00, T00, R, M00 ps_madds0 M02, T02, R, M02 ps_madds0 M10, T10, R, M10 ps_madds0 M12, T12, R, M12 ps_madds0 M20, T20, R, M20 ps_madds0 M22, T22, R, M22 } // clang-format on pModelMtxAttribArray[targetMtxID] &= pModelMtxAttribArray[mtxID]; pByteCode += sizeof(ResByteCodeData::WeightEntry); } register math::MTX34 *pTargetMtx = &pModelMtxArray[targetMtxID]; // clang-format off asm { psq_st M00, MTX34._00(pTargetMtx), 0, 0 psq_st M02, MTX34._02(pTargetMtx), 0, 0 psq_st M10, MTX34._10(pTargetMtx), 0, 0 psq_st M12, MTX34._12(pTargetMtx), 0, 0 psq_st M20, MTX34._20(pTargetMtx), 0, 0 psq_st M22, MTX34._22(pTargetMtx), 0, 0 } // clang-format on } else /* Assume EVPMTX */ { u32 mtxID = (pEvpMtxCmd->mtxIdHi << 8) + pEvpMtxCmd->mtxIdLo; u32 nodeID = (pEvpMtxCmd->nodeIdHi << 8) + pEvpMtxCmd->nodeIdLo; math::MTX34Mult( &pSkinMtxArray[mtxID], &pModelMtxArray[mtxID], static_cast(&mdl.GetResNode(nodeID).ref().invModelMtx) ); pByteCode += sizeof(ResByteCodeData::EvpMtxParams); } } ResMdlInfo mdlInfo = mdl.GetResMdlInfo(); switch (mdlInfo.GetEnvelopeMatrixMode()) { case ResMdlInfo::EVPMATRIXMODE_NORMAL: { return; } break; case ResMdlInfo::EVPMATRIXMODE_APPROX: { u32 numWeighted = mdlInfo.GetNumPosNrmMtx() - mdl.GetResNodeNumEntries(); u32 end = mdlInfo.GetNumViewMtx(); u32 begin = end - numWeighted; for (u32 i = begin; i < end; i++) { pModelMtxAttribArray[i] |= detail::WorldMtxAttr::ATTR_S_UNIFORM; pModelMtxAttribArray[i] |= detail::WorldMtxAttr::ATTR_ALL_S_UNIFORM; } } break; case ResMdlInfo::EVPMATRIXMODE_EXACT: { u32 numWeighted = mdlInfo.GetNumPosNrmMtx() - mdl.GetResNodeNumEntries(); u32 end = mdlInfo.GetNumViewMtx(); u32 begin = end - numWeighted; for (u32 i = begin; i < end; i++) { pModelMtxAttribArray[i] &= ~detail::WorldMtxAttr::ATTR_ALL_S_UNIFORM; } } break; } #undef pWeightCmd #undef pWeightEntry #undef pEvpMtxCmd } } // namespace g3d } // namespace nw4r