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