1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_ResMaterial.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: $
16 *---------------------------------------------------------------------------*/
17
18 #include "../precompiled.h"
19
20 #include <nw/gfx/res/gfx_ResUtil.h>
21 #include <nw/gfx/res/gfx_ResMaterial.h>
22 #include <nw/gfx/res/gfx_ResGraphicsFile.h>
23
24 namespace nw {
25 namespace gfx {
26 namespace res {
27
28 static Result
29 SetupTextureCoordinators(ResMaterial resMaterial);
30
31 //----------------------------------------------------------------------------
32 Result
Setup(os::IAllocator * allocator,ResGraphicsFile graphicsFile)33 ResMaterial::Setup(os::IAllocator* allocator, ResGraphicsFile graphicsFile)
34 {
35 NW_ASSERT( internal::ResCheckRevision( graphicsFile ) );
36 NW_ASSERT( internal::ResCheckRevision( *this ) );
37
38 NW_ASSERT( this->IsValid() );
39
40 Result result = RESOURCE_RESULT_OK;
41
42 // Texture をセットアップします。
43 if (!ut::CheckFlag(this->GetFlags(), ResMaterialData::FLAG_HAS_BEEN_SETUP_TEXTURE))
44 {
45 result |= this->SetupTextures(allocator, *this, graphicsFile);
46 }
47
48 // Shader をセットアップします。
49 if (!ut::CheckFlag(this->GetFlags(), ResMaterialData::FLAG_HAS_BEEN_SETUP_SHADER))
50 {
51 result |= this->SetupShader(allocator, this->GetShader(), graphicsFile);
52 }
53
54 if (!ut::CheckFlag(this->GetFlags(), ResMaterialData::FLAG_HAS_BEEN_SETUP_FRAGMENTSHADER))
55 {
56 // FragmentShader をセットアップします。
57 ResFragmentShader resFragmentShader = this->GetFragmentShader();
58 NW_ASSERT(resFragmentShader.GetFragmentLightingTable().IsValid());
59 Result resultFragmentShader = resFragmentShader.Setup(allocator, graphicsFile);
60 result |= resultFragmentShader;
61
62 // 必ず参照解決が完了している状態で呼び出す必要があります。
63 if (resultFragmentShader.IsSuccess())
64 {
65 this->EnableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_FRAGMENTSHADER);
66
67 // フラグメントライティングテーブルのハッシュを計算します。
68 this->CalcFragmentLightingTableHash();
69 }
70 }
71
72 if (result.IsSuccess())
73 {
74 this->EnableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP);
75 }
76
77 return result;
78 }
79
80 //----------------------------------------------------------------------------
81 void
Cleanup()82 ResMaterial::Cleanup()
83 {
84 this->DisableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP);
85
86 this->DisableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_FRAGMENTSHADER);
87 ut::SafeCleanup(this->GetFragmentShader());
88
89 this->DisableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_SHADER);
90
91 // NOTE: ResBinaryShader がマテリアルに直に入っていることは無くなったので、
92 // ResReferenceShader 用の Cleanup 処理のみをおこなう。
93 ResReferenceShader resShader = ResDynamicCast<ResReferenceShader>(this->GetShader());
94 if (resShader.IsValid())
95 {
96 resShader.ref().toTargetShader.set_ptr(NULL);
97 }
98
99 this->DisableFlags((ResMaterialData::FLAG_HAS_BEEN_SETUP_TEXTURE));
100 int textureMapperNum = this->GetTextureMappersCount();
101 for ( int i = 0; i < textureMapperNum; ++i )
102 {
103 ResPixelBasedTextureMapper textureMapper = this->GetTextureMappers( i );
104 if (textureMapper.IsValid())
105 {
106 textureMapper.Cleanup();
107 }
108 }
109
110 if (this->GetProceduralTextureMapper().IsValid())
111 {
112 this->GetProceduralTextureMapper().Cleanup();
113 }
114 }
115
116 //----------------------------------------
117 Result
SetupTextures(os::IAllocator * allocator,ResMaterial resMaterial,ResGraphicsFile graphicsFile)118 ResMaterial::SetupTextures(os::IAllocator* allocator, ResMaterial resMaterial, ResGraphicsFile graphicsFile)
119 {
120 Result result = RESOURCE_RESULT_OK;
121
122 // TextureCoordinator をセットアップします。
123 result |= SetupTextureCoordinators(resMaterial);
124
125 s32 textureMapperNum = resMaterial.GetTextureMappersCount();
126 u32 textureSamplersHash = resMaterial.GetTextureSamplersHash();
127 // TextureMapper をセットアップします。
128 for ( int i = 0; i < textureMapperNum; ++i )
129 {
130 ResPixelBasedTextureMapper resTextureMapper = resMaterial.GetTextureMappers( i );
131 if (!resTextureMapper.IsValid())
132 {
133 continue;
134 }
135
136 if (!resTextureMapper.GetTexture().IsValid())
137 {
138 continue;
139 }
140
141 result |= resTextureMapper.Setup(allocator, graphicsFile);
142
143 // テクスチャコーディネータを参照してテクスチャサンプラーのコマンドを設定する
144 enum
145 {
146 SAMPLER_TYPE_SHIFT = 28,
147 SAMPLER_TYPE_MASK = 0x7 << SAMPLER_TYPE_SHIFT,
148 SAMPLER_TYPE_2D = 0,
149 SAMPLER_TYPE_CUBE_MAP = 1,
150 SAMPLER_TYPE_SHADOW_2D = 2,
151 SAMPLER_TYPE_PROJECTION = 3,
152 SAMPLER_TYPE_SHADOW_CUBE = 4
153 };
154 u32* command = resTextureMapper.GetCommandCache();
155 NW_NULL_ASSERT(command);
156
157 command[5] &= ~(SAMPLER_TYPE_MASK); // sampler_type
158 // テクスチャ0のみ複数のサンプラータイプが設定できる。
159 if ( i == 0 )
160 {
161 switch (resMaterial.GetTextureCoordinators(0).GetMappingMethod())
162 {
163 case ResTextureCoordinator::MAPPINGMETHOD_UV_COORDINATE:
164 {
165 command[5] |= SAMPLER_TYPE_2D << SAMPLER_TYPE_SHIFT;
166 }
167 break;
168 case ResTextureCoordinator::MAPPINGMETHOD_PROJECTION:
169 {
170 command[5] |= SAMPLER_TYPE_PROJECTION << SAMPLER_TYPE_SHIFT;
171 }
172 break;
173 case ResTextureCoordinator::MAPPINGMETHOD_CAMERA_CUBE_ENV:
174 {
175 command[5] |= SAMPLER_TYPE_CUBE_MAP << SAMPLER_TYPE_SHIFT;
176 }
177 break;
178 case ResTextureCoordinator::MAPPINGMETHOD_CAMERA_SPHERE_ENV:
179 {
180 command[5] |= SAMPLER_TYPE_2D << SAMPLER_TYPE_SHIFT;
181 }
182 break;
183 case ResTextureCoordinator::MAPPINGMETHOD_SHADOW:
184 {
185 command[5] |= SAMPLER_TYPE_SHADOW_2D << SAMPLER_TYPE_SHIFT;
186 }
187 break;
188 case ResTextureCoordinator::MAPPINGMETHOD_SHADOW_CUBE:
189 {
190 command[5] |= SAMPLER_TYPE_SHADOW_CUBE << SAMPLER_TYPE_SHIFT;
191 }
192 break;
193 default:
194 NW_FATAL_ERROR("Unsupported mapping method.");
195 break;
196 }
197 }
198
199 if (result.IsSuccess())
200 {
201 // カメラキューブ座標のマッピング方法でキューブテクスチャでなければ警告を出します。
202 if (resMaterial.GetTextureCoordinators(0).GetMappingMethod() == ResTextureCoordinator::MAPPINGMETHOD_CAMERA_CUBE_ENV &&
203 resTextureMapper.GetTexture().Dereference().GetTypeInfo() != ResCubeTexture::TYPE_INFO)
204 {
205 result |= RESOURCE_RESULT_IRRELEVANT_TEXTURE_MAPPING_METHOD;
206 }
207
208 ResTexture resImageTexture = resTextureMapper.GetTexture().Dereference();
209 u32 hash = 0;
210 if (i == 0)
211 {
212 hash = (u32)resImageTexture.ptr();
213 }
214 else
215 {
216 hash = ((u32)resImageTexture.ptr() << i * 8) | ((u32)resImageTexture.ptr() >> (32 - i * 8));
217 }
218
219 textureSamplersHash ^= hash;
220 }
221 }
222
223 resMaterial.SetTextureMappersHash(textureSamplersHash);
224
225 if (resMaterial.GetProceduralTextureMapper().IsValid())
226 {
227 result |= resMaterial.GetProceduralTextureMapper().Setup(allocator, graphicsFile);
228 }
229
230 if (result.IsSuccess())
231 {
232 this->EnableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_TEXTURE);
233 }
234
235 return result;
236 }
237
SetupTextureCoordinators(ResMaterial resMaterial)238 static Result SetupTextureCoordinators(ResMaterial resMaterial)
239 {
240 Result result = RESOURCE_RESULT_OK;
241
242 // TextureMapper と TextureCoordinator の組み合わせを確認します。
243 s32 textureCoordinatorNum = resMaterial.GetTextureCoordinatorsCount();
244 ResMaterial::TextureCoordinateConfig config = resMaterial.GetTextureCoordinateConfig();
245 bool isProceduralTextureEnabled = resMaterial.GetProceduralTextureMapper().IsValid();
246 for ( int i = 0; i < textureCoordinatorNum; ++i )
247 {
248 ResTextureCoordinator resTextureCoordinator = resMaterial.GetTextureCoordinators(i);
249 bool isTextureMapperEnabled = resMaterial.GetTextureMappers(i).IsValid();
250 bool isTextureCoordinatorEnabled = false;
251
252 switch(i)
253 {
254 case 0:
255 {
256 if (isTextureMapperEnabled)
257 {
258 isTextureCoordinatorEnabled = true;
259 }
260 else if (config == ResMaterial::CONFIG_0120 || config == ResMaterial::CONFIG_0110)
261 {
262 if (isProceduralTextureEnabled)
263 {
264 isTextureCoordinatorEnabled = true;
265 }
266 }
267 }
268 break;
269 case 1:
270 {
271 if (isTextureMapperEnabled)
272 {
273 isTextureCoordinatorEnabled = true;
274 }
275 else
276 {
277 if (config == ResMaterial::CONFIG_0111)
278 {
279 if (resMaterial.GetTextureMappers(2).IsValid() || isProceduralTextureEnabled)
280 {
281 isTextureCoordinatorEnabled = true;
282 }
283 }
284 else if (config == ResMaterial::CONFIG_0110 || config == ResMaterial::CONFIG_0112)
285 {
286 if (resMaterial.GetTextureMappers(2).IsValid())
287 {
288 isTextureCoordinatorEnabled = true;
289 }
290 }
291 else if (config == ResMaterial::CONFIG_0121)
292 {
293 if (isProceduralTextureEnabled)
294 {
295 isTextureCoordinatorEnabled = true;
296 }
297 }
298 }
299 }
300 break;
301 case 2:
302 {
303 if (config == ResMaterial::CONFIG_0122)
304 {
305 if (isTextureMapperEnabled || isProceduralTextureEnabled)
306 {
307 isTextureCoordinatorEnabled = true;
308 }
309 }
310 else if (config == ResMaterial::CONFIG_0120 || config == ResMaterial::CONFIG_0121)
311 {
312 if (isTextureMapperEnabled)
313 {
314 isTextureCoordinatorEnabled = true;
315 }
316 }
317 else if (config == ResMaterial::CONFIG_0112)
318 {
319 if (isProceduralTextureEnabled)
320 {
321 isTextureCoordinatorEnabled = true;
322 }
323 }
324 }
325 break;
326 default:
327 {
328 NW_FATAL_ERROR("Invalid coordinator number.");
329 }
330 }
331
332 resTextureCoordinator.SetEnabled(isTextureCoordinatorEnabled);
333 }
334 return result;
335 }
336
337 //----------------------------------------
338 Result
SetupShader(os::IAllocator * allocator,ResShader resShader,ResGraphicsFile graphicsFile)339 ResMaterial::SetupShader(
340 os::IAllocator* allocator,
341 ResShader resShader,
342 ResGraphicsFile graphicsFile
343 )
344 {
345 Result result = RESOURCE_RESULT_OK;
346 ResShader setupShader;
347
348 if (resShader.IsValid())
349 {
350 bool existShader = false;
351 ResReferenceShader refer = ResDynamicCast<ResReferenceShader>(resShader);
352 if (refer.IsValid())
353 {
354 if (refer.GetTargetShader().IsValid())
355 {
356 setupShader = refer.GetTargetShader();
357 existShader = true;
358 }
359 else
360 {
361 ::std::pair<ResShader, bool> referenceResult;
362 referenceResult = GetReferenceShaderTarget(refer, graphicsFile);
363 if (referenceResult.second)
364 {
365 setupShader = referenceResult.first;
366 existShader = true;
367 }
368 else
369 {
370 result |= Result::MASK_FAIL_BIT;
371 result |= RESOURCE_RESULT_NOT_FOUND_SHADER;
372 }
373 }
374 }
375 else
376 {
377 setupShader = resShader;
378 existShader = true;
379 }
380
381 if (existShader)
382 {
383 result |= setupShader.Setup(allocator, graphicsFile);
384 }
385 }
386
387 bool isSetup = resShader.IsValid() && result.IsSuccess();
388 if (isSetup)
389 {
390 this->EnableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_SHADER);
391
392 // ユーザーシェーダーユニフォームのインデックス番号をキャッシュします。
393 ResBinaryShader resBinaryShader = resShader.Dereference();
394 NW_MINMAXLT_ASSERT(this->GetShaderProgramDescriptionIndex(), 0, resBinaryShader.GetDescriptionsCount());
395
396 ResShaderProgramDescription description =
397 resBinaryShader.GetDescriptions(this->GetShaderProgramDescriptionIndex());
398 this->CacheUserUniformIndex(description.GetSymbols());
399 }
400
401 return result;
402 }
403
404 //----------------------------------------
405 void
SetShader(ResShader resShader)406 ResMaterial::SetShader(ResShader resShader)
407 {
408 NW_ASSERT(resShader.IsValid());
409 ResReferenceShader referenceShader = ResStaticCast<ResReferenceShader>(this->GetShader());
410 referenceShader.ref().toTargetShader.set_ptr(resShader.Dereference().ptr());
411 }
412
413 //----------------------------------------
414 void
CacheUserUniformIndex(ResShaderSymbolArray symbols)415 ResMaterial::CacheUserUniformIndex(ResShaderSymbolArray symbols)
416 {
417 for (int parameterIndex = 0; parameterIndex < this->GetShaderParametersCount(); ++parameterIndex)
418 {
419 ResShaderParameter parameter = this->GetShaderParameters(parameterIndex);
420 if (parameter.IsValid())
421 {
422 const char* name = parameter.GetName();
423
424 if (name == NULL) { continue; }
425
426 for (int symbolIndex = 0; symbolIndex < symbols.size(); ++symbolIndex)
427 {
428 ResShaderSymbol shaderSymbol = symbols[symbolIndex];
429 if (::std::strcmp(name, shaderSymbol.GetName()) == 0)
430 {
431 // ユーザーユニフォームのインデックスをキャッシュします。
432 parameter.SetSymbolIndex(symbolIndex);
433 break;
434 }
435 }
436 }
437 }
438 }
439
440 //----------------------------------------
441 void
CalcFragmentLightingTableHash()442 ResMaterial::CalcFragmentLightingTableHash()
443 {
444 enum
445 {
446 D0_SHIFT = 0,
447 D1_SHIFT = 4,
448 RR_SHIFT = 8,
449 RG_SHIFT = 12,
450 RB_SHIFT = 16,
451 FR_SHIFT = 20,
452 MAX_BITS = 32
453 };
454
455 ResFragmentShader resFragmentShader = this->GetFragmentShader();
456 ResFragmentLightingTable resFragmentLightingTable = resFragmentShader.GetFragmentLightingTable();
457 NW_ASSERT(resFragmentLightingTable.IsValid());
458
459 // FragmentLightingTableのハッシュ値を再計算します。
460 u32 fragmentLightingTableParametersHash = this->GetFragmentLightingTableParametersHash();
461
462 if (resFragmentLightingTable.GetDistribution0Sampler().IsValid())
463 {
464 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetDistribution0Sampler().GetSampler());
465 if (refLut.IsValid())
466 {
467 ResImageLookupTable imageLut = refLut.Dereference();
468 fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr()) << D0_SHIFT;
469 }
470 }
471
472 if (resFragmentLightingTable.GetDistribution1Sampler().IsValid())
473 {
474 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetDistribution1Sampler().GetSampler());
475 if (refLut.IsValid())
476 {
477 ResImageLookupTable imageLut = refLut.Dereference();
478 fragmentLightingTableParametersHash ^=
479 ((u32)imageLut.ptr() << D1_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - D1_SHIFT));
480 }
481 }
482
483 if (resFragmentLightingTable.GetReflectanceRSampler().IsValid())
484 {
485 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetReflectanceRSampler().GetSampler());
486 if (refLut.IsValid())
487 {
488 ResImageLookupTable imageLut = refLut.Dereference();
489 fragmentLightingTableParametersHash ^=
490 ((u32)imageLut.ptr() << RR_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - RR_SHIFT));
491 }
492 }
493
494 if (resFragmentLightingTable.GetReflectanceGSampler().IsValid())
495 {
496 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetReflectanceGSampler().GetSampler());
497 if (refLut.IsValid())
498 {
499 ResImageLookupTable imageLut = refLut.Dereference();
500 fragmentLightingTableParametersHash ^=
501 ((u32)imageLut.ptr() << RG_SHIFT) |
502 ((u32)imageLut.ptr() >> (MAX_BITS - RG_SHIFT));
503 }
504 }
505
506 if (resFragmentLightingTable.GetReflectanceBSampler().IsValid())
507 {
508 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetReflectanceBSampler().GetSampler());
509 if (refLut.IsValid())
510 {
511 ResImageLookupTable imageLut = refLut.Dereference();
512 fragmentLightingTableParametersHash ^=
513 ((u32)imageLut.ptr() << RB_SHIFT) |
514 ((u32)imageLut.ptr() >> (MAX_BITS - RB_SHIFT));
515 }
516 }
517
518 if (resFragmentLightingTable.GetFresnelSampler().IsValid())
519 {
520 ResReferenceLookupTable refLut = ResDynamicCast<ResReferenceLookupTable>(resFragmentLightingTable.GetFresnelSampler().GetSampler());
521 if (refLut.IsValid())
522 {
523 ResImageLookupTable imageLut = refLut.Dereference();
524 fragmentLightingTableParametersHash ^=
525 ((u32)imageLut.ptr() << FR_SHIFT) |
526 ((u32)imageLut.ptr() >> (MAX_BITS - FR_SHIFT));
527 }
528 }
529
530 this->SetFragmentLightingTableHash(fragmentLightingTableParametersHash);
531 }
532
533 //----------------------------------------
534 void
ForceSetupTexture(const char * targetName,ResTexture texture)535 ResMaterial::ForceSetupTexture(const char* targetName, ResTexture texture)
536 {
537 NW_NULL_ASSERT(targetName);
538 const int textureMapperNum = GetTextureMappersCount();
539 u32 textureSamplersHash = GetTextureSamplersHash();
540
541 for (int idx = 0; idx < textureMapperNum; ++idx)
542 {
543 ResPixelBasedTextureMapper texMap = GetTextureMappers(idx);
544 if (texMap.IsValid())
545 {
546 ResReferenceTexture target = ResDynamicCast<ResReferenceTexture>(texMap.GetTexture());
547
548 if (target.IsValid())
549 {
550 const char* name = target.GetPath();
551 if (std::strcmp(name, targetName) == 0)
552 {
553 texMap.ForceSetupTexture(texture);
554 }
555 }
556
557 ResTexture imageTexture = texMap.GetTexture().Dereference();
558 u32 hash = 0;
559 if (idx == 0)
560 {
561 hash = (u32)imageTexture.ptr();
562 }
563 else
564 {
565 hash = ((u32)imageTexture.ptr() << idx * 8) | ((u32)imageTexture.ptr() >> (32 - idx * 8));
566 }
567
568 textureSamplersHash ^= hash;
569 }
570 }
571
572 SetTextureMappersHash(textureSamplersHash);
573 }
574
575 //----------------------------------------
576 void
ForceSetupShader(const char * targetName,ResShader shader)577 ResMaterial::ForceSetupShader(const char* targetName, ResShader shader)
578 {
579 NW_ASSERT(shader.IsValid());
580 NW_NULL_ASSERT(targetName);
581
582 ResReferenceShader referenceShader = ResDynamicCast<ResReferenceShader>(this->GetShader());
583 if (referenceShader.IsValid())
584 {
585 if (std::strcmp(referenceShader.GetPath(), targetName) == 0)
586 {
587 this->SetShader(shader);
588 this->EnableFlags(ResMaterialData::FLAG_HAS_BEEN_SETUP_SHADER);
589
590 // ユーザーシェーダーユニフォームのインデックス番号をキャッシュします。
591 ResBinaryShader resBinaryShader = shader.Dereference();
592 NW_MINMAXLT_ASSERT(this->GetShaderProgramDescriptionIndex(), 0, resBinaryShader.GetDescriptionsCount());
593
594 ResShaderProgramDescription description =
595 resBinaryShader.GetDescriptions(this->GetShaderProgramDescriptionIndex());
596
597 this->CacheUserUniformIndex(description.GetSymbols());
598 }
599 }
600 }
601
602 //----------------------------------------
603 void
ForceSetupLookupTable(const char * targetName,ResLookupTable lut)604 ResMaterial::ForceSetupLookupTable(const char* targetName, ResLookupTable lut)
605 {
606 NW_ASSERT(lut.IsValid());
607 NW_NULL_ASSERT(targetName);
608
609 ResFragmentShader fragmentShader = this->GetFragmentShader();
610 if (fragmentShader.IsValid())
611 {
612 fragmentShader.ForceSetupLightingLut(targetName, lut);
613
614 // フラグメントライティングテーブルのハッシュを計算します。
615 this->CalcFragmentLightingTableHash();
616 }
617 }
618
619 } /* namespace res */
620 } /* namespace gfx */
621 } /* namespace nw */
622
623