/*---------------------------------------------------------------------------* Project: NintendoWare File: ProjectionShadowDemo.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #define NW_DEBUG_CHECK_MEMORY_LEAK #include #include #include #include #include #include #include #include namespace { //---------------------------------------- // シャドウ関係 // シャドウの状態を表す UserFlag です enum UserFlags { SHADOW_CASTER_SHIFT = 0, SHADOW_RECEIVER_SHIFT = 1, SHADOW_CASTER = 0x1 << SHADOW_CASTER_SHIFT, SHADOW_RECEIVER = 0x1 << SHADOW_RECEIVER_SHIFT }; // Shadow の Caster のみを描画要素に加える関数オブジェクトです。 class IsShadowCasterModelFunctor : public nw::gfx::ISceneUpdater::IsVisibleModelFunctor { public: virtual bool IsVisible(const nw::gfx::Model* model) { return nw::ut::CheckFlag(model->GetUserParameter(), SHADOW_CASTER); } }; IsShadowCasterModelFunctor s_ShadowCasterModel; // シャドウの設定を行うレンダーコマンドです。 class StartShadowRenderCommand : public nw::gfx::RenderCommand { NW_DISALLOW_COPY_AND_ASSIGN(StartShadowRenderCommand); public: StartShadowRenderCommand( nw::demo::CommandListSwapper* commandListSwapper, nw::gfx::IRenderTarget* renderTarget, s32 shadowCameraIndex, nw::gfx::Material* shadowMaterial) : m_CommandListSwapper(commandListSwapper), m_RenderTarget(renderTarget), m_ShadowCameraIndex(shadowCameraIndex), m_ShadowMaterial(shadowMaterial), m_CommandSize(0), m_ProfilerPoint(0) {} virtual ~StartShadowRenderCommand() {} virtual void Invoke(nw::gfx::RenderContext* renderContext) { m_CommandSize = m_CommandListSwapper->GetCommandBufferSize(); m_ProfilerPoint = m_CommandListSwapper->AddGpuProfilingStartPoint(true); // オフスクリーンバッファに切り替えます。 renderContext->SetRenderTarget(m_RenderTarget); renderContext->ClearBuffer(GL_COLOR_BUFFER_BIT, nw::ut::FloatColor(1.0f, 1.0f, 1.0f, 1.0f), 1.0f); // シャドウ用のマテリアル、カメラを設定します。 renderContext->SetActiveCamera(m_ShadowCameraIndex); renderContext->SetMaterial(m_ShadowMaterial); nw::gfx::Camera* shadowCamera = renderContext->GetActiveCamera(); renderContext->SetCameraMatrix(shadowCamera); renderContext->ActivateContext(); // 1Pass では RENDERMODE_IGNORE_MATERIAL を用いて以降のマテリアル設定を行いません。 renderContext->SetRenderMode(nw::gfx::RenderContext::RENDERMODE_IGNORE_MATERIAL); } s32 GetCommandSize() const { return this->m_CommandSize; } s32 GetProfilerPoint() const { return this->m_ProfilerPoint; } private: nw::demo::CommandListSwapper* m_CommandListSwapper; nw::gfx::IRenderTarget* m_RenderTarget; s32 m_ShadowCameraIndex; nw::gfx::Material* m_ShadowMaterial; s32 m_CommandSize; s32 m_ProfilerPoint; }; // シャドウの後処理を行うレンダーコマンドです。 class EndShadowRenderCommand : public nw::gfx::RenderCommand { NW_DISALLOW_COPY_AND_ASSIGN(EndShadowRenderCommand); public: EndShadowRenderCommand(nw::demo::CommandListSwapper* commandListSwapper, StartShadowRenderCommand* startCommand) : m_CommandListSwapper(commandListSwapper), m_StartCommand(startCommand), m_CommandSize(0) {} virtual ~EndShadowRenderCommand() {} virtual void Invoke(nw::gfx::RenderContext* renderContext) { // レンダーモードをデフォルトに戻し、マテリアル設定が通常通り行われるようにします。 renderContext->SetRenderMode(nw::gfx::RenderContext::RENDERMODE_DEFAULT); // 描画キャッシュを破棄するために ResetState を行います。 renderContext->ResetState(); m_CommandSize = m_CommandListSwapper->GetCommandBufferSize(); int profilerPoint = m_StartCommand->GetProfilerPoint(); m_CommandListSwapper->SetGpuProfilingEndPoint(profilerPoint); } s32 GetCommandSize() const { return this->m_CommandSize; } private: nw::demo::CommandListSwapper* m_CommandListSwapper; StartShadowRenderCommand* m_StartCommand; s32 m_CommandSize; }; // 描画開始の設定を行うレンダーコマンドです。 class StartRenderCommand : public nw::gfx::RenderCommand { NW_DISALLOW_COPY_AND_ASSIGN(StartRenderCommand); public: StartRenderCommand(nw::demo::RenderSystem* renderSystem, nw::gfx::IRenderTarget* renderTarget, s32 baseCameraIndex) : m_RenderSystem(renderSystem), m_RenderTarget(renderTarget), m_BaseCameraIndex(baseCameraIndex), m_CommandSize(0) {} virtual ~StartRenderCommand() {} virtual void Invoke(nw::gfx::RenderContext* renderContext) { nw::demo::CommandListSwapper* commandListSwapper = m_RenderSystem->GetCommandListSwapper(); // 描画対象をオンスクリーンバッファに切り替えます renderContext->SetRenderTarget(m_RenderTarget); // ライト、環境マップなどの計算用カメラを設定します。 renderContext->SetActiveCamera(m_BaseCameraIndex); // ステレオ表示用コマンドの作成を開始します。作成したコマンドは複数回描画するために再利用されます。 commandListSwapper->StartCommandSave(); m_CommandSize = commandListSwapper->GetCommandBufferSize(); m_RenderSystem->ClearBySkyModel(renderContext->GetActiveCamera()); } s32 GetCommandSize() const { return this->m_CommandSize; } private: nw::demo::RenderSystem* m_RenderSystem; nw::gfx::IRenderTarget* m_RenderTarget; s32 m_BaseCameraIndex; s32 m_CommandSize; }; // 描画終了の設定を行うレンダーコマンドです。 class EndRenderCommand : public nw::gfx::RenderCommand { NW_DISALLOW_COPY_AND_ASSIGN(EndRenderCommand); public: EndRenderCommand( nw::demo::RenderSystem* renderSystem, nw::gfx::Camera* leftCamera, nw::gfx::Camera* rightCamera, StartRenderCommand* startCommand) : m_RenderSystem(renderSystem), m_LeftCamera(leftCamera), m_RightCamera(rightCamera), m_StartCommand(startCommand) {} virtual ~EndRenderCommand() {} virtual void Invoke(nw::gfx::RenderContext* renderContext) { int commandSize = m_RenderSystem->GetCommandListSwapper()->GetCommandBufferSize(); m_RenderSystem->AddLoadMeterCommandSize(commandSize - m_StartCommand->GetCommandSize()); nw::demo::CommandListSwapper* commandListSwapper = m_RenderSystem->GetCommandListSwapper(); // ステレオ表示用コマンドの作成を終了します。 commandListSwapper->EndCommandSave(); // GPU処理時間計測開始 int profilerLeft = commandListSwapper->AddGpuProfilingStartPoint(true); // 保存したコマンドを左目用の描画として再利用します。 renderContext->SetCameraMatrix(m_LeftCamera); commandListSwapper->ReuseCommand(false); // GPU処理時間計測終了 commandListSwapper->SetGpuProfilingEndPoint(profilerLeft); m_RenderSystem->TransferBuffer(nw::demo::UPPER_SCREEN); // GPU処理時間計測開始 int profilerRight = commandListSwapper->AddGpuProfilingStartPoint(true); // 保存したコマンドを右目用の描画として再利用します。 renderContext->SetCameraMatrix(m_RightCamera); commandListSwapper->ReuseCommand(false); // GPU処理時間計測終了 commandListSwapper->SetGpuProfilingEndPoint(profilerRight); m_RenderSystem->TransferBuffer(nw::demo::EXTENSION_SCREEN); } private: nw::demo::RenderSystem* m_RenderSystem; nw::gfx::Camera* m_LeftCamera; nw::gfx::Camera* m_RightCamera; StartRenderCommand* m_StartCommand; }; enum CameraKind { PERSPECTIVE_CAMERA, FRUSTUM_CAMERA, ORTHO_CAMERA }; nw::gfx::Camera* s_ShadowCamera = NULL; // シャドウに用いるカメラの種類です。 CameraKind s_CameraKind = PERSPECTIVE_CAMERA; // シャドウ用のカメラのパラメータです。 const f32 s_OrthoCameraHeight = 32.0f; const f32 s_ShadowNear = 1.0f; const f32 s_ShadowFar = 500.0f; StartShadowRenderCommand* s_StartShadowRenderCommand = NULL; EndShadowRenderCommand* s_EndShadowRenderCommand = NULL; StartRenderCommand* s_StartRenderCommand = NULL; EndRenderCommand* s_EndRenderCommand = NULL; // シャドウのマテリアル、シェーダー設定用モデルです。 // このモデルはマテリアルとシェーダーの設定に利用されます。 // 実際には描画されません。 nw::gfx::Model* s_ShadowDummyModel = NULL; // シャドウ用のテクスチャです。 nw::gfx::ResImageTexture s_ShadowTexture; // シャドウテクスチャのサイズです。 const s32 s_ShadowTextureSize = 512; // 影の強度(濃さ)です。 const f32 s_ShadowIntensity = 0.2f; //---------------------------------------- // メモリ関係 // デバイスメモリを確保するためのアロケータです。 nw::demo::DemoAllocator s_DeviceAllocator; //---------------------------------------- // ファイル名の定義です。 const wchar_t* SKY_SPHERE_FILE_NAME = NW_DEMO_FILE_PATH(L"SkySphere.bcmdl"); const wchar_t* MODEL_RESOURCE_FILES[] = { NW_DEMO_FILE_PATH(L"MaleShadow.bcmdl"), NW_DEMO_FILE_PATH(L"SceneEnvironmentSetting.bcenv"), NW_DEMO_FILE_PATH(L"SpotLight.bcenv"), NW_DEMO_FILE_PATH(L"AmbientLight.bcenv"), }; const wchar_t* SKELETAL_ANIM_RESOURCE_FILE = NW_DEMO_FILE_PATH(L"WalkAimAt.bcskla"); const wchar_t* SHADOW_MATERIAL_FILE_NAME = NW_DEMO_FILE_PATH(L"ShadowModel.bcmdl"); //---------------------------------------- // 描画関係 const int RENDER_TARGET_COUNT = 2; typedef nw::ut::FixedSizeArray RenderTargetArray; RenderTargetArray s_RenderTargets; nw::demo::SceneSystem* s_SceneSystem = NULL; nw::demo::RenderSystem* s_RenderSystem = NULL; nw::demo::GraphicsDrawing s_GraphicsDrawing; //---------------------------------------- // リソース関係 nw::demo::ResourceArray s_Resources; //---------------------------------------- // シーン関係 nw::gfx::SceneNode* s_SceneRoot = NULL; nw::gfx::SceneNode* s_ModelRoot = NULL; nw::gfx::TransformNode* s_LightRoot = NULL; s32 s_FrameCount = 0; nw::gfx::Camera* s_BaseCamera = NULL; nw::gfx::Camera* s_LeftCamera = NULL; nw::gfx::Camera* s_RightCamera = NULL; nw::gfx::FragmentLight* s_SpotLight = NULL; const f32 s_fNearPlane = 0.1f; const f32 s_fFarPlane = 1000.0f; //---------------------------------------- // シーン環境関係 const s32 ENVIRONMENT_SETTINGS_COUNT = 1; typedef nw::ut::FixedSizeArray SceneEnvironmentSettingArray; SceneEnvironmentSettingArray s_SceneEnvironmentSettings; const s32 s_BaseCameraIndex = 0; const s32 s_ShadowCameraIndex = 1; //---------------------------------------- // アニメーション関係 nw::gfx::SkeletalModel* s_AnimModel = NULL; const int MAX_ANIM_OBJECTS = 8; nw::ut::FixedSizeArray s_AnimObjects; /*!--------------------------------------------------------------------------* @brief グラフィックス関連の初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeGraphics() { nw::gfx::CommandCacheManager::SetAllocator( &s_DeviceAllocator ); // renderDescriptionへステレオの設定を行います。 nw::demo::RenderSystem::Description renderDescription; renderDescription.reusableCommandBufferSize = 0x100000; renderDescription.reusableCommandRequestCount = 512; renderDescription.upperScreenMode = nw::demo::UPPER_SCREEN_MODE_STEREO; s_RenderSystem = nw::demo::RenderSystem::Create(&s_DeviceAllocator, renderDescription); s_GraphicsDrawing.SetScreenSize( renderDescription.lowerScreenDescription.width, renderDescription.lowerScreenDescription.height ); nw::demo::Utility::InitializeGraphicsDrawing(&s_DeviceAllocator, s_GraphicsDrawing); s_RenderTargets.push_back( nw::demo::Utility::CreateUpperScreenBuffer(&s_DeviceAllocator, renderDescription) ); // シャドウ描画用のテクスチャを確保します。 // VRAM に配置して OffScreenBuffer として利用するので、 // LocationFlag(NN_GX_MEM_VRAMA), DynamicAllocation(false) とする。 s_ShadowTexture = nw::gfx::ResImageTexture::DynamicBuilder() .Width(s_ShadowTextureSize) .Height(s_ShadowTextureSize) .MipmapSize(1) .LocationFlag(NN_GX_MEM_VRAMA | GL_NO_COPY_FCRAM_DMP) .Format(nw::gfx::ResPixelBasedTexture::FORMAT_HW_RGBA8) .ExecutingMemoryFill(true) .DynamicAllocation(false) .Create(&s_DeviceAllocator); s_ShadowTexture.Setup(&s_DeviceAllocator, nw::gfx::ResGraphicsFile(NULL)); // オフスクリーンバッファを作成します。 nw::gfx::IRenderTarget* offScreenTarget = nw::gfx::IRenderTarget::CreateOffScreenBuffer(&s_DeviceAllocator, s_ShadowTexture); s_RenderTargets.push_back(offScreenTarget); NW_ASSERT(!s_RenderTargets.empty()); s_RenderSystem->GetRenderContext()->SetRenderTarget(s_RenderTargets[0]); // sceneDescriptionへの標準的な設定はコンストラクタで行われています。 nw::demo::SceneSystem::Description sceneDescription; s_SceneSystem = nw::demo::SceneSystem::Create(&s_DeviceAllocator, sceneDescription); // デモ用の最遠景モデルをレンダリングシステムに設定します。 // gfx のデモでは glClear などを用いずに背景で塗りつぶしを行います。 s_RenderSystem->LoadSkyModel(SKY_SPHERE_FILE_NAME); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateGraphics() { s_ShadowTexture.Cleanup(); if (s_ShadowTexture.IsValid()) { s_ShadowTexture.DynamicDestroy(); } nw::gfx::SafeDestroy(s_LeftCamera); nw::gfx::SafeDestroy(s_RightCamera); nw::gfx::SafeDestroy(s_SceneSystem); nw::gfx::SafeDestroyAll(s_RenderTargets); s_GraphicsDrawing.Finalize(); nw::gfx::SafeDestroy(s_RenderSystem); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief ファイルからトランスフォームアニメーション評価を生成します。 @param[in] maxBones 最大メンバ数です。 @param[in] translateAnimEnabled 移動アニメーションが有効かどうかです。 @param[in] filePath トランスフォームアニメーションファイルのフルパスです。 @return トランスフォームアニメーション評価です。 *---------------------------------------------------------------------------*/ nw::gfx::TransformAnimEvaluator* CreateTransformAnimEvaluator( const int maxMembers, const bool translateAnimEnabled, const wchar_t* filePath ) { //---------------------------------------- // アニメーションリソースを生成します。 nw::demo::ResourceSet* resourceSet = nw::demo::Utility::LoadResources(s_Resources, filePath, &s_DeviceAllocator); if (resourceSet->resource.GetSkeletalAnimsCount() == 0) { return NULL; } nw::anim::ResAnim resAnim = resourceSet->resource.GetSkeletalAnims(0); if (!resAnim.IsValid()) { return NULL; } //---------------------------------------- // トランスフォームアニメーション評価を生成します。 // // アニメーションを1つのモデルにのみ適用する場合や、 // コマ形式データの場合は、 AllocCache を false にすると処理負荷が下がります。 nw::gfx::TransformAnimEvaluator* evaluator = nw::gfx::TransformAnimEvaluator::Builder() .AnimData(resAnim) .MaxMembers(maxMembers) .AllocCache(false) .Create(&s_DeviceAllocator); // 移動アニメーションの無効化フラグを設定します。 evaluator->SetIsTranslateDisabled(!translateAnimEnabled); return evaluator; } /*!--------------------------------------------------------------------------* @brief スケルタルアニメーションを初期化します。 @param[in] model スケルタルモデルです。 *---------------------------------------------------------------------------*/ void InitializeSkeletalAnim(nw::gfx::SkeletalModel* model) { nw::gfx::AnimGroup* animGroup = model->GetSkeletalAnimGroup(); if (animGroup == NULL) // スケルタルアニメーション用のアニメーショングループがありません。 { return; } nw::gfx::ResSkeletalModel resModel = model->GetResSkeletalModel(); nw::gfx::ResSkeleton resSkeleton = resModel.GetSkeleton(); const int maxBones = resSkeleton.GetBonesCount(); const bool translateAnimEnabled = nw::ut::CheckFlag(resSkeleton.GetFlags(), nw::gfx::ResSkeletonData::FLAG_TRANSLATE_ANIMATION_ENABLED); //---------------------------------------- // アニメーション評価を生成します。 nw::gfx::TransformAnimEvaluator* evaluator = CreateTransformAnimEvaluator( maxBones, translateAnimEnabled, SKELETAL_ANIM_RESOURCE_FILE); if (evaluator == NULL) { return; } // アニメーションをバインドします。 bool bindResult = evaluator->Bind(animGroup); //---------------------------------------- // アニメーションをモデルに登録します。 model->SetSkeletalAnimObject(evaluator); s_AnimObjects.PushBack(evaluator); } /*!--------------------------------------------------------------------------* @brief アニメーションの後始末をします。 *---------------------------------------------------------------------------*/ void TerminateAnim(void) { for (int animIdx = 0; animIdx < s_AnimObjects.Size(); ++animIdx) { nw::gfx::SafeDestroy(s_AnimObjects[animIdx]); } s_AnimObjects.clear(); } /*!--------------------------------------------------------------------------* @brief ルートノード関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildRootNodes() { NW_ASSERT(s_SceneRoot == NULL); s_SceneRoot = nw::gfx::TransformNode::DynamicBuilder() .IsFixedSizeMemory(false) .Create(&s_DeviceAllocator); NW_NULL_ASSERT(s_SceneRoot); NW_ASSERT(s_ModelRoot == NULL); s_ModelRoot = nw::gfx::TransformNode::DynamicBuilder() .IsFixedSizeMemory(false) .Create(&s_DeviceAllocator); s_SceneRoot->AttachChild(s_ModelRoot); NW_NULL_ASSERT(s_ModelRoot); NW_ASSERT(s_LightRoot == NULL); s_LightRoot = nw::gfx::TransformNode::DynamicBuilder() .IsFixedSizeMemory(false) .Create(&s_DeviceAllocator); s_SceneRoot->AttachChild(s_LightRoot); NW_NULL_ASSERT(s_LightRoot); } /*!--------------------------------------------------------------------------* @brief カメラ関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildCameras() { nw::demo::Utility::CreateStereoCameras( &s_BaseCamera, &s_LeftCamera, &s_RightCamera, &s_DeviceAllocator, nw::math::VEC3(20.0f, 15.0f, 20.0f), nw::math::VEC3(0.0f, 10.0f, 0.0f), s_fNearPlane, s_fFarPlane ); s_SceneRoot->AttachChild(s_BaseCamera); s_SceneSystem->GetCameraController()->Register(s_BaseCamera); // シャドウ用のカメラを作成します。 switch (s_CameraKind) { case PERSPECTIVE_CAMERA: { s_ShadowCamera = nw::demo::Utility::CreateCamera( &s_DeviceAllocator, nw::math::VEC3(20.0f, 15.0f, 20.0f), nw::math::VEC3(0.0f, 10.0f, 0.0f), s_ShadowNear, s_ShadowFar, NW_MATH_DEG_TO_RAD(45.0f), nw::math::PIVOT_NONE); } break; case FRUSTUM_CAMERA: { s_ShadowCamera = nw::demo::Utility::CreateFrustumCamera( &s_DeviceAllocator, nw::math::VEC3(20.0f, 15.0f, 20.0f), nw::math::VEC3(0.0f, 10.0f, 0.0f), s_ShadowNear, s_ShadowFar, nw::math::VEC2(0.0f, 0.0f), 1.0f, nw::math::PIVOT_NONE); } break; case ORTHO_CAMERA: { s_ShadowCamera = nw::demo::Utility::CreateOrthoCamera( &s_DeviceAllocator, nw::math::VEC3(20.0f, 15.0f, 20.0f), nw::math::VEC3(0.0f, 10.0f, 0.0f), s_ShadowNear, s_ShadowFar, nw::math::VEC2(0.0f, 0.0f), s_OrthoCameraHeight, nw::math::PIVOT_NONE); } break; default: { NW_FATAL_ERROR("Unsupported camera type."); } break; } NW_NULL_ASSERT(s_ShadowCamera); s_SceneRoot->AttachChild(s_ShadowCamera); } /*!--------------------------------------------------------------------------* @brief シャドウ用のテクスチャコンバイナ関連の構築をします。 *---------------------------------------------------------------------------*/ void SetupShadowMaterial(nw::gfx::ResMaterial resMaterial) { nw::gfx::ResTextureCoordinator resTexCoord = resMaterial.GetTextureCoordinators(0); resTexCoord.SetReferenceCamera(1); nw::gfx::ResPixelBasedTextureMapper resTextureMapper = resMaterial.GetTextureMappers(0); // ダミーのテクスチャをシャドウ用のテクスチャに差し替えます。 nw::gfx::ResTexture dummyTexture = resTextureMapper.GetTexture().Dereference(); dummyTexture.Cleanup(); resTextureMapper.SetTexture(s_ShadowTexture); } /*!--------------------------------------------------------------------------* @brief リソース関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildResources(nw::demo::ResourceSet* resourceSet) { resourceSet->resource.ForeachTexture(nw::gfx::LocationFlagSetter(NN_GX_MEM_VRAMA | GL_NO_COPY_FCRAM_DMP)); resourceSet->resource.ForeachIndexStream(nw::gfx::LocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP)); resourceSet->resource.ForeachVertexStream(nw::gfx::LocationFlagSetter(NN_GX_MEM_VRAMB | GL_NO_COPY_FCRAM_DMP)); nw::gfx::Result result = resourceSet->resource.Setup(&s_DeviceAllocator); if (result.IsFailure()) { NW_FATAL_ERROR("Fail to set up model. A result code is 0x%x", result.GetCode()); } nw::ut::MoveArray sceneNodeArray(&s_DeviceAllocator); nw::gfx::ResModelArray models = resourceSet->resource.GetModels(); nw::gfx::ResModelArray::iterator modelsEnd = models.end(); for (nw::gfx::ResModelArray::iterator modelResource = models.begin(); modelResource != modelsEnd; ++modelResource) { nw::gfx::SceneNode* node = nw::demo::Utility::CreateSceneNode( &s_DeviceAllocator, (*modelResource) ); // plane の Texture0 にオフスクリーンバッファをテクスチャとしてセットする if (::std::strcmp((*modelResource).GetName(), "Plane") == 0) { nw::gfx::Model* model = nw::ut::DynamicCast(node); NW_ASSERT(model->GetMaterialCount() != 0); nw::gfx::Material* material = model->GetMaterial(0); nw::gfx::ResMaterial resMaterial = material->GetOriginal(); NW_ASSERT(resMaterial.IsValid()); SetupShadowMaterial(resMaterial); } else if (::std::strcmp((*modelResource).GetName(), "Male") == 0) { nw::gfx::SkeletalModel* skeletalModel = nw::ut::DynamicCast(node); s_AnimModel = skeletalModel; // 影を生成するモデルにフラグを設定します。 skeletalModel->SetUserParameter(SHADOW_CASTER); } sceneNodeArray.push_back(node); } nw::gfx::ResLightArray lights = resourceSet->resource.GetLights(); nw::gfx::ResLightArray::iterator lightsEnd = lights.end(); for (nw::gfx::ResLightArray::iterator lightResource = lights.begin(); lightResource != lightsEnd; ++lightResource) { nw::gfx::SceneNode* node = nw::demo::Utility::CreateSceneNode( &s_DeviceAllocator, (*lightResource) ); if (::std::strcmp((*lightResource).GetName(), "SpotLight") == 0) { s_SpotLight = nw::ut::DynamicCast(node); s_LightRoot->AttachChild(s_SpotLight); } sceneNodeArray.push_back(node); } // 親子付け参照関係を解決 nw::gfx::SceneHelper::ResolveReference(sceneNodeArray); // モデルをシーンに追加 nw::gfx::SceneHelper::ForeachRootNodes( sceneNodeArray.Begin(), sceneNodeArray.End(), nw::gfx::AttachNode(s_ModelRoot) ); nw::gfx::ResSceneEnvironmentSettingArray settings = resourceSet->resource.GetSceneEnvironmentSettings(); nw::gfx::ResSceneEnvironmentSettingArray::iterator settingsEnd = settings.end(); for (nw::gfx::ResSceneEnvironmentSettingArray::iterator settingResource = settings.begin(); settingResource != settingsEnd; ++settingResource) { nw::gfx::SceneObject* sceneObject = nw::gfx::SceneBuilder() .Resource(*settingResource) .CreateObject(&s_DeviceAllocator, &s_DeviceAllocator); nw::gfx::SceneEnvironmentSetting* sceneEnvironmentSetting = nw::ut::DynamicCast(sceneObject); NW_NULL_ASSERT(sceneEnvironmentSetting); s_SceneEnvironmentSettings.push_back(sceneEnvironmentSetting); } } /*!--------------------------------------------------------------------------* @brief シーンを初期化します。 *---------------------------------------------------------------------------*/ void InitializeScenes() { BuildRootNodes(); BuildCameras(); NW_FOREACH(const wchar_t* name, MODEL_RESOURCE_FILES) { BuildResources(nw::demo::Utility::LoadResources(s_Resources, name, &s_DeviceAllocator)); } if (s_AnimModel != NULL) { InitializeSkeletalAnim(s_AnimModel); } // シーンツリーを巡回して初期化を行います。 s_SceneSystem->InitializeScene(s_SceneRoot); s_SceneSystem->UpdateScene(); // シーン環境の参照解決を行い設定します。 s_RenderSystem->SetSceneEnvironmentSettings(s_SceneSystem, &s_SceneEnvironmentSettings); // カメラを設定します。 nw::gfx::SceneEnvironment& sceneEnvironment = s_RenderSystem->GetSceneEnvironment(); sceneEnvironment.SetCamera(s_BaseCameraIndex, s_BaseCamera); sceneEnvironment.SetCamera(s_ShadowCameraIndex, s_ShadowCamera); nw::demo::Utility::SetCameraAspectRatio(s_BaseCamera, s_RenderTargets[0]); // シャドウ用のマテリアルとシェーダーを含むモデルを読み込みます。 nw::demo::ResourceSet* resourceSet = nw::demo::Utility::LoadResources(s_Resources, SHADOW_MATERIAL_FILE_NAME, &s_DeviceAllocator); nw::gfx::ResModel resShadowModel = resourceSet->resource.GetModels("ShadowModel"); nw::gfx::Result result = resShadowModel.Setup(&s_DeviceAllocator, resourceSet->resource); NW_ASSERT(result.IsSuccess()); nw::gfx::SceneNode* node = nw::demo::Utility::CreateSceneNode( &s_DeviceAllocator, resShadowModel ); s_ShadowDummyModel = nw::ut::DynamicCast(node); NW_NULL_ASSERT(s_ShadowDummyModel); NW_ASSERT(s_ShadowDummyModel->GetMaterialCount() != 0); // 影の強度(濃さ)を設定します。 nw::gfx::ResMaterialColor materialColor = s_ShadowDummyModel->GetMaterial(0)->GetOriginal().GetMaterialColor(); materialColor.SetDiffuse(s_ShadowIntensity, s_ShadowIntensity, s_ShadowIntensity); nw::demo::CommandListSwapper* commandListSwapper = s_RenderSystem->GetCommandListSwapper(); NW_ASSERT(s_ShadowDummyModel->GetMaterialCount() != 0); nw::gfx::Material* shadowMaterial = s_ShadowDummyModel->GetMaterial(0); // レンダーコマンドを作成します。 void* startShadowCommandMemory = s_DeviceAllocator.Alloc(sizeof(StartShadowRenderCommand)); NW_NULL_ASSERT(startShadowCommandMemory); s_StartShadowRenderCommand = new(startShadowCommandMemory) StartShadowRenderCommand( commandListSwapper, s_RenderTargets[1], s_ShadowCameraIndex, shadowMaterial); void* endShadowCommandMemory = s_DeviceAllocator.Alloc(sizeof(EndShadowRenderCommand)); NW_NULL_ASSERT(endShadowCommandMemory); s_EndShadowRenderCommand = new(endShadowCommandMemory) EndShadowRenderCommand(commandListSwapper, s_StartShadowRenderCommand); void* startCommandMemory = s_DeviceAllocator.Alloc(sizeof(StartRenderCommand)); NW_NULL_ASSERT(startCommandMemory); s_StartRenderCommand = new(startCommandMemory) StartRenderCommand(s_RenderSystem, s_RenderTargets[0], s_BaseCameraIndex); void* endCommandMemory = s_DeviceAllocator.Alloc(sizeof(EndRenderCommand)); NW_NULL_ASSERT(endCommandMemory); s_EndRenderCommand = new(endCommandMemory) EndRenderCommand(s_RenderSystem, s_LeftCamera, s_RightCamera, s_StartRenderCommand); NW_GL_ASSERT(); s_FrameCount = 0; } /*!--------------------------------------------------------------------------* @brief シーン関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateScenes() { s_DeviceAllocator.Free(s_EndRenderCommand); s_DeviceAllocator.Free(s_StartRenderCommand); s_DeviceAllocator.Free(s_EndShadowRenderCommand); s_DeviceAllocator.Free(s_StartShadowRenderCommand); nw::ut::SafeDestroy(s_ShadowDummyModel); nw::gfx::SafeDestroyBranch(s_SceneRoot); nw::demo::SafeCleanupResources(s_Resources); nw::ut::SafeDestroyAll(s_SceneEnvironmentSettings); TerminateAnim(); NW_GL_ASSERT(); s_Resources.clear(); s_SceneEnvironmentSettings.clear(); s_ModelRoot = NULL; s_LightRoot = NULL; } /*!--------------------------------------------------------------------------* @brief シーンを更新します。 *---------------------------------------------------------------------------*/ void UpdateScene() { float radian = static_cast((s_FrameCount) % (314 * 2)) * 0.01f; // ライトルートがY軸中心に回転します。 nw::gfx::TransformNode* lightNode = nw::ut::DynamicCast(s_LightRoot); NW_NULL_ASSERT(lightNode); lightNode->Transform().SetRotateXYZ(0.0f, radian, 0.0f); s_SceneSystem->GetCameraController()->Update(); s_SceneSystem->UpdateScene(); s_BaseCamera->UpdateCameraMatrix(); s_RenderSystem->CalcStereoCamera(s_LeftCamera, s_RightCamera, s_BaseCamera, s_fNearPlane + 5.0f); // シャドウの描画をライト方向から行うために、カメラの位置と方向をライトに追従させます。 nw::math::VEC3 translate; translate.x = s_SpotLight->WorldMatrix().m[0][3]; translate.y = s_SpotLight->WorldMatrix().m[1][3]; translate.z = s_SpotLight->WorldMatrix().m[2][3]; s_ShadowCamera->WorldMatrix().m[0][3] = translate.x; s_ShadowCamera->WorldMatrix().m[1][3] = translate.y; s_ShadowCamera->WorldMatrix().m[2][3] = translate.z; const nw::math::VEC3& direction = s_SpotLight->Direction(); s_ShadowCamera->SetTargetPosition(translate + direction); s_ShadowCamera->UpdateCameraMatrix(); ++s_FrameCount; } /*!--------------------------------------------------------------------------* @brief レンダーキューにレンダーエレメントを追加します。 *---------------------------------------------------------------------------*/ void SubmitView() { nw::gfx::RenderContext* renderContext = s_RenderSystem->GetRenderContext(); nw::gfx::SceneEnvironment& sceneEnvironment = renderContext->GetSceneEnvironment(); nw::gfx::SceneContext* sceneContext = s_SceneSystem->GetSceneContext(); nw::gfx::RenderQueue* renderQueue = s_RenderSystem->GetRenderQueue(); renderQueue->Reset(); // 描画対象とレンダーコマンドをレンダーキューに追加します。 // 分かりやすくするために処理される順に Submit or Enqueue を行います。 // シャドウ用の設定を行うコマンドをレイヤー0の後に実行するコールバックに追加します。 s_RenderSystem->EnqueueRenderCommand( s_StartShadowRenderCommand, nw::gfx::ResMaterial::TRANSLUCENCY_KIND_OPAQUE, 0, 0); // シャドウキャスターとして指定されたモデルのみを レイヤー1としてレンダーキューに追加します。 s_SceneSystem->GetSceneUpdater()->SubmitView( renderQueue, sceneContext, *s_ShadowCamera, 1, 1, &s_ShadowCasterModel, s_RenderSystem->GetRenderSortMode()); // シャドウの後処理を行うコマンドをレイヤー1の後に実行するコールバックに追加します。 s_RenderSystem->EnqueueRenderCommand( s_EndShadowRenderCommand, nw::gfx::ResMaterial::TRANSLUCENCY_KIND_OPAQUE, 0, 1); // ステレオ描画の前処理の設定を行うコマンドをレイヤー1の後に実行するコールバックに追加します。 s_RenderSystem->EnqueueRenderCommand( s_StartRenderCommand, nw::gfx::ResMaterial::TRANSLUCENCY_KIND_OPAQUE, 1, 1); // シーンコンテキストの全てのモデルをレイヤー2でレンダーキューに追加します。 s_SceneSystem->GetSceneUpdater()->SubmitView( renderQueue, sceneContext, *s_BaseCamera, 2, s_RenderSystem->GetRenderSortMode()); // ステレオ描画の後処理の設定を行うコマンドをレイヤー2の後に実行するコールバックに追加します。 s_RenderSystem->EnqueueRenderCommand( s_EndRenderCommand, nw::gfx::ResMaterial::TRANSLUCENCY_KIND_OPAQUE, 0, 2); std::sort( renderQueue->Begin(), renderQueue->End(), nw::gfx::RenderElementCompare()); } //---------------------------------------- struct RenderSceneInternalFunctor : public std::unary_function { nw::gfx::RenderContext* m_RenderContext; nw::gfx::MeshRenderer* m_MeshRenderer; RenderSceneInternalFunctor(nw::gfx::RenderContext* renderContext, nw::gfx::MeshRenderer* meshRenderer) : m_RenderContext(renderContext), m_MeshRenderer(meshRenderer) { NW_NULL_ASSERT(renderContext); NW_NULL_ASSERT(meshRenderer); } void operator()(nw::gfx::RenderElement& element) { if (element.IsCommand()) { nw::gfx::RenderCommand* command = element.GetCommand(); NW_NULL_ASSERT(command); command->Invoke(this->m_RenderContext); } else { nw::gfx::ResMesh mesh = element.GetMesh(); nw::gfx::Model* model = element.GetModel(); model->PreRenderSignal()(model, mesh, this->m_RenderContext); this->m_MeshRenderer->RenderMesh(mesh, model); model->PostRenderSignal()(model, mesh, this->m_RenderContext); } NW_GL_ASSERT(); } }; /*!--------------------------------------------------------------------------* @brief レンダーキューに基づいて描画を行います。 *---------------------------------------------------------------------------*/ void RenderScene() { nw::gfx::RenderContext* renderContext = s_RenderSystem->GetRenderContext(); nw::gfx::RenderQueue* renderQueue = s_RenderSystem->GetRenderQueue(); nw::gfx::MeshRenderer* meshRenderer = s_RenderSystem->GetMeshRenderer(); std::for_each( renderQueue->Begin(), renderQueue->End(), RenderSceneInternalFunctor(renderContext, meshRenderer)); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief 負荷表示やテスト機能の処理をおこないます。 *---------------------------------------------------------------------------*/ void ReportDemo() { NW_PROFILE("ReportDemo"); // 負荷表示からはこれらの負荷は除きます。 s_RenderSystem->SuspendLoadMeter(); s_RenderSystem->AddLoadMeterCommandSize( s_EndShadowRenderCommand->GetCommandSize() - s_StartShadowRenderCommand->GetCommandSize()); nw::demo::DebugUtility::CalcLoadMeter(s_RenderSystem); s_GraphicsDrawing.BeginDrawingShape(); nw::demo::DebugUtility::DrawLoadMeter( s_RenderSystem, &s_GraphicsDrawing ); s_GraphicsDrawing.EndDrawingShape(); s_GraphicsDrawing.BeginDrawingString(); nw::demo::DebugUtility::DrawLoadMeterText( s_RenderSystem, &s_GraphicsDrawing ); s_GraphicsDrawing.EndDrawingString(); s_RenderSystem->ResumeLoadMeter(); } /*!--------------------------------------------------------------------------* @brief シーンをデモンストレーションします。 *---------------------------------------------------------------------------*/ void DemoScene() { NW_ASSERT(!s_RenderTargets.empty()); nw::gfx::RenderContext* renderContext = s_RenderSystem->GetRenderContext(); InitializeScenes(); nw::demo::DebugUtility::PostInitializeScenes(); bool isContinuing = true; while ( isContinuing ) { nw::demo::DebugUtility::AdvanceAutoTestFrame(); nw::demo::PadFactory::GetPad()->Update(); UpdateScene(); SubmitView(); RenderScene(); s_RenderSystem->ClearBySkyModel(s_BaseCamera); ReportDemo(); s_RenderSystem->TransferBuffer(nw::demo::LOWER_SCREEN); s_RenderSystem->PresentBuffer(nw::demo::UPPER_SCREEN | nw::demo::LOWER_SCREEN | nw::demo::EXTENSION_SCREEN); renderContext->ResetState(); if (nw::demo::Utility::IsTerminating()) { isContinuing = false; } } nw::demo::DebugUtility::PreTerminateScenes(); TerminateScenes(); } } // namespace /*!--------------------------------------------------------------------------* @brief メイン関数です。 *---------------------------------------------------------------------------*/ void nnMain() { nw::demo::InitializeGraphicsSystem(&s_DeviceAllocator); nw::demo::PadFactory::Initialize(&s_DeviceAllocator); NW_DEMO_TEST_LOOP(&s_DeviceAllocator, NULL, &s_RenderSystem) { InitializeGraphics(); DemoScene(); TerminateGraphics(); } nw::demo::PadFactory::Finalize(); nw::demo::FinalizeGraphicsSystem(); }