1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     lyt_Layout.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: 31311 $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 
20 #include <nw/lyt/lyt_Animation.h>
21 #include <nw/lyt/lyt_Bounding.h>
22 #include <nw/lyt/lyt_Common.h>
23 #include <nw/lyt/lyt_DrawInfo.h>
24 #include <nw/lyt/lyt_GraphicsResource.h>
25 #include <nw/lyt/lyt_Group.h>
26 #include <nw/lyt/lyt_Layout.h>
27 #include <nw/lyt/lyt_Material.h>
28 #include <nw/lyt/lyt_Picture.h>
29 #include <nw/lyt/lyt_ResourceAccessor.h>
30 #include <nw/lyt/lyt_Resources.h>
31 #include <nw/lyt/lyt_Stopwatch.h>
32 #include <nw/lyt/lyt_TextBox.h>
33 #include <nw/lyt/lyt_Util.h>
34 #include <nw/lyt/lyt_Window.h>
35 
36 #define ARRAY_LENGTH(a)   (sizeof(a) / sizeof((a)[0]))
37 
38 namespace nw
39 {
40 namespace lyt
41 {
42 
43 nw::os::IAllocator* Layout::s_pAllocator = 0;
44 nw::os::IAllocator* Layout::s_pDeviceMemoryAllocator = 0;
45 bool Layout::s_LayoutDrawEnable = false;
46 
47 namespace
48 {
49 
50 /*---------------------------------------------------------------------------*
51   @brief 再帰的にペインにタグプロセッサ設定します。
52  *---------------------------------------------------------------------------*/
53 void
SetTagProcessorImpl(Pane * pPane,font::TagProcessorBase<wchar_t> * pTagProcessor)54 SetTagProcessorImpl(
55     Pane*                           pPane,
56     font::TagProcessorBase<wchar_t>*  pTagProcessor
57 )
58 {
59     // TextBox ペインか
60     if (TextBox* pTextBox = ut::DynamicCast<TextBox*>(pPane))
61     {
62         pTextBox->SetTagProcessor(pTagProcessor);
63     }
64 
65     // 再帰的にセット
66     for (PaneList::Iterator it = pPane->GetChildList().GetBeginIter(); it != pPane->GetChildList().GetEndIter(); ++it)
67     {
68         SetTagProcessorImpl(&(*it), pTagProcessor);
69     }
70 }
71 
72 bool
IsIncludeAnimationGroupRef(GroupContainer * pGroupContainer,const AnimationGroupRef * const groupRefs,u16 bindGroupNum,bool bDescendingBind,Pane * pTargetPane)73 IsIncludeAnimationGroupRef(
74     GroupContainer*                 pGroupContainer,
75     const AnimationGroupRef *const  groupRefs,
76     u16                             bindGroupNum,
77     bool                            bDescendingBind,
78     Pane*                           pTargetPane
79 )
80 {
81     for (u16 grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx)
82     {
83         // 共有対象のペインがバインド指定のグループが持つペインリストに含まれるか調べます。
84         Group *const pGroup = pGroupContainer->FindGroupByName(groupRefs[grpIdx].GetName());
85         PaneLinkList& paneList = pGroup->GetPaneList();
86         for (PaneLinkList::Iterator it = paneList.GetBeginIter(); it != paneList.GetEndIter(); ++it)
87         {
88             if (it->target == pTargetPane)
89             {
90                 return true;
91             }
92 
93             if (bDescendingBind)
94             {
95                 // pTargetPaneの親方向で一致するものが無いか調べる。
96                 for (Pane* pParentPane = pTargetPane->GetParent(); pParentPane; pParentPane = pParentPane->GetParent())
97                 {
98                     if (it->target == pParentPane)
99                     {
100                         return true;
101                     }
102                 }
103             }
104         }
105     }
106 
107     return false;
108 }
109 
110 } // namespace nw::lyt::{no-name}
111 
112 void
SetAllocator(nw::os::IAllocator * pAllocator)113 Layout::SetAllocator(nw::os::IAllocator* pAllocator)
114 {
115     s_pAllocator = pAllocator;
116 }
117 
118 void
SetDeviceMemoryAllocator(nw::os::IAllocator * pAllocator)119 Layout::SetDeviceMemoryAllocator(nw::os::IAllocator* pAllocator)
120 {
121     s_pDeviceMemoryAllocator = pAllocator;
122 }
123 
124 void*
AllocMemory(u32 size,u8 alignment)125 Layout::AllocMemory(u32 size, u8 alignment)
126 {
127     NW_NULL_ASSERT(s_pAllocator);
128 
129     void *const pMem = s_pAllocator->Alloc(size, alignment);
130     NW_WARNING(pMem, "can't alloc memory.");
131     return pMem;
132 }
133 
134 void*
AllocDeviceMemory(u32 size,u8 alignment)135 Layout::AllocDeviceMemory(u32 size, u8 alignment)
136 {
137     NW_NULL_ASSERT(s_pDeviceMemoryAllocator);
138 
139     void *const pMem = s_pDeviceMemoryAllocator->Alloc(size, alignment);
140     NW_WARNING(pMem, "can't alloc memory.");
141     return pMem;
142 }
143 
144 void
FreeMemory(void * mem)145 Layout::FreeMemory(void* mem)
146 {
147     NW_NULL_ASSERT(s_pAllocator);
148 
149     s_pAllocator->Free(mem);
150 }
151 
152 void
FreeDeviceMemory(void * mem)153 Layout::FreeDeviceMemory(void* mem)
154 {
155     NW_NULL_ASSERT(s_pDeviceMemoryAllocator);
156 
157     s_pDeviceMemoryAllocator->Free(mem);
158 }
159 
Layout()160 Layout::Layout()
161 : m_pRootPane(0),
162   m_pGroupContainer(0),
163   m_LayoutSize(0.f, 0.f)
164 {
165 }
166 
~Layout()167 Layout::~Layout()
168 {
169     DeleteObj(m_pGroupContainer);
170 
171     if (m_pRootPane && !m_pRootPane->IsUserAllocated())
172     {
173         DeleteObj(m_pRootPane);
174     }
175 
176     for (AnimTransformList::Iterator it = m_AnimTransList.GetBeginIter(); it != m_AnimTransList.GetEndIter();)
177     {
178         AnimTransformList::Iterator currIt = it++;
179         m_AnimTransList.Erase(currIt);
180         DeleteObj(&(*currIt));
181     }
182 }
183 
184 bool
Build(const void * lytResBuf,ResourceAccessor * pResAcsr)185 Layout::Build(
186     const void* lytResBuf,
187     ResourceAccessor* pResAcsr
188 )
189 {
190     NW_NULL_ASSERT(s_pAllocator);
191     NW_NULL_ASSERT(lytResBuf);
192 
193     const ut::BinaryFileHeader *const pFileHead = static_cast<const ut::BinaryFileHeader*>(lytResBuf);
194 
195     if (! ut::IsValidBinaryFile(pFileHead, res::FILESIGNATURE_CLYT, res::BinaryFileFormatVersion))
196     {
197         NW_WARNING(false, "not valid layout file.");
198     }
199 
200     ResBlockSet resBlockSet = { 0 };
201     resBlockSet.pResAccessor = pResAcsr;
202     Pane* pParentPane = 0;
203     Pane* pLastPane = 0;
204 
205     bool bReadRootGroup = false;
206     int groupNestLevel = 0;
207 
208     const void* dataPtr = static_cast<const u8*>(lytResBuf) + pFileHead->headerSize;
209     for (int i = 0; i < pFileHead->dataBlocks; ++i)
210     {
211         const ut::BinaryBlockHeader* pDataBlockHead = static_cast<const ut::BinaryBlockHeader*>(dataPtr);
212         ut::SigWord kind = pDataBlockHead->kind;
213         switch (kind)
214         {
215         case res::DATABLOCKKIND_LAYOUT:
216             {
217                 const res::Layout* pResLyt = static_cast<const res::Layout*>(dataPtr);
218                 this->SetLayoutSize(pResLyt->layoutSize);
219             }
220             break;
221 
222         case res::DATABLOCKKIND_TEXTURELIST:
223             resBlockSet.pTextureList = static_cast<const res::TextureList*>(dataPtr);
224             break;
225 
226         case res::DATABLOCKKIND_FONTLIST:
227             resBlockSet.pFontList = static_cast<const res::FontList*>(dataPtr);
228             break;
229 
230         case res::DATABLOCKKIND_MATERIALLIST:
231             resBlockSet.pMaterialList = static_cast<const res::MaterialList*>(dataPtr);
232             break;
233 
234         case res::DATABLOCKKIND_PANE:
235         case res::DATABLOCKKIND_PICTURE:
236         case res::DATABLOCKKIND_TEXTBOX:
237         case res::DATABLOCKKIND_WINDOW:
238         case res::DATABLOCKKIND_BOUNDING:
239             {
240                 Pane* pPane = BuildPaneObj(kind, dataPtr, resBlockSet);
241                 if (pPane)
242                 {
243                     // ルートペインがまだ設定されていないときはルートペインに設定
244                     if (this->GetRootPane() == 0)
245                     {
246                         this->SetRootPane(pPane);
247                     }
248 
249                     // 親がいたら親に登録
250                     if (pParentPane)
251                     {
252                         pParentPane->AppendChild(pPane);
253                     }
254 
255                     pLastPane = pPane;
256                 }
257             }
258             break;
259 
260         case res::DATABLOCKKIND_USERDATALIST:
261             NW_NULL_ASSERT(pLastPane);
262             pLastPane->SetExtUserDataList(reinterpret_cast<const res::ExtUserDataList*>(pDataBlockHead));
263             break;
264 
265         case res::DATABLOCKKIND_PANEBEGIN:
266             NW_NULL_ASSERT(pLastPane);
267             pParentPane = pLastPane;    // 最後に作成したペインを親とする
268             break;
269         case res::DATABLOCKKIND_PANEEND:
270             pLastPane = pParentPane;    // 親ペインを最後に作成したペインとする
271             pParentPane = pLastPane->GetParent();
272             break;
273         case res::DATABLOCKKIND_GROUP:
274             if (! bReadRootGroup)   // グループのルートに初めて到達
275             {
276                 bReadRootGroup = true;
277                 this->SetGroupContainer(NewObj<GroupContainer>());
278             }
279             else
280             {
281                 if (this->GetGroupContainer() && groupNestLevel == 1)
282                 {
283                     if (Group* pGroup = NewObj<Group>(reinterpret_cast<const res::Group*>(pDataBlockHead), this->GetRootPane()))
284                     {
285                         this->GetGroupContainer()->AppendGroup(pGroup);
286                     }
287                 }
288             }
289             break;
290         case res::DATABLOCKKIND_GROUPBEGIN:
291             groupNestLevel++;
292             break;
293         case res::DATABLOCKKIND_GROUPEND:
294             groupNestLevel--;
295             break;
296         default:
297             break;
298         }
299 
300         dataPtr = static_cast<const u8*>(dataPtr) + pDataBlockHead->size;    // 次のブロック位置へ
301     }
302 
303     return true;
304 }
305 
306 AnimTransform*
CreateAnimTransform()307 Layout::CreateAnimTransform()
308 {
309     NW_NULL_ASSERT(s_pAllocator);
310 
311     AnimTransformBasic *const pAnimTrans = NewObj<AnimTransformBasic>();
312     if (pAnimTrans)
313     {
314         this->GetAnimTransformList().PushBack(pAnimTrans);
315     }
316     return pAnimTrans;
317 }
318 
319 void
DeleteAnimTransform(AnimTransform * pAnimTransform)320 Layout::DeleteAnimTransform(AnimTransform *pAnimTransform)
321 {
322     NW_NULL_ASSERT(pAnimTransform);
323 
324     this->GetAnimTransformList().erase(pAnimTransform);
325     DeleteObj(pAnimTransform);
326 }
327 
328 AnimTransform*
CreateAnimTransform(const void * animResBuf,ResourceAccessor * pResAcsr)329 Layout::CreateAnimTransform(
330     const void* animResBuf,
331     ResourceAccessor* pResAcsr
332 )
333 {
334     return CreateAnimTransform(AnimResource(animResBuf), pResAcsr);
335 }
336 
337 AnimTransform*
CreateAnimTransform(const AnimResource & animRes,ResourceAccessor * pResAcsr)338 Layout::CreateAnimTransform(
339     const AnimResource& animRes,
340     ResourceAccessor* pResAcsr
341 )
342 {
343     const res::AnimationBlock *const pAnimBlock = animRes.GetResourceBlock();
344     if (! pAnimBlock)
345     {
346         return 0;
347     }
348 
349     AnimTransform *const pAnimTrans = CreateAnimTransform();
350     if (pAnimTrans)
351     {
352         pAnimTrans->SetResource(pAnimBlock, pResAcsr);
353     }
354 
355     return pAnimTrans;
356 }
357 
358 void
BindAnimation(AnimTransform * pAnimTrans)359 Layout::BindAnimation(AnimTransform* pAnimTrans)
360 {
361     if (this->GetRootPane())
362     {
363         this->GetRootPane()->BindAnimation(pAnimTrans, true);
364     }
365 }
366 
367 void
UnbindAnimation(AnimTransform * pAnimTrans)368 Layout::UnbindAnimation(AnimTransform* pAnimTrans)
369 {
370     if (this->GetRootPane())
371     {
372         this->GetRootPane()->UnbindAnimation(pAnimTrans, true);
373     }
374 }
375 
376 void
UnbindAllAnimation()377 Layout::UnbindAllAnimation()
378 {
379     UnbindAnimation(0);
380 }
381 
382 bool
BindAnimationAuto(const AnimResource & animRes,ResourceAccessor * pResAcsr)383 Layout::BindAnimationAuto(
384     const AnimResource& animRes,
385     ResourceAccessor* pResAcsr
386 )
387 {
388     if (! this->GetRootPane())
389     {
390         return false;
391     }
392 
393     if (! animRes.GetResourceBlock())
394     {
395         return false;
396     }
397 
398     AnimTransform *const pAnimTrans = CreateAnimTransform();  // AnimTransform オブジェクトの作成
399     if (pAnimTrans == NULL)
400     {
401         NW_WARNING(false, "Create AnimTransform failed.");
402         return false;
403     }
404 
405     bool bResult = true;
406 
407     // 最初に名前によるバインドを一通り行う
408     const u16 bindGroupNum = animRes.GetGroupNum();
409 
410     u16 animNum = 0;
411     if (bindGroupNum == 0)  // バインドする対象を限定していない
412     {
413         animNum = animRes.GetResourceBlock()->animContNum;
414 
415         // バインドするアニメーション数を明示的に指定してリソースをセットします。
416         pAnimTrans->SetResource(animRes.GetResourceBlock(), pResAcsr, animNum);
417 
418         const bool bRecursive = true;
419         this->GetRootPane()->BindAnimation(pAnimTrans, bRecursive, true/* bDisable */);
420     }
421     else    // グループを指定してのバインド
422     {
423         const AnimationGroupRef *const groupRefs = animRes.GetGroupArray();
424         for (int grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx)
425         {
426             // グループにあるペイン全てで必要になるアニメーションの個数を数えます。
427             Group *const pGroup = this->GetGroupContainer()->FindGroupByName(groupRefs[grpIdx].GetName());
428             if (pGroup == NULL)
429             {
430                 NW_WARNING(false, "Group not found: %s", groupRefs[grpIdx].GetName());
431                 bResult = false;
432                 continue;
433             }
434 
435             animNum += animRes.CalcAnimationNum(pGroup, animRes.IsDescendingBind());
436         }
437 
438         // バインドするアニメーション数を明示的に指定してリソースをセットします。
439         pAnimTrans->SetResource(animRes.GetResourceBlock(), pResAcsr, animNum);
440 
441         for (int grpIdx = 0; grpIdx < bindGroupNum; ++grpIdx)
442         {
443             // グループにあるペイン全てで必要になるアニメーションの個数を数えます。
444             Group *const pGroup = this->GetGroupContainer()->FindGroupByName(groupRefs[grpIdx].GetName());
445             if (pGroup == NULL)
446             {
447                 continue;
448             }
449 
450             // アニメーションをバインドします。
451             nw::lyt::BindAnimation(pGroup, pAnimTrans, animRes.IsDescendingBind(), true/* bDisable */);
452         }
453     }
454 
455     const u16 animShareInfoNum = animRes.GetAnimationShareInfoNum();
456     if (animShareInfoNum > 0)   // アニメーション共有によるバインド
457     {
458         const AnimationShareInfo *const animShareInfoAry = animRes.GetAnimationShareInfoArray();
459         NW_NULL_ASSERT(animShareInfoAry);
460 
461         for (int i = 0; i < animShareInfoNum; ++i)
462         {
463             Pane *const pSrcPane = this->GetRootPane()->FindPaneByName(animShareInfoAry[i].GetSrcPaneName());
464             if (pSrcPane == NULL)
465             {
466                 NW_WARNING(false, "Source pane of animation-share is not found: %s", animShareInfoAry[i].GetSrcPaneName());
467                 bResult = false;
468                 continue;
469             }
470 
471             internal::AnimPaneTree animPaneTree(pSrcPane, animRes);
472             if (! animPaneTree.IsEnabled()) // 共有元ペインにアニメーションが無い?
473             {
474                 continue;
475             }
476 
477             Group *const pGroup = this->GetGroupContainer()->FindGroupByName(animShareInfoAry[i].GetTargetGroupName());
478             if (pGroup == NULL)
479             {
480                 NW_WARNING(false, "Target group of animation-share is not found: %s", animShareInfoAry[i].GetTargetGroupName());
481                 bResult = false;
482                 continue;
483             }
484 
485             PaneLinkList& paneList = pGroup->GetPaneList();
486             u32 animIdx = 0;
487             for (PaneLinkList::Iterator it = paneList.GetBeginIter(); it != paneList.GetEndIter(); ++it, ++animIdx)
488             {
489                 if (it->target != pSrcPane)
490                 {
491                     if (bindGroupNum > 0)   // バインド対象の指定があり
492                     {
493                         // 関連グループに含まれてない場合は共有しない
494                         const bool bInclude = IsIncludeAnimationGroupRef(
495                             this->GetGroupContainer(),
496                             animRes.GetGroupArray(),
497                             bindGroupNum,
498                             animRes.IsDescendingBind(),
499                             it->target);
500 
501                         if (! bInclude)
502                         {
503                             continue;
504                         }
505                     }
506 
507                     // srcPaneNameと異なるペインに対してアニメーションをバインド
508                     animPaneTree.Bind(this, it->target, pResAcsr);
509                 }
510             }
511         }
512     }
513 
514     return bResult;
515 }
516 
517 void
SetAnimationEnable(AnimTransform * pAnimTrans,bool bEnable)518 Layout::SetAnimationEnable(
519     AnimTransform* pAnimTrans,
520     bool bEnable
521 )
522 {
523     if (this->GetRootPane())
524     {
525         this->GetRootPane()->SetAnimationEnable(pAnimTrans, bEnable, true);
526     }
527 }
528 
529 void
CalculateMtx(const DrawInfo & drawInfo)530 Layout::CalculateMtx(const DrawInfo& drawInfo)
531 {
532     NW_LYT_STOPWATCH_MEASURE(-610, "nw::lyt::Layout::CalculateMtx");
533 
534     if (! this->GetRootPane())
535     {
536         return;
537     }
538 
539     this->GetRootPane()->CalculateMtx(drawInfo);
540 }
541 
542 #ifdef NW_LYT_DMPGL_ENABLED
543 void
Draw(const DrawInfo & drawInfo)544 Layout::Draw(const DrawInfo& drawInfo)
545 {
546     NW_LYT_STOPWATCH_MEASURE(-630, "nw::lyt::Layout::Draw");
547 
548     if (! this->GetRootPane())
549     {
550         return;
551     }
552 
553     GraphicsResource* graphicsResource = drawInfo.GetGraphicsResource();
554     if (graphicsResource == NULL)
555     {
556         NW_WARNING(false, "GraphicsResource is not set.");
557         return;
558     }
559 
560     graphicsResource->ResetGlState();
561     graphicsResource->ResetGlProgramState();
562 
563     graphicsResource->SetProjectionMtx(drawInfo.GetProjectionMtx());
564 
565     drawInfo.SetLayout(this);
566 
567     this->GetRootPane()->Draw(drawInfo);
568 
569     internal::FinalizeGraphics();
570 
571     drawInfo.SetLayout(0);
572 }
573 #endif
574 
575 void
Animate(u32 option)576 Layout::Animate(u32 option)
577 {
578     NW_LYT_STOPWATCH_MEASURE(-620, "nw::lyt::Layout::Animate");
579 
580     if (! this->GetRootPane())
581     {
582         return;
583     }
584 
585     this->GetRootPane()->Animate(option);
586 }
587 
588 const ut::Rect
GetLayoutRect() const589 Layout::GetLayoutRect() const
590 {
591     return ut::Rect(- m_LayoutSize.width / 2, m_LayoutSize.height / 2, m_LayoutSize.width / 2, - m_LayoutSize.height / 2);
592 }
593 
594 void
SetTagProcessor(font::TagProcessorBase<wchar_t> * pTagProcessor)595 Layout::SetTagProcessor(font::TagProcessorBase<wchar_t>* pTagProcessor)
596 {
597     SetTagProcessorImpl(this->GetRootPane(), pTagProcessor);
598 }
599 
600 Pane*
BuildPaneObj(s32 kind,const void * dataPtr,const ResBlockSet & resBlockSet)601 Layout::BuildPaneObj(
602     s32                 kind,
603     const void*         dataPtr,
604     const ResBlockSet&  resBlockSet
605 )
606 {
607     switch (kind)
608     {
609     case res::DATABLOCKKIND_PANE:
610         {
611             const res::Pane* pResPane = static_cast<const res::Pane*>(dataPtr);
612             return NewObj<Pane>(pResPane);
613         }
614     case res::DATABLOCKKIND_PICTURE:
615         {
616             const res::Picture* pResPic = static_cast<const res::Picture*>(dataPtr);
617             return NewObj<Picture>(pResPic, resBlockSet);
618         }
619     case res::DATABLOCKKIND_TEXTBOX:
620         {
621             const res::TextBox* pBlock = static_cast<const res::TextBox*>(dataPtr);
622             return NewObj<TextBox>(pBlock, resBlockSet);
623         }
624     case res::DATABLOCKKIND_WINDOW:
625         {
626             const res::Window* pBlock = static_cast<const res::Window*>(dataPtr);
627             return NewObj<Window>(pBlock, resBlockSet);
628         }
629     case res::DATABLOCKKIND_BOUNDING:
630         {
631             const res::Bounding* pResBounding = static_cast<const res::Bounding*>(dataPtr);
632             return NewObj<Bounding>(pResBounding, resBlockSet);
633         }
634     default:
635         NW_ASSERTMSG(false, "unknown data type");
636         break;
637     }
638 
639     return 0;
640 }
641 
642 } // namespace nw::lyt
643 } // namespace nw
644