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