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