/*---------------------------------------------------------------------------* Project: NintendoWare File: LowLayerDemo.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 { //---------------------------------------- // メモリ関係 // デモではデバイスメモリから全てのメモリを確保するようになっています。 // デモで使用するメモリサイズです。 const size_t DEMO_MEMORY_SIZE = 0x1000000; // デバイスメモリを確保するためのアロケータです。 nw::demo::DemoAllocator s_DeviceAllocator; nw::demo::GraphicsMemoryAllocator s_GraphicsMemoryAllocator; const int MAX_FILE = 256; const int MAX_DIRECTORY = 16; //---------------------------------------- // ファイル名の定義です。 static const wchar_t* MODEL_RESOURCE_FILES[] = { NW_DEMO_FILE_PATH(L"Cube.bcmdl"), NW_DEMO_FILE_PATH(L"FragmentLight.bcenv"), }; //---------------------------------------- // 描画関係 nw::gfx::IRenderTarget* s_RenderTarget = NULL; nw::gfx::RenderContext* s_RenderContext = NULL; nw::gfx::MeshRenderer* s_MeshRenderer = NULL; nw::demo::DisplayBufferSwapper* s_UpperSwapper = NULL; nw::demo::DisplayBufferSwapper* s_ExtensionSwapper = NULL; const size_t COMMAND_BUFFER_SIZE = 0x100000; const size_t REQUEST_COUNT = 512; nw::demo::CommandListSwapper* s_CommandListSwapper = NULL; //---------------------------------------- // リソース関係 const int RESOURCES_COUNT = 2; struct ResourceSet { nw::ut::MoveArray buffer; nw::gfx::ResGraphicsFile resource; }; typedef nw::ut::FixedSizeArray ResourceArray; ResourceArray s_Resources; //---------------------------------------- // シーン関係 s32 s_FrameCount = 0; nw::gfx::WorldMatrixUpdater* s_WorldMatrixUpdater = NULL; const s32 s_BaseCameraIndex = 0; nw::gfx::Camera* s_BaseCamera = NULL; nw::gfx::Camera* s_LeftCamera = NULL; nw::gfx::Camera* s_RightCamera = NULL; const f32 s_NearPlane = 0.1f; nw::demo::CameraController* s_CameraController = NULL; nn::ulcd::CTR::StereoCamera* s_StereoCamera; const int MODEL_COUNT = 1; const int LIGHT_COUNT = 1; typedef nw::ut::FixedSizeArray ModelArray; typedef nw::ut::FixedSizeArray FragmentLightArray; ModelArray s_Models; FragmentLightArray s_FragmentLights; void UpdateNode(nw::gfx::TransformNode* node); /*!--------------------------------------------------------------------------* @brief グラフィックスメモリのアロケータです。 関数ポインタが nngxInitialize の引数として使用されます。 *---------------------------------------------------------------------------*/ void* AllocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, GLsizei size) { void* buffer = s_GraphicsMemoryAllocator.Allocate(area, aim, id, size); return buffer; } /*!--------------------------------------------------------------------------* @brief グラフィックスメモリのデアロケータです。 関数ポインタが nngxInitialize の引数として使用されます。 *---------------------------------------------------------------------------*/ void DeallocateGraphicsMemory(GLenum area, GLenum aim, GLuint id, void* addr) { s_GraphicsMemoryAllocator.Deallocate(area, aim, id, addr); } /*!--------------------------------------------------------------------------* @brief グラフィックスシステムに必要なメモリなどの初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeGraphicsSystem() { nn::os::Initialize(); nn::fs::Initialize(); nw::demo::InitializeDemoMemory(); nw::demo::InitializeDemoAllocator(&s_DeviceAllocator, DEMO_MEMORY_SIZE, nn::os::ALLOCATE_OPTION_LINEAR); // デバイスメモリを用いるグラフィックスメモリアロケータを初期化します。 s_GraphicsMemoryAllocator.Initialize(&s_DeviceAllocator); s32 workingMemorySize = nn::fs::GetRomRequiredMemorySize(MAX_FILE, MAX_DIRECTORY); void* workingMemory = nw::demo::Alloc(workingMemorySize); nn::Result result = nn::fs::MountRom(MAX_FILE, MAX_DIRECTORY, workingMemory, workingMemorySize); NW_ASSERT(result.IsSuccess()); } /*!--------------------------------------------------------------------------* @brief グラフィックスシステムに必要なメモリなどの終了処理を行います。 *---------------------------------------------------------------------------*/ void FinalizeGraphicsSystem() { s_GraphicsMemoryAllocator.Finalize(); nw::demo::FinalizeDemoAllocator(&s_DeviceAllocator); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の初期化を行います。 *---------------------------------------------------------------------------*/ void InitializeGraphics() { if (nngxInitialize(AllocateGraphicsMemory, DeallocateGraphicsMemory) == GL_FALSE) { NW_FATAL_ERROR("nngxInitialize failed.\n"); } //----------------------------- nw::demo::DisplayBufferSwapper::Description upperScreenDescription; upperScreenDescription.screenKind = nw::demo::UPPER_SCREEN; upperScreenDescription.width = 400; upperScreenDescription.height = 240; upperScreenDescription.bufferCount = 2; nw::demo::DisplayBufferSwapper::Description extensionScreenDescription; extensionScreenDescription.screenKind = nw::demo::EXTENSION_SCREEN; extensionScreenDescription.width = upperScreenDescription.width; extensionScreenDescription.height = upperScreenDescription.height; extensionScreenDescription.bufferCount = upperScreenDescription.bufferCount; s_UpperSwapper = nw::demo::DisplayBufferSwapper::Builder() .BufferDescription(upperScreenDescription) .Create(&s_DeviceAllocator); s_ExtensionSwapper = nw::demo::DisplayBufferSwapper::Builder() .BufferDescription(extensionScreenDescription) .Create(&s_DeviceAllocator); //----------------------------- nw::demo::CommandListSwapper::Description commandListSwapperDescription; commandListSwapperDescription.commandListCount = 1; commandListSwapperDescription.bufferSize = COMMAND_BUFFER_SIZE; commandListSwapperDescription.requestCount = REQUEST_COUNT; commandListSwapperDescription.reusableBufferSize = COMMAND_BUFFER_SIZE; commandListSwapperDescription.reusableRequestCount = REQUEST_COUNT; commandListSwapperDescription.maxGpuProfilingEntryCount = 0; s_CommandListSwapper = nw::demo::CommandListSwapper::Create(&s_DeviceAllocator, commandListSwapperDescription); s_CommandListSwapper->Bind(); //----------------------------- nngxSetDisplayMode(nw::demo::UPPER_SCREEN_MODE_STEREO); //----------------------------- nw::gfx::CommandCacheManager::SetAllocator( &s_DeviceAllocator ); //----------------------------- s_RenderContext = nw::gfx::RenderContext::Builder() .Create(&s_DeviceAllocator); //----------------------------- void* cameraMemory = s_DeviceAllocator.Alloc(sizeof(nn::ulcd::CTR::StereoCamera)); s_StereoCamera = new(cameraMemory) nn::ulcd::CTR::StereoCamera(); s_StereoCamera->Initialize(); //----------------------------- s_MeshRenderer = nw::gfx::MeshRenderer::Create(&s_DeviceAllocator); s_MeshRenderer->SetRenderContext(s_RenderContext); //----------------------------- nw::gfx::RenderColorFormat renderColorFormat = nw::gfx::RENDER_COLOR_FORMAT_RGBA8; s_RenderTarget = nw::gfx::IRenderTarget::Builder() .BufferSize(upperScreenDescription.height, upperScreenDescription.width) .ColorFormat(renderColorFormat) .Create(&s_DeviceAllocator); NW_NULL_ASSERT(s_RenderTarget); //----------------------------- s_WorldMatrixUpdater = nw::gfx::WorldMatrixUpdater::Builder() .Create(&s_DeviceAllocator); //----------------------------- nngxStartLcdDisplay(); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックス関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateGraphics() { s_StereoCamera->Finalize(); s_DeviceAllocator.Free(s_StereoCamera); nw::gfx::SafeDestroy(s_WorldMatrixUpdater); nw::gfx::SafeDestroy(s_RenderTarget); nw::gfx::SafeDestroy(s_MeshRenderer); nw::gfx::SafeDestroy(s_CommandListSwapper); nw::gfx::SafeDestroy(s_UpperSwapper); nw::gfx::SafeDestroy(s_ExtensionSwapper); nw::gfx::SafeDestroy(s_RenderContext); nngxFinalize(); s_GraphicsMemoryAllocator.Finalize(); NW_GL_ASSERT(); } /*!--------------------------------------------------------------------------* @brief グラフィックスリソースをロードします。 @param[in] resourcePath ファイルのパス名 @return ロードしたグラフィックス情報を返します。 *---------------------------------------------------------------------------*/ ResourceSet* LoadResources(const wchar_t* resourcePath) { ResourceSet resourceSet; // 現在、デバイスメモリ上に読み込む方式にのみ対応しています。 // テクスチャをロードするには128byteアライメントを行う必要があります。 static const int resourceAlignment = 128; resourceSet.buffer = nw::demo::Utility::LoadFile(&s_DeviceAllocator , resourcePath, resourceAlignment); if (!resourceSet.buffer) { return NULL; } resourceSet.resource = nw::gfx::ResGraphicsFile(&(resourceSet.buffer.front())); bool isPushed = s_Resources.push_back(resourceSet); NW_ASSERT(isPushed); return &(s_Resources.back()); } /*!--------------------------------------------------------------------------* @brief シーンノードを生成します。 @param[in] resource シーンノードのリソースです。 @return 生成したシーンノードです。。 *---------------------------------------------------------------------------*/ nw::gfx::SceneNode* CreateSceneNode(nw::gfx::ResSceneObject resource) { nw::gfx::SceneObject* sceneObject = nw::gfx::SceneBuilder() .Resource(resource) .MaxChildren(0) .CreateObject(&s_DeviceAllocator, &s_DeviceAllocator); return nw::ut::DynamicCast(sceneObject); } /*!--------------------------------------------------------------------------* @brief リソース関連の構築をします。 *---------------------------------------------------------------------------*/ void BuildResources(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::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 = CreateSceneNode(*modelResource); NW_NULL_ASSERT(node); s_Models.push_back(static_cast(node)); } nw::gfx::ResLightArray lights = resourceSet->resource.GetLights(); for (nw::gfx::ResLightArray::iterator lightResource = lights.begin(); lightResource != lights.end(); ++lightResource) { nw::gfx::SceneNode* node = CreateSceneNode(*lightResource); NW_NULL_ASSERT(node); NW_ASSERT(nw::ut::IsTypeOf(node)); s_FragmentLights.push_back(static_cast(node)); } } /*!--------------------------------------------------------------------------* @brief カメラの構築をします。 *---------------------------------------------------------------------------*/ void BuildCameras() { nw::gfx::LookAtTargetViewUpdater* viewUpdater = nw::gfx::LookAtTargetViewUpdater::Create(&s_DeviceAllocator); nw::gfx::ResLookAtTargetViewUpdater resViewUpdater = nw::gfx::ResStaticCast( viewUpdater->GetResource()); resViewUpdater.SetTargetPosition(0.0f,0.0f,0.0f); resViewUpdater.SetUpwardVector(0.0f,1.0f,0.0f); nw::gfx::PerspectiveProjectionUpdater* projectionUpdater = nw::gfx::PerspectiveProjectionUpdater::Create(&s_DeviceAllocator); nw::gfx::ResPerspectiveProjectionUpdater resProjectionUpdater = nw::gfx::ResStaticCast( projectionUpdater->GetResource()); resProjectionUpdater.SetNear(s_NearPlane); resProjectionUpdater.SetFar(1000.0f); resProjectionUpdater.SetFovy(NW_MATH_DEG_TO_RAD(37.8f)); resProjectionUpdater.SetAspectRatio( static_cast(s_RenderTarget->GetDescription().height) / static_cast(s_RenderTarget->GetDescription().width)); s_BaseCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .ViewUpdater(viewUpdater) .ProjectionUpdater(projectionUpdater) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_BaseCamera); s_BaseCamera->Transform().SetTranslate(nw::math::VEC3(7.0f, 3.5f, -5.0f)); s_LeftCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_LeftCamera); s_RightCamera = nw::gfx::Camera::DynamicBuilder() .MaxChildren(0) .MaxCallbacks(0) .Create(&s_DeviceAllocator); NW_POINTER_ASSERT(s_RightCamera); s_CameraController = nw::demo::CameraController::Builder() .MaxCameraCount(1) .Create(&s_DeviceAllocator); s_CameraController->Register(s_BaseCamera); } /*!--------------------------------------------------------------------------* @brief シーンを初期化します。 *---------------------------------------------------------------------------*/ void InitializeScenes() { BuildCameras(); NW_FOREACH(const wchar_t* name, MODEL_RESOURCE_FILES) { BuildResources(LoadResources(name)); } NW_GL_ASSERT(); s_FrameCount = 0; } /*!--------------------------------------------------------------------------* @brief シーン関連の後始末をします。 *---------------------------------------------------------------------------*/ void TerminateScenes() { nw::gfx::SafeDestroyAll(s_FragmentLights); nw::gfx::SafeDestroyAll(s_Models); nw::demo::SafeCleanupResources(s_Resources); nw::gfx::SafeDestroy(s_BaseCamera); nw::gfx::SafeDestroy(s_LeftCamera); nw::gfx::SafeDestroy(s_RightCamera); nw::gfx::SafeDestroy(s_CameraController); NW_GL_ASSERT(); s_Resources.clear(); } /*!--------------------------------------------------------------------------* @brief ノードを更新します。 *---------------------------------------------------------------------------*/ void UpdateNode(nw::gfx::TransformNode* node) { // 一度計算したら計算処理をスキップする。 // ただし、ツリー構造は正しく解釈されない。 if (node->Transform().IsEnabledFlags(nw::gfx::CalculatedTransform::FLAG_IS_DIRTY)) { s_WorldMatrixUpdater->UpdateBasic( &node->WorldMatrix(), &node->WorldTransform(), node->Transform(), nw::gfx::CalculatedTransform::Identity(), nw::gfx::CalculatedTransform::Identity()); node->Transform().DisableFlags(nw::gfx::CalculatedTransform::FLAG_IS_DIRTY); } } /*!--------------------------------------------------------------------------* @brief カメラを更新します。 *---------------------------------------------------------------------------*/ void UpdateCamera() { s_CameraController->Update(); UpdateNode(s_BaseCamera); s_BaseCamera->UpdateCameraMatrix(); // ステレオカメラの計算 NW_NULL_ASSERT(s_LeftCamera); NW_NULL_ASSERT(s_RightCamera); NW_NULL_ASSERT(s_BaseCamera); nn::math::MTX44& projOriginal = s_BaseCamera->ProjectionMatrix(); nn::math::MTX34& viewOriginal = s_BaseCamera->ViewMatrix(); nn::math::MTX44& projL = s_LeftCamera->ProjectionMatrix(); nn::math::MTX34& viewL = s_LeftCamera->ViewMatrix(); nn::math::MTX44& projR = s_RightCamera->ProjectionMatrix(); nn::math::MTX34& viewR = s_RightCamera->ViewMatrix(); // Ortho カメラでは別の処理を行う必要があります。 // 例として demo::RenderSystem::CalcStereoCamera() を参照してください。 const f32 DEPTH_LEVEL = 5.0f + s_NearPlane; const f32 DEPTH_RANGE = 1.0f; s_StereoCamera->CalculateMatrices( &projL, &viewL, &projR, &viewR, &projOriginal, &viewOriginal, DEPTH_LEVEL, DEPTH_RANGE, false); } /*!--------------------------------------------------------------------------* @brief 生成されたノードを更新します。 *---------------------------------------------------------------------------*/ void UpdateNodes() { // フラグメントライト更新 FragmentLightArray::iterator lightEnd = s_FragmentLights.end(); for (FragmentLightArray::iterator light = s_FragmentLights.begin(); light != lightEnd; ++light) { UpdateNode(*light); } // モデル更新 ModelArray::iterator modelEnd = s_Models.end(); for (ModelArray::iterator model = s_Models.begin(); model != modelEnd; ++model) { UpdateNode(*model); } } /*!--------------------------------------------------------------------------* @brief シーンを更新します。 *---------------------------------------------------------------------------*/ void UpdateScene() { UpdateNodes(); UpdateCamera(); ++s_FrameCount; } /*!--------------------------------------------------------------------------* @brief カメラ、ライト、フォグなどのシーン環境を設定します。 *---------------------------------------------------------------------------*/ void SetEnvironment() { nw::gfx::SceneEnvironment& sceneEnvironment = s_RenderContext->GetSceneEnvironment(); sceneEnvironment.SetCamera(s_BaseCameraIndex, s_BaseCamera); //----------------------------- FragmentLightArray::iterator lightEnd = s_FragmentLights.end(); for (FragmentLightArray::iterator light = s_FragmentLights.begin(); light != lightEnd; ++light) { sceneEnvironment.SetFragmentLight(*light); } } /*!--------------------------------------------------------------------------* @brief ビューに関連する更新処理を行います。 *---------------------------------------------------------------------------*/ void SubmitView() { ModelArray::iterator modelEnd = s_Models.end(); for (ModelArray::iterator model = s_Models.begin(); model != modelEnd; ++model) { if (!(*model)->IsVisible()) { continue; } (*model)->UpdateModelViewMatrixAndNormalMatrix(s_BaseCamera->ViewMatrix(), false); } } /*!--------------------------------------------------------------------------* @brief シーンを描画します。 *---------------------------------------------------------------------------*/ void RenderScene() { s_RenderContext->SetRenderTarget(s_RenderTarget); // 描画コマンドを生成します。 s_CommandListSwapper->StartCommandSave(); NW_GL_ASSERT(); s_RenderContext->ClearBuffer( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, nw::ut::FloatColor(0.5f, 0.5f, 0.5f, 1.0f), 1.0f); ModelArray::iterator modelEnd = s_Models.end(); for (ModelArray::iterator model = s_Models.begin(); model != modelEnd; ++model) { if ((*model)->IsVisible()) { nw::gfx::ResMeshArray resMeshes = (*model)->GetResMeshes(); nw::gfx::ResMeshArray::iterator meshEnd = resMeshes.end(); for(nw::gfx::ResMeshArray::iterator mesh = resMeshes.begin(); mesh != meshEnd; ++mesh) { if ((*model)->IsMeshVisible(*mesh)) { s_MeshRenderer->RenderMesh(*mesh, *model); } } } } NW_GL_ASSERT(); s_CommandListSwapper->EndCommandSave(); // 左目の描画を行います。 s_RenderContext->SetCameraMatrix(s_LeftCamera); s_CommandListSwapper->ReuseCommand(false); s_UpperSwapper->MakeTransferBufferCommand(s_RenderTarget, false); // 右目の描画を行います。 s_RenderContext->SetCameraMatrix(s_RightCamera); s_CommandListSwapper->ReuseCommand(false); s_ExtensionSwapper->MakeTransferBufferCommand(s_RenderTarget, false); s_CommandListSwapper->WaitDone(); s_CommandListSwapper->RunAsync(); } /*!--------------------------------------------------------------------------* @brief ディスプレイバッファをスワップして表示します。 *---------------------------------------------------------------------------*/ void PresentBuffer() { s_UpperSwapper->ActivateBuffer(); s_ExtensionSwapper->ActivateBuffer(); nngxSwapBuffers(NN_GX_DISPLAY0); glBindFramebuffer(GL_FRAMEBUFFER, 0); NW_GL_ASSERT(); nngxWaitVSync(NN_GX_DISPLAY0); } /*!--------------------------------------------------------------------------* @brief シーンをデモンストレーションします。 *---------------------------------------------------------------------------*/ void DemoScene() { NW_NULL_ASSERT(s_RenderTarget); InitializeScenes(); s_CommandListSwapper->RunAsync(); nw::demo::DebugUtility::PostInitializeScenes(); bool isContinuing = true; while ( isContinuing ) { nw::demo::DebugUtility::AdvanceAutoTestFrame(); nw::demo::PadFactory::GetPad()->Update(); UpdateScene(); SetEnvironment(); s_RenderContext->SetActiveCamera(s_BaseCameraIndex); SubmitView(); RenderScene(); s_RenderContext->ResetState(); PresentBuffer(); if (nw::demo::Utility::IsTerminating()) { isContinuing = false; } } nw::demo::DebugUtility::PreTerminateScenes(); TerminateScenes(); } } // namespace /*!--------------------------------------------------------------------------* @brief メイン関数です。 *---------------------------------------------------------------------------*/ void nnMain() { InitializeGraphicsSystem(); nw::demo::PadFactory::Initialize(&s_DeviceAllocator); NW_DEMO_TEST_LOOP(&s_DeviceAllocator, NULL, NULL) { InitializeGraphics(); DemoScene(); TerminateGraphics(); } nw::demo::PadFactory::Finalize(); FinalizeGraphicsSystem(); }