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