Просмотр исходного кода

Added a new low-level rendering example project

BearishSun 9 лет назад
Родитель
Сommit
a4a8a242bb

+ 1 - 1
Data/Raw/Engine/Includes/ResolveCommon.bslinc

@@ -109,7 +109,7 @@ Technique =
 		Fragment = 
 		{
 			layout(location = 0) in vec2 texcoord0;
-			layout(location = 1) out vec4 fragColor;
+			layout(location = 0) out vec4 fragColor;
 		
 			#ifdef ENABLE_MSAA
 		

+ 0 - 3
Source/BansheeCore/Include/BsCoreApplication.h

@@ -26,9 +26,6 @@ namespace bs
 		RENDER_WINDOW_DESC primaryWindowDesc; /**< Describes the window to create during start-up. */
 
 		Vector<String> importers; /**< A list of importer plugins to load. */
-
-		/** Optional callback function to be called every frame while the application is running. */
-		std::function<void()> updateCallback; 
 	};
 
 	/**

+ 0 - 3
Source/BansheeCore/Source/BsCoreApplication.cpp

@@ -224,9 +224,6 @@ namespace bs
 
 			preUpdate();
 
-			if (mStartUpDesc.updateCallback != nullptr)
-				mStartUpDesc.updateCallback();
-
 			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 			gAudio()._update();
 			gPhysics().update();

+ 20 - 6
Source/BansheeEngine/Include/BsApplication.h

@@ -15,16 +15,30 @@ namespace bs
 	/**	Primary entry point for Banshee engine. Handles startup and shutdown. */
 	class BS_EXPORT Application : public CoreApplication
 	{
+	private:
+		/** 
+		 * Builds the start-up descriptor structure, filling out the provided parameters and using the default values
+		 * for the rest. 
+		 */
+		static START_UP_DESC buildStartUpDesc(VideoMode videoMode, const String& title, bool fullscreen);
 	public:
 		Application(const START_UP_DESC& desc);
 		virtual ~Application();
 
-		/** Starts the Banshee engine. */
-		static void startUp(VideoMode videoMode, const String& title, bool fullscreen,
-							std::function<void()> updateCallback = nullptr);
-
-		/** Starts the Banshee engine. */
-		static void startUp(const START_UP_DESC& desc);
+		/** Starts the Banshee engine. If using a custom Application system, provide it as a template parameter. */
+		template<class T = Application>
+		static void startUp(VideoMode videoMode, const String& title, bool fullscreen)
+		{
+			START_UP_DESC desc = buildStartUpDesc(videoMode, title, fullscreen);
+			CoreApplication::startUp<T>(desc);
+		}
+
+		/** Starts the Banshee engine. If using a custom Application system, provide it as a template parameter. */
+		template<class T = Application>
+		static void startUp(const START_UP_DESC& desc)
+		{
+			CoreApplication::startUp<T>(desc);
+		}
 
 		/**	Returns the absolute path to the builtin managed engine assembly file. */
 		Path getEngineAssemblyPath() const;

+ 24 - 32
Source/BansheeEngine/Source/BsApplication.cpp

@@ -88,38 +88,6 @@ namespace bs
 		CoreApplication::onShutDown();
 	}
 
-	void Application::startUp(VideoMode videoMode, const String& title, bool fullscreen,
-						std::function<void()> updateCallback)
-	{
-		START_UP_DESC desc;
-
-		// Set up default plugins
-		desc.renderAPI = BS_RENDER_API_MODULE;
-		desc.renderer = BS_RENDERER_MODULE;
-		desc.audio = BS_AUDIO_MODULE;
-		desc.physics = BS_PHYSICS_MODULE;
-		desc.input = BS_INPUT_MODULE;
-		desc.scripting = false;
-
-		desc.importers.push_back("BansheeFreeImgImporter");
-		desc.importers.push_back("BansheeFBXImporter");
-		desc.importers.push_back("BansheeFontImporter");
-		desc.importers.push_back("BansheeSL");
-
-		desc.primaryWindowDesc.videoMode = videoMode;
-		desc.primaryWindowDesc.fullscreen = fullscreen;
-		desc.primaryWindowDesc.title = title;
-
-		desc.updateCallback = updateCallback;
-
-		startUp(desc);
-	}
-
-	void Application::startUp(const START_UP_DESC& desc)
-	{
-		CoreApplication::startUp<Application>(desc);
-	}
-
 	void Application::preUpdate()
 	{
 		CoreApplication::preUpdate();
@@ -159,6 +127,30 @@ namespace bs
 		// Do nothing, we activate the renderer at a later stage
 	}
 
+	START_UP_DESC Application::buildStartUpDesc(VideoMode videoMode, const String& title, bool fullscreen)
+	{
+		START_UP_DESC desc;
+
+		// Set up default plugins
+		desc.renderAPI = BS_RENDER_API_MODULE;
+		desc.renderer = BS_RENDERER_MODULE;
+		desc.audio = BS_AUDIO_MODULE;
+		desc.physics = BS_PHYSICS_MODULE;
+		desc.input = BS_INPUT_MODULE;
+		desc.scripting = false;
+
+		desc.importers.push_back("BansheeFreeImgImporter");
+		desc.importers.push_back("BansheeFBXImporter");
+		desc.importers.push_back("BansheeFontImporter");
+		desc.importers.push_back("BansheeSL");
+
+		desc.primaryWindowDesc.videoMode = videoMode;
+		desc.primaryWindowDesc.fullscreen = fullscreen;
+		desc.primaryWindowDesc.title = title;
+
+		return desc;
+	}
+
 	Path Application::getEngineAssemblyPath() const
 	{
 		Path assemblyPath = getBuiltinAssemblyFolder();

+ 1 - 1
Source/BansheeUtility/Source/BsTime.cpp

@@ -27,7 +27,7 @@ namespace bs
 
 		mFrameDelta = (float)((currentFrameTime - mLastFrameTime) * MICROSEC_TO_SEC);
 		mTimeSinceStartMs = (UINT64)(currentFrameTime / 1000);
-		mTimeSinceStart = (mTimeSinceStartMs - mAppStartTime) / 1000.0f;
+		mTimeSinceStart = mTimeSinceStartMs / 1000.0f;
 		
 		mLastFrameTime = currentFrameTime;
 

+ 33 - 17
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -657,34 +657,50 @@ namespace bs { namespace ct
 
 	void VulkanCmdBuffer::reset()
 	{
+		bool wasSubmitted = mState == State::Submitted;
+
 		mState = State::Ready;
 		vkResetCommandBuffer(mCmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); // Note: Maybe better not to release resources?
 
-		for (auto& entry : mResources)
+		if (wasSubmitted)
 		{
-			ResourceUseHandle& useHandle = entry.second;
-			assert(useHandle.used);
+			for (auto& entry : mResources)
+			{
+				ResourceUseHandle& useHandle = entry.second;
+				assert(useHandle.used);
 
-			entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
-		}
+				entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
+			}
 
-		for (auto& entry : mImages)
-		{
-			UINT32 imageInfoIdx = entry.second;
-			ImageInfo& imageInfo = mImageInfos[imageInfoIdx];
+			for (auto& entry : mImages)
+			{
+				UINT32 imageInfoIdx = entry.second;
+				ImageInfo& imageInfo = mImageInfos[imageInfoIdx];
 
-			ResourceUseHandle& useHandle = imageInfo.useHandle;
-			assert(useHandle.used);
+				ResourceUseHandle& useHandle = imageInfo.useHandle;
+				assert(useHandle.used);
 
-			entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
-		}
+				entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
+			}
 
-		for (auto& entry : mBuffers)
+			for (auto& entry : mBuffers)
+			{
+				ResourceUseHandle& useHandle = entry.second.useHandle;
+				assert(useHandle.used);
+
+				entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
+			}
+		}
+		else
 		{
-			ResourceUseHandle& useHandle = entry.second.useHandle;
-			assert(useHandle.used);
+			for (auto& entry : mResources)
+				entry.first->notifyUnbound();
 
-			entry.first->notifyDone(mGlobalQueueIdx, useHandle.flags);
+			for (auto& entry : mImages)
+				entry.first->notifyUnbound();
+
+			for (auto& entry : mBuffers)
+				entry.first->notifyUnbound();
 		}
 
 		mResources.clear();

+ 1 - 0
Source/CMakeLists.txt

@@ -247,6 +247,7 @@ endif()
 
 ## Executables
 add_subdirectory(ExampleProject)
+add_subdirectory(ExampleLowLevelRendering)
 
 if(BUILD_EDITOR OR (INCLUDE_ALL_IN_WORKFLOW AND MSVC))
 	add_subdirectory(BansheeEditorExec)

+ 29 - 0
Source/ExampleLowLevelRendering/CMakeLists.txt

@@ -0,0 +1,29 @@
+# Source files and their filters
+include(CMakeSources.cmake)
+
+# Includes
+set(ExampleLLR_INC 
+	"Include"
+	"../BansheeUtility/Include" 
+	"../BansheeCore/Include"
+	"../BansheeEngine/Include")
+
+include_directories(${ExampleLLR_INC})	
+	
+# Target
+if(WIN32)
+	add_executable(ExampleLowLevelRendering WIN32 ${BS_EXAMPLELLR_SRC})
+else()
+	add_executable(ExampleLowLevelRendering ${BS_EXAMPLELLR_SRC})
+endif()
+	
+# Libraries
+## Local libs
+target_link_libraries(ExampleLowLevelRendering BansheeEngine BansheeUtility BansheeCore)
+
+# IDE specific
+set_property(TARGET ExampleLowLevelRendering PROPERTY FOLDER Examples)
+
+# Dependencies
+add_engine_dependencies(ExampleLowLevelRendering)
+add_dependencies(ExampleLowLevelRendering BansheeFBXImporter BansheeFontImporter BansheeFreeImgImporter)

+ 14 - 0
Source/ExampleLowLevelRendering/CMakeSources.cmake

@@ -0,0 +1,14 @@
+set(BS_EXAMPLELLR_INC_NOFILTER
+)
+
+set(BS_EXAMPLELLR_SRC_NOFILTER
+	"Source/Main.cpp"
+)
+
+source_group("Header Files" FILES ${BS_EXAMPLELLR_INC_NOFILTER})
+source_group("Source Files" FILES ${BS_EXAMPLELLR_SRC_NOFILTER})
+
+set(BS_EXAMPLELLR_SRC
+	${BS_EXAMPLELLR_INC_NOFILTER}
+	${BS_EXAMPLELLR_SRC_NOFILTER}
+)

+ 553 - 0
Source/ExampleLowLevelRendering/Source/Main.cpp

@@ -0,0 +1,553 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include <windows.h>
+
+#include "BsApplication.h"
+#include "BsMaterial.h"
+#include "BsRenderAPI.h"
+#include "BsRenderWindow.h"
+#include "BsCoreThread.h"
+#include "BsCommandBuffer.h"
+#include "BsGpuProgram.h"
+#include "BsGpuPipelineState.h"
+#include "BsBlendState.h"
+#include "BsDepthStencilState.h"
+#include "BsGpuParamBlockBuffer.h"
+#include "BsVertexDataDesc.h"
+#include "BsMeshData.h"
+#include "BsIndexBuffer.h"
+#include "BsQuaternion.h"
+#include "BsTime.h"
+#include "BsRendererUtility.h"
+#include <BsEngineConfig.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example uses the low-level rendering API to render a textured cube mesh. This is opposed to using scene objects
+// and components, in which case objects are rendered automatically based on their transform and other properties.
+// 
+// Using low-level rendering API gives you full control over rendering, similar to using Vulkan, DirectX or OpenGL APIs.
+//
+// In order to use the low-level rendering system we need to override the Application class so we get notified of updates
+// and start-up/shut-down events. This is normally not necessary for a high level scene object based model.
+//
+// The rendering is performed on the core (i.e. rendering) thread, as opposed to the main thread, where majority of
+// Banshee's code executes.
+//
+// The example first sets up necessary resources, like GPU programs, pipeline state, vertex & index buffers. Then every
+// frame it binds the necessary rendering resources and executes the draw call.
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	// Declare the methods we'll use to do work on the core thread. Note the "ct" namespace, which we use because we render
+	// on the core thread (ct = core thread). Every object usable on the core thread lives in this namespace.
+	namespace ct
+	{
+		void setup(const SPtr<RenderWindow>& renderWindow);
+		void render();
+		void shutdown();
+	}
+
+	// Override the default Application so we can get notified when engine starts-up, shuts-down and when it executes
+	// every frame
+	class MyApplication : public Application
+	{
+	public:
+		// Pass along the start-up structure to the parent, we don't need to handle it
+		MyApplication(const START_UP_DESC& desc)
+			:Application(desc)
+		{ }
+
+	private:
+		// Called when the engine is first started up
+		void onStartUp() override
+		{
+			// Ensure all parent systems are initialized first
+			Application::onStartUp();
+
+			// Get the primary window that was created during start-up. This will be the final desination for all our
+			// rendering.
+			SPtr<RenderWindow> renderWindow = getPrimaryWindow();
+
+			// Get the version of the render window usable on the core thread, and send it along to setup()
+			SPtr<ct::RenderWindow> renderWindowCore = renderWindow->getCore();
+
+			// Initialize all the resources we need for rendering. Since we do rendering on a separate thread (the "core
+			// thread"), we don't call the method directly, but rather queue it for execution using the CoreThread class.
+			gCoreThread().queueCommand(std::bind(&ct::setup, renderWindowCore));
+		}
+
+		// Called when the engine is about to be shut down
+		void onShutDown() override
+		{
+			gCoreThread().queueCommand(&ct::shutdown);
+
+			// Shut-down engine components
+			Application::onShutDown();
+		}
+
+		// Called every frame, before any other engine system (optionally use postUpdate())
+		void preUpdate() override
+		{
+			gCoreThread().queueCommand(&ct::render);
+		}
+	};
+}
+
+using namespace bs;
+
+/** Main entry point into the application. */
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+)
+{
+	// Define a video mode for the resolution of the primary rendering window.
+	VideoMode videoMode(windowResWidth, windowResHeight);
+
+	// Start-up the engine using our custom MyApplication class. This will also create the primary rendering window.
+	// We provide the initial resolution of the window, its title and fullscreen state.
+	Application::startUp<MyApplication>(videoMode, "Banshee Example App", false);
+
+	// 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 { namespace ct
+{
+	// Declarations for some helper methods we'll use during setup
+	void writeBoxVertices(const AABox& box, UINT8* positions, UINT8* uvs, UINT32 stride);
+	void writeBoxIndices(UINT32* indices);
+	const char* getVertexProgSource();
+	const char* getFragmentProgSource();
+	Matrix4 createWorldViewProjectionMatrix();
+
+	// Fields where we'll store the resources required during calls to render(). These are initialized in setup()
+	// and cleaned up in shutDown()
+	SPtr<GraphicsPipelineState> gPipelineState;
+	SPtr<Texture> gSurfaceTex;
+	SPtr<SamplerState> gSurfaceSampler;
+	SPtr<GpuParams> gGpuParams;
+	SPtr<VertexDeclaration> gVertexDecl;
+	SPtr<VertexBuffer> gVertexBuffer;
+	SPtr<IndexBuffer> gIndexBuffer;
+	SPtr<RenderTexture> gRenderTarget;
+	SPtr<RenderWindow> gRenderWindow;
+
+	const bool USE_HLSL = BS_RENDER_API_MODULE == "BansheeD3D11RenderAPI";
+	const UINT32 NUM_VERTICES = 24;
+	const UINT32 NUM_INDICES = 36;
+
+	// Structure that will hold uniform block variables for the GPU programs
+	struct UniformBlock
+	{
+		Matrix4 gMatWVP; // World view projection matrix
+		Color gTint; // Tint to apply on top of the texture
+	};
+
+	// Initialized any resources required for rendering
+	void setup(const SPtr<RenderWindow>& renderWindow)
+	{
+		// This will be the primary output for our rendering (created by the main thread on start-up)
+		gRenderWindow = renderWindow;
+
+		// Create a GLSL vertex GPU program
+		const char* vertProgSrc = getVertexProgSource();
+
+		GPU_PROGRAM_DESC vertProgDesc;
+		vertProgDesc.type = GPT_VERTEX_PROGRAM;
+		vertProgDesc.entryPoint = "main";
+		vertProgDesc.language = USE_HLSL ? "hlsl" : "glsl";
+		vertProgDesc.source = vertProgSrc;
+
+		SPtr<GpuProgram> vertProg = GpuProgram::create(vertProgDesc);
+
+		// Create a GLSL fragment GPU program
+		const char* fragProgSrc = getFragmentProgSource();
+
+		GPU_PROGRAM_DESC fragProgDesc;
+		fragProgDesc.type = GPT_FRAGMENT_PROGRAM;
+		fragProgDesc.entryPoint = "main";
+		fragProgDesc.language = USE_HLSL ? "hlsl" : "glsl";
+		fragProgDesc.source = fragProgSrc;
+
+		SPtr<GpuProgram> fragProg = GpuProgram::create(fragProgDesc);
+
+		// Create a graphics pipeline state
+		BLEND_STATE_DESC blendDesc;
+		blendDesc.renderTargetDesc[0].blendEnable = true;
+		blendDesc.renderTargetDesc[0].renderTargetWriteMask = 0b0111; // RGB, don't write to alpha
+		blendDesc.renderTargetDesc[0].blendOp = BO_ADD;
+		blendDesc.renderTargetDesc[0].srcBlend = BF_SOURCE_ALPHA;
+		blendDesc.renderTargetDesc[0].dstBlend = BF_INV_SOURCE_ALPHA;
+
+		DEPTH_STENCIL_STATE_DESC depthStencilDesc;
+		depthStencilDesc.depthWriteEnable = false;
+		depthStencilDesc.depthReadEnable = false;
+
+		PIPELINE_STATE_DESC pipelineDesc;
+		pipelineDesc.blendState = BlendState::create(blendDesc);
+		pipelineDesc.depthStencilState = DepthStencilState::create(depthStencilDesc);
+		pipelineDesc.vertexProgram = vertProg;
+		pipelineDesc.fragmentProgram = fragProg;
+
+		gPipelineState = GraphicsPipelineState::create(pipelineDesc);
+
+		// Create an object containing GPU program parameters
+		gGpuParams = GpuParams::create(gPipelineState);
+
+		// Create a vertex declaration for shader inputs
+		SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
+		vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
+
+		gVertexDecl = VertexDeclaration::create(vertexDesc);
+
+		// Create & fill the vertex buffer for a box mesh
+		UINT32 vertexStride = vertexDesc->getVertexStride();
+
+		VERTEX_BUFFER_DESC vbDesc;
+		vbDesc.numVerts = NUM_VERTICES;
+		vbDesc.vertexSize = vertexStride;
+
+		gVertexBuffer = VertexBuffer::create(vbDesc);
+
+		UINT8* vbData = (UINT8*)gVertexBuffer->lock(0, vertexStride * NUM_VERTICES, GBL_WRITE_ONLY_DISCARD);
+		UINT8* positions = vbData + vertexDesc->getElementOffsetFromStream(VES_POSITION);
+		UINT8* uvs = vbData + vertexDesc->getElementOffsetFromStream(VES_TEXCOORD);
+
+		AABox box(Vector3::ONE * -10.0f, Vector3::ONE * 10.0f);
+		writeBoxVertices(box, positions, uvs, vertexStride);
+
+		gVertexBuffer->unlock();
+
+		// Create & fill the index buffer for a box mesh
+		INDEX_BUFFER_DESC ibDesc;
+		ibDesc.numIndices = NUM_INDICES;
+		ibDesc.indexType = IT_32BIT;
+
+		gIndexBuffer = IndexBuffer::create(ibDesc);
+		UINT32* ibData = (UINT32*)gIndexBuffer->lock(0, NUM_INDICES * sizeof(UINT32), GBL_WRITE_ONLY_DISCARD);
+		writeBoxIndices(ibData);
+
+		gIndexBuffer->unlock();
+
+		// Create a simple 2x2 checkerboard texture to map to the object we're about to render
+		SPtr<PixelData> pixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		pixelData->setColorAt(Color::White, 0, 0);
+		pixelData->setColorAt(Color::Black, 1, 0);
+		pixelData->setColorAt(Color::White, 1, 1);
+		pixelData->setColorAt(Color::Black, 0, 1);
+
+		gSurfaceTex = Texture::create(pixelData);
+
+		// Create a sampler state for the texture above
+		SAMPLER_STATE_DESC samplerDesc;
+		samplerDesc.minFilter = FO_POINT;
+		samplerDesc.magFilter = FO_POINT;
+
+		gSurfaceSampler = SamplerState::create(samplerDesc);
+
+		// Create a color attachment texture for the render surface
+		TEXTURE_DESC colorAttDesc;
+		colorAttDesc.width = windowResWidth;
+		colorAttDesc.height = windowResHeight;
+		colorAttDesc.format = PF_R8G8B8A8;
+		colorAttDesc.usage = TU_RENDERTARGET;
+
+		SPtr<Texture> colorAtt = Texture::create(colorAttDesc);
+
+		// Create a depth attachment texture for the render surface
+		TEXTURE_DESC depthAttDesc;
+		depthAttDesc.width = windowResWidth;
+		depthAttDesc.height = windowResHeight;
+		depthAttDesc.format = PF_D32;
+		depthAttDesc.usage = TU_DEPTHSTENCIL;
+
+		SPtr<Texture> depthAtt = Texture::create(depthAttDesc);
+
+		// Create the render surface
+		RENDER_TEXTURE_DESC desc;
+		desc.colorSurfaces[0].texture = colorAtt;
+		desc.depthStencilSurface.texture = depthAtt;
+
+		gRenderTarget = RenderTexture::create(desc);
+	}
+
+	// Render the box, called every frame
+	void render()
+	{
+		// Fill out the uniform block variables
+		UniformBlock uniformBlock;
+		uniformBlock.gMatWVP = createWorldViewProjectionMatrix();
+		uniformBlock.gTint = Color(1.0f, 1.0f, 1.0f, 0.5f);
+
+		// Create a uniform block buffer for holding the uniform variables
+		SPtr<GpuParamBlockBuffer> uniformBuffer = GpuParamBlockBuffer::create(sizeof(UniformBlock));
+		uniformBuffer->write(0, &uniformBlock, sizeof(uniformBlock));
+
+		// Assign the uniform buffer & texture
+		gGpuParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Params", uniformBuffer);
+		gGpuParams->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "Params", uniformBuffer);
+
+		gGpuParams->setTexture(GPT_FRAGMENT_PROGRAM, "gMainTexture", gSurfaceTex);
+
+		// HLSL uses separate sampler states, so we need to use a different name for the sampler
+		if(USE_HLSL)
+			gGpuParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gMainTexSamp", gSurfaceSampler);
+		else
+			gGpuParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gMainTexture", gSurfaceSampler);
+
+		// Create a command buffer
+		SPtr<CommandBuffer> cmds = CommandBuffer::create(GQT_GRAPHICS);
+
+		// Get the primary render API access point
+		RenderAPI& rapi = RenderAPI::instance();
+
+		// Bind render surface & clear it
+		rapi.setRenderTarget(gRenderTarget, false, RT_NONE, cmds);
+		rapi.clearRenderTarget(FBT_COLOR | FBT_DEPTH, Color::Blue, 1, 0, 0xFF, cmds);
+
+		// Bind the pipeline state
+		rapi.setGraphicsPipeline(gPipelineState, cmds);
+
+		// Bind the GPU program parameters (i.e. resource descriptors)
+		rapi.setGpuParams(gGpuParams, cmds);
+
+		// Set the vertex & index buffers, as well as vertex declaration and draw type
+		rapi.setVertexBuffers(0, &gVertexBuffer, 1, cmds);
+		rapi.setIndexBuffer(gIndexBuffer, cmds);
+		rapi.setVertexDeclaration(gVertexDecl, cmds);
+		rapi.setDrawOperation(DOT_TRIANGLE_LIST, cmds);
+
+		// Draw
+		rapi.drawIndexed(0, NUM_INDICES, 0, NUM_VERTICES, 1, cmds);
+
+		// Blit the image from the render texture, to the render window
+		rapi.setRenderTarget(gRenderWindow);
+
+		// Get the color attachment
+		SPtr<Texture> colorTexture = gRenderTarget->getColorTexture(0);
+
+		// Use the helper RendererUtility to draw a full-screen quad of the provided texture and output it to the currently
+		// bound render target. Internally this uses the same calls we used above, just with a different pipeline and mesh.
+		gRendererUtility().blit(colorTexture);
+
+		// Submit the command buffer
+		rapi.submitCommandBuffer(cmds);
+
+		// Present the rendered image to the user
+		rapi.swapBuffers(gRenderWindow);
+	}
+
+	// Clean up any resources
+	void shutdown()
+	{
+		gPipelineState = nullptr;
+		gSurfaceTex = nullptr;
+		gGpuParams = nullptr;
+		gVertexDecl = nullptr;
+		gVertexBuffer = nullptr;
+		gIndexBuffer = nullptr;
+		gRenderTarget = nullptr;
+		gRenderWindow = nullptr;
+		gSurfaceSampler = nullptr;
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////////
+	//////////////////////////////////HELPER METHODS/////////////////////////////////////
+	/////////////////////////////////////////////////////////////////////////////////////
+	void writeBoxVertices(const AABox& box, UINT8* positions, UINT8* uvs, UINT32 stride)
+	{
+		AABox::CornerEnum vertOrder[] =
+		{
+			AABox::NEAR_LEFT_BOTTOM,	AABox::NEAR_RIGHT_BOTTOM,	AABox::NEAR_RIGHT_TOP,		AABox::NEAR_LEFT_TOP,
+			AABox::FAR_RIGHT_BOTTOM,	AABox::FAR_LEFT_BOTTOM,		AABox::FAR_LEFT_TOP,		AABox::FAR_RIGHT_TOP,
+			AABox::FAR_LEFT_BOTTOM,		AABox::NEAR_LEFT_BOTTOM,	AABox::NEAR_LEFT_TOP,		AABox::FAR_LEFT_TOP,
+			AABox::NEAR_RIGHT_BOTTOM,	AABox::FAR_RIGHT_BOTTOM,	AABox::FAR_RIGHT_TOP,		AABox::NEAR_RIGHT_TOP,
+			AABox::FAR_LEFT_TOP,		AABox::NEAR_LEFT_TOP,		AABox::NEAR_RIGHT_TOP,		AABox::FAR_RIGHT_TOP,
+			AABox::FAR_LEFT_BOTTOM,		AABox::FAR_RIGHT_BOTTOM,	AABox::NEAR_RIGHT_BOTTOM,	AABox::NEAR_LEFT_BOTTOM
+		};
+
+		for (auto& entry : vertOrder)
+		{
+			Vector3 pos = box.getCorner(entry);
+			memcpy(positions, &pos, sizeof(pos));
+
+			positions += stride;
+		}
+
+		for (UINT32 i = 0; i < 6; i++)
+		{
+			Vector2 uv;
+
+			uv = Vector2(0.0f, 1.0f);
+			memcpy(uvs, &uv, sizeof(uv));
+			uvs += stride;
+
+			uv = Vector2(1.0f, 1.0f);
+			memcpy(uvs, &uv, sizeof(uv));
+			uvs += stride;
+
+			uv = Vector2(1.0f, 0.0f);
+			memcpy(uvs, &uv, sizeof(uv));
+			uvs += stride;
+
+			uv = Vector2(0.0f, 0.0f);
+			memcpy(uvs, &uv, sizeof(uv));
+			uvs += stride;
+		}
+	}
+
+	void writeBoxIndices(UINT32* indices)
+	{
+		for (UINT32 face = 0; face < 6; face++)
+		{
+			UINT32 faceVertOffset = face * 4;
+
+			indices[face * 6 + 0] = faceVertOffset + 2;
+			indices[face * 6 + 1] = faceVertOffset + 1;
+			indices[face * 6 + 2] = faceVertOffset + 0;
+			indices[face * 6 + 3] = faceVertOffset + 0;
+			indices[face * 6 + 4] = faceVertOffset + 3;
+			indices[face * 6 + 5] = faceVertOffset + 2;
+		}
+	}
+
+	const char* getVertexProgSource()
+	{
+		if(BS_RENDER_API_MODULE == "BansheeD3D11RenderAPI")
+		{
+			static char* src = R"(
+cbuffer Params
+{
+	float4x4 gMatWVP;
+	float4 gTint;
+}	
+
+void main(
+	in float3 inPos : POSITION,
+	in float2 uv : TEXCOORD0,
+	out float4 oPosition : SV_Position,
+	out float2 oUv : TEXCOORD0)
+{
+	oPosition = mul(gMatWVP, float4(inPos.xyz, 1));
+	oUv = uv;
+}
+)";
+
+			return src;
+		}
+		else
+		{
+			static char* src = R"(
+layout (binding = 0, std140) uniform Params
+{
+	mat4 gMatWVP;
+	vec4 gTint;
+};		
+
+layout (location = 0) in vec3 bs_position;
+layout (location = 1) in vec2 bs_texcoord0;
+	
+layout (location = 0) out vec2 texcoord0;
+
+out gl_PerVertex
+{
+	vec4 gl_Position;
+};
+	
+void main()
+{
+	gl_Position = gMatWVP * vec4(bs_position.xyz, 1);
+	texcoord0 = bs_texcoord0;
+}
+)";
+
+			return src;
+		}
+	}
+
+	const char* getFragmentProgSource()
+	{
+		if (BS_RENDER_API_MODULE == "BansheeD3D11RenderAPI")
+		{
+			static char* src = R"(
+cbuffer Params
+{
+	float4x4 gMatWVP;
+	float4 gTint;
+}
+
+SamplerState gMainTexSamp : register(s0);
+Texture2D gMainTexture : register(t0);
+
+float4 main(in float4 inPos : SV_Position, float2 uv : TEXCOORD0) : SV_Target
+{
+	float4 color = gMainTexture.Sample(gMainTexSamp, uv);
+	return color * gTint;
+}
+)";
+
+			return src;
+		}
+		else
+		{
+			static char* src = R"(
+layout (binding = 0, std140) uniform Params
+{
+	mat4 gMatWVP;
+	vec4 gTint;
+};	
+
+layout (binding = 1) uniform sampler2D gMainTexture;
+	
+layout (location = 0) in vec2 texcoord0;
+layout (location = 0) out vec4 fragColor;
+
+void main()
+{
+	vec4 color = texture(gMainTexture, texcoord0.st);
+	fragColor = color * gTint;
+}
+)";
+
+			return src;
+		}
+	}
+
+	Matrix4 createWorldViewProjectionMatrix()
+	{
+		Matrix4 proj = Matrix4::projectionPerspective(Degree(75.0f), 16.0f / 9.0f, 0.05f, 1000.0f);
+		bs::RenderAPI::convertProjectionMatrix(proj, proj);
+
+		Vector3 cameraPos = Vector3(0.0f, -20.0f, 50.0f);
+		Vector3 lookDir = -Vector3::normalize(cameraPos);
+
+		Quaternion cameraRot(BsIdentity);
+		cameraRot.lookRotation(lookDir);
+
+		Matrix4 view = Matrix4::view(cameraPos, cameraRot);
+
+		Quaternion rotation(Vector3::UNIT_Y, Degree(gTime().getTime() * 90.0f));
+		Matrix4 world = Matrix4::TRS(Vector3::ZERO, rotation, Vector3::ONE);
+
+		Matrix4 viewProj = proj * view * world;
+
+		// GLSL uses column major matrices, so transpose
+		if(!USE_HLSL)
+			viewProj = viewProj.transpose();
+
+		return viewProj;
+	}
+}}

+ 1 - 1
Source/ExampleProject/CMakeLists.txt

@@ -22,7 +22,7 @@ endif()
 target_link_libraries(ExampleProject BansheeEngine BansheeUtility BansheeCore)
 
 # IDE specific
-set_property(TARGET ExampleProject PROPERTY FOLDER Executable)
+set_property(TARGET ExampleProject PROPERTY FOLDER Examples)
 
 # Plugin dependencies
 add_engine_dependencies(ExampleProject)