/*---------------------------------------------------------------------------* Project: NintendoWare File: lyt_Layout.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 25594 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) namespace nw { namespace lyt { nw::os::IAllocator* Layout::s_pAllocator = 0; nw::os::IAllocator* Layout::s_pDeviceMemoryAllocator = 0; bool Layout::s_LayoutDrawEnable = false; namespace { /*---------------------------------------------------------------------------* @brief 再帰的にペインにタグプロセッサ設定します。 *---------------------------------------------------------------------------*/ void SetTagProcessorImpl( Pane* pPane, font::TagProcessorBase* pTagProcessor ) { // TextBox ペインか if (TextBox* pTextBox = ut::DynamicCast(pPane)) { pTextBox->SetTagProcessor(pTagProcessor); } // 再帰的にセット for (PaneList::Iterator it = pPane->GetChildList().GetBeginIter(); it != pPane->GetChildList().GetEndIter(); ++it) { SetTagProcessorImpl(&(*it), pTagProcessor); } } bool IsIncludeAnimationGroupRef( GroupContainer* pGroupContainer, const AnimationGroupRef *const groupRefs, u16 bindGroupNum, bool bDescendingBind, Pane* pTargetPane ) { for (u16 grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx) { // 共有対象のペインがバインド指定のグループが持つペインリストに含まれるか調べます。 Group *const pGroup = pGroupContainer->FindGroupByName(groupRefs[grpIdx].GetName()); PaneLinkList& paneList = pGroup->GetPaneList(); for (PaneLinkList::Iterator it = paneList.GetBeginIter(); it != paneList.GetEndIter(); ++it) { if (it->target == pTargetPane) { return true; } if (bDescendingBind) { // pTargetPaneの親方向で一致するものが無いか調べる。 for (Pane* pParentPane = pTargetPane->GetParent(); pParentPane; pParentPane = pParentPane->GetParent()) { if (it->target == pParentPane) { return true; } } } } } return false; } } // namespace nw::lyt::{no-name} void Layout::SetAllocator(nw::os::IAllocator* pAllocator) { s_pAllocator = pAllocator; } void Layout::SetDeviceMemoryAllocator(nw::os::IAllocator* pAllocator) { s_pDeviceMemoryAllocator = pAllocator; } void* Layout::AllocMemory(u32 size, u8 alignment) { NW_NULL_ASSERT(s_pAllocator); void *const pMem = s_pAllocator->Alloc(size, alignment); NW_WARNING(pMem, "can't alloc memory."); return pMem; } void* Layout::AllocDeviceMemory(u32 size, u8 alignment) { NW_NULL_ASSERT(s_pDeviceMemoryAllocator); void *const pMem = s_pDeviceMemoryAllocator->Alloc(size, alignment); NW_WARNING(pMem, "can't alloc memory."); return pMem; } void Layout::FreeMemory(void* mem) { NW_NULL_ASSERT(s_pAllocator); s_pAllocator->Free(mem); } void Layout::FreeDeviceMemory(void* mem) { NW_NULL_ASSERT(s_pDeviceMemoryAllocator); s_pDeviceMemoryAllocator->Free(mem); } Layout::Layout() : m_pRootPane(0), m_pGroupContainer(0), m_LayoutSize(0.f, 0.f) { } Layout::~Layout() { DeleteObj(m_pGroupContainer); if (m_pRootPane && !m_pRootPane->IsUserAllocated()) { DeleteObj(m_pRootPane); } for (AnimTransformList::Iterator it = m_AnimTransList.GetBeginIter(); it != m_AnimTransList.GetEndIter();) { AnimTransformList::Iterator currIt = it++; m_AnimTransList.Erase(currIt); DeleteObj(&(*currIt)); } } bool Layout::Build( const void* lytResBuf, ResourceAccessor* pResAcsr ) { NW_NULL_ASSERT(s_pAllocator); NW_NULL_ASSERT(lytResBuf); const ut::BinaryFileHeader *const pFileHead = static_cast(lytResBuf); if (! ut::IsValidBinaryFile(pFileHead, res::FILESIGNATURE_CLYT, res::BinaryFileFormatVersion)) { NW_WARNING(false, "not valid layout file."); } ResBlockSet resBlockSet = { 0 }; resBlockSet.pResAccessor = pResAcsr; Pane* pParentPane = 0; Pane* pLastPane = 0; bool bReadRootGroup = false; int groupNestLevel = 0; const void* dataPtr = static_cast(lytResBuf) + pFileHead->headerSize; for (int i = 0; i < pFileHead->dataBlocks; ++i) { const ut::BinaryBlockHeader* pDataBlockHead = static_cast(dataPtr); ut::SigWord kind = pDataBlockHead->kind; switch (kind) { case res::DATABLOCKKIND_LAYOUT: { const res::Layout* pResLyt = static_cast(dataPtr); this->SetLayoutSize(pResLyt->layoutSize); } break; case res::DATABLOCKKIND_TEXTURELIST: resBlockSet.pTextureList = static_cast(dataPtr); break; case res::DATABLOCKKIND_FONTLIST: resBlockSet.pFontList = static_cast(dataPtr); break; case res::DATABLOCKKIND_MATERIALLIST: resBlockSet.pMaterialList = static_cast(dataPtr); break; case res::DATABLOCKKIND_PANE: case res::DATABLOCKKIND_PICTURE: case res::DATABLOCKKIND_TEXTBOX: case res::DATABLOCKKIND_WINDOW: case res::DATABLOCKKIND_BOUNDING: { Pane* pPane = BuildPaneObj(kind, dataPtr, resBlockSet); if (pPane) { // ルートペインがまだ設定されていないときはルートペインに設定 if (this->GetRootPane() == 0) { this->SetRootPane(pPane); } // 親がいたら親に登録 if (pParentPane) { pParentPane->AppendChild(pPane); } pLastPane = pPane; } } break; case res::DATABLOCKKIND_USERDATALIST: NW_NULL_ASSERT(pLastPane); pLastPane->SetExtUserDataList(reinterpret_cast(pDataBlockHead)); break; case res::DATABLOCKKIND_PANEBEGIN: NW_NULL_ASSERT(pLastPane); pParentPane = pLastPane; // 最後に作成したペインを親とする break; case res::DATABLOCKKIND_PANEEND: pLastPane = pParentPane; // 親ペインを最後に作成したペインとする pParentPane = pLastPane->GetParent(); break; case res::DATABLOCKKIND_GROUP: if (! bReadRootGroup) // グループのルートに初めて到達 { bReadRootGroup = true; this->SetGroupContainer(NewObj()); } else { if (this->GetGroupContainer() && groupNestLevel == 1) { if (Group* pGroup = NewObj(reinterpret_cast(pDataBlockHead), this->GetRootPane())) { this->GetGroupContainer()->AppendGroup(pGroup); } } } break; case res::DATABLOCKKIND_GROUPBEGIN: groupNestLevel++; break; case res::DATABLOCKKIND_GROUPEND: groupNestLevel--; break; default: break; } dataPtr = static_cast(dataPtr) + pDataBlockHead->size; // 次のブロック位置へ } return true; } AnimTransform* Layout::CreateAnimTransform() { NW_NULL_ASSERT(s_pAllocator); AnimTransformBasic *const pAnimTrans = NewObj(); if (pAnimTrans) { this->GetAnimTransformList().PushBack(pAnimTrans); } return pAnimTrans; } void Layout::DeleteAnimTransform(AnimTransform *pAnimTransform) { NW_NULL_ASSERT(pAnimTransform); this->GetAnimTransformList().erase(pAnimTransform); DeleteObj(pAnimTransform); } AnimTransform* Layout::CreateAnimTransform( const void* animResBuf, ResourceAccessor* pResAcsr ) { return CreateAnimTransform(AnimResource(animResBuf), pResAcsr); } AnimTransform* Layout::CreateAnimTransform( const AnimResource& animRes, ResourceAccessor* pResAcsr ) { const res::AnimationBlock *const pAnimBlock = animRes.GetResourceBlock(); if (! pAnimBlock) { return 0; } AnimTransform *const pAnimTrans = CreateAnimTransform(); if (pAnimTrans) { pAnimTrans->SetResource(pAnimBlock, pResAcsr); } return pAnimTrans; } void Layout::BindAnimation(AnimTransform* pAnimTrans) { if (this->GetRootPane()) { this->GetRootPane()->BindAnimation(pAnimTrans, true); } } void Layout::UnbindAnimation(AnimTransform* pAnimTrans) { if (this->GetRootPane()) { this->GetRootPane()->UnbindAnimation(pAnimTrans, true); } } void Layout::UnbindAllAnimation() { UnbindAnimation(0); } bool Layout::BindAnimationAuto( const AnimResource& animRes, ResourceAccessor* pResAcsr ) { if (! this->GetRootPane()) { return false; } if (! animRes.GetResourceBlock()) { return false; } AnimTransform *const pAnimTrans = CreateAnimTransform(); // AnimTransform オブジェクトの作成 if (pAnimTrans == NULL) { NW_WARNING(false, "Create AnimTransform failed."); return false; } bool bResult = true; // 最初に名前によるバインドを一通り行う const u16 bindGroupNum = animRes.GetGroupNum(); u16 animNum = 0; if (bindGroupNum == 0) // バインドする対象を限定していない { animNum = animRes.GetResourceBlock()->animContNum; // バインドするアニメーション数を明示的に指定してリソースをセットします。 pAnimTrans->SetResource(animRes.GetResourceBlock(), pResAcsr, animNum); const bool bRecursive = true; this->GetRootPane()->BindAnimation(pAnimTrans, bRecursive, true/* bDisable */); } else // グループを指定してのバインド { const AnimationGroupRef *const groupRefs = animRes.GetGroupArray(); for (int grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx) { // グループにあるペイン全てで必要になるアニメーションの個数を数えます。 Group *const pGroup = this->GetGroupContainer()->FindGroupByName(groupRefs[grpIdx].GetName()); if (pGroup == NULL) { NW_WARNING(false, "Group not found: %s", groupRefs[grpIdx].GetName()); bResult = false; continue; } animNum += animRes.CalcAnimationNum(pGroup, animRes.IsDescendingBind()); } // バインドするアニメーション数を明示的に指定してリソースをセットします。 pAnimTrans->SetResource(animRes.GetResourceBlock(), pResAcsr, animNum); for (int grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx) { // グループにあるペイン全てで必要になるアニメーションの個数を数えます。 Group *const pGroup = this->GetGroupContainer()->FindGroupByName(groupRefs[grpIdx].GetName()); if (pGroup == NULL) { continue; } // アニメーションをバインドします。 nw::lyt::BindAnimation(pGroup, pAnimTrans, animRes.IsDescendingBind(), true/* bDisable */); } } const u16 animShareInfoNum = animRes.GetAnimationShareInfoNum(); if (animShareInfoNum > 0) // アニメーション共有によるバインド { const AnimationShareInfo *const animShareInfoAry = animRes.GetAnimationShareInfoArray(); NW_NULL_ASSERT(animShareInfoAry); for (int i = 0; i < animShareInfoNum; ++i) { Pane *const pSrcPane = this->GetRootPane()->FindPaneByName(animShareInfoAry[i].GetSrcPaneName()); if (pSrcPane == NULL) { NW_WARNING(false, "Source pane of animation-share is not found: %s", animShareInfoAry[i].GetSrcPaneName()); bResult = false; continue; } internal::AnimPaneTree animPaneTree(pSrcPane, animRes); if (! animPaneTree.IsEnabled()) // 共有元ペインにアニメーションが無い? { continue; } Group *const pGroup = this->GetGroupContainer()->FindGroupByName(animShareInfoAry[i].GetTargetGroupName()); if (pGroup == NULL) { NW_WARNING(false, "Target group of animation-share is not found: %s", animShareInfoAry[i].GetTargetGroupName()); bResult = false; continue; } PaneLinkList& paneList = pGroup->GetPaneList(); u32 animIdx = 0; for (PaneLinkList::Iterator it = paneList.GetBeginIter(); it != paneList.GetEndIter(); ++it, ++animIdx) { if (it->target != pSrcPane) { if (bindGroupNum > 0) // バインド対象の指定があり { // 関連グループに含まれてない場合は共有しない const bool bInclude = IsIncludeAnimationGroupRef( this->GetGroupContainer(), animRes.GetGroupArray(), bindGroupNum, animRes.IsDescendingBind(), it->target); if (! bInclude) { continue; } } // srcPaneNameと異なるペインに対してアニメーションをバインド animPaneTree.Bind(this, it->target, pResAcsr); } } } } return bResult; } void Layout::SetAnimationEnable( AnimTransform* pAnimTrans, bool bEnable ) { if (this->GetRootPane()) { this->GetRootPane()->SetAnimationEnable(pAnimTrans, bEnable, true); } } void Layout::CalculateMtx(const DrawInfo& drawInfo) { NW_LYT_STOPWATCH_MEASURE(-610, "nw::lyt::Layout::CalculateMtx"); if (! this->GetRootPane()) { return; } this->GetRootPane()->CalculateMtx(drawInfo); } #ifdef NW_LYT_DMPGL_ENABLED void Layout::Draw(const DrawInfo& drawInfo) { NW_LYT_STOPWATCH_MEASURE(-630, "nw::lyt::Layout::Draw"); if (! this->GetRootPane()) { return; } GraphicsResource* graphicsResource = drawInfo.GetGraphicsResource(); if (graphicsResource == NULL) { NW_WARNING(false, "GraphicsResource is not set."); return; } graphicsResource->ResetGlState(); graphicsResource->ResetGlProgramState(); graphicsResource->SetProjectionMtx(drawInfo.GetProjectionMtx()); drawInfo.SetLayout(this); this->GetRootPane()->Draw(drawInfo); internal::FinalizeGraphics(); drawInfo.SetLayout(0); } #endif void Layout::Animate(u32 option) { NW_LYT_STOPWATCH_MEASURE(-620, "nw::lyt::Layout::Animate"); if (! this->GetRootPane()) { return; } this->GetRootPane()->Animate(option); } const ut::Rect Layout::GetLayoutRect() const { return ut::Rect(- m_LayoutSize.width / 2, m_LayoutSize.height / 2, m_LayoutSize.width / 2, - m_LayoutSize.height / 2); } void Layout::SetTagProcessor(font::TagProcessorBase* pTagProcessor) { SetTagProcessorImpl(this->GetRootPane(), pTagProcessor); } Pane* Layout::BuildPaneObj( s32 kind, const void* dataPtr, const ResBlockSet& resBlockSet ) { switch (kind) { case res::DATABLOCKKIND_PANE: { const res::Pane* pResPane = static_cast(dataPtr); return NewObj(pResPane); } case res::DATABLOCKKIND_PICTURE: { const res::Picture* pResPic = static_cast(dataPtr); return NewObj(pResPic, resBlockSet); } case res::DATABLOCKKIND_TEXTBOX: { const res::TextBox* pBlock = static_cast(dataPtr); return NewObj(pBlock, resBlockSet); } case res::DATABLOCKKIND_WINDOW: { const res::Window* pBlock = static_cast(dataPtr); return NewObj(pBlock, resBlockSet); } case res::DATABLOCKKIND_BOUNDING: { const res::Bounding* pResBounding = static_cast(dataPtr); return NewObj(pResBounding, resBlockSet); } default: NW_ASSERTMSG(false, "unknown data type"); break; } return 0; } } // namespace nw::lyt } // namespace nw