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