1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_Model.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: 26457 $
14 *---------------------------------------------------------------------------*/
15
16 #include "precompiled.h"
17
18 #include <nw/gfx/gfx_Model.h>
19 #include <nw/gfx/gfx_Material.h>
20 #include <nw/gfx/gfx_ISceneVisitor.h>
21 #include <nw/gfx/gfx_AnimHelper.h>
22
23 #include <nw/ut/ut_ResUtil.h>
24 #include <nw/ut/ut_ResDictionary.h>
25 #include <nw/ut/ut_Foreach.h>
26
27 namespace nw
28 {
29 namespace gfx
30 {
31
32 NW_UT_RUNTIME_TYPEINFO_DEFINITION( Model, TransformNode );
33
34 //----------------------------------------
35 Model*
Create(SceneNode * parent,ResSceneObject resource,const Model::Description & description,os::IAllocator * allocator)36 Model::Create(
37 SceneNode* parent,
38 ResSceneObject resource,
39 const Model::Description& description,
40 os::IAllocator* allocator
41 )
42 {
43 NW_NULL_ASSERT(allocator);
44
45 ResModel resNode = ResDynamicCast<ResModel>(resource);
46 NW_ASSERT(resNode.IsValid());
47
48 // ノード生成
49 void* memory = allocator->Alloc(sizeof(Model));
50 NW_NULL_ASSERT(memory);
51
52 Model* node = new(memory) Model(
53 allocator,
54 resNode,
55 description);
56
57 Result result = node->Initialize(allocator);
58
59 NW_ASSERT(result.IsSuccess());
60
61 if (parent)
62 {
63 bool isAttached = parent->AttachChild(node);
64 NW_ASSERT(isAttached);
65 }
66 return node;
67 }
68
69 //----------------------------------------
70 Result
Initialize(os::IAllocator * allocator)71 Model::Initialize(os::IAllocator* allocator)
72 {
73 Result result = INITIALIZE_RESULT_OK;
74
75 result |= TransformNode::Initialize(allocator);
76 NW_ENSURE_AND_RETURN(result);
77
78 result |= CreateResMeshes(allocator);
79 NW_ENSURE_AND_RETURN(result);
80
81 result |= CreateResMeshNodeVisibilities(allocator);
82 NW_ENSURE_AND_RETURN(result);
83
84 result |= CreateMaterials(allocator);
85 NW_ENSURE_AND_RETURN(result);
86
87 result |= CreateCallbacks(allocator);
88 NW_ENSURE_AND_RETURN(result);
89
90 result |= CreateAnimGroups(allocator);
91 NW_ENSURE_AND_RETURN(result);
92
93 result |= CreateMaterialActivator(allocator);
94 NW_ENSURE_AND_RETURN(result);
95
96 return result;
97 }
98
99 //----------------------------------------
100 void
Accept(ISceneVisitor * visitor)101 Model::Accept(
102 ISceneVisitor* visitor
103 )
104 {
105 visitor->VisitModel(this);
106 AcceptChildren(visitor);
107 }
108
109 //----------------------------------------
110 void
BindMaterialAnim(AnimGroup * animGroup)111 Model::BindMaterialAnim(AnimGroup* animGroup)
112 {
113 const int animMemberCount = animGroup->GetMemberCount();
114 for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx)
115 {
116 anim::ResAnimGroupMember resAnimGroupMember =
117 animGroup->GetResAnimGroupMember(memberIdx);
118 const int matIdx = GetMaterialIndex(resAnimGroupMember);
119
120 Material* pMat = GetMaterial(matIdx);
121
122 animGroup->SetTargetObjectIndex(memberIdx, matIdx);
123 animGroup->SetTargetPtr(memberIdx, GetMaterialAnimTargetPtr(pMat, resAnimGroupMember, false));
124
125 void* object = pMat->GetAnimTargetObject(
126 resAnimGroupMember, pMat->GetActiveResource(resAnimGroupMember.GetObjectType()));
127 animGroup->SetTargetObject(memberIdx, object);
128
129 // Bufferがあるときは、OriginalValueを使用できる
130 bool useOriginalValue = pMat->CanUseBuffer(resAnimGroupMember.GetObjectType());
131 if (useOriginalValue)
132 {
133 animGroup->SetOriginalValue(
134 memberIdx, GetMaterialAnimTargetPtr(pMat, resAnimGroupMember, true));
135 }
136 }
137 }
138
139 //----------------------------------------
140 void
BindVisibilityAnim(AnimGroup * animGroup)141 Model::BindVisibilityAnim(AnimGroup* animGroup)
142 {
143 using namespace anim;
144
145 const int animMemberCount = animGroup->GetMemberCount();
146 for (int memberIdx = 0; memberIdx < animMemberCount; ++memberIdx)
147 {
148 anim::ResAnimGroupMember resAnimGroupMember =
149 animGroup->GetResAnimGroupMember(memberIdx);
150
151 switch(resAnimGroupMember.GetObjectType())
152 {
153 case anim::ResAnimGroupMember::OBJECT_TYPE_MODEL:
154 {
155 // モデルのビジビリティ
156 // インスタンス側にバインドする
157 // TODO: BranchVisibleもインスタンス側にバインド
158 u8* target = NULL;
159 if (resAnimGroupMember.GetMemberType() == ResModelMember::MEMBER_TYPE_VISIBLE)
160 {
161 target = reinterpret_cast<u8*>(&m_Visible);
162 }
163 else
164 {
165 target = reinterpret_cast<u8*>(GetResModel().ptr());
166 target += resAnimGroupMember.GetMemberOffset();
167 }
168
169 // TODO: -1は仮。そもそも、intだけではメンバをユニークに特定できなくなっている。
170 // インターフェイス自体を見直す必要がある
171 animGroup->SetTargetObjectIndex(memberIdx, -1);
172 animGroup->SetTargetPtr(memberIdx, target);
173 animGroup->SetOriginalValue(memberIdx, &m_OriginalVisibility);
174 }
175 break;
176 case anim::ResAnimGroupMember::OBJECT_TYPE_MESH:
177 {
178 // メッシュのビジビリティ
179 // こちらはバッファにバインドする
180 ResMeshMember meshMember =
181 ResDynamicCast<ResMeshMember>(resAnimGroupMember);
182 NW_ASSERT(meshMember.IsValid());
183
184 const int meshIndex = meshMember.GetMeshIndex();
185 u8* target = reinterpret_cast<u8*>(GetResMeshes()[meshIndex].ptr());
186 target += resAnimGroupMember.GetMemberOffset();
187
188 animGroup->SetTargetObjectIndex(memberIdx, meshIndex);
189 animGroup->SetTargetPtr(memberIdx, target);
190 animGroup->SetOriginalValue(memberIdx, &m_MeshOriginalVisibilities[meshIndex]);
191 }
192 break;
193 case anim::ResAnimGroupMember::OBJECT_TYPE_MESH_NODE_VISIBILITY:
194 {
195 ResMeshNodeVisibilityMember nodeVisibilityMember =
196 ResDynamicCast<ResMeshNodeVisibilityMember>(resAnimGroupMember);
197 NW_ASSERT(nodeVisibilityMember.IsValid());
198
199 const char* nodeName = nodeVisibilityMember.GetNodeName();
200 const int visibilityIndex = GetResModel().GetMeshNodeVisibilitiesIndex(nodeName);
201 NW_ASSERT(visibilityIndex >= 0);
202
203 u8* target = reinterpret_cast<u8*>(GetResMeshNodeVisibilities(visibilityIndex).ptr());
204 target += resAnimGroupMember.GetMemberOffset();
205
206 animGroup->SetTargetObjectIndex(memberIdx, visibilityIndex);
207 animGroup->SetTargetPtr(memberIdx, target);
208 animGroup->SetOriginalValue(memberIdx, &m_MeshNodeOriginalVisibilities[visibilityIndex]);
209 }
210 break;
211 default:
212 {
213 NW_FATAL_ERROR("Unknown animation member type");
214 }
215 break;
216 }
217
218 void* object = GetAnimTargetObject(resAnimGroupMember);
219 animGroup->SetTargetObject(memberIdx, object);
220 }
221 }
222
223 //----------------------------------------
224 Result
CreateAnimGroups(os::IAllocator * allocator)225 Model::CreateAnimGroups(os::IAllocator* allocator)
226 {
227 Result result = INITIALIZE_RESULT_OK;
228
229 AnimBinding* animBinding = GetAnimBinding();
230 if (animBinding == NULL)
231 {
232 return result;
233 }
234
235 ResModel resModel = GetResModel();
236 NW_ASSERT(resModel.IsValid());
237
238 const int animGroupCount = resModel.GetAnimGroupsCount();
239 for (int animGroupIdx = 0; animGroupIdx < animGroupCount; ++animGroupIdx)
240 {
241 anim::ResAnimGroup resAnimGroup = resModel.GetAnimGroups(animGroupIdx);
242 const int targetType = resAnimGroup.GetTargetType();
243
244 if (targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_MATERIAL ||
245 targetType == anim::ResGraphicsAnimGroup::TARGET_TYPE_VISIBILITY)
246 {
247 AnimGroup* animGroup = AnimGroup::Builder()
248 .ResAnimGroup(resAnimGroup)
249 .SetSceneNode(this)
250 .UseOriginalValue(true)
251 .Create(allocator);
252
253 if (animGroup == NULL)
254 {
255 result |= Result::MASK_FAIL_BIT;
256 }
257
258 NW_ENSURE_AND_RETURN(result);
259
260 // caseが増えるときは、上のifの条件も修正すること
261 switch (targetType)
262 {
263 case anim::ResGraphicsAnimGroup::TARGET_TYPE_MATERIAL:
264 {
265 BindMaterialAnim(animGroup);
266
267 animBinding->SetAnimGroup(animGroupIdx, animGroup);
268 m_MaterialAnimGroup = animGroup;
269 m_MaterialAnimBindingIndex = animGroupIdx;
270 }
271 break;
272
273 case anim::ResGraphicsAnimGroup::TARGET_TYPE_VISIBILITY:
274 {
275 BindVisibilityAnim(animGroup);
276
277 animBinding->SetAnimGroup(animGroupIdx, animGroup);
278 m_VisibilityAnimGroup = animGroup;
279 m_VisibilityAnimBindingIndex = animGroupIdx;
280 }
281 break;
282
283 default:
284 NW_ASSERT(false);
285 }
286 }
287 }
288
289 return result;
290 }
291
292 //----------------------------------------
293 Result
CreateMaterials(os::IAllocator * allocator)294 Model::CreateMaterials(os::IAllocator* allocator)
295 {
296 Result result = INITIALIZE_RESULT_OK;
297
298 if (m_SharingMaterial)
299 {
300 NW_ASSERTMSG(m_BufferOption == 0, "In the case of using a shared material, it can not use buffer option.");
301 m_BufferOption = m_Description.sharedMaterialModel->GetBufferOption();
302
303 int materialCount = m_Description.sharedMaterialModel->GetMaterialCount();
304
305 NW_ASSERT(materialCount != 0);
306
307 void* memory = allocator->Alloc(sizeof(Material*) * materialCount);
308
309 if (memory == NULL)
310 {
311 result |= Result::MASK_FAIL_BIT;
312 }
313 NW_ENSURE_AND_RETURN(result);
314
315 m_Materials = gfx::MaterialArray(memory, materialCount, allocator);
316
317 for (int i=0; i < materialCount ; i++)
318 {
319 m_Materials.push_back(m_Description.sharedMaterialModel->GetMaterial(i));
320 }
321 }
322 else
323 {
324 ResModel resModel = this->GetResModel();
325 NW_ASSERT(resModel.IsValid());
326
327 int materialCount = resModel.GetMaterialsCount();
328
329 if (materialCount != 0)
330 {
331 void* memory = allocator->Alloc(sizeof(Material*) * materialCount);
332
333 if (memory == NULL)
334 {
335 result |= Result::MASK_FAIL_BIT;
336 }
337 NW_ENSURE_AND_RETURN(result);
338
339 m_Materials = gfx::MaterialArray(memory, materialCount, allocator);
340 s32 bufferCount = (m_BufferOption == 0x0) ? 0 : 1;
341
342 for (int i=0; i < materialCount ; i++)
343 {
344 Material* material = Material::Create(
345 resModel.GetMaterials(i),
346 bufferCount,
347 this,
348 allocator);
349
350 if (material == NULL)
351 {
352 result |= Result::MASK_FAIL_BIT;
353 }
354
355 NW_ENSURE_AND_RETURN(result);
356
357 m_Materials.push_back(material);
358 }
359 }
360 }
361
362 return result;
363 }
364
365 //----------------------------------------
CreateResMeshes(os::IAllocator * allocator)366 Result Model::CreateResMeshes(os::IAllocator* allocator)
367 {
368 Result result = INITIALIZE_RESULT_OK;
369
370 ResModel resModel = this->GetResModel();
371 NW_ASSERT(resModel.IsValid());
372
373 s32 meshesCount = resModel.GetMeshesCount();
374
375 if (meshesCount != 0)
376 {
377 ut::Offset* meshOffsets = NULL;
378 meshOffsets = allocator->AllocAndFill<ut::Offset>(meshesCount, ut::Offset());
379 ResMeshData* buffer = allocator->Alloc<ResMeshData>(meshesCount);
380
381 if (meshOffsets == NULL || buffer == NULL)
382 {
383 if (meshOffsets != NULL)
384 {
385 allocator->Free(meshOffsets);
386 }
387
388 if (buffer != NULL)
389 {
390 allocator->Free(buffer);
391 }
392
393 result |= Result::MASK_FAIL_BIT;
394 }
395
396 NW_ENSURE_AND_RETURN(result);
397
398 for (int i = 0; i < meshesCount; ++i)
399 {
400 buffer[i] = *(resModel.GetMeshes(i).ptr());
401 buffer[i].toOwnerModel.set_ptr(GetResModel().ptr());
402 meshOffsets[i].set_ptr(&buffer[i]);
403 }
404 m_MeshBuffers = ResMeshArray(meshOffsets, meshesCount);
405
406 m_OriginalVisibility = resModel.IsVisible();
407
408 void* memory = allocator->Alloc(sizeof(bool) * meshesCount);
409
410 if (memory == NULL)
411 {
412 result |= Result::MASK_FAIL_BIT;
413 }
414 NW_ENSURE_AND_RETURN(result);
415
416 m_MeshOriginalVisibilities.Reset(memory, meshesCount, allocator);
417
418 for (int i = 0; i < meshesCount; ++i)
419 {
420 m_MeshOriginalVisibilities.push_back(m_MeshBuffers[i].IsVisible());
421 }
422 }
423
424 return result;
425 }
426
427 //----------------------------------------
CreateResMeshNodeVisibilities(os::IAllocator * allocator)428 Result Model::CreateResMeshNodeVisibilities(os::IAllocator* allocator)
429 {
430 Result result = INITIALIZE_RESULT_OK;
431
432 ResModel resModel = this->GetResModel();
433 NW_ASSERT(resModel.IsValid());
434
435 s32 visibilitiesConut = resModel.GetMeshNodeVisibilitiesCount();
436
437 if (visibilitiesConut != 0)
438 {
439 ResMeshNodeVisibilityData* buffer = allocator->Alloc<ResMeshNodeVisibilityData>(visibilitiesConut);
440
441 if (buffer == NULL)
442 {
443 result |= Result::MASK_FAIL_BIT;
444 }
445
446 NW_ENSURE_AND_RETURN(result);
447
448 m_MeshNodeVisibilityBuffers.Reset(buffer, visibilitiesConut, allocator);
449
450 for (int i = 0; i < visibilitiesConut; ++i)
451 {
452 m_MeshNodeVisibilityBuffers.push_back(*(resModel.GetMeshNodeVisibilities(i).ptr()));
453 }
454
455 void* memory = allocator->Alloc(sizeof(bool) * visibilitiesConut);
456
457 if (memory == NULL)
458 {
459 result |= Result::MASK_FAIL_BIT;
460 }
461 NW_ENSURE_AND_RETURN(result);
462
463 m_MeshNodeOriginalVisibilities.Reset(memory, visibilitiesConut, allocator);
464
465 for (int i = 0; i < visibilitiesConut; ++i)
466 {
467 m_MeshNodeOriginalVisibilities.push_back(resModel.GetMeshNodeVisibilities(i).IsVisible());
468 }
469 }
470
471 return result;
472 }
473
474 //----------------------------------------
DestroyResMeshes(os::IAllocator * allocator,ResMeshArray resMeshes)475 void Model::DestroyResMeshes(os::IAllocator* allocator, ResMeshArray resMeshes)
476 {
477 NW_NULL_ASSERT(allocator);
478
479 if (resMeshes.empty()) { return; }
480
481 // 全てのデータのメモリを解放する。
482 allocator->Free((*resMeshes.begin()).ptr());
483 // データのオフセット情報のメモリを解放する。
484 allocator->Free(static_cast<ResMeshArray::pointer>(resMeshes));
485 }
486
487 //----------------------------------------
488 Result
CreateCallbacks(os::IAllocator * allocator)489 Model::CreateCallbacks(os::IAllocator* allocator)
490 {
491 Result result = INITIALIZE_RESULT_OK;
492
493 if (m_Description.isFixedSizeMemory)
494 {
495 if (m_Description.maxCallbacks == 0)
496 {
497 m_PreRenderSignal = RenderSignal::CreateInvalidateSignal(allocator);
498 m_PostRenderSignal = RenderSignal::CreateInvalidateSignal(allocator);
499 }
500 else
501 {
502 m_PreRenderSignal = RenderSignal::CreateFixedSizedSignal(m_Description.maxCallbacks, allocator);
503 m_PostRenderSignal = RenderSignal::CreateFixedSizedSignal(m_Description.maxCallbacks, allocator);
504 }
505 }
506 else
507 {
508 m_PreRenderSignal = RenderSignal::CreateVariableSizeSignal(allocator);
509 m_PostRenderSignal = RenderSignal::CreateVariableSizeSignal(allocator);
510 }
511
512 // 動的配列のメモリ確保に失敗した場合
513 if (m_PreRenderSignal == NULL || m_PostRenderSignal == NULL)
514 {
515 result |= Result::MASK_FAIL_BIT;
516 }
517
518 return result;
519 }
520
521 //----------------------------------------
522 Result
CreateMaterialActivator(os::IAllocator * allocator)523 Model::CreateMaterialActivator(os::IAllocator* allocator)
524 {
525 Result result = INITIALIZE_RESULT_OK;
526
527 if (!m_SharingMaterial)
528 {
529 // TODO: Builder でアクティベータを渡せるようにする。
530 // バッファオプションに応じてアクティベータを変更します。
531 // 0x0 か FLAG_BUFFER_SCENE_ENVIRONMENT のみ指定された場合は SimpleMaterialActivator を利用します。
532 IMaterialActivator* activator =
533 (m_Description.bufferOption == 0 || m_Description.bufferOption == FLAG_BUFFER_SCENE_ENVIRONMENT) ?
534 SimpleMaterialActivator::Create(allocator) : MaterialActivator::Create(allocator);
535
536 if (activator != NULL)
537 {
538 m_MaterialActivator.Reset(activator);
539 }
540 else
541 {
542 result |= Result::MASK_FAIL_BIT;
543 }
544 }
545
546 return result;
547 }
548
549 //----------------------------------------
550 void*
GetAnimTargetObject(const anim::ResAnimGroupMember & anim)551 Model::GetAnimTargetObject(const anim::ResAnimGroupMember& anim)
552 {
553 switch(anim.GetObjectType())
554 {
555 case anim::ResAnimGroupMember::OBJECT_TYPE_MODEL:
556 {
557 return this;
558 }
559
560 case anim::ResAnimGroupMember::OBJECT_TYPE_MESH:
561 {
562 anim::ResMeshMember member = ResStaticCast<anim::ResMeshMember>(anim);
563 int meshIndex = member.GetMeshIndex();
564
565 ResMeshData* ptr = GetResMeshes()[meshIndex].ptr();
566 return ptr;
567 }
568
569 case anim::ResAnimGroupMember::OBJECT_TYPE_MESH_NODE_VISIBILITY:
570 {
571 anim::ResMeshNodeVisibilityMember member = ResStaticCast<anim::ResMeshNodeVisibilityMember>(anim);
572 const char* nodeName = member.GetNodeName();
573
574 ResMeshNodeVisibilityData* ptr = GetResModel().GetMeshNodeVisibilities(nodeName).ptr();
575 return ptr;
576 }
577
578 default:
579 NW_ASSERT(false);
580 return NULL;
581 }
582 }
583
584 //----------------------------------------
585 void*
GetMaterialAnimTargetPtr(Material * material,const anim::ResAnimGroupMember & anim,bool isOriginalValue)586 Model::GetMaterialAnimTargetPtr(Material* material, const anim::ResAnimGroupMember& anim, bool isOriginalValue)
587 {
588 ResMaterial resMat = isOriginalValue ?
589 material->GetOriginal() :
590 material->GetActiveResource(anim.GetObjectType());
591
592 void* object = material->GetAnimTargetObject(anim, resMat);
593
594 // TextureSamplerのみ、TargetObjectがTextureMapperになっている
595 // TargetPtrはTextureSampler内のメンバを指す必要があるので、変換する
596 if (anim.GetObjectType() == anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_SAMPLER)
597 {
598 ResPixelBasedTextureMapper mapper(object);
599 object = mapper.ref().toSampler.to_ptr();
600 }
601
602 return ut::AddOffsetToPtr(object, anim.GetMemberOffset());
603 }
604
605 //----------------------------------------
606 int
GetMaterialIndex(const anim::ResAnimGroupMember & anim) const607 Model::GetMaterialIndex(const anim::ResAnimGroupMember& anim) const
608 {
609 // TODO: GetMaterialName()をResMaterialMemberにくくり出し、分岐を不要にする
610 const char* matName = NULL;
611
612 switch (anim.GetObjectType())
613 {
614 case anim::ResAnimGroupMember::OBJECT_TYPE_MATERIAL_COLOR:
615 {
616 anim::ResMaterialColorMember member = ResStaticCast<anim::ResMaterialColorMember>(anim);
617 matName = member.GetMaterialName();
618 break;
619 }
620
621 case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_SAMPLER:
622 {
623 anim::ResTextureSamplerMember member = ResStaticCast<anim::ResTextureSamplerMember>(anim);
624 matName = member.GetMaterialName();
625 break;
626 }
627
628 case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_MAPPER:
629 {
630 anim::ResTextureMapperMember member = ResStaticCast<anim::ResTextureMapperMember>(anim);
631 matName = member.GetMaterialName();
632 break;
633 }
634
635 case anim::ResAnimGroupMember::OBJECT_TYPE_BLEND_OPERATION:
636 {
637 anim::ResBlendOperationMember member = ResStaticCast<anim::ResBlendOperationMember>(anim);
638 matName = member.GetMaterialName();
639 break;
640 }
641
642 case anim::ResAnimGroupMember::OBJECT_TYPE_TEXTURE_COORDINATOR:
643 {
644 anim::ResTextureCoordinatorMember member = ResStaticCast<anim::ResTextureCoordinatorMember>(anim);
645 matName = member.GetMaterialName();
646 break;
647 }
648
649 default:
650 NW_ASSERT(false);
651 return 0;
652 }
653
654 int matIdx = GetResModel().GetMaterialsIndex(matName);
655 NW_ASSERT(matIdx != -1);
656
657 return matIdx;
658 }
659
660 //----------------------------------------
661 void
InvalidateRenderKeyCache()662 Model::InvalidateRenderKeyCache()
663 {
664 ResMeshArray::iterator end = this->m_MeshBuffers.end();
665 for (ResMeshArray::iterator mesh = this->m_MeshBuffers.begin(); mesh != end; ++mesh)
666 {
667 (*mesh).SetFlags(
668 ut::DisableFlag<u32, u32>((*mesh).GetFlags(), ResMesh::FLAG_VALID_RENDER_KEY_CACHE));
669 }
670 }
671
672 } // namespace gfx
673 } // namespace nw
674