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