Browse Source

Added an example for testing the initial PBS implementation

BearishSun 8 years ago
parent
commit
720cf52617

BIN
Data/Examples/Pistol/Pistol01.fbx


BIN
Data/Examples/Pistol/Pistol01.fbx.asset


BIN
Data/Examples/Pistol/Pistol_DFS.png


BIN
Data/Examples/Pistol/Pistol_DFS.png.asset


BIN
Data/Examples/Pistol/Pistol_MTL.png


BIN
Data/Examples/Pistol/Pistol_MTL.png.asset


BIN
Data/Examples/Pistol/Pistol_NM.png


BIN
Data/Examples/Pistol/Pistol_NM.png.asset


BIN
Data/Examples/Pistol/Pistol_RGH.png


BIN
Data/Examples/Pistol/Pistol_RGH.png.asset


+ 2 - 1
Source/BansheeCore/Source/BsCamera.cpp

@@ -59,7 +59,8 @@ namespace bs
 	{
 	{
 		if (nearPlane <= 0)
 		if (nearPlane <= 0)
 		{
 		{
-			BS_EXCEPT(InvalidParametersException, "Near clip distance must be greater than zero.");
+			LOGERR("Near clip distance must be greater than zero.");
+            return;
 		}
 		}
 
 
 		mNearDist = nearPlane;
 		mNearDist = nearPlane;

+ 1 - 0
Source/CMakeLists.txt

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

+ 29 - 0
Source/ExamplePhysicallyBasedShading/CMakeLists.txt

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

+ 16 - 0
Source/ExamplePhysicallyBasedShading/CMakeSources.cmake

@@ -0,0 +1,16 @@
+set(BS_EXAMPLEPHYSICALLYBASEDSHADING_INC_NOFILTER
+	"Include/CameraFlyer.h"
+)
+
+set(BS_EXAMPLEPHYSICALLYBASEDSHADING_SRC_NOFILTER
+	"Source/CameraFlyer.cpp"
+	"Source/Main.cpp"
+)
+
+source_group("Header Files" FILES ${BS_EXAMPLEPHYSICALLYBASEDSHADING_INC_NOFILTER})
+source_group("Source Files" FILES ${BS_EXAMPLEPHYSICALLYBASEDSHADING_SRC_NOFILTER})
+
+set(BS_EXAMPLEPHYSICALLYBASEDSHADING_SRC
+	${BS_EXAMPLEPHYSICALLYBASEDSHADING_INC_NOFILTER}
+	${BS_EXAMPLEPHYSICALLYBASEDSHADING_SRC_NOFILTER}
+)

+ 46 - 0
Source/ExamplePhysicallyBasedShading/Include/CameraFlyer.h

@@ -0,0 +1,46 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsComponent.h"
+#include "BsMath.h"
+#include "BsVirtualInput.h"
+
+namespace bs
+{
+	/** 
+	 * Component that controls movement and rotation of a Camera component. The Camera component must be attached to the 
+	 * same SceneObject this component is on.
+	 */
+	class CameraFlyer : public Component
+	{
+	public:
+		CameraFlyer(const HSceneObject& parent);
+
+		/** Triggered once per frame. Allows the component to handle input and move. */
+		virtual void update();
+
+	private:
+		float mCurrentSpeed; /**< Current speed of the camera. */
+
+		Degree mPitch; /**< Current pitch rotation of the camera (looking up or down). */
+		Degree mYaw; /**< Current yar rotation of the camera (looking left or right). */
+		bool mLastButtonState; /**< Determines was the user rotating the camera last frame. */
+
+		VirtualButton mMoveForward; /**< Key binding for moving the camera forward. */
+		VirtualButton mMoveBack; /**< Key binding for moving the camera backward. */
+		VirtualButton mMoveLeft; /**< Key binding for moving the camera left. */
+		VirtualButton mMoveRight; /**< Key binding for moving the camera right. */
+		VirtualButton mFastMove; /**< Key that speeds up movement while held. */
+		VirtualButton mRotateCam; /**< Key that allows camera to be rotated while held. */
+		VirtualAxis mVerticalAxis; /**< Input device axis used for controlling camera's pitch rotation (up/down). */
+		VirtualAxis mHorizontalAxis; /**< Input device axis used for controlling camera's yaw rotation (left/right). */
+
+		static const float START_SPEED; /**< Initial movement speed. */
+		static const float TOP_SPEED; /**< Maximum movement speed. */
+		static const float ACCELERATION; /**< Acceleration that determines how quickly to go from starting to top speed. */
+		static const float FAST_MODE_MULTIPLIER; /**< Multiplier applied to the speed when the fast move button is held. */
+		static const float ROTATION_SPEED; /**< Determines speed for rotation, in degrees per second. */
+	};
+}

+ 126 - 0
Source/ExamplePhysicallyBasedShading/Source/CameraFlyer.cpp

@@ -0,0 +1,126 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "CameraFlyer.h"
+#include "BsVector3.h"
+#include "BsTime.h"
+#include "BsMath.h"
+#include "BsSceneObject.h"
+#include "BsCCamera.h"
+#include "BsCursor.h"
+
+namespace bs
+{
+	const float CameraFlyer::START_SPEED = 4.0f;
+	const float CameraFlyer::TOP_SPEED = 13.0f;
+	const float CameraFlyer::ACCELERATION = 1.0f;
+	const float CameraFlyer::FAST_MODE_MULTIPLIER = 2.0f;
+	const float CameraFlyer::ROTATION_SPEED = 360.0f; // Degrees/second
+
+	/** Wraps an angle so it always stays in [0, 360) range. */
+	Degree wrapAngle(Degree angle)
+	{
+		if (angle.valueDegrees() < -360.0f)
+			angle += Degree(360.0f);
+
+		if (angle.valueDegrees() > 360.0f)
+			angle -= Degree(360.0f);
+
+		return angle;
+	}
+
+	CameraFlyer::CameraFlyer(const HSceneObject& parent)
+		:Component(parent), mPitch(0.0f), mYaw(0.0f), mLastButtonState(false)
+	{
+		// Set a name for the component, so we can find it later if needed
+		setName("CameraFlyer");
+
+		// Get handles for key bindings. Actual keys attached to these bindings will be registered during app start-up.
+		mMoveForward = VirtualButton("Forward");
+		mMoveBack = VirtualButton("Back");
+		mMoveLeft = VirtualButton("Left");
+		mMoveRight = VirtualButton("Right");
+		mFastMove = VirtualButton("FastMove");
+		mRotateCam = VirtualButton("RotateCam");
+		mHorizontalAxis = VirtualAxis("Horizontal");
+		mVerticalAxis = VirtualAxis("Vertical");
+	}
+
+	void CameraFlyer::update()
+	{
+		// Check if any movement or rotation keys are being held
+		bool goingForward = gVirtualInput().isButtonHeld(mMoveForward);
+		bool goingBack = gVirtualInput().isButtonHeld(mMoveBack);
+		bool goingLeft = gVirtualInput().isButtonHeld(mMoveLeft);
+		bool goingRight = gVirtualInput().isButtonHeld(mMoveRight);
+		bool fastMove = gVirtualInput().isButtonHeld(mFastMove);
+		bool camRotating = gVirtualInput().isButtonHeld(mRotateCam);
+
+		// If switch to or from rotation mode, hide or show the cursor
+		if (camRotating != mLastButtonState)
+		{
+			if (camRotating)
+				Cursor::instance().hide();
+			else
+				Cursor::instance().show();
+
+			mLastButtonState = camRotating;
+		}
+
+		// If camera is rotating, apply new pitch/yaw rotation values depending on the amount of rotation from the
+		// vertical/horizontal axes.
+		float frameDelta = gTime().getFrameDelta();
+		if (camRotating)
+		{
+			mYaw += Degree(gVirtualInput().getAxisValue(mHorizontalAxis) * ROTATION_SPEED * frameDelta);
+			mPitch += Degree(gVirtualInput().getAxisValue(mVerticalAxis) * ROTATION_SPEED * frameDelta);
+
+			mYaw = wrapAngle(mYaw);
+			mPitch = wrapAngle(mPitch);
+
+			Quaternion yRot;
+			yRot.fromAxisAngle(Vector3::UNIT_Y, Radian(mYaw));
+
+			Quaternion xRot;
+			xRot.fromAxisAngle(Vector3::UNIT_X, Radian(mPitch));
+
+			Quaternion camRot = yRot * xRot;
+			camRot.normalize();
+
+			SO()->setRotation(camRot);
+		}
+
+		// If the movement button is pressed, determine direction to move in
+		Vector3 direction = Vector3::ZERO;
+		if (goingForward) direction += SO()->getForward();
+		if (goingBack) direction -= SO()->getForward();
+		if (goingRight) direction += SO()->getRight();
+		if (goingLeft) direction -= SO()->getRight();
+
+		// If a direction is chosen, normalize it to determine final direction.
+		if (direction.squaredLength() != 0)
+		{
+			direction.normalize();
+
+			// Apply fast move multiplier if the fast move button is held.
+			float multiplier = 1.0f;
+			if (fastMove)
+				multiplier = FAST_MODE_MULTIPLIER;
+
+			// Calculate current speed of the camera
+			mCurrentSpeed = Math::clamp(mCurrentSpeed + ACCELERATION * frameDelta, START_SPEED, TOP_SPEED);
+			mCurrentSpeed *= multiplier;
+		}
+		else
+		{
+			mCurrentSpeed = 0.0f;
+		}
+
+		// If the current speed isn't too small, move the camera in the wanted direction
+		float tooSmall = std::numeric_limits<float>::epsilon();
+		if (mCurrentSpeed > tooSmall)
+		{
+			Vector3 velocity = direction * mCurrentSpeed;
+			SO()->move(velocity * frameDelta);
+		}
+	}
+}

+ 366 - 0
Source/ExamplePhysicallyBasedShading/Source/Main.cpp

@@ -0,0 +1,366 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include <windows.h>
+
+// Engine includes
+#include "BsApplication.h"
+#include "BsImporter.h"
+#include "BsResources.h"
+#include "BsTextureImportOptions.h"
+#include "BsMeshImportOptions.h"
+#include "BsMaterial.h"
+#include "BsShader.h"
+#include "BsVirtualInput.h"
+#include "BsCCamera.h"
+#include "BsCRenderable.h"
+#include "BsCLight.h"
+#include "BsGUIButton.h"
+#include "BsRenderAPI.h"
+#include "BsBuiltinResources.h"
+#include "BsRTTIType.h"
+#include "BsHString.h"
+#include "BsRenderWindow.h"
+#include "BsSceneObject.h"
+#include "BsCoreThread.h"
+#include "BsProfilerOverlay.h"
+#include "BsEngineConfig.h"
+
+// Example includes
+#include "CameraFlyer.h"
+
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Imports all of our assets and prepares GameObject that handle the example logic. */
+	void setUpExample();
+
+	/** Import mesh & textures used by the example. */
+	void loadAssets();
+
+	/** Imports a mesh at the provided path and optionally scales it. */
+	HMesh loadMesh(const Path& path, float scale = 1.0f);
+	
+	/** 
+	 * Imports a texture at the provided path. Textures not in sRGB space (e.g. normal maps) need to be specially marked by
+	 * setting 'isSRGB' to false. 
+	 */
+	HTexture loadTexture(const Path& path, bool isSRGB = true);	
+	
+	/** Create a material used by our example model. */
+	HMaterial createMaterial();
+
+	/** Set up example scene objects. */
+	void setUp3DScene(const HMesh& mesh, const HMaterial& material);
+
+	/** Set up input configuration and callbacks. */
+	void setUpInput();
+
+	/** Toggles the primary window between full-screen and windowed mode. */
+	void toggleFullscreen();
+
+	/** Called whenever the main render window is resized. */
+	void renderWindowResized();
+
+	/** Called when the selected video mode changes in the video mode list box. */
+	void videoModeChanged(UINT32 idx, bool enabled);
+
+	/** Triggered whenever a virtual button is released. */
+	void buttonUp(const VirtualButton& button, UINT32 deviceIdx);
+}
+
+using namespace bs;
+
+/** Main entry point into the application. */
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+{
+	// Descriptor used for initializing the engine
+	START_UP_DESC startUpDesc;
+
+	// Use default values as specified by the build system
+	startUpDesc.renderAPI = BS_RENDER_API_MODULE;
+	startUpDesc.renderer = BS_RENDERER_MODULE;
+	startUpDesc.audio = BS_AUDIO_MODULE;
+	startUpDesc.physics = BS_PHYSICS_MODULE;
+	startUpDesc.input = BS_INPUT_MODULE;
+
+	// Descriptor used for initializing the primary application window.
+	startUpDesc.primaryWindowDesc.videoMode = VideoMode(windowResWidth, windowResHeight);
+	startUpDesc.primaryWindowDesc.title = "Banshee Example App";
+	startUpDesc.primaryWindowDesc.fullscreen = false;
+	startUpDesc.primaryWindowDesc.depthBuffer = false;
+
+	// List of importer plugins we plan on using for importing various resources
+	startUpDesc.importers.push_back("BansheeFreeImgImporter"); // For importing textures
+	startUpDesc.importers.push_back("BansheeFBXImporter"); // For importing meshes
+	startUpDesc.importers.push_back("BansheeFontImporter"); // For importing fonts
+	startUpDesc.importers.push_back("BansheeSL"); // For importing shaders
+
+	// Initializes the application with systems and primary window as defined above
+	Application::startUp(startUpDesc);
+
+	// Imports all of ours assets and prepares GameObjects that handle the example logic.
+	setUpExample();
+	
+	// Runs the main loop that does most of the work. This method will exit when user closes the main
+	// window or exits in some other way.
+	Application::instance().runMainLoop();
+
+	Application::shutDown();
+
+	return 0;
+}
+
+namespace bs
+{
+	Path dataPath = Paths::getRuntimeDataPath();
+	Path exampleModelPath = dataPath + "Examples\\Pistol\\Pistol01.fbx";
+	Path exampleAlbedoTexPath = dataPath + "Examples\\Pistol\\Pistol_DFS.png";
+	Path exampleNormalsTexPath = dataPath + "Examples\\Pistol\\Pistol_NM.png";
+	Path exampleRoughnessTexPath = dataPath + "Examples\\Pistol\\Pistol_RGH.png";
+	Path exampleMetalnessTexPath = dataPath + "Examples\\Pistol\\Pistol_MTL.png";
+
+	HCamera sceneCamera;
+
+    HMesh exampleModel;
+    HTexture exampleAlbedoTex;
+    HTexture exampleNormalsTex;
+    HTexture exampleRoughnessTex;
+    HTexture exampleMetalnessTex;
+    HShader exampleShader;
+    HMaterial exampleMaterial;
+
+	void setUpExample()
+	{
+		loadAssets();
+		exampleMaterial = createMaterial();
+
+		setUp3DScene(exampleModel, exampleMaterial);
+		setUpInput();
+	}
+
+	/**
+	 * Load the required resources. First try to load a pre-processed version of the resources. If they don't exist import
+	 * resources from the source formats into engine format, and save them for next time.
+	 */
+	void loadAssets()
+	{
+		// Load an FBX mesh.
+		exampleModel = loadMesh(exampleModelPath, 10.0f);
+
+        // Load textures
+        exampleAlbedoTex = loadTexture(exampleAlbedoTexPath);
+        exampleNormalsTex = loadTexture(exampleNormalsTexPath, false);
+        exampleRoughnessTex = loadTexture(exampleRoughnessTexPath, false);
+        exampleMetalnessTex = loadTexture(exampleMetalnessTexPath, false);
+
+		// Load the default physically based shader for rendering opaque objects
+        exampleShader = BuiltinResources::instance().getBuiltinShader(BuiltinShader::Standard);
+	}
+	
+	HMesh loadMesh(const Path& path, float scale)
+	{
+		Path assetPath = path;
+		assetPath.setExtension(path.getExtension() + ".asset");
+
+		HMesh model = gResources().load<Mesh>(assetPath);
+		if (model == nullptr) // Mesh file doesn't exist, import from the source file.
+		{
+			// When importing you may specify optional import options that control how is the asset imported.
+			SPtr<ImportOptions> meshImportOptions = Importer::instance().createImportOptions(path);
+
+			// rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a
+			// non-mesh resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
+			if (rtti_is_of_type<MeshImportOptions>(meshImportOptions))
+			{
+				MeshImportOptions* importOptions = static_cast<MeshImportOptions*>(meshImportOptions.get());
+
+				importOptions->setImportScale(scale);
+			}
+
+			model = gImporter().import<Mesh>(path, meshImportOptions);
+
+			// Save for later use, so we don't have to import on the next run.
+			gResources().save(model, assetPath, true);
+		}
+
+		return model;
+	}
+
+	HTexture loadTexture(const Path& path, bool isSRGB)
+	{
+		Path assetPath = path;
+		assetPath.setExtension(path.getExtension() + ".asset");
+
+		HTexture texture = gResources().load<Texture>(assetPath);
+		if (texture == nullptr) // Texture file doesn't exist, import from the source file.
+		{
+			// When importing you may specify optional import options that control how is the asset imported.
+			SPtr<ImportOptions> textureImportOptions = Importer::instance().createImportOptions(path);
+
+			// rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a 
+			// non-texture resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
+			if (rtti_is_of_type<TextureImportOptions>(textureImportOptions))
+			{
+				TextureImportOptions* importOptions = static_cast<TextureImportOptions*>(textureImportOptions.get());
+
+				// We want maximum number of mipmaps to be generated
+				importOptions->setGenerateMipmaps(true);
+
+				// If the texture is in sRGB space the system needs to know about it
+				importOptions->setSRGB(isSRGB);
+
+				// Ensures we can save the texture contents
+				importOptions->setCPUCached(true);
+			}
+
+			// Import texture with specified import options
+			texture = gImporter().import<Texture>(path, textureImportOptions);
+
+			// Save for later use, so we don't have to import on the next run.
+			gResources().save(texture, assetPath, true);
+		}
+
+		return texture;
+	}	
+
+	/** Create a material using the active shader, and assign the relevant textures to it. */
+	HMaterial createMaterial()
+	{
+		// Create a material with the active shader.
+		HMaterial exampleMaterial = Material::create(exampleShader);
+
+        // Assign the four textures requires by the PBS shader
+        exampleMaterial->setTexture("gAlbedoTex", exampleAlbedoTex);
+        exampleMaterial->setTexture("gNormalTex", exampleNormalsTex);
+        exampleMaterial->setTexture("gRoughnessTex", exampleRoughnessTex);
+        exampleMaterial->setTexture("gMetalnessTex", exampleMetalnessTex);
+
+		return exampleMaterial;
+	}
+
+	/** Set up the 3D object used by the example, and the camera to view the world through. */
+	void setUp3DScene(const HMesh& mesh, const HMaterial& material)
+	{
+		/************************************************************************/
+		/* 								SCENE OBJECT                      		*/
+		/************************************************************************/
+
+		// Now we create a scene object that has a position, orientation, scale and optionally
+		// components to govern its logic. In this particular case we are creating a SceneObject
+		// with a Renderable component which will render a mesh at the position of the scene object
+		// with the provided material.
+
+		// Create new scene object at (0, 0, 0)
+		HSceneObject pistolSO = SceneObject::create("Pistol");
+		
+		// Attach the Renderable component and hook up the mesh we imported earlier,
+		// and the material we created in the previous section.
+		HRenderable renderable = pistolSO->addComponent<CRenderable>();
+		renderable->setMesh(mesh);
+		renderable->setMaterial(material);
+
+        /************************************************************************/
+        /* 									LIGHTS                      		*/
+        /************************************************************************/
+
+        // Add a light so that our object isn't completely in the dark.
+
+        HSceneObject lightSO = SceneObject::create("Light");
+        lightSO->setPosition(Vector3(0.1f, 0.0f, 0.0f));
+
+        HLight light = lightSO->addComponent<CLight>();
+
+        // Disable physically based attentuation because we want to set our own range
+        light->setPhysicallyBasedAttenuation(false);
+
+	    light->setRange(10.0f);
+        light->setIntensity(100.0f);
+
+		/************************************************************************/
+		/* 									CAMERA	                     		*/
+		/************************************************************************/
+
+		// In order something to render on screen we need at least one camera.
+
+		// Like before, we create a new scene object at (0, 0, 0).
+		HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
+
+		// Get the primary render window we need for creating the camera. Additionally
+		// hook up a callback so we are notified when user resizes the window.
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+		window->onResized.connect(&renderWindowResized);
+
+		// Add a Camera component that will output whatever it sees into that window 
+		// (You could also use a render texture or another window you created).
+		sceneCamera = sceneCameraSO->addComponent<CCamera>(window);
+
+		// Set up camera component properties
+
+		// Set closest distance that is visible. Anything below that is clipped.
+		sceneCamera->setNearClipDistance(0.005f);
+
+		// Set farthest distance that is visible. Anything above that is clipped.
+		sceneCamera->setFarClipDistance(1000);
+
+		// Set aspect ratio depending on the current resolution
+		sceneCamera->setAspectRatio(windowResWidth / (float)windowResHeight);
+
+        // Enable multi-sample anti-aliasing for better quality
+        sceneCamera->setMSAACount(4);
+
+		// Add a CameraFlyer component that allows us to move the camera. See CameraFlyer for more information.
+		sceneCameraSO->addComponent<CameraFlyer>();
+
+		// Position and orient the camera scene object
+		sceneCameraSO->setPosition(Vector3(0.2f, 0.1f, 0.2f));
+		sceneCameraSO->lookAt(Vector3(-0.1f, 0, 0));
+	}
+
+	/** Register mouse and keyboard inputs that will be used for controlling the camera. */
+	void setUpInput()
+	{
+		// Register input configuration
+		// Banshee allows you to use VirtualInput system which will map input device buttons
+		// and axes to arbitrary names, which allows you to change input buttons without affecting
+		// the code that uses it, since the code is only aware of the virtual names. 
+		// If you want more direct input, see Input class.
+		auto inputConfig = VirtualInput::instance().getConfiguration();
+
+		// Camera controls for buttons (digital 0-1 input, e.g. keyboard or gamepad button)
+		inputConfig->registerButton("Forward", BC_W);
+		inputConfig->registerButton("Back", BC_S);
+		inputConfig->registerButton("Left", BC_A);
+		inputConfig->registerButton("Right", BC_D);
+		inputConfig->registerButton("Forward", BC_UP);
+		inputConfig->registerButton("Back", BC_BACK);
+		inputConfig->registerButton("Left", BC_LEFT);
+		inputConfig->registerButton("Right", BC_RIGHT);
+		inputConfig->registerButton("FastMove", BC_LSHIFT);
+		inputConfig->registerButton("RotateCam", BC_MOUSE_RIGHT);
+
+		// Camera controls for axes (analog input, e.g. mouse or gamepad thumbstick)
+		// These return values in [-1.0, 1.0] range.
+		inputConfig->registerAxis("Horizontal", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseX));
+		inputConfig->registerAxis("Vertical", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseY));
+	}
+
+	/** Callback triggered wheneve the user resizes the example window. */
+	void renderWindowResized()
+	{
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+		const RenderWindowProperties& rwProps = window->getProperties();
+
+		windowResWidth = rwProps.getWidth();
+		windowResHeight = rwProps.getHeight();
+
+		sceneCamera->setAspectRatio(rwProps.getWidth() / (float)rwProps.getHeight());
+	}
+}
+

+ 4 - 1
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -1240,7 +1240,10 @@ namespace bs { namespace ct
 
 
 		bs_frame_mark();
 		bs_frame_mark();
 		{		
 		{		
-            UINT32 currentCubeArraySize = mCubemapArrayTex->getProperties().getNumArraySlices();
+            UINT32 currentCubeArraySize = 0;
+		    
+            if(mCubemapArrayTex != nullptr)
+		        mCubemapArrayTex->getProperties().getNumArraySlices();
 
 
 			bool forceArrayUpdate = false;
 			bool forceArrayUpdate = false;
 			if(mCubemapArrayTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
 			if(mCubemapArrayTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))