Main.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #include <windows.h>
  2. #include "BsApplication.h"
  3. #include "BsImporter.h"
  4. #include "BsGpuProgramImportOptions.h"
  5. #include "BsTextureImportOptions.h"
  6. #include "BsMaterial.h"
  7. #include "BsShader.h"
  8. #include "BsTechnique.h"
  9. #include "BsPass.h"
  10. #include "BsCoreThreadAccessor.h"
  11. #include "BsApplication.h"
  12. #include "BsVirtualInput.h"
  13. #include "BsCamera.h"
  14. #include "BsRenderable.h"
  15. #include "BsGUIWidget.h"
  16. #include "BsGUIArea.h"
  17. #include "BsGUILayoutX.h"
  18. #include "BsGUILayoutY.h"
  19. #include "BsGUISpace.h"
  20. #include "BsGUILabel.h"
  21. #include "BsGUIButton.h"
  22. #include "BsGUIListBox.h"
  23. #include "BsBuiltinResources.h"
  24. #include "BsRTTIType.h"
  25. #include "BsHString.h"
  26. #include "BsRenderWindow.h"
  27. #include "BsSceneObject.h"
  28. #include "BsCoreThread.h"
  29. #include "BsProfilerOverlay.h"
  30. #include "BsRenderer.h"
  31. #include "CameraFlyer.h"
  32. namespace BansheeEngine
  33. {
  34. UINT32 resolutionWidth = 1280;
  35. UINT32 resolutionHeight = 720;
  36. /**
  37. * Imports all of ours assets and prepares GameObject that handle the example logic.
  38. */
  39. void setUpExample();
  40. /**
  41. * Toggles the primary window between full-screen and windowed mode.
  42. */
  43. void toggleFullscreen();
  44. /**
  45. * Triggered whenever a virtual button is released.
  46. */
  47. void buttonUp(const VirtualButton& button, UINT32 deviceIdx);
  48. }
  49. using namespace BansheeEngine;
  50. /**
  51. * Main entry point into the application.
  52. */
  53. int CALLBACK WinMain(
  54. _In_ HINSTANCE hInstance,
  55. _In_ HINSTANCE hPrevInstance,
  56. _In_ LPSTR lpCmdLine,
  57. _In_ int nCmdShow
  58. )
  59. {
  60. // Descriptor used for initializing the primary application window.
  61. RENDER_WINDOW_DESC renderWindowDesc;
  62. renderWindowDesc.videoMode = VideoMode(resolutionWidth, resolutionHeight);
  63. renderWindowDesc.title = "Banshee Example App";
  64. renderWindowDesc.fullscreen = false;
  65. // Initializes the application with primary window defined as above and DirectX 11 render system.
  66. // You may use other render systems than DirectX 11, however this example for simplicity only uses DirectX 11.
  67. // If you wanted other render systems you would need to create separate shaders for them and import them above
  68. // along with (or replace) the DX11 ones.
  69. Application::startUp(renderWindowDesc, RenderSystemPlugin::DX11);
  70. // Imports all of ours assets and prepares GameObject that handle the example logic.
  71. setUpExample();
  72. // Runs the main loop that does most of the work. This method will exit when user closes the main
  73. // window or exits in some other way.
  74. Application::instance().runMainLoop();
  75. // Perform cleanup
  76. Application::shutDown();
  77. return 0;
  78. }
  79. namespace BansheeEngine
  80. {
  81. Path exampleModelPath = "..\\..\\..\\..\\Data\\Examples\\Pyromancer.fbx";
  82. Path exampleTexturePath = "..\\..\\..\\..\\Data\\Examples\\Pyromancer.psd";
  83. Path exampleFragmentShaderPath = "..\\..\\..\\..\\Data\\Examples\\example_fs.gpuprog";
  84. Path exampleVertexShaderPath = "..\\..\\..\\..\\Data\\Examples\\example_vs.gpuprog";
  85. GUIButton* toggleFullscreenButton = nullptr;
  86. bool fullscreen = false;
  87. const VideoMode* videoMode = nullptr;
  88. HMesh exampleModel;
  89. HTexture exampleTexture;
  90. HGpuProgram exampleFragmentGPUProg;
  91. HGpuProgram exampleVertexGPUProg;
  92. HCamera sceneCamera;
  93. HProfilerOverlay profilerOverlay;
  94. VirtualButton toggleCPUProfilerBtn;
  95. VirtualButton toggleGPUProfilerBtn;
  96. bool cpuProfilerActive = false;
  97. bool gpuProfilerActive = false;
  98. void setUpExample()
  99. {
  100. /************************************************************************/
  101. /* IMPORT ASSETS */
  102. /************************************************************************/
  103. // Import mesh, texture and shader from the disk. In a normal application you would want to save the imported assets
  104. // so next time the application is ran you can just load them directly. This can be done with Resources::save/load.
  105. // Import an FBX mesh.
  106. exampleModel = static_resource_cast<Mesh>(Importer::instance().import(exampleModelPath));
  107. // When importing you may specify optional import options that control how is the asset imported.
  108. ImportOptionsPtr textureImportOptions = Importer::instance().createImportOptions(exampleTexturePath);
  109. // 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.
  110. // This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
  111. if (rtti_is_of_type<TextureImportOptions>(textureImportOptions))
  112. {
  113. TextureImportOptions* importOptions = static_cast<TextureImportOptions*>(textureImportOptions.get());
  114. // We want the texture to be compressed, just a basic non-alpha format
  115. importOptions->setFormat(PF_BC1);
  116. // We want maximum number of mipmaps to be generated
  117. importOptions->setGenerateMipmaps(true);
  118. }
  119. // Import texture with specified import options
  120. exampleTexture = static_resource_cast<Texture>(Importer::instance().import(exampleTexturePath, textureImportOptions));
  121. // Create import options for fragment GPU program
  122. ImportOptionsPtr gpuProgImportOptions = Importer::instance().createImportOptions(exampleFragmentShaderPath);
  123. if (rtti_is_of_type<GpuProgramImportOptions>(gpuProgImportOptions))
  124. {
  125. GpuProgramImportOptions* importOptions = static_cast<GpuProgramImportOptions*>(gpuProgImportOptions.get());
  126. // Name of the entry function in the GPU program
  127. importOptions->setEntryPoint("ps_main");
  128. // Language the GPU program is written in. Can only be hlsl for DX11
  129. importOptions->setLanguage("hlsl");
  130. // GPU program profile specifying what feature-set the shader code uses.
  131. importOptions->setProfile(GPP_PS_4_0);
  132. // Type of the shader.
  133. importOptions->setType(GPT_FRAGMENT_PROGRAM);
  134. }
  135. // Import fragment GPU program
  136. exampleFragmentGPUProg = static_resource_cast<GpuProgram>(Importer::instance().import(exampleFragmentShaderPath, gpuProgImportOptions));
  137. // Create import options for vertex GPU program. Similar as above.
  138. gpuProgImportOptions = Importer::instance().createImportOptions(exampleVertexShaderPath);
  139. if (rtti_is_of_type<GpuProgramImportOptions>(gpuProgImportOptions))
  140. {
  141. GpuProgramImportOptions* importOptions = static_cast<GpuProgramImportOptions*>(gpuProgImportOptions.get());
  142. importOptions->setEntryPoint("vs_main");
  143. importOptions->setLanguage("hlsl");
  144. importOptions->setProfile(GPP_VS_4_0);
  145. importOptions->setType(GPT_VERTEX_PROGRAM);
  146. }
  147. // Import vertex GPU program
  148. exampleVertexGPUProg = static_resource_cast<GpuProgram>(Importer::instance().import(exampleVertexShaderPath, gpuProgImportOptions));
  149. /************************************************************************/
  150. /* CREATE SHADER */
  151. /************************************************************************/
  152. // Create a shader that references our vertex and fragment GPU programs, and set
  153. // up shader input parameters.
  154. ShaderPtr exampleShader = Shader::create("ExampleShader");
  155. // Set up shader parameters and renderer semantics.
  156. // Renderer semantics allow our renderer to automatically populate certain shader parameters (e.g. a world view projection matrix).
  157. // These semantics are purely optional and depend on the renderer used. Certain renderers expect certain semantics to be set up
  158. // otherwise they will not render the objects. You always have the option to populate all the parameters manually, but in this example
  159. // we go with the semantics route as it allows for a "set up and forget" approach.
  160. // Add a world view projection matrix parameter, which will be populated by the renderer.
  161. // We map our shader parameter name to the actual GPU program variable, both being "matWorldViewProj" in this case.
  162. exampleShader->addParameter("matWorldViewProj", "matWorldViewProj", GPDT_MATRIX_4X4, RPS_WorldViewProjTfrm);
  163. // Add a sampler and a texture semantic that we will populate manually.
  164. exampleShader->addParameter("samp", "samp", GPOT_SAMPLER2D);
  165. exampleShader->addParameter("tex", "tex", GPOT_TEXTURE2D);
  166. // Our GPU programs use parameter blocks (constant buffers in DX11 lingo). Here we notify the renderer
  167. // that this particular parameter block contains object-specific data (like the world view projection parameter
  168. // we defined above).
  169. exampleShader->setParamBlockAttribs("PerObject", true, GPBU_DYNAMIC, RBS_PerObject);
  170. /************************************************************************/
  171. /* CREATE MATERIAL */
  172. /************************************************************************/
  173. // Create a shader technique. Shader can have many different techniques and the renderer will automatically
  174. // use the most appropriate technique depending on the active renderer and render system. e.g. you can have different
  175. // techniques using HLSL9, HLSL11 and GLSL GPU programs for DirectX 9, DirectX 11 and OpenGL render systems respectively.
  176. TechniquePtr technique = exampleShader->addTechnique(RenderSystemDX11, RendererDefault);
  177. // Add a new pass to the technique. Each technique can have multiple passes that allow you to render the same
  178. // object multiple times using different shaders.
  179. PassPtr pass = technique->addPass();
  180. pass->setVertexProgram(exampleVertexGPUProg);
  181. pass->setFragmentProgram(exampleFragmentGPUProg);
  182. // And finally create a material with the newly created shader
  183. HMaterial exampleMaterial = Material::create(exampleShader);
  184. // And set the texture to be used by the "tex" shader parameter. We leave the "samp"
  185. // parameter at its defaults.
  186. exampleMaterial->setTexture("tex", exampleTexture);
  187. /************************************************************************/
  188. /* SCENE OBJECT */
  189. /************************************************************************/
  190. // Now we create a scene object that has a position, orientation, scale and optionally
  191. // components to govern its logic. In this particular case we are creating a SceneObject
  192. // with a Renderable component which will render a mesh at the position of the scene object
  193. // with the provided material.
  194. // Create new scene object at (0, 0, 0)
  195. HSceneObject pyromancerSO = SceneObject::create("Pyromancer");
  196. // Attach the Renderable component and hook up the mesh we imported earlier,
  197. // and the material we created in the previous section.
  198. HRenderable renderable = pyromancerSO->addComponent<Renderable>();
  199. renderable->setMesh(exampleModel);
  200. renderable->setMaterial(exampleMaterial);
  201. /************************************************************************/
  202. /* CAMERA */
  203. /************************************************************************/
  204. // In order something to render on screen we need at least one camera.
  205. // Like before, we create a new scene object at (0, 0, 0).
  206. HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
  207. // We retrieve the primary render window and add a Camera component that
  208. // will output whatever it sees into that window (You could also use a render texture
  209. // or another window you created).
  210. RenderWindowPtr window = gApplication().getPrimaryWindow();
  211. sceneCamera = sceneCameraSO->addComponent<Camera>(window);
  212. // Set up camera component properties
  213. // Priority determines in what order are cameras rendered in case multiple cameras render to the same render target.
  214. // We raise the priority slightly because later in code we have defined a GUI camera that we want to render second.
  215. sceneCamera->setPriority(1);
  216. // Set closest distance that is visible. Anything below that is clipped.
  217. sceneCamera->setNearClipDistance(5);
  218. // Set aspect ratio depending on the current resolution
  219. sceneCamera->setAspectRatio(resolutionWidth / (float)resolutionHeight); // TODO - This needs to get called whenever resolution changes
  220. // Add a CameraFlyer component that allows us to move the camera. See CameraFlyer for more information.
  221. sceneCameraSO->addComponent<CameraFlyer>();
  222. // Position and orient the camera scene object
  223. sceneCameraSO->setPosition(Vector3(40.0f, 30.0f, 230.0f));
  224. sceneCameraSO->lookAt(Vector3(0, 0, 0));
  225. /************************************************************************/
  226. /* INPUT */
  227. /************************************************************************/
  228. // Register input configuration
  229. // Banshee allows you to use VirtualInput system which will map input device buttons
  230. // and axes to arbitrary names, which allows you to change input buttons without affecting
  231. // the code that uses it, since the code is only aware of the virtual names.
  232. // If you want more direct input, see Input class.
  233. auto inputConfig = VirtualInput::instance().getConfiguration();
  234. // Camera controls for buttons (digital 0-1 input, e.g. keyboard or gamepad button)
  235. inputConfig->registerButton("Forward", BC_W);
  236. inputConfig->registerButton("Back", BC_S);
  237. inputConfig->registerButton("Left", BC_A);
  238. inputConfig->registerButton("Right", BC_D);
  239. inputConfig->registerButton("Forward", BC_UP);
  240. inputConfig->registerButton("Back", BC_BACK);
  241. inputConfig->registerButton("Left", BC_LEFT);
  242. inputConfig->registerButton("Right", BC_RIGHT);
  243. inputConfig->registerButton("FastMove", BC_LSHIFT);
  244. inputConfig->registerButton("RotateCam", BC_MOUSE_RIGHT);
  245. // Camera controls for axes (analog input, e.g. mouse or gamepad thumbstick)
  246. // These return values in [-1.0, 1.0] range.
  247. inputConfig->registerAxis("Horizontal", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseX));
  248. inputConfig->registerAxis("Vertical", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseY));
  249. // Controls that toggle the profiler overlays
  250. inputConfig->registerButton("CPUProfilerOverlay", BC_F1);
  251. inputConfig->registerButton("GPUProfilerOverlay", BC_F2);
  252. // Cache the profiler overlay buttons so when a button is pressed we can quickly
  253. // use these to determine its the one we want
  254. toggleCPUProfilerBtn = VirtualButton("CPUProfilerOverlay");
  255. toggleGPUProfilerBtn = VirtualButton("GPUProfilerOverlay");
  256. // Hook up a callback that gets triggered whenever a virtual button is released
  257. VirtualInput::instance().onButtonUp.connect(&buttonUp);
  258. /************************************************************************/
  259. /* GUI */
  260. /************************************************************************/
  261. // Create a scene object that will contain GUI components
  262. HSceneObject guiSO = SceneObject::create("Example");
  263. // First we want another camera that is responsible for rendering GUI
  264. HCamera guiCamera = guiSO->addComponent<Camera>(window);
  265. // Set up GUI camera properties.
  266. // We don't care about aspect ratio for GUI camera.
  267. guiCamera->setAspectRatio(1.0f);
  268. // This camera should ignore any Renderable objects in the scene
  269. guiCamera->setIgnoreSceneRenderables(true);
  270. // Don't clear this camera as that would clear anything the main camera has rendered.
  271. guiCamera->getViewport()->setRequiresClear(false, false, false);
  272. // Add a GUIWidget, the top-level GUI component, parent to all GUI elements. GUI widgets
  273. // require you to specify a viewport that they will output rendered GUI elements to.
  274. HGUIWidget gui = guiSO->addComponent<GUIWidget>(guiCamera->getViewport().get());
  275. // Depth allows you to control how is a GUI widget rendered in relation to other widgets
  276. // Lower depth means the widget will be rendered in front of those with higher. In this case we just
  277. // make the depth mid-range as there are no other widgets.
  278. gui->setDepth(128);
  279. // GUI skin defines how are all child elements of the GUI widget renderer. It contains all their styles
  280. // and default layout properties. We use the default skin that comes built into Banshee.
  281. gui->setSkin(BuiltinResources::instance().getGUISkin());
  282. // Create a GUI area that is used for displaying messages about buttons for toggling profiler overlays.
  283. // This area will stretch the entire surface of its parent widget, even if the widget is resized.
  284. GUIArea* topArea = GUIArea::createStretchedXY(*gui, 0, 0, 0, 0);
  285. // Add a vertical layout that will automatically position any child elements top to bottom.
  286. GUILayout& topLayout = topArea->getLayout().addLayoutY();
  287. // Add a couple of labels to the layout with the needed messages. Labels expect a HString object that
  288. // maps into a string table and allows for easily localization.
  289. topLayout.addElement(GUILabel::create(HString(L"Press F1 to toggle CPU profiler overlay")));
  290. topLayout.addElement(GUILabel::create(HString(L"Press F2 to toggle GPU profiler overlay")));
  291. // Add a flexible space that fills up any remaining area in the area, making the two labels above be aligned
  292. // to the top of the GUI widget (and the screen).
  293. topLayout.addFlexibleSpace();
  294. // Create a GUI area that is used for displaying resolution and fullscreen options.
  295. GUIArea* rightArea = GUIArea::createStretchedXY(*gui, 0, 0, 0, 0);
  296. // We want all the GUI elements be right aligned, so we add a flexible space first.
  297. rightArea->getLayout().addFlexibleSpace();
  298. // And we want the elements to be vertically placed, top to bottom
  299. GUILayout& rightLayout = rightArea->getLayout().addLayoutY();
  300. // Add an empty space of 50 pixels
  301. rightLayout.addSpace(50);
  302. // Add a button that will trigger a callback when clicked
  303. toggleFullscreenButton = GUIButton::create(HString(L"Toggle fullscreen"));
  304. toggleFullscreenButton->onClick.connect(&toggleFullscreen);
  305. rightLayout.addElement(toggleFullscreenButton);
  306. // Add a profiler overlay object that is resposible for displaying CPU and GPU profiling GUI
  307. profilerOverlay = guiSO->addComponent<ProfilerOverlay>(guiCamera->getViewport());
  308. }
  309. void toggleFullscreen()
  310. {
  311. RenderWindowPtr window = gApplication().getPrimaryWindow();
  312. // In order to toggle between full-screen and windowed mode we need to use a CoreAccessor.
  313. // Banshee is a multi-threaded engine and when you need to communicate between simulation and
  314. // core thread you will use a CoreAccessor. Calling a core accessor method will essentially
  315. // queue the method to be executed later. Since RenderWindow is a core object you need to use
  316. // CoreAccessor to modify and access it from simulation thread, except where noted otherwise.
  317. // Classes where it is not clear if they are to be used on the core or simulation thread have
  318. // it noted in their documentation. e.g. RenderWindow::setWindowed method is marked as "Core only".
  319. // Additional asserts are normally in place for debug builds which make it harder for you to accidentally
  320. // call something from the wrong thread.
  321. if (fullscreen)
  322. {
  323. gCoreAccessor().setWindowed(window, resolutionWidth, resolutionHeight);
  324. }
  325. else
  326. {
  327. //gCoreAccessor().setFullscreen(window, *videoMode);
  328. gCoreAccessor().setFullscreen(window, 1920, 1200);
  329. }
  330. fullscreen = !fullscreen;
  331. }
  332. void buttonUp(const VirtualButton& button, UINT32 deviceIdx)
  333. {
  334. // Check if the pressed button is one of the either buttons we defined
  335. // in "setUpExample", and toggle profiler overlays accordingly.
  336. // Device index is ignored for now, as it is assumed the user is using a single keyboard,
  337. // but if you wanted support for multiple gamepads you would check deviceIdx.
  338. if (button == toggleCPUProfilerBtn)
  339. {
  340. if (cpuProfilerActive)
  341. {
  342. profilerOverlay->hide();
  343. cpuProfilerActive = false;
  344. }
  345. else
  346. {
  347. profilerOverlay->show(ProfilerOverlayType::CPUSamples);
  348. cpuProfilerActive = true;
  349. gpuProfilerActive = false;
  350. }
  351. }
  352. else if (button == toggleGPUProfilerBtn)
  353. {
  354. if (gpuProfilerActive)
  355. {
  356. profilerOverlay->hide();
  357. gpuProfilerActive = false;
  358. }
  359. else
  360. {
  361. profilerOverlay->show(ProfilerOverlayType::GPUSamples);
  362. gpuProfilerActive = true;
  363. cpuProfilerActive = false;
  364. }
  365. }
  366. }
  367. }