/*---------------------------------------------------------------------------* Project: NintendoWare File: AnimationDemo.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 { //---------------------------------------- // メモリ関係 // デバイスメモリを確保するためのアロケータです。 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"Male.bcmdl"), NW_DEMO_FILE_PATH(L"SceneEnvironmentSetting.bcenv"), NW_DEMO_FILE_PATH(L"FragmentLight.bcenv"), }; const wchar_t* SKELETAL_ANIM_RESOURCE_FILE = NW_DEMO_FILE_PATH(L"Walk.bcskla"); //---------------------------------------- // 描画関係 const s32 RENDER_TARGET_COUNT = 1; 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; //---------------------------------------- // シーン関係 const int SCENE_NODE_COUNT = 4; nw::gfx::SceneNode* s_SceneRoot = NULL; nw::gfx::SceneNode* s_ModelRoot = NULL; s32 s_FrameCount = 0; nw::gfx::Camera* s_BaseCamera = NULL; nw::gfx::Camera* s_LeftCamera = NULL; nw::gfx::Camera* s_RightCamera = NULL; const f32 s_fNearPlane = 0.1f; //---------------------------------------- // シーン環境関係 const s32 ENVIRONMENT_SETTINGS_COUNT = 1; typedef nw::ut::FixedSizeArray SceneEnvironmentSettingArray; SceneEnvironmentSettingArray s_SceneEnvironmentSettings; const s32 s_BaseCameraIndex = 0; //---------------------------------------- // アニメーション関係 nw::gfx::SkeletalModel* s_AnimModel = NULL; nw::gfx::TransformAnimEvaluator* s_AnimEvaluator = NULL; float s_StepFrame = 1.0f; /*!--------------------------------------------------------------------------* @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) ); NW_ASSERT(!s_RenderTargets.empty()); s_RenderSystem->GetRenderContext()->SetRenderTarget(s_RenderTargets.front()); // sceneDescriptionへの標準的な設定はコンストラクタで行われています。 nw::demo::SceneSystem::Description sceneDescription; sceneDescription.isFixedSizeMemory = true; s_SceneSystem = nw::demo::SceneSystem::Create(&s_DeviceAllocator, sceneDescription); // デモ用の最遠景モデルをレンダリングシステムに設定します。 // gfx のデモでは glClear などを用いずに背景で塗りつぶしを行います。 s_RenderSystem->LoadSkyModel(SKY_SPHERE_FILE_NAME); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateGraphics() { 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 ファイルから TransformAnimEvaluator を生成します。 リソースからアニメーションを計算するための AnimEvaluator(アニメーション評価)を生成します。 @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; } // resAnim から、AnimEvaluator::Builder を用いて AnimEvaluator を生成します。 // スケルタルアニメーションの場合は、TransformAnimEvaluator::Builder を使用します。 // // アニメーションを1つのモデルにのみ適用する場合や、 // コマ形式データの場合は、 AllocCache を false にすると処理負荷が下がります。 nw::gfx::TransformAnimEvaluator* evaluator = nw::gfx::TransformAnimEvaluator::Builder() .AnimData(resAnim) .MaxMembers(maxMembers) .MaxAnimMembers(resAnim.GetMemberAnimSetCount()) .AllocCache(false) .Create(&s_DeviceAllocator); // 移動アニメーションの無効化フラグを設定します。 evaluator->SetIsTranslateDisabled(!translateAnimEnabled); return evaluator; } /*!--------------------------------------------------------------------------* @brief スケルタルアニメーションを初期化します。 生成した AnimEvaluator のインスタンスにアニメーショングループを バインドし、その AnimEvaluator をモデルにセットします。 @param[in] model スケルタルモデルです。 @return トランスフォームアニメーション評価です。 *---------------------------------------------------------------------------*/ nw::gfx::TransformAnimEvaluator* InitializeSkeletalAnim(nw::gfx::SkeletalModel* model) { // モデルからアニメーショングループを取得します。 // // アニメーショングループはアニメーション対象メンバへのポインタを保持します。 // 対象メンバとは、モデルがもつアニメーション可能な要素 //(例えば、マテリアルアニメーションでは Diffuse や Texture など)です。 // ここでは、スケルタルモデルから、ボーンのトランスフォームを含む // アニメーショングループを取得します。 // // アニメーションの対象や種類によって用いる関数が異なります。 // ・SkeletalModel::GetSkeletalAnimGroup // ・Model::GetVisibilityAnimGroup // ・Model::GetMaterialAnimGroup // ・Light::GetAnimGroup // ・Camera::GetAnimGroup nw::gfx::AnimGroup* animGroup = model->GetSkeletalAnimGroup(); if (animGroup == NULL) // スケルタルアニメーション用のアニメーショングループがありません。 { return NULL; } nw::gfx::ResSkeletalModel resModel = model->GetResSkeletalModel(); nw::gfx::ResSkeleton resSkeleton = resModel.GetSkeleton(); const int maxBones = resSkeleton.GetBonesCount(); // 体型の異なるモデルでアニメーションを共有する場合には // アニメーションの平行移動成分を無効にする必要があります。 // 本デモでは ResSkeleton から取得したフラグにより // TransformAnimEvaluator の移動アニメを無効化していますが、 // 現バージョンでは ResSkeleton にあらかじめフラグを設定しておく手段は用意されておりません。 // またフラグが有効であれば自動的に移動アニメを無効にするといった機能もありません。 // この機能についての将来的な対応は未定です。 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 NULL; } // アニメーショングループを AnimEvaluator にバインドします。 // これにより、アニメーション対象メンバにアニメーションデータが関連付けられます。 bool bindResult = evaluator->Bind(animGroup); // AnimEvaluator をモデルに設定します。 // AnimEvaluator は一つのモデルに対して複数設定することができ、 // その際には、AnimEvaluator 毎に objectIndex を指定します。 // 詳しくは、 PartialAnimationDemo を参照してください。 // // アニメーションの対象や種類によって用いる関数が異なります。 // SkeletalModel::SetSkeletalAnimObject // Model::SetVisibilityAnimObject // Model::SetMaterialAnimObject // Light::SetAnimObject // Camera::SetAnimObject model->SetSkeletalAnimObject(evaluator); return evaluator; } /*!--------------------------------------------------------------------------* @brief アニメーションの再生速度を変更します。 十字ボタンの左右でアニメーションの更新フレームを変更します。 @param[in] model モデルです。 *---------------------------------------------------------------------------*/ void ChangeSpeed(nw::gfx::SkeletalModel* model) { NW_NULL_ASSERT(model); nw::gfx::AnimObject* animObject = model->GetSkeletalAnimObject(); nw::gfx::TransformAnimEvaluator* evaluator = nw::ut::DynamicCast(animObject); if (evaluator != NULL) { nw::demo::Pad* pad = nw::demo::PadFactory::GetPad(); if (pad->IsButtonPress(nw::demo::Pad::BUTTON_LEFT)) { s_StepFrame -= 0.02f; } if (pad->IsButtonPress(nw::demo::Pad::BUTTON_RIGHT)) { s_StepFrame += 0.02f; } s_StepFrame = nw::ut::Clamp(s_StepFrame, 0.0f, 50.0f); // アニメーションの更新フレームを変更します。 evaluator->SetStepFrame(s_StepFrame); } } /*!--------------------------------------------------------------------------* @brief ルートノード関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildRootNodes() { NW_ASSERT(s_SceneRoot == NULL); s_SceneRoot = nw::gfx::TransformNode::DynamicBuilder() .Create(&s_DeviceAllocator); NW_NULL_ASSERT(s_SceneRoot); NW_ASSERT(s_ModelRoot == NULL); s_ModelRoot = nw::gfx::TransformNode::DynamicBuilder() .Create(&s_DeviceAllocator); s_SceneRoot->AttachChild(s_ModelRoot); NW_NULL_ASSERT(s_ModelRoot); } /*!--------------------------------------------------------------------------* @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_SceneRoot->AttachChild(s_BaseCamera); s_SceneSystem->GetCameraController()->Register(s_BaseCamera); } /*!--------------------------------------------------------------------------* @brief リソース関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildResources(nw::demo::ResourceSet* resourceSet) { nw::gfx::ResGraphicsFile resFile = resourceSet->resource; void* imageData = resFile.GetImageBlockData(); // テクスチャ、頂点バッファを手動で VRAM に転送します。 if (imageData) { s32 size = resFile.GetImageBlockDataSize(); void* vramAddr = nw::demo::AllocateGraphicsMemory(NN_GX_MEM_VRAMA, NN_GX_MEM_TEXTURE, 0, size); // データキャッシュをフラッシュし、VRAM に転送します。 // nngxAddVramDmaCommand 経由でキャッシュのフラッシュがおこなわれます。 nngxAddVramDmaCommand( imageData, vramAddr, size ); // 転送したイメージデータのアドレスを Setup 前にリソースに設定します。 nw::gfx::TransferedVramAddressSetter locationSetter( imageData, vramAddr ); resFile.ForeachTexture(locationSetter); resFile.ForeachIndexStream(locationSetter); resFile.ForeachVertexStream(locationSetter); } nw::gfx::Result result = resFile.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(SCENE_NODE_COUNT, &s_DeviceAllocator); // スケルタルモデルのインスタンスを生成します。 nw::gfx::ResModelArray models = resFile.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) ); NW_NULL_ASSERT(node); sceneNodeArray.push_back(node); s_AnimModel = nw::ut::DynamicCast(node); } nw::gfx::ResLightArray lights = resFile.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) ); NW_NULL_ASSERT(node); 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 = resFile.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() { s_StepFrame = 1.0f; BuildRootNodes(); BuildCameras(); NW_FOREACH(const wchar_t* name, MODEL_RESOURCE_FILES) { BuildResources(nw::demo::Utility::LoadResources(s_Resources, name, &s_DeviceAllocator)); } // スケルタルアニメーションの初期化を行います。 s_AnimEvaluator = InitializeSkeletalAnim(s_AnimModel); NW_NULL_ASSERT(s_AnimEvaluator); 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); nw::demo::Utility::SetCameraAspectRatio(s_BaseCamera, s_RenderTargets[0]); NW_GL_ASSERT(); s_FrameCount = 0; } /*!--------------------------------------------------------------------------* @brief シーン関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateScenes() { nw::gfx::SafeDestroyBranch(s_SceneRoot); nw::demo::SafeCleanupResources(s_Resources); nw::ut::SafeDestroyAll(s_SceneEnvironmentSettings); nw::ut::SafeDestroy(s_AnimEvaluator); NW_GL_ASSERT(); s_Resources.clear(); s_SceneEnvironmentSettings.clear(); s_ModelRoot = NULL; } /*!--------------------------------------------------------------------------* @brief シーンを更新します。 *---------------------------------------------------------------------------*/ void UpdateScene() { ChangeSpeed(s_AnimModel); s_SceneSystem->GetCameraController()->Update(); // SceneSystem::UpdateScene で SceneUpdater::UpdateAll が呼ばれます。 // UpdateAll を行うことにより、モデルにバインドされた AnimEvaluator が // 評価され、アニメーションが毎フレーム更新されます。 s_SceneSystem->UpdateScene(); s_BaseCamera->UpdateCameraMatrix(); s_RenderSystem->CalcStereoCamera(s_LeftCamera, s_RightCamera, s_BaseCamera, s_fNearPlane + 5.0f); ++s_FrameCount; } /*!--------------------------------------------------------------------------* @brief 負荷表示やテスト機能の処理をおこないます。 *---------------------------------------------------------------------------*/ void ReportDemo() { NW_PROFILE("ReportDemo"); // 負荷表示からはこれらの負荷は除きます。 s_RenderSystem->SuspendLoadMeter(); 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 ); const int dumpPositionX = 10; const int dumpPositionY = 10; s_GraphicsDrawing.DrawString( dumpPositionX, dumpPositionY, "step frame: %0.2f", s_StepFrame ); 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(); renderContext->SetActiveCamera(s_BaseCameraIndex); s_RenderSystem->SubmitView(s_SceneSystem); s_RenderSystem->SetRenderTarget(s_RenderTargets[0]); s_RenderSystem->RenderStereoScene(s_LeftCamera, s_RightCamera); 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(); }