/*---------------------------------------------------------------------------* Project: NintendoWare File: ParticleMissileDemo.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: $ *---------------------------------------------------------------------------*/ #define NW_DEBUG_CHECK_MEMORY_LEAK #include #include #include #include #include #include #include namespace { //---------------------------------------- // メモリ関係 // デバイスメモリを確保するためのアロケータです。 nw::demo::DemoAllocator s_DeviceAllocator; nw::demo::DemoAllocator s_ParticleAllocator; //---------------------------------------- // ファイル名の定義です。 const wchar_t* SKY_SPHERE_FILE_NAME = NW_DEMO_FILE_PATH(L"SkySphere.bcmdl"); //---------------------------------------- // 描画関係 const int 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::gfx::ParticleContext* s_ParticleContext = NULL; nw::demo::GraphicsDrawing s_GraphicsDrawing; //---------------------------------------- // シーン関係 nw::gfx::SceneNode* s_SceneRoot = 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 s_BaseCameraIndex = 0; //---------------------------------------- // パーティクル関係 nw::gfx::ParticleSceneUpdater* s_ParticleSceneUpdater = NULL; nw::demo::FlushCache* s_FlushCache; /*!--------------------------------------------------------------------------* @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; s_SceneSystem = nw::demo::SceneSystem::Create(&s_DeviceAllocator, sceneDescription); s_ParticleContext = nw::gfx::ParticleContext::Builder() .Create(&s_DeviceAllocator); s_ParticleSceneUpdater = nw::gfx::ParticleSceneUpdater::Builder() .Create(&s_DeviceAllocator); // デモ用の最遠景モデルをレンダリングシステムに設定します。 // 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_ParticleSceneUpdater); nw::gfx::SafeDestroy(s_ParticleContext); nw::gfx::SafeDestroy(s_SceneSystem); nw::gfx::SafeDestroyAll(s_RenderTargets); s_GraphicsDrawing.Finalize(); nw::gfx::SafeDestroy(s_RenderSystem); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief ルートノード関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildRootNodes() { NW_ASSERT(s_SceneRoot == NULL); s_SceneRoot = nw::gfx::TransformNode::DynamicBuilder() .IsFixedSizeMemory(false) .Create(&s_DeviceAllocator); NW_NULL_ASSERT(s_SceneRoot); } /*!--------------------------------------------------------------------------* @brief カメラ関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildCameras() { nw::demo::Utility::CreateStereoCameras( &s_BaseCamera, &s_LeftCamera, &s_RightCamera, &s_DeviceAllocator, nw::math::VEC3(0.0f, 15.0f, 80.f), nw::math::VEC3(0.0f, 15.0f, 0.0f), s_fNearPlane ); s_SceneRoot->AttachChild(s_BaseCamera); s_SceneSystem->GetCameraController()->Register(s_BaseCamera); } /*!--------------------------------------------------------------------------* @brief シーンを初期化します。 *---------------------------------------------------------------------------*/ void InitializeScenes() { BuildRootNodes(); BuildCameras(); // シーンツリーを巡回して初期化を行います。 s_SceneSystem->InitializeScene(s_SceneRoot); s_SceneSystem->UpdateScene(); // カメラを設定します。 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_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief シーンを更新します。 *---------------------------------------------------------------------------*/ void UpdateScene() { NW_ASSERT(0 < s_RenderTargets.size()); s_SceneSystem->GetCameraController()->Update(); s_SceneSystem->UpdateScene(); s_BaseCamera->UpdateCameraMatrix(); NW_NULL_ASSERT(s_ParticleSceneUpdater); s_ParticleSceneUpdater->UpdateNode( s_SceneSystem->GetSceneContext(), s_ParticleContext); s_RenderSystem->CalcStereoCamera(s_LeftCamera, s_RightCamera, s_BaseCamera, s_fNearPlane + 5.0f); s_FrameCount++; s_FlushCache->Execute(); } /*!--------------------------------------------------------------------------* @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 ); s_GraphicsDrawing.EndDrawingString(); s_RenderSystem->ResumeLoadMeter(); } //---------------------------------------- // デモ固有の変数 // パーティクルエフェクトクラス nw::demo::ParticleEffect* s_ParticleEffect = NULL; // パーティクルノードクラス nw::demo::ParticleNode* s_PaticleNode = NULL; //---------------------------------------- // リソース関係 nw::demo::ResourceArray s_Resources; // ロードするエフェクトファイル const wchar_t* EFFECT_RESOURCE_FILE = NW_DEMO_FILE_PATH(L"missile_particle.bcptl"); // ロードするエフェクトシェーダファイル const wchar_t* SHADER_RESOURCE_FILE_NAME = NW_DEMO_FILE_PATH(L"nwgfx_ParticleDefaultShader.bcsdr"); // 射出角度 f32 s_ShootAngle = 0.0f; /*!--------------------------------------------------------------------------* @brief ParticleMissileDemoの初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeParticleMissileDemo() { // 親からチャイルドへ速度継承を利用してパーティクルでミサイルを作成するデモです。 // データ内には、ParticleSetが親1つ、子2つの3つのParticleSetがあり、 // 2つ目のParticleSetが生成時に親からの速度継承を行っています。 // ParticleSet0:ミサイル本体 // ParticleSet1:ミサイル本体に追従するパーティクル(親速度を継承する) // ParticleSet2:ミサイル本体消滅後に再生されるパーティクル s_ShootAngle = 0.0f; // シェーダバイナリをロードします。 nw::demo::ParticleEffect::InitializeShaderBinary(SHADER_RESOURCE_FILE_NAME, &s_DeviceAllocator); // パーティクルエフェクトクラスを生成します。 s_ParticleEffect = nw::demo::ParticleEffect::Create(&s_DeviceAllocator, &s_DeviceAllocator, false, s_ParticleContext); // エフェクトデータをロードしてセットアップします。 nw::demo::ResourceSet* resourceSet = nw::demo::Utility::LoadResources(s_Resources, EFFECT_RESOURCE_FILE, &s_DeviceAllocator); // リソースをセットアップを行います。 s_ParticleEffect->Setup(resourceSet->resource, false); // ParticleEffectにリソースをセットします。 s_ParticleEffect->Register(resourceSet->resource); s_ParticleEffect->AddPool(1); // パーティクルノードクラスのインスタンスをリースします。 s_PaticleNode = s_ParticleEffect->LeaseInstance(); // シーンに追加します。 s_SceneRoot->AttachChild(s_PaticleNode); // シーンを更新します。 s_SceneSystem->InitializeScene(s_SceneRoot); s_SceneSystem->UpdateScene(); } /*!--------------------------------------------------------------------------* @brief ParticleMissileDemoの定期処理です。 *---------------------------------------------------------------------------*/ void UpdateParticleMissileDemo() { // 任意のタイミングで削除するParticleSetの名前です。 const char* DESTROY_MISSILE_NAME0 = "Missile_Particle"; // ミサイル本体 const char* DESTROY_MISSILE_NAME1 = "Missile_Flash_Particle"; // ミサイルノズルの光 // パッドで射出角度を調整します。 if (nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_RIGHT)) { s_ShootAngle -= nw::math::F_PI * 0.01f; if (s_ShootAngle < -nw::math::F_PI/2.f) { s_ShootAngle = -nw::math::F_PI/2.f; } } if (nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_LEFT)) { s_ShootAngle += nw::math::F_PI * 0.01f; if (s_ShootAngle > nw::math::F_PI/2.f) { s_ShootAngle = nw::math::F_PI/2.f; } } // ミサイルの射出角度を調整します。 for (u32 i = 0; i < s_PaticleNode->GetParticleEmitterSize(); ++i) { nw::gfx::ParticleEmitter* emitter = s_PaticleNode->GetParticleEmitter(i); emitter->Transform().SetRotateXYZ( 0.0f, 0.0f, s_ShootAngle ); } // パッドAで射出(放出)します。 // リソース側で同時に発射できるミサイル数だけ実行時ワークメモリを設定しておく必要があります。 if (nw::demo::PadFactory::GetPad()->IsButtonDown(nw::demo::Pad::BUTTON_A)) { s_PaticleNode->Emission(s_ParticleContext); } // パッドBで全てのミサイルを削除します。 if (nw::demo::PadFactory::GetPad()->IsButtonDown(nw::demo::Pad::BUTTON_B)) { nw::gfx::ParticleSet* particleSet0 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME0); nw::gfx::ParticleSet* particleSet1 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME1); nw::gfx::ParticleCollection* collection0= particleSet0->GetParticleCollection(); nw::gfx::ParticleCollection* collection1= particleSet1->GetParticleCollection(); collection0->KillParticles(); collection1->KillParticles(); } // 射出されたミサイル(粒)を任意のタイミング(コリジョンなど)で削除します。 { nw::gfx::ParticleSet* particleSet0 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME0); nw::gfx::ParticleSet* particleSet1 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME1); nw::gfx::ParticleCollection* collection0= particleSet0->GetParticleCollection(); nw::gfx::ParticleCollection* collection1= particleSet1->GetParticleCollection(); // ミサイル本体の有効なパーティクルの個数を取得します。 const int count0 = collection0->GetCount(); if (count0 != 0) { // 有効なパーティクルへのインデックス u16* activeIndex = (u16*)collection0->GetStreamPtr( nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT); // パーティクルの位置 nw::math::VEC3* translate = (nw::math::VEC3*)collection0->GetStreamPtr( nw::gfx::PARTICLEUSAGE_TRANSLATE, nw::gfx::PARTICLE_BUFFER_FRONT); for (int i = 0; i < count0; ++i) { int index = activeIndex[i]; // 発射位置から距離25.fでミサイル本体を削除します。 if ( translate[index].Length() > 25.f ) { collection0->KillParticle(index); } } } // ミサイルノズル光にも同様の処理を行います。 const int count1 = collection0->GetCount(); if (count1 != 0) { // 有効なパーティクルへのインデックス u16* activeIndex = (u16*)collection1->GetStreamPtr( nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT); // パーティクルの位置 nw::math::VEC3* translate = (nw::math::VEC3*)collection1->GetStreamPtr( nw::gfx::PARTICLEUSAGE_TRANSLATE, nw::gfx::PARTICLE_BUFFER_FRONT); for (int i = 0; i < count1; ++i) { int index = activeIndex[i]; // 発射位置から距離25.fでミサイル本体を削除します。 if ( translate[index].Length() > 25.f ) { collection1->KillParticle(index); } } } } } /*!--------------------------------------------------------------------------* @brief ParticleMissileDemoのスクリーンデバッグ表示です。 *---------------------------------------------------------------------------*/ void ReportParticleMissileDemo() { s32 degree = s_ShootAngle * 114.6f; s_GraphicsDrawing.DrawString(10, 10, "Angle :%d\n", degree); s_GraphicsDrawing.DrawString(10, 22, "Button A :Shoot\n", degree); s_GraphicsDrawing.DrawString(10, 36, "Button B :Destruction\n", degree); s_GraphicsDrawing.DrawString(10, 48, "Pad L<->R:Angle Adjust\n", degree); } /*!--------------------------------------------------------------------------* @brief ParticleMissileDemoの終了処理を行います。 *---------------------------------------------------------------------------*/ void TerminateParticleMissileDemo() { nw::demo::ParticleNode* prevTarget = s_ParticleEffect->GetActiveEffect(0); while (prevTarget != NULL) { s_ParticleEffect->ReleaseInstance(prevTarget); prevTarget = s_ParticleEffect->GetActiveEffect(0); } s_ParticleEffect->FreePool(); nw::ut::SafeDestroy(s_ParticleEffect); // ロードしたパーティクルシェーダを破棄します。 nw::demo::ParticleEffect::FinalizeShaderBinary(); nw::demo::SafeCleanupResources(s_Resources); s_Resources.clear(); } /*!--------------------------------------------------------------------------* @brief シーンをデモンストレーションします。 *---------------------------------------------------------------------------*/ void DemoScene() { NW_ASSERT(!s_RenderTargets.empty()); nw::gfx::RenderContext* renderContext = s_RenderSystem->GetRenderContext(); InitializeScenes(); // ParticleMissileDemoの初期化処理です。 InitializeParticleMissileDemo(); bool isContinuing = true; while ( isContinuing ) { nw::demo::DebugUtility::AdvanceAutoTestFrame(); nw::demo::PadFactory::GetPad()->Update(); // ParticleMissileDemoの定期処理です。 UpdateParticleMissileDemo(); 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(); // ParticleMissileDemoのレポートを表示します。 ReportParticleMissileDemo(); 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; } } // ParticleMissileDemoの終了処理です。 TerminateParticleMissileDemo(); TerminateScenes(); } } // namespace /*!--------------------------------------------------------------------------* @brief メイン関数です。 *---------------------------------------------------------------------------*/ void nnMain() { nw::demo::InitializeGraphicsSystem(&s_DeviceAllocator); nw::demo::InitializeDemoAllocator(&s_ParticleAllocator, nw::demo::DEMO_PARTICLE_MEMORY_SIZE); nw::demo::PadFactory::Initialize(&s_DeviceAllocator); NW_DEMO_TEST_LOOP(&s_DeviceAllocator, &s_ParticleAllocator, &s_RenderSystem) { InitializeGraphics(); s_FlushCache = nw::demo::FlushCache::Create(&s_DeviceAllocator); DemoScene(); nw::ut::SafeDestroy(s_FlushCache); TerminateGraphics(); } nw::demo::PadFactory::Finalize(); nw::demo::FinalizeDemoAllocator(&s_ParticleAllocator); nw::demo::FinalizeGraphicsSystem(); }