/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_RenderContext.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: 29284 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { nw::math::VEC4 GetTranslate(const nw::math::MTX34& transform) { nw::math::VEC3 translate = transform.GetColumn(3); return nw::math::VEC4(translate); } } namespace nw { namespace gfx { const ut::FloatColor RenderContext::NULL_AMBIENT = ut::FloatColor(); //---------------------------------------- RenderContext* RenderContext::Builder::Create( os::IAllocator* allocator ) { NW_NULL_ASSERT(allocator); void* renderContextMemory = allocator->Alloc(sizeof(RenderContext)); NW_NULL_ASSERT(renderContextMemory); void* shaderProgramMemory = allocator->Alloc(sizeof(ShaderProgram)); NW_NULL_ASSERT(shaderProgramMemory); if (!renderContextMemory) { NW_WARNING(false, "Allocation failed\n"); return NULL; } GfxPtr shaderProgram(new(shaderProgramMemory) ShaderProgram(allocator)); // パーティクル用アクティベータが存在しない場合は自動的に生成します。 if (m_ParticleMaterialActivator == NULL) { m_ParticleMaterialActivator = ParticleMaterialActivator::Create(allocator); } GfxPtr particleMaterialActivator(m_ParticleMaterialActivator); SceneEnvironment::Description description; description.vertexLights = VertexLightArray(m_MaxVertexLights, allocator); description.cameras = CameraArray(m_MaxCameras, allocator); description.fogs = FogArray(m_MaxFogs, allocator); description.lightSets = LightSetArray(m_MaxLightSets, allocator); return new(renderContextMemory) RenderContext( allocator, shaderProgram, particleMaterialActivator, description); } //---------------------------------------- RenderContext::RenderContext( os::IAllocator* allocator, GfxPtr shaderProgram, GfxPtr particleMaterialActivator, const SceneEnvironment::Description& description) : GfxObject(allocator), m_ShaderProgram(shaderProgram), m_MatrixPaletteCount(0), m_IsVertexAlphaEnabled(false), m_IsBoneWeightWEnabled(false), m_IsVertexAttributeDirty(true), m_IsShaderProgramDirty(true), m_RenderMode(RENDERMODE_DEFAULT), m_RenderTarget(NULL), m_ModelCache(NULL), m_Material(NULL), m_MaterialCache(NULL), m_CameraCache(NULL), m_SceneEnvironment(description), m_ParticleMaterialActivator(particleMaterialActivator) #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED , m_ModelTranslateOffset(0.0f, 0.0f, 0.0f) #endif { } //---------------------------------------- void RenderContext::SetRenderTarget( IRenderTarget* renderTarget, const Viewport& viewport ) { this->m_RenderTarget = renderTarget; if (this->m_RenderTarget) { const FrameBufferObject& fbo = this->m_RenderTarget->GetBufferObject(); fbo.ActivateBuffer(); GraphicsDevice::SetRenderBufferSize( this->m_RenderTarget->GetDescription().width, this->m_RenderTarget->GetDescription().height); GraphicsDevice::ActivateViewport(viewport); GraphicsDevice::SetDepthRange(viewport.GetDepthNear(), viewport.GetDepthFar()); GraphicsDevice::SetDepthFormat(this->m_RenderTarget->GetDescription().depthFormat); } } //---------------------------------------- void RenderContext::SetRenderTarget( IRenderTarget* renderTarget ) { f32 width = 0.0f; f32 height = 0.0f; if (renderTarget) { width = static_cast(renderTarget->GetDescription().width); height = static_cast(renderTarget->GetDescription().height); } const float x = 0.0f; const float y = 0.0f; const float near = 0.0f; const float far = 1.0f; this->SetRenderTarget(renderTarget, Viewport(x, y, width, height, near, far)); } //---------------------------------------- void RenderContext::ResetState() { m_ModelCache = NULL; m_Material = NULL; m_MaterialCache = NULL; m_CameraCache = NULL; m_SceneEnvironment.Reset(); m_IsVertexAttributeDirty = true; m_ShaderProgram.Get()->DeactivateDescription(); GraphicsDevice::InvalidateAllLookupTables(); m_MaterialHash.ResetMaterialHash(Model::MULTI_FLAG_BUFFER_MATERIAL); } //---------------------------------------- // こちらの実装を修正した場合 ResetState(u32 resetStateMode) も修正する必要があります。 void RenderContext::ResetState(s32 hashMask) { m_ModelCache = NULL; m_Material = NULL; m_MaterialCache = NULL; m_CameraCache = NULL; m_SceneEnvironment.Reset(); m_IsVertexAttributeDirty = true; m_ShaderProgram.Get()->DeactivateDescription(); GraphicsDevice::InvalidateAllLookupTables(); m_MaterialHash.ResetMaterialHash(hashMask); } //---------------------------------------- void RenderContext::ResetState(s32 resetStateMode, s32 hashMask) { if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_MODEL_CACHE)) { m_ModelCache = NULL; } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_MATERIAL)) { m_Material = NULL; } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_MATERIAL_CACHE)) { m_MaterialCache = NULL; } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_CAMERA_CACHE)) { m_CameraCache = NULL; } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_SCENE_ENVIRONMENT)) { m_SceneEnvironment.Reset(); } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_SHADER_PROGRAM)) { m_ShaderProgram.Get()->DeactivateDescription(); m_IsShaderProgramDirty = true; } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_LOOK_UP_TABLE)) { GraphicsDevice::InvalidateAllLookupTables(); } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_HASH)) { m_MaterialHash.ResetMaterialHash(hashMask); } if (ut::CheckFlag(resetStateMode, RenderContext::RESETSTATEMODE_VERTEX_ATTRIBUTE)) { m_IsVertexAttributeDirty = true; } } //---------------------------------------- void RenderContext::ClearBuffer( GLbitfield mask, const ut::FloatColor& color, f32 depth ) { const FrameBufferObject& fbo = this->m_RenderTarget->GetBufferObject(); #if 0 if ( fbo.GetFboID() != 0 ) { glBindFramebuffer(GL_FRAMEBUFFER, fbo); glDepthMask(true); glClearColor(color.r, color.g, color.b, color.a); glClearDepthf(depth); glClearStencil(0); glClear(mask); } else #endif { fbo.ClearBuffer( mask, color, depth, 0 ); } } //---------------------------------------- // コンテキスト設定関係 //---------------------------------------- void RenderContext::SetCameraMatrix(Camera* camera, bool isForce) { if (m_CameraCache != camera || isForce) { m_CameraCache = camera; internal::NWSetVertexUniform4fv( VERTEX_SHADER_UNIFORM_VIEWMTX_INDEX, 3, camera->ViewMatrix()); internal::NWSetVertexUniform4fv( VERTEX_SHADER_UNIFORM_PROJMTX_INDEX, 4, camera->ProjectionMatrix()); internal::NWSetGeometryUniform4fv( VERTEX_SHADER_UNIFORM_VIEWMTX_INDEX, 3, camera->ViewMatrix()); internal::NWSetGeometryUniform4fv( VERTEX_SHADER_UNIFORM_PROJMTX_INDEX, 4, camera->ProjectionMatrix()); NW_GL_ASSERT(); } } //---------------------------------------- void RenderContext::SetModelMatrix(Model* model) { if (model == NULL) { this->m_ModelCache = NULL; return; } NW_NULL_ASSERT(this->m_ShaderProgram); SkeletalModel* skeletalModel = ut::DynamicCast(model); if (skeletalModel) { Skeleton* skeleton = skeletalModel->GetSkeleton(); NW_NULL_ASSERT(skeleton); ResSkeleton resSkeleton = skeleton->GetResSkeleton(); NW_ASSERT(resSkeleton.IsValid()); if (ut::CheckFlag(resSkeleton.GetFlags(), ResSkeletonData::FLAG_MODEL_COORDINATE)) { #ifdef NW_GFX_MODEL_TRANSLATE_OFFSET_ENABLED math::MTX34 worldMatrix(model->WorldMatrix()); MTX34MultTranslate(&worldMatrix, m_ModelTranslateOffset, worldMatrix); this->m_ShaderProgram->SetWorldMatrix(worldMatrix); #else this->m_ShaderProgram->SetWorldMatrix(model->WorldMatrix()); #endif } else { this->m_ShaderProgram->SetWorldMatrix(math::Matrix34::Identity()); } } else { this->m_ShaderProgram->SetWorldMatrix(math::Matrix34::Identity()); } this->m_ShaderProgram->SetModelNormalMatrix(model->NormalMatrix()); bool useNormalMatrix = false; this->m_ShaderProgram->SetUseBoneNormalMatrix(useNormalMatrix); this->m_ModelCache = model; } //---------------------------------------- // アクティベート関係 //--------------------------------------------------------------------------- void RenderContext::ActivateContext() { if (m_MaterialCache == m_Material) { return; } this->ActivateShaderProgram(); this->ActivateSceneEnvironment(); this->ActivateMaterial(); this->m_SceneEnvironment.SetAllFlagsDirty(false); m_MaterialCache = m_Material; NW_GL_ASSERT(); } //--------------------------------------------------------------------------- void RenderContext::ActivateParticleContext() { if (this->m_IsVertexAttributeDirty) { internal::ClearVertexAttribute(); m_IsVertexAttributeDirty = false; } if (m_MaterialCache == m_Material) { return; } bool isParticleMaterialEnabled = false; if (m_MaterialCache != NULL) { isParticleMaterialEnabled = ut::CheckFlag(m_MaterialCache->GetOriginal().GetFlags(), ResMaterialData::FLAG_PARTICLE_MATERIAL_ENABLED) && ut::CheckFlag(m_Material->GetOriginal().GetFlags(), ResMaterialData::FLAG_PARTICLE_MATERIAL_ENABLED); } if (isParticleMaterialEnabled) { // シェーダープログラムが変更されていないことを表します。 m_IsShaderProgramDirty = false; this->ActivateParticleMaterial(); } else { this->ActivateContext(); } NW_GL_ASSERT(); m_MaterialCache = m_Material; } //--------------------------------------------------------------------------- void RenderContext::ActivateVertexAttribute( ResMesh mesh ) { NW_NULL_ASSERT( mesh.ref().m_ActivateCommandCache ); if (m_IsVertexAttributeDirty) { internal::ClearVertexAttribute(); m_IsVertexAttributeDirty = false; } internal::NWUseCmdlist( mesh.ref().m_ActivateCommandCache, mesh.ref().m_ActivateCommandCacheSize ); internal::NWUseCmdlist( mesh.ref().m_IrScaleCommand, sizeof(mesh.ref().m_IrScaleCommand) ); ShaderProgram* shaderProgram = this->GetShaderProgram(); const bool hasVertexAlpha = (mesh.ref().m_Flags & ResMesh::FLAG_HAS_VERTEX_ALPHA) ? true : false; const bool hasBoneWeightW = (mesh.ref().m_Flags & ResMesh::FLAG_HAS_BONE_WEIGHT_W) ? true : false; shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISVERTA), hasVertexAlpha); shaderProgram->SetVertexUniformBool(NW_GFX_VERTEX_UNIFORM(ISBONEW), hasBoneWeightW); } //---------------------------------------- void RenderContext::ActivateShaderProgram() { ResShaderProgramDescription description = this->m_Material->GetDescription(); NW_ASSERT(description.IsValid()); ResShaderProgramDescription previousDescription = this->m_ShaderProgram.Get()->GetActiveDescription(); if ( previousDescription != description ) { // ジオメトリシェーダ使用後はクリア処理が必要。 if ( previousDescription.IsValid() && previousDescription.GetGeometryShaderIndex() >= 0 && description.GetGeometryShaderIndex() < 0 ) { this->m_IsVertexAttributeDirty = true; const s32 hashMask = Model::FLAG_BUFFER_SHADER_PARAMETER | Model::FLAG_BUFFER_MATERIAL_COLOR | Model::FLAG_BUFFER_TEXTURE_COORDINATOR | Model::FLAG_BUFFER_SCENE_ENVIRONMENT; m_MaterialHash.ResetMaterialHash( hashMask ); m_SceneEnvironment.SetAllFlagsDirty( true ); } m_ShaderProgram.Get()->ActivateDescription(description); m_IsShaderProgramDirty = true; } else { m_IsShaderProgramDirty = false; } } //---------------------------------------- void RenderContext::ActivateSceneEnvironment() { NW_NULL_ASSERT(this->m_Material); const Model* owner = this->m_Material->GetOwnerModel(); NW_NULL_ASSERT(owner); ResMaterial resMaterial = this->m_Material->GetSceneEnvironmentResMaterial(); this->m_SceneEnvironment.SetActiveLightSet(resMaterial.GetLightSetIndex()); if (this->m_SceneEnvironment.IsFragmentLightsDirty()) { this->ActivateFragmentLights(); } if (this->m_SceneEnvironment.IsHemiSphereLightDirty()) { this->ActivateHemiSphereLight(); } #if defined(NW_GFX_VERTEX_LIGHT_ENABLED) if (this->m_SceneEnvironment.IsVertexLightsDirty()) { this->ActivateVertexLights(); } #endif this->m_SceneEnvironment.SetActiveFog(resMaterial.GetFogIndex()); if (this->m_SceneEnvironment.IsFogDirty()) { this->ActivateFog(); } } //---------------------------------------- void RenderContext::ActivateFog() { if (this->m_SceneEnvironment.GetActiveFog() != NULL && this->m_SceneEnvironment.GetActiveFog()->GetResFog().IsValid() && this->m_SceneEnvironment.GetActiveFog()->GetResFog().GetFogSampler().IsValid()) { ResFog resFog = m_SceneEnvironment.GetActiveFog()->GetResFog(); ResImageLookupTable lut = resFog.GetFogSampler(); enum { REG_FOG_ZFLIP = 0xe0, REG_FOG_COLOR = 0xe1, REG_FOG_ZFLIP_SHIFT = 16 }; ut::Color8 color8 = resFog.GetColor(); const u32 HEADER_FOG_ZFLIP = internal::MakeCommandHeader(REG_FOG_ZFLIP, 1, false, 0x4); const u32 HEADER_FOG_COLOR = internal::MakeCommandHeader(REG_FOG_COLOR, 1, false, 0xf); // TODO: ZFLIP の設定が逆だが、FogUpdater で逆順の LUT を生成しているので辻褄が合っている。 // 両方の足並みを揃えて修正する。 u32 FOG_COMMAND[] = { (resFog.IsZFlip() ? (0x1 << REG_FOG_ZFLIP_SHIFT) : 0x0), HEADER_FOG_ZFLIP, color8.r | color8.g << 8 | color8.b << 16, HEADER_FOG_COLOR }; internal::NWUseCmdlist(&FOG_COMMAND[0]); GraphicsDevice::ActivateLookupTable(lut, GraphicsDevice::LUT_TARGET_FOG); } } //---------------------------------------- void RenderContext::ActivateHemiSphereLight() { const ShaderProgram* shaderProgram = this->GetShaderProgram(); if (m_SceneEnvironment.GetHemiSphereLight() && shaderProgram && ut::CheckFlag( shaderProgram->GetActiveDescription().GetFlags(), ResShaderProgramDescription::FLAG_IS_SUPPORTING_HEMISPHERE_LIGHTING)) { ResHemiSphereLight resLight = m_SceneEnvironment.GetHemiSphereLight()->GetResHemiSphereLight(); NW_ASSERT(resLight.IsValid()); internal::NWSetVertexUniform4fv(VERTEX_SHADER_UNIFORM_HSLGCOL_INDEX, 1, resLight.GetGroundColor()); internal::NWSetVertexUniform4fv(VERTEX_SHADER_UNIFORM_HSLSCOL_INDEX, 1, resLight.GetSkyColor()); math::VEC3 direction(resLight.GetDirection()); if (ut::CheckFlag(resLight.GetFlags(), ResHemiSphereLightData::FLAG_IS_INHERITING_DIRECTION_ROTATE) && m_SceneEnvironment.GetHemiSphereLight()->GetParent() != NULL) { math::VEC3TransformNormal( &direction, &m_SceneEnvironment.GetHemiSphereLight()->TrackbackWorldMatrix(), &resLight.GetDirection()); } const Camera* camera = this->GetActiveCamera(); NW_NULL_ASSERT(camera); const math::MTX34& viewMatrix = camera->ViewMatrix(); math::VEC3TransformNormal(&direction, &viewMatrix, &direction); math::VEC4 directionAndLerp(direction.x, direction.y, direction.z, resLight.GetLerpFactor()); internal::NWSetVertexUniform4fv(VERTEX_SHADER_UNIFORM_HSLSDIR_INDEX, 1, directionAndLerp); } } //---------------------------------------- void RenderContext::ActivateFragmentLights() { GraphicsDevice::ResetFragmentLightEnabled(); int lightCount = this->m_SceneEnvironment.GetFragmentLightCount(); for (int i = 0; i < lightCount; ++i) { const FragmentLight* light = this->m_SceneEnvironment.GetFragmentLight(i); this->ActivateFragmentLight(i, light); } GraphicsDevice::ActivateFragmentLightEnabled(); } //---------------------------------------- void RenderContext::ActivateFragmentLight(int index, const FragmentLight* light) { ResFragmentLight resLight = light->GetResFragmentLight(); NW_ASSERT(resLight.IsValid()); s32 lightKind = resLight.GetLightKind(); NW_ASSERT(lightKind < ResFragmentLight::KIND_COUNT); Camera* camera = this->GetActiveCamera(); NW_NULL_ASSERT(camera); const math::MTX34& viewMatrix = camera->ViewMatrix(); if ( lightKind == ResFragmentLight::KIND_DIRECTIONAL ) { nw::math::VEC4 direction(light->Direction()); direction.w = 0.0f; this->TransformToViewCoordinate(&direction, &viewMatrix, &direction); // 平行光源はポジションの逆方向のベクトルをディレクションとします。 GraphicsDevice::ActivateFragmentLightPosition(index, -direction); GraphicsDevice::SetFragmentLightPositionW(index, (direction.w == 0.0f)); GraphicsDevice::SetFragmentLightSpotEnabled(index, false); GraphicsDevice::SetFragmentLightDistanceAttnEnabled(index, false); } else if ( lightKind == ResFragmentLight::KIND_POINT ) { GraphicsDevice::SetFragmentLightSpotEnabled(index, false); if ( ut::CheckFlag(resLight.GetFlags(), ResFragmentLightData::FLAG_DISTANCE_ATTENUATION_ENABLED) && resLight.GetDistanceSampler().IsValid() ) { GraphicsDevice::SetFragmentLightDistanceAttnEnabled(index, true); GraphicsDevice::ActivateFragmentLightDistanceAttnTable(index, resLight.GetDistanceSampler().Dereference()); GraphicsDevice::ActivateFragmentLightDistanceAttnScaleBias( index, resLight.ref().m_DistanceAttenuationScale, resLight.ref().m_DistanceAttenuationBias ); } else { GraphicsDevice::SetFragmentLightDistanceAttnEnabled(index, false); } nw::math::VEC4 position = GetTranslate(light->WorldMatrix()); position.w = 1.0f; this->TransformToViewCoordinate(&position, &viewMatrix, &position); GraphicsDevice::ActivateFragmentLightPosition(index, position); GraphicsDevice::SetFragmentLightPositionW(index, (position.w == 0.0f)); } else if ( lightKind == ResFragmentLight::KIND_SPOT ) { NW_ASSERT(resLight.GetAngleSampler().IsValid()); NW_ASSERT(resLight.GetAngleSampler().GetSampler().IsValid()); GraphicsDevice::SetFragmentLightSpotEnabled(index, true); GraphicsDevice::ActivateFragmentLightSpotTable(index, resLight.GetAngleSampler().GetSampler().Dereference()); GraphicsDevice::SetLutIsAbs( GraphicsDevice::LUT_TARGET_SP, resLight.GetAngleSampler().GetSampler().Dereference().IsAbs() ); GraphicsDevice::SetLutInput( GraphicsDevice::LUT_TARGET_SP, resLight.GetAngleSampler().GetInput() ); GraphicsDevice::SetLutScale( GraphicsDevice::LUT_TARGET_SP, resLight.GetAngleSampler().GetScale() ); if ( ut::CheckFlag(resLight.GetFlags(), ResFragmentLightData::FLAG_DISTANCE_ATTENUATION_ENABLED ) && resLight.GetDistanceSampler().IsValid()) { GraphicsDevice::SetFragmentLightDistanceAttnEnabled(index, true); GraphicsDevice::ActivateFragmentLightDistanceAttnTable(index, resLight.GetDistanceSampler().Dereference()); GraphicsDevice::ActivateFragmentLightDistanceAttnScaleBias( index, resLight.ref().m_DistanceAttenuationScale, resLight.ref().m_DistanceAttenuationBias ); } else { GraphicsDevice::SetFragmentLightDistanceAttnEnabled(index, false); } nw::math::VEC4 direction(light->Direction()); direction.w = 0.0f; this->TransformToViewCoordinate(&direction, &viewMatrix, &direction); nw::math::VEC4 position = GetTranslate(light->WorldMatrix()); position.w = 1.0f; this->TransformToViewCoordinate(&position, &viewMatrix, &position); math::VEC3 spotDirection(direction); GraphicsDevice::ActivateFragmentLightPosition(index, position, spotDirection); GraphicsDevice::SetFragmentLightPositionW(index, (position.w == 0.0f)); } } #if defined(NW_GFX_VERTEX_LIGHT_ENABLED) //---------------------------------------- void RenderContext::ActivateVertexLights() { int size = this->m_SceneEnvironment.GetVertexLightCount(); if (size > 0) { const ShaderProgram* shaderProgram = this->GetShaderProgram(); // 整数レジスタにループカウンタとして頂点ライトの数をセットする。 // ただし、頂点シェーダーでは整数レジスタ+1回のループを行うために頂点ライト数-1を設定する。 // 頂点ライトが0の場合はIsVertLを用いてループを行わないようにしている。 if(shaderProgram->GetActiveDescription().GetMaxVertexLightCount() > 0) { shaderProgram->SetVertexUniformInt( NW_GFX_VERTEX_UNIFORM(LIGHTCT), ut::Max(size - 1, 0), 0, 1); } for (int i = 0; i < size; i++) { this->ActivateVertexLight(i, m_SceneEnvironment.GetVertexLight(i)); } } } //---------------------------------------- void RenderContext::ActivateVertexLight(int index, const VertexLight* light) { const ShaderProgram* shaderProgram = this->GetShaderProgram(); if (shaderProgram->GetActiveDescription().GetMaxVertexLightCount() > index) { const float LIGHT_INFINITY = 0.0f; const float LIGHT_FINITY = 1.0f; s32 endUniform = shaderProgram->GetActiveDescription().GetVertexLightEndUniform(); ResVertexLight resLight = light->GetResVertexLight(); NW_ASSERT(resLight.IsValid()); Camera* camera = this->GetActiveCamera(); NW_NULL_ASSERT(camera); const math::MTX34& viewMatrix = camera->ViewMatrix(); shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_AMBIENT, resLight.GetAmbient()); shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_DIFFUSE, resLight.GetDiffuse()); if (resLight.GetLightKind() == ResVertexLight::KIND_DIRECTIONAL) { // OpenGLではライトを無限遠に置くことで平行光源を実現しています。 // デフォルトシェーダーも同様の仕様となります。 math::VEC4 position(light->Direction()); position.w = LIGHT_INFINITY; this->TransformToViewCoordinate(&position, &viewMatrix, &position); shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_POSITION, -position); } else { math::VEC4 position = GetTranslate(light->WorldMatrix()); position.w = LIGHT_FINITY; this->TransformToViewCoordinate(&position, &viewMatrix, &position); shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_POSITION, position); // 距離減衰パラメータを設定します。 shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_DISTANCE_ATTENUATION, resLight.GetDistanceAttenuationAndEnabled()); if (resLight.GetLightKind() == ResVertexLight::KIND_SPOT) { // スポットパラメータを設定します。 math::VEC4 spotDirection(light->Direction()); spotDirection.w = 0.0f; this->TransformToViewCoordinate(&spotDirection, &viewMatrix, &spotDirection); math::VEC4 spotFactor(resLight.GetSpotFactor().x, resLight.GetSpotFactor().y, 0.0f, 0.0f); if (resLight.GetSpotFactor().y > 0.0f && resLight.GetSpotFactor().y < math::F_PI) { spotFactor.y = nw::math::CosRad(resLight.GetSpotFactor().y); spotDirection.w = 1.0f; } else if (resLight.GetSpotFactor().y == 0.0f) { const float EPSILON = 1e-5f; const float GREATER_THAN_ONE = 1.0f + EPSILON; spotFactor.y = GREATER_THAN_ONE; spotDirection.w = 1.0f; } else { spotDirection.w = 0.0f; } shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_SPOT_DIRECTION, spotDirection); shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_SPOT_FACTOR, spotFactor); } else { shaderProgram->SetUniversal( endUniform - (index + 1) * VERTEX_LIGHT_SET_COUNT + VERTEX_LIGHT_SPOT_DIRECTION, math::VEC4::Zero()); } } } } #endif //---------------------------------------- void RenderContext::TransformToViewCoordinate( math::VEC4* out, const math::MTX34* __restrict view, const math::VEC4* v) { f32 vx = v->x; f32 vy = v->y; f32 vz = v->z; f32 vw = v->w; out->x = view->f._00 * vx + view->f._01 * vy + view->f._02 * vz + view->f._03 * vw; out->y = view->f._10 * vx + view->f._11 * vy + view->f._12 * vz + view->f._13 * vw; out->z = view->f._20 * vx + view->f._21 * vy + view->f._22 * vz + view->f._23 * vw; out->w = vw; } //---------------------------------------- void RenderContext::RenderPrimitive( ResPrimitive primitive ) { NW_ASSERT(primitive.IsValid()); ResIndexStreamArray indexStreams = primitive.GetIndexStreams(); GLuint* bufferObjects = reinterpret_cast(primitive.GetBufferObjects()); const ShaderProgram* shaderProgram = this->GetShaderProgram(); shaderProgram->FlushUniform(); math::VEC4 float4( 1.0f, 1.0f, static_cast(m_MatrixPaletteCount), 1.0f); internal::NWSetVertexUniform4fv( VERTEX_SHADER_UNIFORM_IRSCALE_INDEX + 2, 1, float4 ); //NW_FOREACH(ResIndexStream indexStream, indexStreams) ResIndexStreamArray::iterator end = indexStreams.end(); for (ResIndexStreamArray::iterator stream = indexStreams.begin(); stream != end; ++stream) { ResIndexStream indexStream = *stream; GLuint bufferObject = *bufferObjects; ++bufferObjects; if (!indexStream.IsVisible()) { continue; } if (indexStream.ref().m_CommandCache) { NW_ASSERT(indexStream.ref().m_CommandCacheSize > 0); internal::NWUseCmdlist(indexStream.ref().m_CommandCache, indexStream.ref().m_CommandCacheSize); } else { // TODO: 初期化場所を初回 Draw 時から Setup 後に変更する。 enum { REG_INDEX_STREAM_OFFSET = 0x227, REG_INDEX_STREAM_COUNT = 0x228, REG_ELEMENTS_MODE = 0x229, // [23:16]にはバイトイネーブルでアクセスしてはいけない。 REG_ELEMENTS_MODE_2 = 0x253, // [31:16]にはバイトイネーブルでアクセスしてはいけない。 REG_ELEMENTS_MODE_3 = 0x25e, // [31:16]にはバイトイネーブルでアクセスしてはいけない。 REG_TRIANGLE_INDEX_RESET = 0x25f, REG_DRAW_READY = 0x245, REG_DRAW_KICK = 0x22f, REG_VERTEX_CACHE_CLEAR = 0x231, REG_COLOR_DEPTH_CACHE_CLEAR = 0x110, REG_COLOR_DEPTH_CACHE_FLUSH = 0x111 }; ResShaderProgramDescription description = shaderProgram->GetActiveDescription(); GLuint mode = ToPrimitiveModeGL( indexStream.GetPrimitiveMode(), description.GetGeometryShaderObject() > 0); internal::CommandCacheBuilder builder; builder.Begin(); const size_t commandCount = 26; u32* command = reinterpret_cast(internal::NWGetCurrentCmdBuffer()); std::memset(command, 0, sizeof(u32) * commandCount); u32 baseAddr = nngxGetPhysicalAddr(nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMA)); u32 bufferAddr = nngxGetPhysicalAddr( indexStream.GetImageAddress() ); NW_ASSERT((bufferAddr - baseAddr) < 0x10000000); const u32 HEADER_INDEX_STREAM_OFFSET = internal::MakeCommandHeader(REG_INDEX_STREAM_OFFSET, 1, false, 0xF); const u32 HEADER_INDEX_STREAM_COUNT = internal::MakeCommandHeader(REG_INDEX_STREAM_COUNT, 1, false, 0xF); command[0] = (bufferAddr - baseAddr); command[1] = HEADER_INDEX_STREAM_OFFSET; command[2] = indexStream.GetStreamCount(); command[3] = HEADER_INDEX_STREAM_COUNT; // CTR では GL_UNSIGNED_INT は未対応です。 if (indexStream.GetFormatType() == GL_UNSIGNED_SHORT) { command[0] |= 0x80000000; command[2] /= 2; } const u32 HEADER_ELEMENTS_MODE = internal::MakeCommandHeader(REG_ELEMENTS_MODE, 1, false, 0x2); const u32 HEADER_ELEMENTS_MODE_2 = internal::MakeCommandHeader(REG_ELEMENTS_MODE_2, 1, false, 0x2); if (mode == GL_TRIANGLES) { command[4] = 1 << 8; command[5] = HEADER_ELEMENTS_MODE; command[6] = 1 << 8; command[7] = HEADER_ELEMENTS_MODE_2; } else { command[4] = 0; command[5] = HEADER_ELEMENTS_MODE; command[6] = 0; command[7] = HEADER_ELEMENTS_MODE_2; } switch (mode) { case GL_TRIANGLES: command[8] = 3 << 8; break; case GL_TRIANGLE_STRIP: command[8] = 1 << 8; break; case GL_TRIANGLE_FAN: command[8] = 2 << 8; break; case GL_GEOMETRY_PRIMITIVE_DMP: command[8] = 3 << 8; break; } u32 commandIndex = 9; command[commandIndex++] = internal::MakeCommandHeader(REG_ELEMENTS_MODE_3, 1, false, 0x2); command[commandIndex++] = 0; command[commandIndex++] = internal::MakeCommandHeader(REG_ELEMENTS_MODE_3, 1, false, 0x4); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_TRIANGLE_INDEX_RESET, 1, false, 0xF); command[commandIndex++] = 0; command[commandIndex++] = internal::MakeCommandHeader(REG_DRAW_READY, 1, false, 0xF); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_DRAW_KICK, 1, false, 0xF); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_DRAW_READY, 1, false, 0xF); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_VERTEX_CACHE_CLEAR, 1, false, 0xF); command[commandIndex++] = 0; command[commandIndex++] = internal::MakeCommandHeader(REG_ELEMENTS_MODE_3, 1, false, 0x8); command[commandIndex++] = 0; command[commandIndex++] = internal::MakeCommandHeader(REG_ELEMENTS_MODE_3, 1, false, 0x8); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_COLOR_DEPTH_CACHE_FLUSH, 1, false, 0xF); command[commandIndex++] = 1; command[commandIndex++] = internal::MakeCommandHeader(REG_COLOR_DEPTH_CACHE_CLEAR, 1, false, 0xF); internal::NWForwardCurrentCmdBuffer(sizeof(u32) * commandIndex); builder.End(); indexStream.ref().m_CommandCache = builder.AllocAndCopy(); indexStream.ref().m_CommandCacheSize = builder.GetSize(); } } NW_GL_ASSERT(); } } // namespace gfx } // namespace nw