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