1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     ParticleMissileDemo.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 25056 $
14  *---------------------------------------------------------------------------*/
15 
16 #define NW_DEBUG_CHECK_MEMORY_LEAK
17 
18 #include <nn/os.h>
19 #include <nn/fs.h>
20 
21 #include <nw/types.h>
22 #include <nw/demo.h>
23 #include <nw/dev.h>
24 #include <nw/gfx.h>
25 #include <nw/ut.h>
26 
27 namespace
28 {
29 
30 //----------------------------------------
31 // メモリ関係
32 
33 // デバイスメモリを確保するためのアロケータです。
34 nw::demo::DemoAllocator s_DeviceAllocator;
35 nw::demo::DemoAllocator s_ParticleAllocator;
36 
37 //----------------------------------------
38 // ファイル名の定義です。
39 const wchar_t* SKY_SPHERE_FILE_NAME  = NW_DEMO_FILE_PATH(L"SkySphere.bcmdl");
40 
41 //----------------------------------------
42 // 描画関係
43 const int RENDER_TARGET_COUNT = 1;
44 typedef nw::ut::FixedSizeArray<nw::gfx::IRenderTarget*, RENDER_TARGET_COUNT> RenderTargetArray;
45 
46 RenderTargetArray s_RenderTargets;
47 nw::demo::SceneSystem*  s_SceneSystem = NULL;
48 nw::demo::RenderSystem* s_RenderSystem = NULL;
49 nw::gfx::ParticleContext* s_ParticleContext = NULL;
50 
51 nw::demo::GraphicsDrawing  s_GraphicsDrawing;
52 
53 
54 
55 //----------------------------------------
56 // シーン関係
57 nw::gfx::SceneNode* s_SceneRoot = NULL;
58 s32 s_FrameCount = 0;
59 nw::gfx::Camera* s_BaseCamera = NULL;
60 nw::gfx::Camera* s_LeftCamera = NULL;
61 nw::gfx::Camera* s_RightCamera = NULL;
62 const f32 s_fNearPlane = 0.1f;
63 
64 const s32 s_BaseCameraIndex = 0;
65 
66 //----------------------------------------
67 // パーティクル関係
68 nw::gfx::ParticleSceneUpdater* s_ParticleSceneUpdater = NULL;
69 
70 nw::demo::FlushCache* s_FlushCache;
71 
72 /*!--------------------------------------------------------------------------*
73   @brief        グラフィックス関連の初期化を行います。
74  *---------------------------------------------------------------------------*/
75 void
InitializeGraphics()76 InitializeGraphics()
77 {
78     nw::gfx::CommandCacheManager::SetAllocator( &s_DeviceAllocator );
79 
80     // renderDescriptionへステレオの設定を行います。
81     nw::demo::RenderSystem::Description renderDescription;
82 
83     renderDescription.reusableCommandBufferSize = 0x100000;
84     renderDescription.reusableCommandRequestCount      = 512;
85     renderDescription.upperScreenMode = nw::demo::UPPER_SCREEN_MODE_STEREO;
86 
87     s_RenderSystem = nw::demo::RenderSystem::Create(&s_DeviceAllocator, renderDescription);
88 
89     s_GraphicsDrawing.SetScreenSize(
90         renderDescription.lowerScreenDescription.width,
91         renderDescription.lowerScreenDescription.height
92     );
93 
94     nw::demo::Utility::InitializeGraphicsDrawing(&s_DeviceAllocator, s_GraphicsDrawing);
95 
96     s_RenderTargets.push_back(
97         nw::demo::Utility::CreateUpperScreenBuffer(&s_DeviceAllocator, renderDescription)
98     );
99     NW_ASSERT(!s_RenderTargets.empty());
100     s_RenderSystem->GetRenderContext()->SetRenderTarget(s_RenderTargets.front());
101 
102     // sceneDescriptionへの標準的な設定はコンストラクタで行われています。
103     nw::demo::SceneSystem::Description sceneDescription;
104     s_SceneSystem = nw::demo::SceneSystem::Create(&s_DeviceAllocator, sceneDescription);
105 
106     s_ParticleContext = nw::gfx::ParticleContext::Builder()
107         .Create(&s_DeviceAllocator);
108 
109     s_ParticleSceneUpdater = nw::gfx::ParticleSceneUpdater::Builder()
110         .Create(&s_DeviceAllocator);
111 
112     // デモ用の最遠景モデルをレンダリングシステムに設定します。
113     // gfx のデモでは glClear などを用いずに背景で塗りつぶしを行います。
114     s_RenderSystem->LoadSkyModel(SKY_SPHERE_FILE_NAME);
115 
116     NW_GL_ASSERT();
117 }
118 
119 /*!--------------------------------------------------------------------------*
120   @brief        グラフィックス関連の後始末をします。
121  *---------------------------------------------------------------------------*/
122 void
TerminateGraphics()123 TerminateGraphics()
124 {
125     nw::gfx::SafeDestroy(s_LeftCamera);
126 
127     nw::gfx::SafeDestroy(s_RightCamera);
128 
129     nw::gfx::SafeDestroy(s_ParticleSceneUpdater);
130 
131     nw::gfx::SafeDestroy(s_ParticleContext);
132 
133     nw::gfx::SafeDestroy(s_SceneSystem);
134 
135     nw::gfx::SafeDestroyAll(s_RenderTargets);
136 
137     s_GraphicsDrawing.Finalize();
138 
139     nw::gfx::SafeDestroy(s_RenderSystem);
140 
141     NW_GL_ASSERT();
142 }
143 
144 /*!--------------------------------------------------------------------------*
145   @brief        ルートノード関連の構築をします。
146  *---------------------------------------------------------------------------*/
147 void
BuildRootNodes()148 BuildRootNodes()
149 {
150     NW_ASSERT(s_SceneRoot == NULL);
151     s_SceneRoot = nw::gfx::TransformNode::DynamicBuilder()
152         .IsFixedSizeMemory(false)
153         .Create(&s_DeviceAllocator);
154     NW_NULL_ASSERT(s_SceneRoot);
155 }
156 
157 /*!--------------------------------------------------------------------------*
158   @brief        カメラ関連の構築をします。
159  *---------------------------------------------------------------------------*/
160 void
BuildCameras()161 BuildCameras()
162 {
163     nw::demo::Utility::CreateStereoCameras(
164         &s_BaseCamera,
165         &s_LeftCamera,
166         &s_RightCamera,
167         &s_DeviceAllocator,
168         nw::math::VEC3(0.0f, 15.0f, 80.f),
169         nw::math::VEC3(0.0f, 15.0f, 0.0f),
170         s_fNearPlane
171     );
172 
173     s_SceneRoot->AttachChild(s_BaseCamera);
174     s_SceneSystem->GetCameraController()->Register(s_BaseCamera);
175 }
176 
177 
178 /*!--------------------------------------------------------------------------*
179   @brief        シーンを初期化します。
180  *---------------------------------------------------------------------------*/
181 void
InitializeScenes()182 InitializeScenes()
183 {
184     BuildRootNodes();
185 
186     BuildCameras();
187 
188     // シーンツリーを巡回して初期化を行います。
189     s_SceneSystem->InitializeScene(s_SceneRoot);
190     s_SceneSystem->UpdateScene();
191 
192     // カメラを設定します。
193     nw::gfx::SceneEnvironment& sceneEnvironment = s_RenderSystem->GetSceneEnvironment();
194     sceneEnvironment.SetCamera(s_BaseCameraIndex, s_BaseCamera);
195     nw::demo::Utility::SetCameraAspectRatio(s_BaseCamera, s_RenderTargets[0]);
196 
197     NW_GL_ASSERT();
198 
199     s_FrameCount = 0;
200 }
201 
202 /*!--------------------------------------------------------------------------*
203   @brief        シーン関連の後始末をします。
204  *---------------------------------------------------------------------------*/
205 void
TerminateScenes()206 TerminateScenes()
207 {
208     nw::gfx::SafeDestroyBranch(s_SceneRoot);
209 
210     NW_GL_ASSERT();
211 }
212 
213 /*!--------------------------------------------------------------------------*
214   @brief        シーンを更新します。
215  *---------------------------------------------------------------------------*/
216 void
UpdateScene()217 UpdateScene()
218 {
219     NW_ASSERT(0 < s_RenderTargets.size());
220 
221     s_SceneSystem->GetCameraController()->Update();
222 
223     s_SceneSystem->UpdateScene();
224 
225     s_BaseCamera->UpdateCameraMatrix();
226 
227     NW_NULL_ASSERT(s_ParticleSceneUpdater);
228     s_ParticleSceneUpdater->UpdateNode(
229         s_SceneSystem->GetSceneContext(),
230         s_ParticleContext);
231 
232     s_RenderSystem->CalcStereoCamera(s_LeftCamera, s_RightCamera, s_BaseCamera, s_fNearPlane + 5.0f);
233 
234     s_FrameCount++;
235 
236     s_FlushCache->Execute();
237 }
238 
239 /*!--------------------------------------------------------------------------*
240   @brief        負荷表示やテスト機能の処理をおこないます。
241  *---------------------------------------------------------------------------*/
242 void
ReportDemo()243 ReportDemo()
244 {
245     NW_PROFILE("ReportDemo");
246 
247     // 負荷表示からはこれらの負荷は除きます。
248     s_RenderSystem->SuspendLoadMeter();
249 
250     nw::demo::DebugUtility::CalcLoadMeter(s_RenderSystem);
251 
252     s_GraphicsDrawing.BeginDrawingShape();
253 
254     nw::demo::DebugUtility::DrawLoadMeter(
255         s_RenderSystem,
256         &s_GraphicsDrawing
257     );
258 
259     s_GraphicsDrawing.EndDrawingShape();
260 
261     s_GraphicsDrawing.BeginDrawingString();
262 
263     nw::demo::DebugUtility::DrawLoadMeterText(
264         s_RenderSystem,
265         &s_GraphicsDrawing
266     );
267 
268     s_GraphicsDrawing.EndDrawingString();
269 
270     s_RenderSystem->ResumeLoadMeter();
271 }
272 
273 
274 
275 
276 //----------------------------------------
277 // デモ固有の変数
278 
279 // パーティクルエフェクトクラス
280 nw::demo::ParticleEffect* s_ParticleEffect = NULL;
281 
282 // パーティクルノードクラス
283 nw::demo::ParticleNode* s_PaticleNode = NULL;
284 
285 //----------------------------------------
286 // リソース関係
287 nw::demo::ResourceArray s_Resources;
288 
289 // ロードするエフェクトファイル
290 const wchar_t* EFFECT_RESOURCE_FILE  = NW_DEMO_FILE_PATH(L"missile_particle.bcptl");
291 
292 // ロードするエフェクトシェーダファイル
293 const wchar_t* SHADER_RESOURCE_FILE_NAME = NW_DEMO_FILE_PATH(L"nwgfx_ParticleDefaultShader.bcsdr");
294 
295 // 射出角度
296 f32 s_ShootAngle = 0.0f;
297 
298 /*!--------------------------------------------------------------------------*
299   @brief        ParticleMissileDemoの初期化を行います。
300  *---------------------------------------------------------------------------*/
301 void
InitializeParticleMissileDemo()302 InitializeParticleMissileDemo()
303 {
304     // 親からチャイルドへ速度継承を利用してパーティクルでミサイルを作成するデモです。
305     // データ内には、ParticleSetが親1つ、子2つの3つのParticleSetがあり、
306     // 2つ目のParticleSetが生成時に親からの速度継承を行っています。
307 
308     // ParticleSet0:ミサイル本体
309     // ParticleSet1:ミサイル本体に追従するパーティクル(親速度を継承する)
310     // ParticleSet2:ミサイル本体消滅後に再生されるパーティクル
311 
312     s_ShootAngle = 0.0f;
313 
314     // シェーダバイナリをロードします。
315     nw::demo::ParticleEffect::InitializeShaderBinary(SHADER_RESOURCE_FILE_NAME, &s_DeviceAllocator);
316 
317     // パーティクルエフェクトクラスを生成します。
318     s_ParticleEffect = nw::demo::ParticleEffect::Create(&s_DeviceAllocator, &s_DeviceAllocator, false, s_ParticleContext);
319 
320     // エフェクトデータをロードしてセットアップします。
321     nw::demo::ResourceSet* resourceSet = nw::demo::Utility::LoadResources(s_Resources, EFFECT_RESOURCE_FILE, &s_DeviceAllocator);
322 
323     // リソースをセットアップを行います。
324     s_ParticleEffect->Setup(resourceSet->resource, false);
325 
326     // ParticleEffectにリソースをセットします。
327     s_ParticleEffect->Register(resourceSet->resource);
328     s_ParticleEffect->AddPool(1);
329 
330     // パーティクルノードクラスのインスタンスをリースします。
331     s_PaticleNode = s_ParticleEffect->LeaseInstance();
332 
333     // シーンに追加します。
334     s_SceneRoot->AttachChild(s_PaticleNode);
335 
336     // シーンを更新します。
337     s_SceneSystem->InitializeScene(s_SceneRoot);
338     s_SceneSystem->UpdateScene();
339 }
340 
341 /*!--------------------------------------------------------------------------*
342   @brief        ParticleMissileDemoの定期処理です。
343  *---------------------------------------------------------------------------*/
344 void
UpdateParticleMissileDemo()345 UpdateParticleMissileDemo()
346 {
347     // 任意のタイミングで削除するParticleSetの名前です。
348     const char* DESTROY_MISSILE_NAME0  = "Missile_Particle";        // ミサイル本体
349     const char* DESTROY_MISSILE_NAME1  = "Missile_Flash_Particle";  // ミサイルノズルの光
350 
351     // パッドで射出角度を調整します。
352     if (nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_RIGHT))
353     {
354         s_ShootAngle -= nw::math::F_PI * 0.01f;
355         if (s_ShootAngle < -nw::math::F_PI/2.f)
356         {
357             s_ShootAngle = -nw::math::F_PI/2.f;
358         }
359     }
360 
361     if (nw::demo::PadFactory::GetPad()->IsButtonPress(nw::demo::Pad::BUTTON_LEFT))
362     {
363         s_ShootAngle += nw::math::F_PI * 0.01f;
364         if (s_ShootAngle > nw::math::F_PI/2.f)
365         {
366             s_ShootAngle = nw::math::F_PI/2.f;
367         }
368     }
369 
370     // ミサイルの射出角度を調整します。
371     for (u32 i = 0; i < s_PaticleNode->GetParticleEmitterSize(); ++i)
372     {
373         nw::gfx::ParticleEmitter* emitter = s_PaticleNode->GetParticleEmitter(i);
374         emitter->Transform().SetRotateXYZ( 0.0f, 0.0f, s_ShootAngle );
375     }
376 
377 
378     // パッドAで射出(放出)します。
379     // リソース側で同時に発射できるミサイル数だけ実行時ワークメモリを設定しておく必要があります。
380     if (nw::demo::PadFactory::GetPad()->IsButtonDown(nw::demo::Pad::BUTTON_A))
381     {
382         s_PaticleNode->Emission(s_ParticleContext);
383     }
384 
385     // パッドBで全てのミサイルを削除します。
386     if (nw::demo::PadFactory::GetPad()->IsButtonDown(nw::demo::Pad::BUTTON_B))
387     {
388         nw::gfx::ParticleSet* particleSet0 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME0);
389         nw::gfx::ParticleSet* particleSet1 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME1);
390 
391         nw::gfx::ParticleCollection* collection0= particleSet0->GetParticleCollection();
392         nw::gfx::ParticleCollection* collection1= particleSet1->GetParticleCollection();
393 
394         collection0->KillParticles();
395         collection1->KillParticles();
396     }
397 
398 
399     // 射出されたミサイル(粒)を任意のタイミング(コリジョンなど)で削除します。
400     {
401         nw::gfx::ParticleSet* particleSet0 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME0);
402         nw::gfx::ParticleSet* particleSet1 = s_PaticleNode->GetParticleSet(DESTROY_MISSILE_NAME1);
403 
404         nw::gfx::ParticleCollection* collection0= particleSet0->GetParticleCollection();
405         nw::gfx::ParticleCollection* collection1= particleSet1->GetParticleCollection();
406 
407         // ミサイル本体の有効なパーティクルの個数を取得します。
408         const int count0 = collection0->GetCount();
409         if (count0 != 0)
410         {
411             // 有効なパーティクルへのインデックス
412             u16* activeIndex =
413                 (u16*)collection0->GetStreamPtr(
414                 nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT);
415 
416             // パーティクルの位置
417             nw::math::VEC3* translate =
418                 (nw::math::VEC3*)collection0->GetStreamPtr(
419                 nw::gfx::PARTICLEUSAGE_TRANSLATE, nw::gfx::PARTICLE_BUFFER_FRONT);
420 
421             for (int i = 0; i < count0; ++i)
422             {
423                 int index = activeIndex[i];
424 
425                 // 発射位置から距離25.fでミサイル本体を削除します。
426                 if ( translate[index].Length() > 25.f )
427                 {
428                     collection0->KillParticle(index);
429                 }
430             }
431         }
432 
433 
434         // ミサイルノズル光にも同様の処理を行います。
435         const int count1 = collection0->GetCount();
436         if (count1 != 0)
437         {
438             // 有効なパーティクルへのインデックス
439             u16* activeIndex =
440                 (u16*)collection1->GetStreamPtr(
441                 nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT);
442 
443             // パーティクルの位置
444             nw::math::VEC3* translate =
445                 (nw::math::VEC3*)collection1->GetStreamPtr(
446                 nw::gfx::PARTICLEUSAGE_TRANSLATE, nw::gfx::PARTICLE_BUFFER_FRONT);
447 
448             for (int i = 0; i < count1; ++i)
449             {
450                 int index = activeIndex[i];
451 
452                 // 発射位置から距離25.fでミサイル本体を削除します。
453                 if ( translate[index].Length() > 25.f )
454                 {
455                     collection1->KillParticle(index);
456                 }
457             }
458         }
459     }
460 }
461 
462 /*!--------------------------------------------------------------------------*
463   @brief        ParticleMissileDemoのスクリーンデバッグ表示です。
464  *---------------------------------------------------------------------------*/
465 void
ReportParticleMissileDemo()466 ReportParticleMissileDemo()
467 {
468     s32 degree = s_ShootAngle * 114.6f;
469     s_GraphicsDrawing.DrawString(10, 10, "Angle    :%d\n", degree);
470     s_GraphicsDrawing.DrawString(10, 22, "Button A :Shoot\n", degree);
471     s_GraphicsDrawing.DrawString(10, 36, "Button B :Destruction\n", degree);
472     s_GraphicsDrawing.DrawString(10, 48, "Pad L<->R:Angle Adjust\n", degree);
473 }
474 
475 /*!--------------------------------------------------------------------------*
476   @brief        ParticleMissileDemoの終了処理を行います。
477  *---------------------------------------------------------------------------*/
478 void
TerminateParticleMissileDemo()479 TerminateParticleMissileDemo()
480 {
481     nw::demo::ParticleNode* prevTarget = s_ParticleEffect->GetActiveEffect(0);
482     while (prevTarget != NULL)
483     {
484         s_ParticleEffect->ReleaseInstance(prevTarget);
485         prevTarget = s_ParticleEffect->GetActiveEffect(0);
486     }
487 
488     s_ParticleEffect->FreePool();
489 
490     nw::ut::SafeDestroy(s_ParticleEffect);
491 
492     // ロードしたパーティクルシェーダを破棄します。
493     nw::demo::ParticleEffect::FinalizeShaderBinary();
494 
495     nw::demo::SafeCleanupResources(s_Resources);
496     s_Resources.clear();
497 }
498 
499 
500 
501 
502 /*!--------------------------------------------------------------------------*
503   @brief        シーンをデモンストレーションします。
504  *---------------------------------------------------------------------------*/
505 void
DemoScene()506 DemoScene()
507 {
508     NW_ASSERT(!s_RenderTargets.empty());
509 
510     nw::gfx::RenderContext* renderContext = s_RenderSystem->GetRenderContext();
511 
512     InitializeScenes();
513 
514     // ParticleMissileDemoの初期化処理です。
515     InitializeParticleMissileDemo();
516 
517     bool isContinuing = true;
518 
519     while ( isContinuing )
520     {
521         nw::demo::DebugUtility::AdvanceAutoTestFrame();
522 
523         nw::demo::PadFactory::GetPad()->Update();
524 
525 
526         // ParticleMissileDemoの定期処理です。
527         UpdateParticleMissileDemo();
528 
529         UpdateScene();
530 
531         renderContext->SetActiveCamera(s_BaseCameraIndex);
532         s_RenderSystem->SubmitView(s_SceneSystem);
533 
534         s_RenderSystem->SetRenderTarget(s_RenderTargets[0]);
535         s_RenderSystem->RenderStereoScene(s_LeftCamera, s_RightCamera);
536 
537         s_RenderSystem->ClearBySkyModel(s_BaseCamera);
538         ReportDemo();
539 
540         // ParticleMissileDemoのレポートを表示します。
541         ReportParticleMissileDemo();
542 
543         s_RenderSystem->TransferBuffer(nw::demo::LOWER_SCREEN);
544 
545         s_RenderSystem->PresentBuffer(nw::demo::UPPER_SCREEN | nw::demo::LOWER_SCREEN | nw::demo::EXTENSION_SCREEN);
546 
547         renderContext->ResetState();
548 
549         if (nw::demo::Utility::IsTerminating())
550         {
551             isContinuing = false;
552         }
553     }
554 
555     // ParticleMissileDemoの終了処理です。
556     TerminateParticleMissileDemo();
557 
558     TerminateScenes();
559 }
560 
561 } // namespace
562 
563 /*!--------------------------------------------------------------------------*
564   @brief        メイン関数です。
565  *---------------------------------------------------------------------------*/
566 void
nnMain()567 nnMain()
568 {
569     nw::demo::InitializeGraphicsSystem(&s_DeviceAllocator);
570     nw::demo::InitializeDemoAllocator(&s_ParticleAllocator, nw::demo::DEMO_PARTICLE_MEMORY_SIZE);
571 
572     nw::demo::PadFactory::Initialize(&s_DeviceAllocator);
573 
574     NW_DEMO_TEST_LOOP(&s_DeviceAllocator, &s_ParticleAllocator, &s_RenderSystem)
575     {
576         InitializeGraphics();
577 
578         s_FlushCache = nw::demo::FlushCache::Create(&s_DeviceAllocator);
579 
580         DemoScene();
581 
582         nw::ut::SafeDestroy(s_FlushCache);
583 
584         TerminateGraphics();
585     }
586 
587     nw::demo::PadFactory::Finalize();
588 
589     nw::demo::FinalizeDemoAllocator(&s_ParticleAllocator);
590 
591     nw::demo::FinalizeGraphicsSystem();
592 }
593