Main.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. #include <windows.h>
  2. #include "BsApplication.h"
  3. #include "BsImporter.h"
  4. #include "BsTextureImportOptions.h"
  5. #include "BsMaterial.h"
  6. #include "BsShader.h"
  7. #include "BsTechnique.h"
  8. #include "BsPass.h"
  9. #include "BsCoreThreadAccessor.h"
  10. #include "BsApplication.h"
  11. #include "BsVirtualInput.h"
  12. #include "BsCCamera.h"
  13. #include "BsCRenderable.h"
  14. #include "BsCGUIWidget.h"
  15. #include "BsGUILayoutX.h"
  16. #include "BsGUILayoutY.h"
  17. #include "BsGUISpace.h"
  18. #include "BsGUILabel.h"
  19. #include "BsGUIButton.h"
  20. #include "BsGUIListBox.h"
  21. #include "BsBuiltinResources.h"
  22. #include "BsRTTIType.h"
  23. #include "BsHString.h"
  24. #include "BsRenderWindow.h"
  25. #include "BsSceneObject.h"
  26. #include "BsCoreThread.h"
  27. #include "BsProfilerOverlay.h"
  28. #include "BsCoreRenderer.h"
  29. #include "BsResources.h"
  30. #include "BsGUIPanel.h"
  31. #include "CameraFlyer.h"
  32. namespace BansheeEngine
  33. {
  34. UINT32 windowResWidth = 1280;
  35. UINT32 windowResHeight = 720;
  36. /**
  37. * Imports all of our assets and prepares GameObject that handle the example logic.
  38. */
  39. void setUpExample();
  40. /**
  41. * Import mesh/texture/GPU programs used by the example.
  42. */
  43. void importAssets(HMesh& model, HTexture& texture, HShader& shader);
  44. /**
  45. * Create a material used by our example model.
  46. */
  47. HMaterial createMaterial(const HTexture& texture, const HShader& shader);
  48. /**
  49. * Set up example scene objects.
  50. */
  51. void setUp3DScene(const HMesh& mesh, const HMaterial& material);
  52. /**
  53. * Set up example GUI.
  54. */
  55. void setUpGUI();
  56. /**
  57. * Set up input configuration and callbacks.
  58. */
  59. void setUpInput();
  60. /**
  61. * Toggles the primary window between full-screen and windowed mode.
  62. */
  63. void toggleFullscreen();
  64. /**
  65. * Called whenever the main render window is resized.
  66. */
  67. void renderWindowResized();
  68. /**
  69. * Called when the selected video mode changes in the video mode list box.
  70. */
  71. void videoModeChanged(UINT32 idx);
  72. /**
  73. * Triggered whenever a virtual button is released.
  74. */
  75. void buttonUp(const VirtualButton& button, UINT32 deviceIdx);
  76. }
  77. using namespace BansheeEngine;
  78. /**
  79. * Main entry point into the application.
  80. */
  81. int CALLBACK WinMain(
  82. _In_ HINSTANCE hInstance,
  83. _In_ HINSTANCE hPrevInstance,
  84. _In_ LPSTR lpCmdLine,
  85. _In_ int nCmdShow
  86. )
  87. {
  88. // Descriptor used for initializing the primary application window.
  89. RENDER_WINDOW_DESC renderWindowDesc;
  90. renderWindowDesc.videoMode = VideoMode(windowResWidth, windowResHeight);
  91. renderWindowDesc.title = "Banshee Example App";
  92. renderWindowDesc.fullscreen = false;
  93. // List of importer plugins we plan on using for importing various resources
  94. Vector<String> importers;
  95. importers.push_back("BansheeFreeImgImporter"); // For importing textures
  96. importers.push_back("BansheeFBXImporter"); // For importing meshes
  97. importers.push_back("BansheeFontImporter"); // For importing fonts
  98. importers.push_back("BansheeSL"); // For importing shaders
  99. // Initializes the application with primary window defined as above and DirectX 11 render system.
  100. // You may use other render systems than DirectX 11, however this example for simplicity only uses DirectX 11.
  101. // If you wanted other render systems you would need to create separate shaders for them and import them
  102. // along with (or replace) the DX11 ones.
  103. Application::startUp(renderWindowDesc, RenderAPIPlugin::DX11, RendererPlugin::Default, importers);
  104. // Imports all of ours assets and prepares GameObject that handle the example logic.
  105. setUpExample();
  106. // Runs the main loop that does most of the work. This method will exit when user closes the main
  107. // window or exits in some other way.
  108. Application::instance().runMainLoop();
  109. Application::shutDown();
  110. return 0;
  111. }
  112. namespace BansheeEngine
  113. {
  114. Path dataPath = Application::findPath(RUNTIME_DATA_PATH);
  115. Path exampleModelPath = dataPath + "Examples\\Dragon.fbx";
  116. Path exampleTexturePath = dataPath + "Examples\\Dragon.tga";
  117. Path exampleShaderPath = dataPath + "Examples\\Example.bsl";
  118. GUIButton* toggleFullscreenButton = nullptr;
  119. bool fullscreen = false;
  120. const VideoMode* selectedVideoMode = nullptr;
  121. Vector<const VideoMode*> videoModes;
  122. HCamera sceneCamera;
  123. HProfilerOverlay profilerOverlay;
  124. VirtualButton toggleCPUProfilerBtn;
  125. VirtualButton toggleGPUProfilerBtn;
  126. bool cpuProfilerActive = false;
  127. bool gpuProfilerActive = false;
  128. void setUpExample()
  129. {
  130. HMesh exampleModel;
  131. HTexture exampleTexture;
  132. HShader exampleShader;
  133. importAssets(exampleModel, exampleTexture, exampleShader);
  134. HMaterial exampleMaterial = createMaterial(exampleTexture, exampleShader);
  135. setUp3DScene(exampleModel, exampleMaterial);
  136. setUpGUI();
  137. setUpInput();
  138. }
  139. void importAssets(HMesh& model, HTexture& texture, HShader& shader)
  140. {
  141. // Import mesh, texture and shader from the disk. In a normal application you would want to save the imported assets
  142. // so next time the application is ran you can just load them directly. This can be done with Resources::save/load.
  143. // Import an FBX mesh.
  144. model = Importer::instance().import<Mesh>(exampleModelPath);
  145. // When importing you may specify optional import options that control how is the asset imported.
  146. ImportOptionsPtr textureImportOptions = Importer::instance().createImportOptions(exampleTexturePath);
  147. // 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.
  148. // This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
  149. if (rtti_is_of_type<TextureImportOptions>(textureImportOptions))
  150. {
  151. TextureImportOptions* importOptions = static_cast<TextureImportOptions*>(textureImportOptions.get());
  152. // We want maximum number of mipmaps to be generated
  153. importOptions->setGenerateMipmaps(true);
  154. }
  155. // Import texture with specified import options
  156. texture = Importer::instance().import<Texture>(exampleTexturePath, textureImportOptions);
  157. // Import shader
  158. shader = Importer::instance().import<Shader>(exampleShaderPath);
  159. }
  160. HMaterial createMaterial(const HTexture& texture, const HShader& shader)
  161. {
  162. // And finally create a material with the newly created shader
  163. HMaterial exampleMaterial = Material::create(shader);
  164. // And set the texture to be used by the "tex" shader parameter. We leave the "samp"
  165. // parameter at its defaults.
  166. exampleMaterial->setTexture("tex", texture);
  167. return exampleMaterial;
  168. }
  169. void setUp3DScene(const HMesh& mesh, const HMaterial& material)
  170. {
  171. /************************************************************************/
  172. /* SCENE OBJECT */
  173. /************************************************************************/
  174. // Now we create a scene object that has a position, orientation, scale and optionally
  175. // components to govern its logic. In this particular case we are creating a SceneObject
  176. // with a Renderable component which will render a mesh at the position of the scene object
  177. // with the provided material.
  178. // Create new scene object at (0, 0, 0)
  179. HSceneObject dragonSO = SceneObject::create("Dragon");
  180. // Attach the Renderable component and hook up the mesh we imported earlier,
  181. // and the material we created in the previous section.
  182. HRenderable renderable = dragonSO->addComponent<CRenderable>();
  183. renderable->setMesh(mesh);
  184. renderable->setMaterial(material);
  185. /************************************************************************/
  186. /* CAMERA */
  187. /************************************************************************/
  188. // In order something to render on screen we need at least one camera.
  189. // Like before, we create a new scene object at (0, 0, 0).
  190. HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
  191. // Get the primary render window we need for creating the camera. Additionally
  192. // hook up a callback so we are notified when user resizes the window.
  193. RenderWindowPtr window = gApplication().getPrimaryWindow();
  194. window->onResized.connect(&renderWindowResized);
  195. // Add a Camera component that will output whatever it sees into that window
  196. // (You could also use a render texture or another window you created).
  197. sceneCamera = sceneCameraSO->addComponent<CCamera>(window);
  198. // Set up camera component properties
  199. // Priority determines in what order are cameras rendered in case multiple cameras render to the same render target.
  200. // We raise the priority slightly because later in code we have defined a GUI camera that we want to render second.
  201. sceneCamera->setPriority(1);
  202. // Set closest distance that is visible. Anything below that is clipped.
  203. sceneCamera->setNearClipDistance(5);
  204. // Set aspect ratio depending on the current resolution
  205. sceneCamera->setAspectRatio(windowResWidth / (float)windowResHeight);
  206. // Add a CameraFlyer component that allows us to move the camera. See CameraFlyer for more information.
  207. sceneCameraSO->addComponent<CameraFlyer>();
  208. // Position and orient the camera scene object
  209. sceneCameraSO->setPosition(Vector3(-130.0f, 140.0f, 650.0f));
  210. sceneCameraSO->lookAt(Vector3(0, 0, 0));
  211. }
  212. void setUpInput()
  213. {
  214. // Register input configuration
  215. // Banshee allows you to use VirtualInput system which will map input device buttons
  216. // and axes to arbitrary names, which allows you to change input buttons without affecting
  217. // the code that uses it, since the code is only aware of the virtual names.
  218. // If you want more direct input, see Input class.
  219. auto inputConfig = VirtualInput::instance().getConfiguration();
  220. // Camera controls for buttons (digital 0-1 input, e.g. keyboard or gamepad button)
  221. inputConfig->registerButton("Forward", BC_W);
  222. inputConfig->registerButton("Back", BC_S);
  223. inputConfig->registerButton("Left", BC_A);
  224. inputConfig->registerButton("Right", BC_D);
  225. inputConfig->registerButton("Forward", BC_UP);
  226. inputConfig->registerButton("Back", BC_BACK);
  227. inputConfig->registerButton("Left", BC_LEFT);
  228. inputConfig->registerButton("Right", BC_RIGHT);
  229. inputConfig->registerButton("FastMove", BC_LSHIFT);
  230. inputConfig->registerButton("RotateCam", BC_MOUSE_RIGHT);
  231. // Camera controls for axes (analog input, e.g. mouse or gamepad thumbstick)
  232. // These return values in [-1.0, 1.0] range.
  233. inputConfig->registerAxis("Horizontal", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseX));
  234. inputConfig->registerAxis("Vertical", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseY));
  235. // Controls that toggle the profiler overlays
  236. inputConfig->registerButton("CPUProfilerOverlay", BC_F1);
  237. inputConfig->registerButton("GPUProfilerOverlay", BC_F2);
  238. // Cache the profiler overlay buttons so when a button is pressed we can quickly
  239. // use these to determine its the one we want
  240. toggleCPUProfilerBtn = VirtualButton("CPUProfilerOverlay");
  241. toggleGPUProfilerBtn = VirtualButton("GPUProfilerOverlay");
  242. // Hook up a callback that gets triggered whenever a virtual button is released
  243. VirtualInput::instance().onButtonUp.connect(&buttonUp);
  244. }
  245. void setUpGUI()
  246. {
  247. // Create a scene object that will contain GUI components
  248. HSceneObject guiSO = SceneObject::create("Example");
  249. // Get the primary render window we need for creating the camera.
  250. RenderWindowPtr window = gApplication().getPrimaryWindow();
  251. // First we want another camera that is responsible for rendering GUI
  252. HCamera guiCamera = guiSO->addComponent<CCamera>(window);
  253. // Set up GUI camera properties.
  254. // We don't care about aspect ratio for GUI camera.
  255. guiCamera->setAspectRatio(1.0f);
  256. // This camera should ignore any Renderable objects in the scene
  257. guiCamera->setLayers(0);
  258. // Don't clear this camera as that would clear anything the main camera has rendered.
  259. guiCamera->getViewport()->setRequiresClear(false, false, false);
  260. // Add a GUIWidget, the top-level GUI component, parent to all GUI elements. GUI widgets
  261. // require you to specify a viewport that they will output rendered GUI elements to.
  262. HGUIWidget gui = guiSO->addComponent<CGUIWidget>(guiCamera);
  263. // Depth allows you to control how is a GUI widget rendered in relation to other widgets
  264. // Lower depth means the widget will be rendered in front of those with higher. In this case we just
  265. // make the depth mid-range as there are no other widgets.
  266. gui->setDepth(128);
  267. // GUI skin defines how are all child elements of the GUI widget renderered. It contains all their styles
  268. // and default layout properties. We use the default skin that comes built into Banshee.
  269. gui->setSkin(BuiltinResources::instance().getGUISkin());
  270. // Get the primary GUI panel that stretches over the entire window and add to it a vertical layout
  271. // that will be using for vertically positioning messages about toggling profiler overlay.
  272. GUILayout* bottomLayout = gui->getPanel()->addNewElement<GUILayoutY>();
  273. // Add a flexible space that fills up any remaining area in the layout, making the two labels above be aligned
  274. // to the bottom of the GUI widget (and the screen).
  275. bottomLayout->addNewElement<GUIFlexibleSpace>();
  276. // Add a couple of labels to the layout with the needed messages. Labels expect a HString object that
  277. // maps into a string table and allows for easily localization.
  278. bottomLayout->addElement(GUILabel::create(HString(L"Press F1 to toggle CPU profiler overlay")));
  279. bottomLayout->addElement(GUILabel::create(HString(L"Press F2 to toggle GPU profiler overlay")));
  280. // Create a GUI panel that is used for displaying resolution and fullscreen options.
  281. GUILayout* rightLayout = gui->getPanel()->addNewElement<GUILayoutX>();
  282. rightLayout->setPosition(30, 30);
  283. // We want all the GUI elements be right aligned, so we add a flexible space first.
  284. rightLayout->addNewElement<GUIFlexibleSpace>();
  285. // And we want the elements to be vertically placed, top to bottom
  286. GUILayout* elemLayout = rightLayout->addNewElement<GUILayoutY>();
  287. // Add a button that will trigger a callback when clicked
  288. toggleFullscreenButton = GUIButton::create(HString(L"Toggle fullscreen"));
  289. toggleFullscreenButton->onClick.connect(&toggleFullscreen);
  290. elemLayout->addElement(toggleFullscreenButton);
  291. // Leave 30 pixels to the right free
  292. rightLayout->addNewElement<GUIFixedSpace>(30);
  293. // Add a profiler overlay object that is responsible for displaying CPU and GPU profiling GUI
  294. profilerOverlay = guiSO->addComponent<ProfilerOverlay>(guiCamera.getInternalPtr());
  295. // Set up video mode list box
  296. // First get a list of output devices
  297. const VideoModeInfo& videoModeInfo = RenderAPI::getVideoModeInfo();
  298. // Get video mode info for the primary monitor
  299. const VideoOutputInfo& primaryMonitorInfo = videoModeInfo.getOutputInfo(0);
  300. // Make the current desktop mode the default video mode
  301. selectedVideoMode = &primaryMonitorInfo.getDesktopVideoMode();
  302. // Create list box elements for each available video mode
  303. UINT32 numVideoModes = primaryMonitorInfo.getNumVideoModes();
  304. Vector<HString> videoModeLabels(numVideoModes);
  305. UINT32 selectedVideoModeIdx = 0;
  306. for (UINT32 i = 0; i < numVideoModes; i++)
  307. {
  308. const VideoMode& curVideoMode = primaryMonitorInfo.getVideoMode(i);
  309. HString videoModeLabel(L"{0} x {1} at {2}Hz");
  310. videoModeLabel.setParameter(0, toWString(curVideoMode.getWidth()));
  311. videoModeLabel.setParameter(1, toWString(curVideoMode.getHeight()));
  312. videoModeLabel.setParameter(2, toWString(Math::roundToInt(curVideoMode.getRefreshRate())));
  313. videoModeLabels[i] = videoModeLabel;
  314. videoModes.push_back(&curVideoMode);
  315. if (curVideoMode == *selectedVideoMode)
  316. selectedVideoModeIdx = i;
  317. }
  318. // Create the list box
  319. GUIListBox* videoModeListBox = GUIListBox::create(videoModeLabels);
  320. rightLayout->addElement(videoModeListBox);
  321. // Select the default (desktop) video mode
  322. videoModeListBox->selectElement(selectedVideoModeIdx);
  323. // Set up a callback to be notified when video mode changes
  324. videoModeListBox->onSelectionToggled.connect(&videoModeChanged);
  325. }
  326. void toggleFullscreen()
  327. {
  328. RenderWindowPtr window = gApplication().getPrimaryWindow();
  329. // In order to toggle between full-screen and windowed mode we need to use a CoreAccessor.
  330. // Banshee is a multi-threaded engine and when you need to communicate between simulation and
  331. // core thread you will use a CoreAccessor. Calling a core accessor method will essentially
  332. // queue the method to be executed later. Since RenderWindow is a core object you need to use
  333. // CoreAccessor to modify and access it from simulation thread, except where noted otherwise.
  334. // Classes where it is not clear if they are to be used on the core or simulation thread have
  335. // it noted in their documentation. e.g. RenderWindow::setWindowed method is marked as "Core only".
  336. // Additional asserts are normally in place for debug builds which make it harder for you to accidentally
  337. // call something from the wrong thread.
  338. if (fullscreen)
  339. {
  340. window->setWindowed(gCoreAccessor(), windowResWidth, windowResHeight);
  341. }
  342. else
  343. {
  344. window->setFullscreen(gCoreAccessor(), *selectedVideoMode);
  345. }
  346. fullscreen = !fullscreen;
  347. }
  348. void renderWindowResized()
  349. {
  350. RenderWindowPtr window = gApplication().getPrimaryWindow();
  351. const RenderWindowProperties& rwProps = window->getProperties();
  352. if (!fullscreen)
  353. {
  354. windowResWidth = rwProps.getWidth();
  355. windowResHeight = rwProps.getHeight();
  356. }
  357. sceneCamera->setAspectRatio(rwProps.getWidth() / (float)rwProps.getHeight());
  358. }
  359. void videoModeChanged(UINT32 idx, bool enabled)
  360. {
  361. selectedVideoMode = videoModes[idx];
  362. if (fullscreen)
  363. {
  364. RenderWindowPtr window = gApplication().getPrimaryWindow();
  365. window->setFullscreen(gCoreAccessor(), *selectedVideoMode);
  366. }
  367. }
  368. void buttonUp(const VirtualButton& button, UINT32 deviceIdx)
  369. {
  370. // Check if the pressed button is one of the either buttons we defined
  371. // in "setUpExample", and toggle profiler overlays accordingly.
  372. // Device index is ignored for now, as it is assumed the user is using a single keyboard,
  373. // but if you wanted support for multiple gamepads you would check deviceIdx.
  374. if (button == toggleCPUProfilerBtn)
  375. {
  376. if (cpuProfilerActive)
  377. {
  378. profilerOverlay->hide();
  379. cpuProfilerActive = false;
  380. }
  381. else
  382. {
  383. profilerOverlay->show(ProfilerOverlayType::CPUSamples);
  384. cpuProfilerActive = true;
  385. gpuProfilerActive = false;
  386. }
  387. }
  388. else if (button == toggleGPUProfilerBtn)
  389. {
  390. if (gpuProfilerActive)
  391. {
  392. profilerOverlay->hide();
  393. gpuProfilerActive = false;
  394. }
  395. else
  396. {
  397. profilerOverlay->show(ProfilerOverlayType::GPUSamples);
  398. gpuProfilerActive = true;
  399. cpuProfilerActive = false;
  400. }
  401. }
  402. }
  403. }