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