| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- // Engine includes
- #include "BsApplication.h"
- #include "Resources/BsResources.h"
- #include "Resources/BsBuiltinResources.h"
- #include "Importer/BsImporter.h"
- #include "Importer/BsTextureImportOptions.h"
- #include "Importer/BsMeshImportOptions.h"
- #include "Material/BsMaterial.h"
- #include "Material/BsShader.h"
- #include "Input/BsVirtualInput.h"
- #include "Components/BsCCamera.h"
- #include "Components/BsCRenderable.h"
- #include "Components/BsCLight.h"
- #include "GUI/BsCGUIWidget.h"
- #include "GUI/BsGUILayoutX.h"
- #include "GUI/BsGUILayoutY.h"
- #include "GUI/BsGUIPanel.h"
- #include "GUI/BsGUISpace.h"
- #include "GUI/BsGUILabel.h"
- #include "GUI/BsGUIButton.h"
- #include "GUI/BsGUIListBox.h"
- #include "GUI/BsProfilerOverlay.h"
- #include "RenderAPI/BsRenderAPI.h"
- #include "RenderAPI/BsRenderWindow.h"
- #include "Scene/BsSceneObject.h"
- #include "BsEngineConfig.h"
- #if BS_PLATFORM == BS_PLATFORM_WIN32
- #include <windows.h>
- #endif
- // Example includes
- #include "CameraFlyer.h"
- namespace bs
- {
- UINT32 windowResWidth = 1280;
- UINT32 windowResHeight = 720;
- /** Imports all of our assets and prepares GameObject that handle the example logic. */
- void setUpExample();
- /** Import mesh/texture/GPU programs used by the example. */
- void loadAssets(HMesh& model, HTexture& texture, HShader& shader);
- /** Create a material used by our example model. */
- HMaterial createMaterial(const HTexture& texture, const HShader& shader);
- /** Set up example scene objects. */
- void setUp3DScene(const HMesh& mesh, const HMaterial& material);
- /** Set up example GUI. */
- void setUpGUI();
- /** Set up input configuration and callbacks. */
- void setUpInput();
- /** Toggles the primary window between full-screen and windowed mode. */
- void toggleFullscreen();
- /** Called whenever the main render window is resized. */
- void renderWindowResized();
- /** Called when the selected video mode changes in the video mode list box. */
- void videoModeChanged(UINT32 idx, bool enabled);
- /** Triggered whenever a virtual button is released. */
- void buttonUp(const VirtualButton& button, UINT32 deviceIdx);
- }
- using namespace bs;
- /** Main entry point into the application. */
- #if BS_PLATFORM == BS_PLATFORM_WIN32
- int CALLBACK WinMain(
- _In_ HINSTANCE hInstance,
- _In_ HINSTANCE hPrevInstance,
- _In_ LPSTR lpCmdLine,
- _In_ int nCmdShow
- )
- #else
- int main()
- #endif
- {
- // Descriptor used for initializing the engine
- START_UP_DESC startUpDesc;
- // Use default values as specified by the build system
- startUpDesc.renderAPI = BS_RENDER_API_MODULE;
- startUpDesc.renderer = BS_RENDERER_MODULE;
- startUpDesc.audio = BS_AUDIO_MODULE;
- startUpDesc.physics = BS_PHYSICS_MODULE;
- // Descriptor used for initializing the primary application window.
- startUpDesc.primaryWindowDesc.videoMode = VideoMode(windowResWidth, windowResHeight);
- startUpDesc.primaryWindowDesc.title = "Banshee Example App";
- startUpDesc.primaryWindowDesc.fullscreen = false;
- startUpDesc.primaryWindowDesc.depthBuffer = false;
- // List of importer plugins we plan on using for importing various resources
- startUpDesc.importers.push_back("BansheeFreeImgImporter"); // For importing textures
- startUpDesc.importers.push_back("BansheeFBXImporter"); // For importing meshes
- startUpDesc.importers.push_back("BansheeFontImporter"); // For importing fonts
- startUpDesc.importers.push_back("BansheeSL"); // For importing shaders
- // Initializes the application with systems and primary window as defined above
- Application::startUp(startUpDesc);
- // Imports all of ours assets and prepares GameObjects that handle the example logic.
- setUpExample();
-
- // Runs the main loop that does most of the work. This method will exit when user closes the main
- // window or exits in some other way.
- Application::instance().runMainLoop();
- Application::shutDown();
- return 0;
- }
- namespace bs
- {
- Path dataPath = Paths::getRuntimeDataPath();
- Path exampleModelPath = dataPath + "Examples/Dragon.fbx";
- Path exampleTexturePath = dataPath + "Examples/Dragon.tga";
- Path exampleShaderPath = dataPath + "Examples/Example.bsl";
- GUIButton* toggleFullscreenButton = nullptr;
- bool fullscreen = false;
- const VideoMode* selectedVideoMode = nullptr;
- Vector<const VideoMode*> videoModes;
- HCamera sceneCamera;
- HProfilerOverlay profilerOverlay;
- VirtualButton toggleCPUProfilerBtn;
- VirtualButton toggleGPUProfilerBtn;
- bool cpuProfilerActive = false;
- bool gpuProfilerActive = false;
- void setUpExample()
- {
- HMesh exampleModel;
- HTexture exampleTexture;
- HShader exampleShader;
-
- loadAssets(exampleModel, exampleTexture, exampleShader);
- HMaterial exampleMaterial = createMaterial(exampleTexture, exampleShader);
- setUp3DScene(exampleModel, exampleMaterial);
- setUpGUI();
- setUpInput();
- }
- /**
- * Load the required resources. First try to load a pre-processed version of the resources. If they don't exist import
- * resources from the source formats into engine format, and save them for next time.
- */
- void loadAssets(HMesh& model, HTexture& texture, HShader& shader)
- {
- // Set up paths to pre-processed versions of example resources.
- Path exampleModelAssetPath = exampleModelPath;
- Path exampleTextureAssetPath = exampleTexturePath;
- Path exampleShaderAssetPath = exampleShaderPath;
- exampleModelAssetPath.setExtension(exampleModelAssetPath.getExtension() + ".asset");
- exampleTextureAssetPath.setExtension(exampleTextureAssetPath.getExtension() + ".asset");
- exampleShaderAssetPath.setExtension(exampleShaderAssetPath.getExtension() + ".asset");
- // Load an FBX mesh.
- model = gResources().load<Mesh>(exampleModelAssetPath);
- if(model == nullptr) // Mesh file doesn't exist, import from the source file.
- {
- // When importing you may specify optional import options that control how is the asset imported.
- SPtr<ImportOptions> meshImportOptions = Importer::instance().createImportOptions(exampleModelPath);
- // rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a
- // non-mesh resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
- if (rtti_is_of_type<MeshImportOptions>(meshImportOptions))
- {
- MeshImportOptions* importOptions = static_cast<MeshImportOptions*>(meshImportOptions.get());
- // Ensures we can save the mesh contents
- importOptions->setCPUCached(true);
- }
- model = gImporter().import<Mesh>(exampleModelPath, meshImportOptions);
- // Save for later use, so we don't have to import on the next run.
- gResources().save(model, exampleModelAssetPath, true);
- }
- // Load an TGA texture.
- texture = gResources().load<Texture>(exampleTextureAssetPath);
- if (texture == nullptr) // Texture file doesn't exist, import from the source file.
- {
- // When importing you may specify optional import options that control how is the asset imported.
- SPtr<ImportOptions> textureImportOptions = Importer::instance().createImportOptions(exampleTexturePath);
- // rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a
- // non-texture resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
- if (rtti_is_of_type<TextureImportOptions>(textureImportOptions))
- {
- TextureImportOptions* importOptions = static_cast<TextureImportOptions*>(textureImportOptions.get());
- // We want maximum number of mipmaps to be generated
- importOptions->setGenerateMipmaps(true);
- // The texture is in sRGB space
- importOptions->setSRGB(true);
- // Ensures we can save the texture contents
- importOptions->setCPUCached(true);
- }
- // Import texture with specified import options
- texture = gImporter().import<Texture>(exampleTexturePath, textureImportOptions);
- // Save for later use, so we don't have to import on the next run.
- gResources().save(texture, exampleTextureAssetPath, true);
- }
- // Load a shader.
- shader = gResources().load<Shader>(exampleShaderAssetPath);
- if (shader == nullptr) // Mesh file doesn't exist, import from the source file.
- {
- shader = gImporter().import<Shader>(exampleShaderPath);
- // Save for later use, so we don't have to import on the next run.
- gResources().save(shader, exampleShaderAssetPath, true);
- }
- }
- /** Create a material using the provided shader, and assign the provided texture to it. */
- HMaterial createMaterial(const HTexture& texture, const HShader& shader)
- {
- // Create a material with the provided shader.
- HMaterial exampleMaterial = Material::create(shader);
- // And set the texture to be used by the "tex" shader parameter. We leave the "samp"
- // parameter at its defaults. These parameters are defined in the shader (.bsl) file.
- exampleMaterial->setTexture("tex", texture);
- return exampleMaterial;
- }
- /** Set up the 3D object used by the example, and the camera to view the world through. */
- void setUp3DScene(const HMesh& mesh, const HMaterial& material)
- {
- /************************************************************************/
- /* SCENE OBJECT */
- /************************************************************************/
- // Now we create a scene object that has a position, orientation, scale and optionally
- // components to govern its logic. In this particular case we are creating a SceneObject
- // with a Renderable component which will render a mesh at the position of the scene object
- // with the provided material.
- // Create new scene object at (0, 0, 0)
- HSceneObject dragonSO = SceneObject::create("Dragon");
-
- // Attach the Renderable component and hook up the mesh we imported earlier,
- // and the material we created in the previous section.
- HRenderable renderable = dragonSO->addComponent<CRenderable>();
- renderable->setMesh(mesh);
- renderable->setMaterial(material);
- /************************************************************************/
- /* LIGHTS */
- /************************************************************************/
- // Add a couple of lights so that our object isn't completely in the dark.
- HSceneObject lightASO = SceneObject::create("Light A");
- HSceneObject lightBSO = SceneObject::create("Light B");
- lightASO->setPosition(Vector3(0, 50, 0));
- lightBSO->setPosition(Vector3(-130, 140, 450));
- HLight lightA = lightASO->addComponent<CLight>();
- HLight lightB = lightBSO->addComponent<CLight>();
- // Disable physically based attentuation because we want to set our own range
- lightA->setUseAutoAttenuation(false);
- lightB->setUseAutoAttenuation(false);
- lightA->setAttenuationRadius(500.0f);
- lightB->setAttenuationRadius(300.0f);
- lightA->setIntensity(10000.0f);
- lightB->setIntensity(10000.0f);
- /************************************************************************/
- /* CAMERA */
- /************************************************************************/
- // In order something to render on screen we need at least one camera.
- // Like before, we create a new scene object at (0, 0, 0).
- HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
- // Get the primary render window we need for creating the camera. Additionally
- // hook up a callback so we are notified when user resizes the window.
- SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
- window->onResized.connect(&renderWindowResized);
- // Add a Camera component that will output whatever it sees into that window
- // (You could also use a render texture or another window you created).
- sceneCamera = sceneCameraSO->addComponent<CCamera>(window);
- // Set up camera component properties
- // Priority determines in what order are cameras rendered in case multiple cameras render to the same render target.
- // We raise the priority slightly because later in code we have defined a GUI camera that we want to render second.
- sceneCamera->setPriority(1);
- // Set closest distance that is visible. Anything below that is clipped.
- sceneCamera->setNearClipDistance(5);
- // Set farthest distance that is visible. Anything above that is clipped.
- sceneCamera->setFarClipDistance(10000);
- // Set aspect ratio depending on the current resolution
- sceneCamera->setAspectRatio(windowResWidth / (float)windowResHeight);
- // Add a CameraFlyer component that allows us to move the camera. See CameraFlyer for more information.
- sceneCameraSO->addComponent<CameraFlyer>();
- // Position and orient the camera scene object
- sceneCameraSO->setPosition(Vector3(-130.0f, 140.0f, 650.0f));
- sceneCameraSO->lookAt(Vector3(0, 0, 0));
- }
- /** Register mouse and keyboard inputs that will be used for controlling the camera. */
- void setUpInput()
- {
- // Register input configuration
- // Banshee allows you to use VirtualInput system which will map input device buttons
- // and axes to arbitrary names, which allows you to change input buttons without affecting
- // the code that uses it, since the code is only aware of the virtual names.
- // If you want more direct input, see Input class.
- auto inputConfig = VirtualInput::instance().getConfiguration();
- // Camera controls for buttons (digital 0-1 input, e.g. keyboard or gamepad button)
- inputConfig->registerButton("Forward", BC_W);
- inputConfig->registerButton("Back", BC_S);
- inputConfig->registerButton("Left", BC_A);
- inputConfig->registerButton("Right", BC_D);
- inputConfig->registerButton("Forward", BC_UP);
- inputConfig->registerButton("Back", BC_BACK);
- inputConfig->registerButton("Left", BC_LEFT);
- inputConfig->registerButton("Right", BC_RIGHT);
- inputConfig->registerButton("FastMove", BC_LSHIFT);
- inputConfig->registerButton("RotateCam", BC_MOUSE_RIGHT);
- // Camera controls for axes (analog input, e.g. mouse or gamepad thumbstick)
- // These return values in [-1.0, 1.0] range.
- inputConfig->registerAxis("Horizontal", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseX));
- inputConfig->registerAxis("Vertical", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseY));
- // Controls that toggle the profiler overlays
- inputConfig->registerButton("CPUProfilerOverlay", BC_F1);
- inputConfig->registerButton("GPUProfilerOverlay", BC_F2);
- // Cache the profiler overlay buttons so when a button is pressed we can quickly
- // use these to determine its the one we want
- toggleCPUProfilerBtn = VirtualButton("CPUProfilerOverlay");
- toggleGPUProfilerBtn = VirtualButton("GPUProfilerOverlay");
- // Hook up a callback that gets triggered whenever a virtual button is released
- VirtualInput::instance().onButtonUp.connect(&buttonUp);
- }
- /** Set up graphical user interface used by the example. */
- void setUpGUI()
- {
- // Create a scene object that will contain GUI components
- HSceneObject guiSO = SceneObject::create("Example");
- // Get the primary render window we need for creating the camera.
- SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
- // First we want another camera that is responsible for rendering GUI
- HCamera guiCamera = guiSO->addComponent<CCamera>(window);
- // Notify the renderer that the camera will only be used for overlays (e.g. GUI) so it can optimize its usage
- SPtr<RenderSettings> settings = guiCamera->getRenderSettings();
- settings->overlayOnly = true;
- // Set up GUI camera properties.
- // We don't care about aspect ratio for GUI camera.
- guiCamera->setAspectRatio(1.0f);
- // This camera should ignore any Renderable objects in the scene
- guiCamera->setLayers(0);
- // Don't clear this camera as that would clear anything the main camera has rendered.
- guiCamera->getViewport()->setRequiresClear(false, false, false);
- // Add a GUIWidget, the top-level GUI component, parent to all GUI elements. GUI widgets
- // require you to specify a viewport that they will output rendered GUI elements to.
- HGUIWidget gui = guiSO->addComponent<CGUIWidget>(guiCamera);
- // Depth allows you to control how is a GUI widget rendered in relation to other widgets
- // Lower depth means the widget will be rendered in front of those with higher. In this case we just
- // make the depth mid-range as there are no other widgets.
- gui->setDepth(128);
- // GUI skin defines how are all child elements of the GUI widget renderered. It contains all their styles
- // and default layout properties. We use the default skin that comes built into Banshee.
- gui->setSkin(BuiltinResources::instance().getGUISkin());
- // Get the primary GUI panel that stretches over the entire window and add to it a vertical layout
- // that will be using for vertically positioning messages about toggling profiler overlay.
- GUILayout* bottomLayout = gui->getPanel()->addNewElement<GUILayoutY>();
- // Add a flexible space that fills up any remaining area in the layout, making the two labels above be aligned
- // to the bottom of the GUI widget (and the screen).
- bottomLayout->addNewElement<GUIFlexibleSpace>();
- // Add a couple of labels to the layout with the needed messages. Labels expect a HString object that
- // maps into a string table and allows for easily localization.
- bottomLayout->addElement(GUILabel::create(HString(L"Press F1 to toggle CPU profiler overlay")));
- bottomLayout->addElement(GUILabel::create(HString(L"Press F2 to toggle GPU profiler overlay")));
- // Create a GUI panel that is used for displaying resolution and fullscreen options.
- GUILayout* rightLayout = gui->getPanel()->addNewElement<GUILayoutX>();
- // We want all the GUI elements be right aligned, so we add a flexible space first.
- rightLayout->addNewElement<GUIFlexibleSpace>();
- // And we want the elements to be vertically placed, top to bottom
- GUILayout* elemLayout = rightLayout->addNewElement<GUILayoutY>();
- // Leave 30 pixels to the right free
- rightLayout->addNewElement<GUIFixedSpace>(30);
- // Add a button that will trigger a callback when clicked
- toggleFullscreenButton = GUIButton::create(HString(L"Toggle fullscreen"));
- toggleFullscreenButton->onClick.connect(&toggleFullscreen);
- elemLayout->addElement(toggleFullscreenButton);
- // Add a profiler overlay object that is responsible for displaying CPU and GPU profiling GUI
- profilerOverlay = guiSO->addComponent<CProfilerOverlay>(guiCamera->_getCamera());
- // Set up video mode list box
- // First get a list of output devices
- const VideoModeInfo& videoModeInfo = RenderAPI::getVideoModeInfo();
- // Get video mode info for the primary monitor
- const VideoOutputInfo& primaryMonitorInfo = videoModeInfo.getOutputInfo(0);
- // Make the current desktop mode the default video mode
- selectedVideoMode = &primaryMonitorInfo.getDesktopVideoMode();
- // Create list box elements for each available video mode
- UINT32 numVideoModes = primaryMonitorInfo.getNumVideoModes();
- Vector<HString> videoModeLabels(numVideoModes);
- UINT32 selectedVideoModeIdx = 0;
- for (UINT32 i = 0; i < numVideoModes; i++)
- {
- const VideoMode& curVideoMode = primaryMonitorInfo.getVideoMode(i);
- HString videoModeLabel(L"{0} x {1} at {2}Hz");
- videoModeLabel.setParameter(0, toWString(curVideoMode.getWidth()));
- videoModeLabel.setParameter(1, toWString(curVideoMode.getHeight()));
- videoModeLabel.setParameter(2, toWString(Math::roundToInt(curVideoMode.getRefreshRate())));
- videoModeLabels[i] = videoModeLabel;
- videoModes.push_back(&curVideoMode);
- if (curVideoMode == *selectedVideoMode)
- selectedVideoModeIdx = i;
- }
- // Create the list box
- GUIListBox* videoModeListBox = GUIListBox::create(videoModeLabels);
- elemLayout->addElement(videoModeListBox);
- // Select the default (desktop) video mode
- videoModeListBox->selectElement(selectedVideoModeIdx);
- // Set up a callback to be notified when video mode changes
- videoModeListBox->onSelectionToggled.connect(&videoModeChanged);
- }
- /** Callback method that toggles between fullscreen and windowed modes. */
- void toggleFullscreen()
- {
- SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
- if (fullscreen)
- window->setWindowed(windowResWidth, windowResHeight);
- else
- window->setFullscreen(*selectedVideoMode);
- fullscreen = !fullscreen;
- }
- /** Callback triggered wheneve the user resizes the example window. */
- void renderWindowResized()
- {
- SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
- const RenderWindowProperties& rwProps = window->getProperties();
- if (!fullscreen)
- {
- windowResWidth = rwProps.width;
- windowResHeight = rwProps.height;
- }
- sceneCamera->setAspectRatio(rwProps.width / (float)rwProps.height);
- }
- /** Callback triggered when the user selects a new video mode from the GUI drop down element. */
- void videoModeChanged(UINT32 idx, bool enabled)
- {
- if (!enabled)
- return;
- selectedVideoMode = videoModes[idx];
- if (fullscreen)
- {
- SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
- window->setFullscreen(*selectedVideoMode);
- }
- }
- /** Callback triggered when a user hits a button. */
- void buttonUp(const VirtualButton& button, UINT32 deviceIdx)
- {
- // Check if the pressed button is one of the either buttons we defined in "setUpExample", and toggle profiler
- // overlays accordingly. Device index is ignored for now, as it is assumed the user is using a single keyboard,
- // but if you wanted support for multiple gamepads you would check deviceIdx.
- if (button == toggleCPUProfilerBtn)
- {
- if (cpuProfilerActive)
- {
- profilerOverlay->hide();
- cpuProfilerActive = false;
- }
- else
- {
- profilerOverlay->show(ProfilerOverlayType::CPUSamples);
- cpuProfilerActive = true;
- gpuProfilerActive = false;
- }
- }
- else if (button == toggleGPUProfilerBtn)
- {
- if (gpuProfilerActive)
- {
- profilerOverlay->hide();
- gpuProfilerActive = false;
- }
- else
- {
- profilerOverlay->show(ProfilerOverlayType::GPUSamples);
- gpuProfilerActive = true;
- cpuProfilerActive = false;
- }
- }
- }
- }
|