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