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