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