/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ResMaterial.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 18139 $ *---------------------------------------------------------------------------*/ #include "../precompiled.h" #include #include #include namespace nw { namespace gfx { namespace res { static Result SetupTextureCoordinators(ResMaterial resMaterial); //---------------------------------------------------------------------------- Result ResMaterial::Setup(os::IAllocator* allocator, ResGraphicsFile graphicsFile) { NW_ASSERT( internal::ResCheckRevision( graphicsFile ) ); NW_ASSERT( internal::ResCheckRevision( *this ) ); NW_ASSERT( this->IsValid() ); Result result = RESOURCE_RESULT_OK; result |= this->SetupTextures(allocator, *this, graphicsFile); // Shaderをセットアップします。 result |= this->SetupShader(allocator, this->GetShader(), graphicsFile); // ユーザーシェーダーユニフォームのインデックス番号をキャッシュします。 ResShader resShader = this->GetShader(); if (resShader.IsValid() && ((result.GetDescription() & nw::gfx::RESOURCE_RESULT_NOT_FOUND_SHADER) == 0)) { ResBinaryShader resBinaryShader = resShader.Dereference(); NW_MINMAXLT_ASSERT(this->GetShaderProgramDescriptionIndex(), 0, resBinaryShader.GetDescriptionsCount()); ResShaderProgramDescription description = resBinaryShader.GetDescriptions(this->GetShaderProgramDescriptionIndex()); this->CacheUserUniformIndex(description.GetSymbols()); } // FragmentLightingTableをセットアップします。 ResFragmentShader resFragmentShader = this->GetFragmentShader(); NW_ASSERT(resFragmentShader.GetFragmentLightingTable().IsValid()); result |= resFragmentShader.Setup(allocator, graphicsFile); // フラグメントライティングテーブルのハッシュを計算します。 if ((result.GetDescription() & nw::gfx::RESOURCE_RESULT_NOT_FOUND_LUT) == 0) { // 必ず参照解決が完了している状態で呼び出す必要があります。 this->CalcFragmentLightingTableHash(); } return result; } //---------------------------------------------------------------------------- void ResMaterial::Cleanup() { int textureMapperNum = this->GetTextureMappersCount(); for ( int i = 0; i < textureMapperNum; ++i ) { ResPixelBasedTextureMapper textureMapper = this->GetTextureMappers( i ); if (textureMapper.IsValid()) { textureMapper.Cleanup(); } } if (this->GetProceduralTextureMapper().IsValid()) { this->GetProceduralTextureMapper().Cleanup(); } ResShader resShader = this->GetShader(); ut::SafeCleanup(resShader); ut::SafeCleanup(this->GetFragmentShader()); } //---------------------------------------- Result ResMaterial::SetupTextures(os::IAllocator* allocator, ResMaterial resMaterial, ResGraphicsFile graphicsFile) { Result result = RESOURCE_RESULT_OK; s32 textureMapperNum = resMaterial.GetTextureMappersCount(); result |= SetupTextureCoordinators(resMaterial); u32 textureSamplersHash = resMaterial.GetTextureSamplersHash(); // TextureMapper をセットアップします。 for ( int i = 0; i < textureMapperNum; ++i ) { ResPixelBasedTextureMapper resTextureMapper = resMaterial.GetTextureMappers( i ); if (!resTextureMapper.IsValid()) { continue; } if (!resTextureMapper.GetTexture().IsValid()) { continue; } result |= resTextureMapper.Setup(allocator, graphicsFile); // テクスチャコーディネータを参照してテクスチャサンプラーのコマンドを設定する enum { SAMPLER_TYPE_SHIFT = 28, SAMPLER_TYPE_MASK = 0x7 << SAMPLER_TYPE_SHIFT, SAMPLER_TYPE_2D = 0, SAMPLER_TYPE_CUBE_MAP = 1, SAMPLER_TYPE_SHADOW_2D = 2, SAMPLER_TYPE_PROJECTION = 3, SAMPLER_TYPE_SHADOW_CUBE = 4 }; u32* command = resTextureMapper.GetCommandCache(); NW_NULL_ASSERT(command); command[5] &= ~(SAMPLER_TYPE_MASK); // sampler_type // テクスチャ0のみ複数のサンプラータイプが設定できる。 if ( i == 0 ) { switch (resMaterial.GetTextureCoordinators(0).GetMappingMethod()) { case ResTextureCoordinator::MAPPINGMETHOD_UV_COORDINATE: { command[5] |= SAMPLER_TYPE_2D << SAMPLER_TYPE_SHIFT; } break; case ResTextureCoordinator::MAPPINGMETHOD_PROJECTION: { command[5] |= SAMPLER_TYPE_PROJECTION << SAMPLER_TYPE_SHIFT; } break; case ResTextureCoordinator::MAPPINGMETHOD_CAMERA_CUBE_ENV: { command[5] |= SAMPLER_TYPE_CUBE_MAP << SAMPLER_TYPE_SHIFT; } break; case ResTextureCoordinator::MAPPINGMETHOD_CAMERA_SPHERE_ENV: { command[5] |= SAMPLER_TYPE_2D << SAMPLER_TYPE_SHIFT; } break; default: NW_FATAL_ERROR("Unsupported mapping method."); break; } } if ((result.GetDescription() & RESOURCE_RESULT_NOT_FOUND_TEXTURE) == 0) { ResTexture resImageTexture = resTextureMapper.GetTexture().Dereference(); u32 hash = 0; if (i == 0) { hash = (u32)resImageTexture.ptr(); } else { hash = ((u32)resImageTexture.ptr() << i * 8) | ((u32)resImageTexture.ptr() >> (32 - i * 8)); } textureSamplersHash ^= hash; } } resMaterial.SetTextureMappersHash(textureSamplersHash); if (resMaterial.GetProceduralTextureMapper().IsValid()) { result |= resMaterial.GetProceduralTextureMapper().Setup(allocator, graphicsFile); } return result; } static Result SetupTextureCoordinators(ResMaterial resMaterial) { Result result = RESOURCE_RESULT_OK; // TextureMapper と TextureCoordinator の組み合わせを確認します。 s32 textureCoordinatorNum = resMaterial.GetTextureCoordinatorsCount(); ResMaterial::TextureCoordinateConfig config = resMaterial.GetTextureCoordinateConfig(); bool isProceduralTextureEnabled = resMaterial.GetProceduralTextureMapper().IsValid(); for ( int i = 0; i < textureCoordinatorNum; ++i ) { ResTextureCoordinator resTextureCoordinator = resMaterial.GetTextureCoordinators(i); bool isTextureMapperEnabled = resMaterial.GetTextureMappers(i).IsValid(); resTextureCoordinator.SetEnabled(false); switch(i) { case 0: { if (isTextureMapperEnabled) { resTextureCoordinator.SetEnabled(true); } else if (config == ResMaterial::CONFIG_0120 || config == ResMaterial::CONFIG_0110) { if (isProceduralTextureEnabled) { resTextureCoordinator.SetEnabled(true); } } } break; case 1: { if (isTextureMapperEnabled) { resTextureCoordinator.SetEnabled(true); } else { if (config == ResMaterial::CONFIG_0111) { if (resMaterial.GetTextureMappers(2).IsValid() || isProceduralTextureEnabled) { resTextureCoordinator.SetEnabled(true); } } else if (config == ResMaterial::CONFIG_0110 || config == ResMaterial::CONFIG_0112) { if (resMaterial.GetTextureMappers(2).IsValid()) { resTextureCoordinator.SetEnabled(true); } } else if (config == ResMaterial::CONFIG_0121) { if (isProceduralTextureEnabled) { resTextureCoordinator.SetEnabled(true); } } } } break; case 2: { if (config == ResMaterial::CONFIG_0122) { if (isTextureMapperEnabled || isProceduralTextureEnabled) { resTextureCoordinator.SetEnabled(true); } } else if (config == ResMaterial::CONFIG_0120 || config == ResMaterial::CONFIG_0121) { if (isTextureMapperEnabled) { resTextureCoordinator.SetEnabled(true); } } else if (config == ResMaterial::CONFIG_0112) { if (isProceduralTextureEnabled) { resTextureCoordinator.SetEnabled(true); } } } break; default: { NW_FATAL_ERROR("Invalid coordinator number."); } } } return result; } //---------------------------------------- Result ResMaterial::SetupShader( os::IAllocator* allocator, ResShader resShader, ResGraphicsFile graphicsFile ) { Result result = RESOURCE_RESULT_OK; ResShader setupShader; if (resShader.IsValid()) { bool existShader = false; ResReferenceShader refer = ResDynamicCast(resShader); if (refer.IsValid()) { if (refer.GetTargetShader().IsValid()) { setupShader = refer.GetTargetShader(); existShader = true; } else { ::std::pair referenceResult; referenceResult = GetReferenceShaderTarget(refer, graphicsFile); if (referenceResult.second) { setupShader = referenceResult.first; existShader = true; } else { result |= Result::MASK_FAIL_BIT; result |= RESOURCE_RESULT_NOT_FOUND_SHADER; } } } else { setupShader = resShader; existShader = true; } if (existShader) { result |= setupShader.Setup(allocator, graphicsFile); } } return result; } //---------------------------------------- void ResMaterial::SetShader(ResShader resShader) { NW_ASSERT(resShader.IsValid()); ResReferenceShader referenceShader = ResStaticCast(this->GetShader()); referenceShader.ref().toTargetShader.set_ptr(resShader.Dereference().ptr()); } //---------------------------------------- void ResMaterial::CacheUserUniformIndex(ResShaderSymbolArray symbols) { for (int parameterIndex = 0; parameterIndex < this->GetShaderParametersCount(); ++parameterIndex) { ResShaderParameter parameter = this->GetShaderParameters(parameterIndex); if (parameter.IsValid()) { const char* name = parameter.GetName(); if (name == NULL) { continue; } for (int symbolIndex = 0; symbolIndex < symbols.size(); ++symbolIndex) { ResShaderSymbol shaderSymbol = symbols[symbolIndex]; if (::std::strcmp(name, shaderSymbol.GetName()) == 0) { // ユーザーユニフォームのインデックスをキャッシュします。 parameter.SetSymbolIndex(symbolIndex); break; } } } } } //---------------------------------------- void ResMaterial::CalcFragmentLightingTableHash() { enum { D0_SHIFT = 0, D1_SHIFT = 4, RR_SHIFT = 8, RG_SHIFT = 12, RB_SHIFT = 16, FR_SHIFT = 20, MAX_BITS = 32 }; ResFragmentShader resFragmentShader = this->GetFragmentShader(); ResFragmentLightingTable resFragmentLightingTable = resFragmentShader.GetFragmentLightingTable(); NW_ASSERT(resFragmentLightingTable.IsValid()); // FragmentLightingTableのハッシュ値を再計算します。 u32 fragmentLightingTableParametersHash = this->GetFragmentLightingTableParametersHash(); if (resFragmentLightingTable.GetDistribution0Sampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetDistribution0Sampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr()) << D0_SHIFT; } } if (resFragmentLightingTable.GetDistribution1Sampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetDistribution1Sampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr() << D1_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - D1_SHIFT)); } } if (resFragmentLightingTable.GetReflectanceRSampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetReflectanceRSampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr() << RR_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - RR_SHIFT)); } } if (resFragmentLightingTable.GetReflectanceGSampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetReflectanceGSampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr() << RG_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - RG_SHIFT)); } } if (resFragmentLightingTable.GetReflectanceBSampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetReflectanceBSampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr() << RB_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - RB_SHIFT)); } } if (resFragmentLightingTable.GetFresnelSampler().IsValid()) { ResReferenceLookupTable refLut = ResDynamicCast(resFragmentLightingTable.GetFresnelSampler().GetSampler()); if (refLut.IsValid()) { ResImageLookupTable imageLut = refLut.Dereference(); fragmentLightingTableParametersHash ^= ((u32)imageLut.ptr() << FR_SHIFT) | ((u32)imageLut.ptr() >> (MAX_BITS - FR_SHIFT)); } } this->SetFragmentLightingTableHash(fragmentLightingTableParametersHash); } } /* namespace res */ } /* namespace gfx */ } /* namespace nw */