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