Преглед на файлове

Project should be buildable again:
- Added last precompiled binaries under Backup
- Added examples for bsf
- Chagned target framework for EditorCore/EditorManaged to .Net 4.8.
- Enabled git lfs for binaries.
- Integrated examples for bsf.
- Readded bsf framework (everything should be in this repository)
- Updated gitignore, ReadMe.txt

larioteo преди 2 години
родител
ревизия
073e47376c
променени са 67 файла, в които са добавени 4720 реда и са изтрити 6 реда
  1. 2 0
      .gitattributes
  2. 1 0
      .gitignore
  3. 0 3
      .gitmodules
  4. 3 0
      Backup/Banshee#x64#Preview.zip
  5. 3 0
      Backup/Banshee#x64#v0.4.0.zip
  6. 3 0
      Backup/BansheeCompiledData_Master_12.zip
  7. 3 0
      Backup/ToDo#Unknown#Data.zip
  8. 3 0
      Backup/bsfCompiledData_Master_22.zip
  9. 3 0
      Backup/bsfCompiledData_Master_35.zip
  10. 3 0
      Backup/bsfDependencies_VS2015_Master_11.zip
  11. 3 0
      Backup/bsfExamplesData_Master_10.zip
  12. 12 0
      Examples/.gitignore
  13. 3 0
      Examples/.gitmodules
  14. 89 0
      Examples/.travis.yml
  15. 57 0
      Examples/CMakeLists.txt
  16. 7 0
      Examples/LICENSE.md
  17. 35 0
      Examples/README.md
  18. 23 0
      Examples/Source/Audio/CMakeLists.txt
  19. 247 0
      Examples/Source/Audio/Main.cpp
  20. 4 0
      Examples/Source/CMake/BsExampleConfig.h.in
  21. 126 0
      Examples/Source/Common/BsCameraFlyer.cpp
  22. 41 0
      Examples/Source/Common/BsCameraFlyer.h
  23. 423 0
      Examples/Source/Common/BsExampleFramework.h
  24. 80 0
      Examples/Source/Common/BsFPSCamera.cpp
  25. 42 0
      Examples/Source/Common/BsFPSCamera.h
  26. 95 0
      Examples/Source/Common/BsFPSWalker.cpp
  27. 34 0
      Examples/Source/Common/BsFPSWalker.h
  28. 83 0
      Examples/Source/Common/BsObjectRotator.cpp
  29. 30 0
      Examples/Source/Common/BsObjectRotator.h
  30. 21 0
      Examples/Source/Common/CMakeLists.txt
  31. 19 0
      Examples/Source/Common/CMakeSources.cmake
  32. 23 0
      Examples/Source/CustomMaterials/CMakeLists.txt
  33. 363 0
      Examples/Source/CustomMaterials/Main.cpp
  34. 20 0
      Examples/Source/Decals/CMakeLists.txt
  35. 264 0
      Examples/Source/Decals/Main.cpp
  36. 23 0
      Examples/Source/GUI/CMakeLists.txt
  37. 324 0
      Examples/Source/GUI/Main.cpp
  38. 23 0
      Examples/Source/LowLevelRendering/CMakeLists.txt
  39. 616 0
      Examples/Source/LowLevelRendering/Main.cpp
  40. 20 0
      Examples/Source/Particles/CMakeLists.txt
  41. 666 0
      Examples/Source/Particles/Main.cpp
  42. 23 0
      Examples/Source/PhysicallyBasedShading/CMakeLists.txt
  43. 177 0
      Examples/Source/PhysicallyBasedShading/Main.cpp
  44. 23 0
      Examples/Source/Physics/CMakeLists.txt
  45. 346 0
      Examples/Source/Physics/Main.cpp
  46. 23 0
      Examples/Source/SkeletalAnimation/CMakeLists.txt
  47. 215 0
      Examples/Source/SkeletalAnimation/Main.cpp
  48. 12 0
      Examples/appveyor.yml
  49. BIN
      Game/Internal/Assemblies/MScriptGame.dll
  50. BIN
      Game/Internal/Assemblies/MScriptGame.dll.mdb
  51. BIN
      Game/Internal/BuildData.asset
  52. BIN
      Game/Internal/Layout.asset
  53. BIN
      Game/Internal/ProjectLibrary.asset
  54. BIN
      Game/Internal/ResourceManifest.asset
  55. BIN
      Game/Internal/Resources/12cb8106-456a-0df9-9db4-456a83a83e85.asset
  56. BIN
      Game/Internal/Resources/8a4ee213-4880-6950-5c8a-4880fdf2dc7d.asset
  57. BIN
      Game/Internal/Settings.asset
  58. 31 0
      Game/Resources/New Shader.bsl
  59. BIN
      Game/Resources/New Shader.bsl.meta
  60. 20 0
      Game/Resources/test.cs
  61. BIN
      Game/Resources/test.cs.meta
  62. 8 2
      README.md
  63. BIN
      Source/Banshee3D/RCa09888
  64. BIN
      Source/Banshee3D/RCa12528
  65. BIN
      Source/Banshee3D/RCa21224
  66. 1 1
      Source/EditorManaged/MBansheeEditor.csproj.in
  67. 1 0
      Source/bsf/.gitignore

+ 2 - 0
.gitattributes

@@ -0,0 +1,2 @@
+*.zip filter=lfs diff=lfs merge=lfs -text
+Source/bsf/Dependencies/** filter=lfs diff=lfs merge=lfs -text

+ 1 - 0
.gitignore

@@ -5,6 +5,7 @@
 *.opensdf
 bin
 obj
+out
 lib
 Dependencies
 Builds

+ 0 - 3
.gitmodules

@@ -1,3 +0,0 @@
-[submodule "Source/bsf"]
-	path = Source/bsf
-	url = https://github.com/GameFoundry/bsf.git

+ 3 - 0
Backup/Banshee#x64#Preview.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0e962ea2779a781380ad094e22e639b8459485d3ee056534b99ef9b41eab2cf
+size 2578757

+ 3 - 0
Backup/Banshee#x64#v0.4.0.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfd50b7956a93fcaf4faeb4376e99856fec9db8471a19e823b471b43c4756114
+size 97166051

+ 3 - 0
Backup/BansheeCompiledData_Master_12.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a253c694959ad4363f4c668a4060fdd3a4d8c0bc6c86848f41fc3ce999bd274
+size 849817

+ 3 - 0
Backup/ToDo#Unknown#Data.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:663cef0421f1c4d1dcf90b9531ecb948ac0f2119cbe2b7c3ab0db11eb78178d2
+size 761218

+ 3 - 0
Backup/bsfCompiledData_Master_22.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0befbdc6a9cd26045ec21fe759a9e2be8955fc13998d33467ed1e25dfea6013
+size 13851644

+ 3 - 0
Backup/bsfCompiledData_Master_35.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:848973eef189b16b00947655d2895d0195f4078159884d585653390ca781d125
+size 14581625

+ 3 - 0
Backup/bsfDependencies_VS2015_Master_11.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90727be649509bb3e79e0838608f02a46d2ebcd61779e0c91d8ec64db7cd3e97
+size 22394346

+ 3 - 0
Backup/bsfExamplesData_Master_10.zip

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d22079bb2df4dd2689ca1fd1ab8ce4c69f7aaa142df358a399c68af22b62ebd
+size 26505607

+ 12 - 0
Examples/.gitignore

@@ -0,0 +1,12 @@
+# Ignored files
+*~
+*.suo
+*.sdf
+*.opensdf
+bin
+lib
+Build
+Data
+BsExampleConfig.h
+Source/Experimental
+ExperimentalData

+ 3 - 0
Examples/.gitmodules

@@ -0,0 +1,3 @@
+[submodule "bsf"]
+	path = bsf
+	url = https://github.com/gamefoundry/bsf

+ 89 - 0
Examples/.travis.yml

@@ -0,0 +1,89 @@
+sudo: false
+language: cpp
+
+matrix:
+  include:
+    - os: linux
+      compiler:
+        - gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - libgl1-mesa-dev
+            - libglu1-mesa-dev
+            - mesa-common-dev
+            - libx11-dev
+            - libxcursor-dev
+            - libxrandr-dev
+            - libxi-dev
+            - uuid-dev
+    - os: osx
+      compiler:
+        - clang
+      osx_image: xcode9
+  
+install:
+  - DEPS_DIR="${TRAVIS_BUILD_DIR}/Deps"
+  - mkdir ${DEPS_DIR}
+
+  # Download libUUID
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+      brew update
+      brew install ossp-uuid
+    fi
+
+  # Download CMake
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      CMAKE_URL="https://cmake.org/files/v3.12/cmake-3.12.4-Linux-x86_64.tar.gz"
+      cd ${DEPS_DIR}
+      mkdir cmake
+      travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
+      export PATH=${DEPS_DIR}/cmake/bin:${PATH}
+    else
+      brew install cmake || brew upgrade cmake
+    fi
+
+  # Download GCC 7.1
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      GCC_URL="http://data.banshee3d.com/gcc-7.1-trusty.zip"
+      cd ${DEPS_DIR}
+      mkdir gcc
+      cd gcc
+      wget --no-check-certificate --quiet - ${GCC_URL}
+      unzip -q gcc-7.1-trusty.zip
+      export PATH=${DEPS_DIR}/gcc/bin:$PATH
+      export LD_LIBRARY_PATH=${DEPS_DIR}/gcc/lib:$LD_LIBRARY_PATH
+      export LD_LIBRARY_PATH=${DEPS_DIR}/gcc/lib64:$LD_LIBRARY_PATH
+    fi		
+
+  # Download binutils 2.28
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      BINUTILS_URL="http://data.banshee3d.com/binutils-2.28-trusty.zip"
+      cd ${DEPS_DIR}
+      mkdir binutils
+      cd binutils
+      wget --no-check-certificate --quiet - ${BINUTILS_URL}
+      unzip -q binutils-2.28-trusty.zip
+      export PATH=${DEPS_DIR}/binutils/bin:${PATH}
+      export LD_LIBRARY_PATH=${DEPS_DIR}/binutils/lib:$LD_LIBRARY_PATH
+    fi	
+
+  - cd ${TRAVIS_BUILD_DIR}
+
+script:
+  - INSTALL_DIR="${TRAVIS_BUILD_DIR}/Install"
+  - mkdir ${INSTALL_DIR}
+  - mkdir Build && cd Build
+  - |
+    if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      CC=gcc CXX=g++ cmake -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ -DCMAKE_INSTALL_PREFIX:STRING=${INSTALL_DIR} .. 
+    else
+      cmake -DCMAKE_INSTALL_PREFIX:STRING=${INSTALL_DIR} ..
+    fi
+  - make

+ 57 - 0
Examples/CMakeLists.txt

@@ -0,0 +1,57 @@
+cmake_minimum_required (VERSION 3.12.0)
+project (bsfExamples)
+
+set(BSF_DIRECTORY "${PROJECT_SOURCE_DIR}/bsf" CACHE STRING "bsf directory")
+
+set (BSF_SOURCE_DIR ${BSF_DIRECTORY}/Source)
+set (APP_ROOT_DIR ${BSF_DIRECTORY})
+set (SECONDARY_APP_ROOT_DIR "${PROJECT_SOURCE_DIR}")
+set (BS_IS_BANSHEE3D 0)
+
+set(BSF_AUTO_FETCH ON CACHE BOOL "If true BSF submodule will be automatically initialized and updated whenever CMake is ran.")
+
+# Grab BSF
+find_path(SUBMODULE_SOURCES "Source/Foundation/bsfEngine/BsApplication.h" "bsf/")
+mark_as_advanced(SUBMODULE_SOURCES)
+
+if(BSF_AUTO_FETCH)
+	message(STATUS "Fetching 'bsf' submodule...")
+	if(NOT SUBMODULE_SOURCES)
+		execute_process(COMMAND git submodule update 
+							--init 
+							-- bsf
+						WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+	else()
+		execute_process(COMMAND git submodule update
+							-- bsf
+						WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+	endif()
+endif()
+
+include(${BSF_SOURCE_DIR}/CMake/Properties.cmake)
+include(${BSF_SOURCE_DIR}/CMake/FindPackageOrBuild.cmake)
+include(${BSF_SOURCE_DIR}/CMake/HelperMethods.cmake)
+
+add_subdirectory(${BSF_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/bsf)
+
+set(BS_EXAMPLES_BUILTIN_ASSETS_VERSION 9)
+
+# Generate config file
+configure_file("${PROJECT_SOURCE_DIR}/Source/CMake/BsExampleConfig.h.in" "${PROJECT_SOURCE_DIR}/Source/Common/BsExampleConfig.h")
+
+# Check data dependencies
+check_and_update_builtin_assets(bsfExamples ${PROJECT_SOURCE_DIR}/Data Data ${BS_EXAMPLES_BUILTIN_ASSETS_VERSION} NO)
+	
+# Sub-directories
+add_subdirectory(Source/Common)
+add_subdirectory(Source/LowLevelRendering)
+add_subdirectory(Source/PhysicallyBasedShading)
+add_subdirectory(Source/CustomMaterials)
+add_subdirectory(Source/GUI)
+add_subdirectory(Source/Audio)
+add_subdirectory(Source/SkeletalAnimation)
+add_subdirectory(Source/Physics)
+add_subdirectory(Source/Particles)
+add_subdirectory(Source/Decals)
+add_subdirectory_optional(Source/Experimental/Shadows)
+add_subdirectory_optional(Source/Experimental/Particles)

+ 7 - 0
Examples/LICENSE.md

@@ -0,0 +1,7 @@
+Copyright 2018 Marko Pintera
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 35 - 0
Examples/README.md

@@ -0,0 +1,35 @@
+| CI            | Community   | Support |
+| ------------- |-------------|--------|
+[![Build Status](https://travis-ci.org/GameFoundry/bsfExamples.svg?branch=master)](https://travis-ci.org/GameFoundry/bsfExamples) [![Build status](https://ci.appveyor.com/api/projects/status/wvb1erthd4pvehqr?svg=true)](https://ci.appveyor.com/project/BearishSun/bsfexamples) | [![Community](https://img.shields.io/discourse/https/discourse.bsframework.io/posts.svg)](https://discourse.bsframework.io) | [![Patreon](https://img.shields.io/badge/Donate-Patreon-orange.svg)](https://www.patreon.com/bePatron?c=1646501) [![Paypal](https://img.shields.io/badge/Donate-Paypal-blue.svg)](https://www.paypal.me/MarkoPintera/10)
+
+# Compile steps
+
+- Install git (https://git-scm.com) and CMake 3.12.4 or higher (https://cmake.org)
+	- Ensure they are added to your *PATH* environment variable
+- Run the following commands in the terminal/command line:
+	- `git clone https://github.com/GameFoundry/bsfExamples.git`
+	- `cd bsfExamples`
+	- `mkdir Build`
+	- `cd Build`
+	- `cmake -G "$generator$" ../`
+		- Where *$generator$* should be replaced with any of the supported generators. Some common ones:
+			- `Visual Studio 15 2017 Win64` - Visual Studio 2017 (64-bit build)
+			- `Unix Makefiles`
+			- `Ninja`
+			- `Xcode`
+			- See all valid generators: [cmake-generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
+- Build the project using your chosen tool
+	- Build files are in the `bsfExamples/Build` folder
+- Run the examples
+	- Example binaries are placed in the `bsfExamples/Build/bin` folder
+
+# Examples
+* Audio - Demonstrates how to import audio clips and use audio sources and listeners.
+* CustomMaterials - Demonstrates how to use custom materials that override vertex, surface and lighting aspects of the renderer.
+* Decals - Demonstrates how to project decal textures onto other surfaces.
+* GUI - Demonstrates how to use the built-in GUI system. Demoes a variety of basic controls, the layout system and shows how to use styles to customize the look of GUI elements.
+* LowLevelRendering - Demonstrates how to use the low-level rendering system to manually issue rendering commands. This is similar to using DirectX/OpenGL/Vulkan, except it uses bs::framework's platform-agnostic rendering layer.
+* Particles - Demonstrates how to use the particle system to render traditional billboard particles, 3D mesh particles and GPU simulated particles.
+* PhysicallyBasedRendering - Demonstrates the physically based renderer using the built-in shaders & lighting by rendering an object in a HDR environment.
+* Physics - Demonstrates the use of variety of physics related components, including a character controller, rigidbodies and colliders.
+* SkeletalAnimation - Demonstrates how to import an animation clip and animate a 3D model using skeletal (skinned) animation.

+ 23 - 0
Examples/Source/Audio/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(Audio WIN32 "Main.cpp")
+else()
+	add_executable(Audio "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(Audio PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(Audio Common)
+
+# Plugin dependencies
+add_engine_dependencies(Audio)
+add_dependencies(Audio bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET Audio PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(Audio)

+ 247 - 0
Examples/Source/Audio/Main.cpp

@@ -0,0 +1,247 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Audio/BsAudioClip.h"
+#include "Audio/BsAudioClipImportOptions.h"
+#include "Audio/BsAudio.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCAudioSource.h"
+#include "Components/BsCAudioListener.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "Importer/BsImporter.h"
+#include "Utility/BsTime.h"
+#include "Input/BsInput.h"
+#include "GUI/BsCGUIWidget.h"
+#include "GUI/BsGUIPanel.h"
+#include "GUI/BsGUILayoutY.h"
+#include "GUI/BsGUILabel.h"
+
+// Example headers
+#include "BsExampleConfig.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example demonstrates how to import audio clips and then play them back using a variety of settings.
+//
+// The example starts off by import the relevant audio clips, demonstrating various settings for streaming, compression and
+// 2D/3D audio. It then sets up a camera that will be used for GUI rendering, unrelated to audio. It proceeds to
+// add an AudioListener component which is required to play back 3D sounds (it determines what are sounds relative to). It
+// then creates a couple of AudioSources - one that is static and used for music playback (2D audio), and another that
+// moves around the listener and demonstrates 3D audio playback. Follow that, input is hooked up that lets the user switch
+// between the playback of the two audio sources. It also demonstrates how to play one-shot audio clips without the
+// AudioSource component. Finally, GUI is set up that lets the user know which input controls are available.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Create a helper component that causes its scene object to move around in a circle around the world origin. */
+	class ObjectFlyer : public Component
+	{
+	public:
+		ObjectFlyer(const HSceneObject& parent)
+			:Component(parent)
+		{ }
+
+		/** Triggered once per frame. */
+		void update() override
+		{
+			const float time = gTime().getTime();
+
+			const float x = cos(time);
+			const float z = sin(time);
+			
+			SO()->setPosition(Vector3(x, 0.0f, z));
+		}
+	};
+
+	/** Import audio clips and set up the audio sources and listeners. */
+	void setUpScene()
+	{
+		/************************************************************************/
+		/* 									ASSETS	                     		*/
+		/************************************************************************/
+
+		// First import any audio clips we plan on using
+
+		// Set up paths to the audio file resources
+		const Path exampleDataPath = EXAMPLE_DATA_PATH;
+		const Path musicClipPath = exampleDataPath + "Audio/BrokeForFree-NightOwl.ogg";
+		const Path environmentClipPath = exampleDataPath + "Audio/FilteredPianoAmbient.ogg";
+		const Path cueClipPath = exampleDataPath + "Audio/GunShot.wav";
+
+		// Import the music audio clip. Compress the imported data to Vorbis format to save space, at the cost of decoding
+		// performance. Also since it's a longer audio clip, use streaming to avoid loading the entire clip into memory,
+		// at the additional cost of performance and IO overhead.
+		SPtr<AudioClipImportOptions> musicImportOptions = AudioClipImportOptions::create();
+		musicImportOptions->setFormat(AudioFormat::VORBIS);
+		musicImportOptions->setReadMode(AudioReadMode::Stream);
+		musicImportOptions->setIs3D(false);
+
+		HAudioClip musicClip = gImporter().import<AudioClip>(musicClipPath, musicImportOptions);
+
+		// Import a loopable environment ambient sound. Compress the imported data to Vorbis format to save space, at the
+		// cost of decoding performance. Same as the music clip, this is also a longer clip, but instead of streaming we
+		// load the compressed data and just uncompress on the fly. This saves on IO overhead at the cost of little
+		// extra memory.
+		SPtr<AudioClipImportOptions> environmentImportOptions = AudioClipImportOptions::create();
+		environmentImportOptions->setFormat(AudioFormat::VORBIS);
+		environmentImportOptions->setReadMode(AudioReadMode::LoadCompressed);
+		environmentImportOptions->setIs3D(true);
+
+		HAudioClip environmentClip = gImporter().import<AudioClip>(environmentClipPath, environmentImportOptions);
+
+		// Import a short audio cue. Use the uncompressed PCM audio format for fast playback, at the cost of memory.
+		SPtr<AudioClipImportOptions> cueImportOptions = AudioClipImportOptions::create();
+		cueImportOptions->setFormat(AudioFormat::PCM);
+		cueImportOptions->setIs3D(true);
+
+		HAudioClip cueClip = gImporter().import<AudioClip>(cueClipPath, cueImportOptions);
+
+		/************************************************************************/
+		/* 									CAMERA	                     		*/
+		/************************************************************************/
+
+		// Add a camera that will be used for rendering out GUI elements
+
+		// 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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(window);
+
+		// Set background color
+		sceneCamera->getViewport()->setClearColorValue(Color::Black);
+
+		/************************************************************************/
+		/* 									AUDIO	                     		*/
+		/************************************************************************/
+
+		// Set up an audio listener. Every sound will be played relative to this listener. We'll add it to the same
+		// scene object as our main camera.
+		HAudioListener listener = sceneCameraSO->addComponent<CAudioListener>();
+
+		// Add an audio source for playing back the music. Position of the audio source is not important as it is not
+		// a 3D sound. 
+		HSceneObject musicSourceSO = SceneObject::create("Music");
+		HAudioSource musicSource = musicSourceSO->addComponent<CAudioSource>();
+
+		// Assign the clip we want to use for the audio source
+		musicSource->setClip(musicClip);
+
+		// Start playing the audio clip immediately
+		musicSource->play();
+
+		// Add an audio source for playing back an environment sound. This sound is played back on a scene object that
+		// orbits the viewer.
+		HSceneObject environmentSourceSO = SceneObject::create("Environment");
+		HAudioSource environmentSource = environmentSourceSO->addComponent<CAudioSource>();
+
+		// Assign the clip we want to use for the audio source
+		environmentSource->setClip(environmentClip);
+
+		// Make sure the sound keeps looping if it reaches the end
+		environmentSource->setIsLooping(true);
+
+		// Make the audio source orbit the listener, by attaching an ObjectFlyer component
+		environmentSourceSO->addComponent<ObjectFlyer>();
+
+		/************************************************************************/
+		/* 									INPUT	                     		*/
+		/************************************************************************/
+
+		// Hook up input commands that toggle between the different audio sources.
+		gInput().onButtonUp.connect([=](const ButtonEvent& event)
+		{
+			switch(event.buttonCode)
+			{
+			case BC_1:
+				// Start or resume playing music, if not already playing. Stop the ambient sound playback.
+				environmentSource->stop();
+				musicSource->play();
+				break;
+			case BC_2:
+				// Start playing ambient sound, if not already playing. Pause music playback.
+				musicSource->pause();
+				environmentSource->play();
+				break;
+			case BC_MOUSE_LEFT:
+				// Play a one-shot sound at origin. We don't use an AudioSource component because it's a short sound cue
+				// that we don't require additional control over.
+				gAudio().play(cueClip, Vector3::ZERO);
+				break;
+			default:
+				break;
+			}
+		});
+
+		/************************************************************************/
+		/* 									GUI		                     		*/
+		/************************************************************************/
+
+		// Display GUI elements indicating to the user which input keys are available
+
+		// Add a GUIWidget component we will use for rendering the GUI
+		HSceneObject guiSO = SceneObject::create("GUI");
+		HGUIWidget gui = guiSO->addComponent<CGUIWidget>(sceneCamera);
+
+		// Grab the main panel onto which to attach the GUI elements to
+		GUIPanel* mainPanel = gui->getPanel();
+
+		// Create a vertical GUI layout to align the labels one below each other
+		GUILayoutY* vertLayout = GUILayoutY::create();
+
+		// Create the GUI labels displaying the available input commands
+		HString musicString(u8"Press 1 to play music");
+		HString environmentString(u8"Press 2 to play 3D ambient sound");
+		HString cueString(u8"Press left mouse button to play a gun shot sound");
+
+		vertLayout->addNewElement<GUILabel>(musicString);
+		vertLayout->addNewElement<GUILabel>(environmentString);
+		vertLayout->addNewElement<GUILabel>(cueString);
+
+		// Register the layout with the main GUI panel, placing the layout in top left corner of the screen by default
+		mainPanel->addElement(vertLayout);
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Custom example code goes here
+	setUpScene();
+
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 4 - 0
Examples/Source/CMake/BsExampleConfig.h.in

@@ -0,0 +1,4 @@
+#pragma once
+
+static constexpr const char* EXAMPLE_ROOT = "@PROJECT_SOURCE_DIR@/"; 
+static constexpr const char* EXAMPLE_DATA_PATH = "@PROJECT_SOURCE_DIR@/Data/"; 

+ 126 - 0
Examples/Source/Common/BsCameraFlyer.cpp

@@ -0,0 +1,126 @@
+#include "BsCameraFlyer.h"
+#include "Math/BsVector3.h"
+#include "Utility/BsTime.h"
+#include "Math/BsMath.h"
+#include "Scene/BsSceneObject.h"
+#include "Components/BsCCamera.h"
+#include "Platform/BsCursor.h"
+
+namespace bs
+{
+	const float CameraFlyer::START_SPEED = 40.0f;
+	const float CameraFlyer::TOP_SPEED = 130.0f;
+	const float CameraFlyer::ACCELERATION = 10.0f;
+	const float CameraFlyer::FAST_MODE_MULTIPLIER = 2.0f;
+	const float CameraFlyer::ROTATION_SPEED = 3.0f;
+
+	/** 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)
+	{
+		// 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);
+			mPitch += Degree(gVirtualInput().getAxisValue(mVerticalAxis) * ROTATION_SPEED);
+
+			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);
+		}
+
+		const Transform& tfrm = SO()->getTransform();
+
+		// If the movement button is pressed, determine direction to move in
+		Vector3 direction = Vector3::ZERO;
+		if (goingForward) direction += tfrm.getForward();
+		if (goingBack) direction -= tfrm.getForward();
+		if (goingRight) direction += tfrm.getRight();
+		if (goingLeft) direction -= tfrm.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);
+		}
+	}
+}

+ 41 - 0
Examples/Source/Common/BsCameraFlyer.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "Scene/BsComponent.h"
+#include "Math/BsMath.h"
+#include "Input/BsVirtualInput.h"
+
+namespace bs
+{
+	/** Component that controls movement and rotation of the scene object it's attached to. */
+	class CameraFlyer : public Component
+	{
+	public:
+		CameraFlyer(const HSceneObject& parent);
+
+		/** Triggered once per frame. Allows the component to handle input and move. */
+		void update() override;
+
+	private:
+		float mCurrentSpeed; /**< Current speed of the camera. */
+
+		Degree mPitch = Degree(0.0f); /**< Current pitch rotation of the camera (looking up or down). */
+		Degree mYaw = Degree(0.0f); /**< Current yaw rotation of the camera (looking left or right). */
+		bool mLastButtonState = false; /**< 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 of camera rotation. */
+	};
+}

+ 423 - 0
Examples/Source/Common/BsExampleFramework.h

@@ -0,0 +1,423 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "Reflection/BsRTTIType.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsResourceManifest.h"
+#include "Mesh/BsMesh.h"
+#include "Importer/BsImporter.h"
+#include "Importer/BsMeshImportOptions.h"
+#include "Importer/BsTextureImportOptions.h"
+#include "BsExampleConfig.h"
+#include "Text/BsFontImportOptions.h"
+#include "FileSystem/BsFileSystem.h"
+#include "Input/BsVirtualInput.h"
+
+namespace bs
+{
+	/** A list of mesh assets provided with the example projects. */
+	enum class ExampleMesh
+	{
+		Pistol,
+		Cerberus
+	};
+
+	/** A list of texture assets provided with the example projects. */
+	enum class ExampleTexture
+	{
+		PistolAlbedo,
+		PistolNormal,
+		PistolRoughness,
+		PistolMetalness,
+		EnvironmentPaperMill,
+		GUIBansheeIcon,
+		GUIExampleButtonNormal,
+		GUIExampleButtonHover,
+		GUIExampleButtonActive,
+		DroneAlbedo,
+		DroneNormal,
+		DroneRoughness,
+		DroneMetalness,
+		GridPattern,
+		GridPattern2,
+		EnvironmentDaytime,
+		EnvironmentRathaus,
+		CerberusAlbedo,
+		CerberusNormal,
+		CerberusRoughness,
+		CerberusMetalness,
+		ParticleSmoke,
+		DecalAlbedo,
+		DecalNormal
+	};
+
+	/** A list of shader assets provided with the example projects. */
+	enum class ExampleShader
+	{
+		CustomVertex,
+		CustomDeferredSurface,
+		CustomDeferredLighting,
+		CustomForward
+	};
+
+	/** A list of font assets provided with the example projects. */
+	enum class ExampleFont
+	{
+		SegoeUILight,
+		SegoeUISemiBold
+	};
+
+	/** A list of assets without a speccific type provided with the example projects. */
+	enum class ExampleResource
+	{
+		VectorField
+	};
+
+	/** Various helper functionality used throught the examples. */
+	class ExampleFramework
+	{
+	public:
+		/** Loads a manifest of all resources that were previously saved using this class. */
+		static void loadResourceManifest()
+		{
+			const Path dataPath = EXAMPLE_DATA_PATH;
+			const Path manifestPath = dataPath + "ResourceManifest.asset";
+
+			if (FileSystem::exists(manifestPath))
+				manifest = ResourceManifest::load(manifestPath, dataPath);
+			else
+				manifest = ResourceManifest::create("ExampleAssets");
+
+			gResources().registerResourceManifest(manifest);
+		}
+
+		/** Saves the current resource manifest. */
+		static void saveResourceManifest()
+		{
+			const Path dataPath = EXAMPLE_DATA_PATH;
+			const Path manifestPath = dataPath + "ResourceManifest.asset";
+
+			if(manifest)
+				ResourceManifest::save(manifest, manifestPath, dataPath);
+		}
+
+		/** Registers a common set of keys/buttons that are used for controlling the examples. */
+		static void setupInputConfig()
+		{
+			// Register input configuration
+			// bsf 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 = gVirtualInput().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_DOWN);
+			inputConfig->registerButton("Left", BC_LEFT);
+			inputConfig->registerButton("Right", BC_RIGHT);
+			inputConfig->registerButton("FastMove", BC_LSHIFT);
+			inputConfig->registerButton("RotateObj", BC_MOUSE_LEFT);
+			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));
+		}
+
+		/** 
+		 * Loads one of the builtin mesh assets. If the asset doesn't exist, the mesh will be re-imported from the source
+		 * file, and then saved so it can be loaded on the next call to this method. 
+		 * 
+		 * Use the 'scale' parameter to control the size of the mesh. Note this option is only relevant when a mesh is
+		 * being imported (i.e. when the asset file is missing).
+		 */
+		static HMesh loadMesh(ExampleMesh type, float scale = 1.0f)
+		{
+			// Map from the enum to the actual file path
+			static Path assetPaths[] =
+			{
+				Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol01.fbx",
+				Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus.FBX",
+			};
+
+			const Path& srcAssetPath = assetPaths[(UINT32)type];
+
+			// Attempt to load the previously processed asset
+			Path assetPath = srcAssetPath;
+			assetPath.setExtension(srcAssetPath.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(srcAssetPath);
+
+				// 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>(srcAssetPath, meshImportOptions);
+
+				// Save for later use, so we don't have to import on the next run.
+				gResources().save(model, assetPath, true);
+
+				// Register with manifest, if one is present. Manifest allows the engine to find the resource even after
+				// the application was restarted, which is important if resource was referenced in some serialized object.
+				if(manifest)
+					manifest->registerResource(model.getUUID(), assetPath);
+			}
+
+			return model;
+		}
+
+		/**
+		 * Loads one of the builtin texture assets. If the asset doesn't exist, the texture will be re-imported from the 
+		 * source file, and then saved so it can be loaded on the next call to this method. 
+		 * 
+		 * Textures not in sRGB space (e.g. normal maps) need to be specially marked by setting 'isSRGB' to false. Also 
+		 * allows for conversion of texture to cubemap by setting the 'isCubemap' parameter. If the data should be imported
+		 * in a floating point format, specify 'isHDR' to true. Note these options are only relevant when a texture is
+		 * being imported (i.e. when asset file is missing). If 'mips' is true, mip-map levels will be generated.
+		 */
+		static HTexture loadTexture(ExampleTexture type, bool isSRGB = true, bool isCubemap = false, bool isHDR = false, 
+			bool mips = true)
+		{
+			// Map from the enum to the actual file path
+			static Path assetPaths[] =
+			{
+				Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_DFS.png",
+				Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_NM.png",
+				Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_RGH.png",
+				Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_MTL.png",
+				Path(EXAMPLE_DATA_PATH) + "Environments/PaperMill_E_3k.hdr",
+				Path(EXAMPLE_DATA_PATH) + "GUI/BansheeIcon.png",
+				Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonNormal.png",
+				Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonHover.png",
+				Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonActive.png",
+				Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_diff.jpg",
+				Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_normal.jpg",
+				Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_rough.jpg",
+				Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_metal.jpg",
+				Path(EXAMPLE_DATA_PATH) + "Grid/GridPattern.png",
+				Path(EXAMPLE_DATA_PATH) + "Grid/GridPattern2.png",
+				Path(EXAMPLE_DATA_PATH) + "Environments/daytime.hdr",
+				Path(EXAMPLE_DATA_PATH) + "Environments/rathaus.hdr",
+				Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_A.tga",
+				Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_N.tga",
+				Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_R.tga",
+				Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_M.tga",
+				Path(EXAMPLE_DATA_PATH) + "Particles/Smoke.png",
+				Path(EXAMPLE_DATA_PATH) + "Decal/DecalAlbedo.png",
+				Path(EXAMPLE_DATA_PATH) + "Decal/DecalNormal.png",
+			};
+
+			const Path& srcAssetPath = assetPaths[(UINT32)type];
+
+			// Attempt to load the previously processed asset
+			Path assetPath = srcAssetPath;
+			assetPath.setExtension(srcAssetPath.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(srcAssetPath);
+
+				// 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(mips);
+
+					// 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 as cubemap if needed
+					importOptions->setIsCubemap(isCubemap);
+
+					// If importing as cubemap, assume source is a panorama
+					importOptions->setCubemapSourceType(CubemapSourceType::Cylindrical);
+
+					// Importing using a HDR format if requested
+					if (isHDR)
+						importOptions->setFormat(PF_RG11B10F);
+				}
+
+				// Import texture with specified import options
+				texture = gImporter().import<Texture>(srcAssetPath, textureImportOptions);
+
+				// Save for later use, so we don't have to import on the next run.
+				gResources().save(texture, assetPath, true);
+
+				// Register with manifest, if one is present. Manifest allows the engine to find the resource even after
+				// the application was restarted, which is important if resource was referenced in some serialized object.
+				if(manifest)
+					manifest->registerResource(texture.getUUID(), assetPath);
+			}
+
+			return texture;
+		}
+
+		/** 
+		 * Loads one of the builtin shader assets. If the asset doesn't exist, the shader will be re-imported from the 
+		 * source file, and then saved so it can be loaded on the next call to this method. 
+		 */
+		static HShader loadShader(ExampleShader type)
+		{
+			// Map from the enum to the actual file path
+			static Path assetPaths[] =
+			{
+				Path(EXAMPLE_DATA_PATH) + "Shaders/CustomVertex.bsl",
+				Path(EXAMPLE_DATA_PATH) + "Shaders/CustomDeferredSurface.bsl",
+				Path(EXAMPLE_DATA_PATH) + "Shaders/CustomDeferredLighting.bsl",
+				Path(EXAMPLE_DATA_PATH) + "Shaders/CustomForward.bsl",
+			};
+
+			const Path& srcAssetPath = assetPaths[(UINT32)type];
+
+			// Attempt to load the previously processed asset
+			Path assetPath = srcAssetPath;
+			assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
+
+			HShader shader = gResources().load<Shader>(assetPath);
+			if (shader == nullptr) // Shader file doesn't exist, import from the source file.
+			{
+				shader = gImporter().import<Shader>(srcAssetPath);
+
+				// Save for later use, so we don't have to import on the next run.
+				gResources().save(shader, assetPath, true);
+
+				// Register with manifest, if one is present. Manifest allows the engine to find the resource even after
+				// the application was restarted, which is important if resource was referenced in some serialized object.
+				if(manifest)
+					manifest->registerResource(shader.getUUID(), assetPath);
+			}
+
+			return shader;
+		}
+
+		/** 
+		 * Loads one of the builtin font assets. If the asset doesn't exist, the font will be re-imported from the 
+		 * source file, and then saved so it can be loaded on the next call to this method. 
+		 *
+		 * Use the 'fontSizes' parameter to determine which sizes of this font should be imported. Note this option is only
+		 * relevant when a font is being imported (i.e. when the asset file is missing).
+		 */
+		static HFont loadFont(ExampleFont type, Vector<UINT32> fontSizes)
+		{
+			// Map from the enum to the actual file path
+			static Path assetPaths[] =
+			{
+				Path(EXAMPLE_DATA_PATH) + "GUI/segoeuil.ttf",
+				Path(EXAMPLE_DATA_PATH) + "GUI/seguisb.ttf",
+			};
+
+			const Path& srcAssetPath = assetPaths[(UINT32)type];
+
+			// Attempt to load the previously processed asset
+			Path assetPath = srcAssetPath;
+			assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
+
+			HFont font = gResources().load<Font>(assetPath);
+			if (font == nullptr) // Font 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<FontImportOptions> fontImportOptions = FontImportOptions::create();
+				fontImportOptions->setFontSizes(fontSizes);
+
+				font = gImporter().import<Font>(srcAssetPath, fontImportOptions);
+
+				// Save for later use, so we don't have to import on the next run.
+				gResources().save(font, assetPath, true);
+
+				// Register with manifest, if one is present. Manifest allows the engine to find the resource even after
+				// the application was restarted, which is important if resource was referenced in some serialized object.
+				if(manifest)
+				{
+					manifest->registerResource(font.getUUID(), assetPath);
+
+					// Font has child resources, which also need to be registered
+					for (auto& size : fontSizes)
+					{
+						SPtr<const FontBitmap> fontData = font->getBitmap(size);
+
+						Path texPageOutputPath = Path(EXAMPLE_DATA_PATH) + "GUI/";
+
+						UINT32 pageIdx = 0;
+						for (const auto& tex : fontData->texturePages)
+						{
+							String fontName = srcAssetPath.getFilename(false);
+							texPageOutputPath.setFilename(fontName + "_" + toString(size) + "_texpage_" +
+								toString(pageIdx) + ".asset");
+
+							gResources().save(tex, texPageOutputPath, true);
+							manifest->registerResource(tex.getUUID(), texPageOutputPath);
+
+							pageIdx++;
+						}
+					}
+				}
+			}
+
+			return font;
+		}
+
+		/** 
+		 * Loads one of the builtin non-specific assets. If the asset doesn't exist, it will be re-imported from the 
+		 * source file, and then saved so it can be loaded on the next call to this method. 
+		 */
+		template<class T>
+		static ResourceHandle<T> loadResource(ExampleResource type)
+		{
+			// Map from the enum to the actual file path
+			static Path assetPaths[] =
+			{
+				Path(EXAMPLE_DATA_PATH) + "Particles/VectorField.fga",
+			};
+
+			const Path& srcAssetPath = assetPaths[(UINT32)type];
+
+			// Attempt to load the previously processed asset
+			Path assetPath = srcAssetPath;
+			assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
+
+			ResourceHandle<T> resource = gResources().load<T>(assetPath);
+			if (resource == nullptr) // Shader file doesn't exist, import from the source file.
+			{
+				resource = gImporter().import<T>(srcAssetPath);
+
+				// Save for later use, so we don't have to import on the next run.
+				gResources().save(resource, assetPath, true);
+
+				// Register with manifest, if one is present. Manifest allows the engine to find the resource even after
+				// the application was restarted, which is important if resource was referenced in some serialized object.
+				if(manifest)
+					manifest->registerResource(resource.getUUID(), assetPath);
+			}
+
+			return resource;
+		}
+
+
+	private:
+		static SPtr<ResourceManifest> manifest;
+	};
+
+	SPtr<ResourceManifest> ExampleFramework::manifest;
+}

+ 80 - 0
Examples/Source/Common/BsFPSCamera.cpp

@@ -0,0 +1,80 @@
+#include "BsFPSCamera.h"
+#include "Math/BsVector3.h"
+#include "Math/BsMath.h"
+#include "Scene/BsSceneObject.h"
+#include "Physics/BsPhysics.h"
+
+namespace bs
+{ 
+	/** Determines speed of camera rotation. */
+	constexpr float ROTATION_SPEED = 3.0f;
+
+	/** Determines range of movement for pitch rotation, in either direction. */
+	constexpr Degree PITCH_RANGE = Degree(45.0f); 
+
+	FPSCamera::FPSCamera(const HSceneObject& parent)
+		:Component(parent)
+	{
+		// Set a name for the component, so we can find it later if needed
+		setName("FPSCamera");
+
+		// Get handles for key bindings. Actual keys attached to these bindings will be registered during app start-up.
+		mHorizontalAxis = VirtualAxis("Horizontal");
+		mVerticalAxis = VirtualAxis("Vertical");
+
+		// Determine initial yaw and pitch
+		Quaternion rotation = SO()->getTransform().getRotation();
+
+		Radian pitch, yaw, roll;
+		(void)rotation.toEulerAngles(pitch, yaw, roll);
+
+		mPitch = pitch;
+		mYaw = yaw;
+
+		applyAngles();
+	}
+
+	void FPSCamera::update()
+	{
+		// If camera is rotating, apply new pitch/yaw rotation values depending on the amount of rotation from the
+		// vertical/horizontal axes.
+		mYaw += Degree(gVirtualInput().getAxisValue(mHorizontalAxis) * ROTATION_SPEED);
+		mPitch += Degree(gVirtualInput().getAxisValue(mVerticalAxis) * ROTATION_SPEED);
+
+		applyAngles();
+	}
+
+	void FPSCamera::applyAngles()
+	{
+		mYaw.wrap();
+		mPitch.wrap();
+
+		const Degree pitchMax = PITCH_RANGE;
+		const Degree pitchMin = Degree(360.0f) - PITCH_RANGE;
+
+		if(mPitch > pitchMax && mPitch < pitchMin)
+		{
+			if((mPitch - pitchMax) > (pitchMin - mPitch))
+				mPitch = pitchMin;
+			else
+				mPitch = pitchMax;
+		}
+
+		Quaternion yRot(Vector3::UNIT_Y, Radian(mYaw));
+		Quaternion xRot(Vector3::UNIT_X, Radian(mPitch));
+
+		if(!mCharacterSO)
+		{
+			Quaternion camRot = yRot * xRot;
+			camRot.normalize();
+
+			SO()->setRotation(camRot);
+		}
+		else
+		{
+			mCharacterSO->setRotation(yRot);
+			SO()->setRotation(xRot);
+		}
+	}
+
+}

+ 42 - 0
Examples/Source/Common/BsFPSCamera.h

@@ -0,0 +1,42 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "Scene/BsComponent.h"
+#include "Math/BsDegree.h"
+#include "Input/BsVirtualInput.h"
+
+namespace bs
+{
+	/** 
+	 * Component that controls rotation of the scene objects it's attached to through mouse input. Used for first person 
+	 * views. 
+	 */
+	class FPSCamera : public Component
+	{
+	public:
+		FPSCamera(const HSceneObject& parent);
+
+		/** 
+		 * Sets the character scene object to manipulate during rotations. When set, all yaw rotations will be applied to
+		 * the provided scene object, otherwise they will be applied to the current object.
+		 */
+		void setCharacter(const HSceneObject& characterSO) { mCharacterSO = characterSO; }
+
+		/** Triggered once per frame. Allows the component to handle input and move. */
+		void update() override;
+
+	private:
+		/** Applies the current yaw and pitch angles, rotating the object. Also wraps and clamps the angles as necessary. */
+		void applyAngles();
+
+		HSceneObject mCharacterSO; /**< Optional parent object to manipulate. */
+
+		Degree mPitch = Degree(0.0f); /**< Current pitch rotation of the camera (looking up or down). */
+		Degree mYaw = Degree(0.0f); /**< Current yaw rotation of the camera (looking left or right). */
+
+		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). */
+	};
+
+	using HFPSCamera = GameObjectHandle<FPSCamera>;
+}

+ 95 - 0
Examples/Source/Common/BsFPSWalker.cpp

@@ -0,0 +1,95 @@
+#include "BsFPSWalker.h"
+#include "Math/BsVector3.h"
+#include "Math/BsMath.h"
+#include "Scene/BsSceneObject.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCCharacterController.h"
+#include "BsApplication.h"
+#include "Physics/BsPhysics.h"
+#include "Utility/BsTime.h"
+
+namespace bs
+{
+	/** Initial movement speed. */
+	constexpr float START_SPEED = 4.0f; // m/s
+
+	/** Maximum movement speed. */
+	constexpr float TOP_SPEED = 7.0f; // m/s
+
+	/** Acceleration that determines how quickly to go from starting to top speed. */
+	constexpr float ACCELERATION = 1.5f;
+
+	/** Multiplier applied to the speed when the fast move button is held. */
+	constexpr float FAST_MODE_MULTIPLIER = 2.0f;
+
+	FPSWalker::FPSWalker(const HSceneObject& parent)
+		:Component(parent)
+	{
+		// Set a name for the component, so we can find it later if needed
+		setName("FPSWalker");
+
+		// Find the CharacterController we'll be using for movement
+		mController = SO()->getComponent<CCharacterController>();
+
+		// 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");
+	}
+
+	void FPSWalker::fixedUpdate()
+	{
+		// Check if any movement 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);
+
+		const Transform& tfrm = SO()->getTransform();
+
+		// If the movement button is pressed, determine direction to move in
+		Vector3 direction = Vector3::ZERO;
+		if (goingForward) direction += tfrm.getForward();
+		if (goingBack) direction -= tfrm.getForward();
+		if (goingRight) direction += tfrm.getRight();
+		if (goingLeft) direction -= tfrm.getRight();
+
+		// Eliminate vertical movement
+		direction.y = 0.0f;
+		direction.normalize();
+
+		const float frameDelta = gTime().getFixedFrameDelta();
+
+		// 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
+		Vector3 velocity(BsZero);
+		float tooSmall = std::numeric_limits<float>::epsilon();
+		if (mCurrentSpeed > tooSmall)
+			velocity = direction * mCurrentSpeed;
+
+		// Note: Gravity is acceleration, but since the walker doesn't support falling, just apply it as a velocity
+		Vector3 gravity = gPhysics().getGravity();
+		mController->move((velocity + gravity) * frameDelta);
+	}
+}

+ 34 - 0
Examples/Source/Common/BsFPSWalker.h

@@ -0,0 +1,34 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "Scene/BsComponent.h"
+#include "Input/BsVirtualInput.h"
+
+namespace bs
+{
+	/** 
+	 * Component that controls movement through a character controller, used for first-person movement. The 
+	 * CharacterController component must be attached to the same SceneObject this component is on.
+	 */
+	class FPSWalker : public Component
+	{
+	public:
+		FPSWalker(const HSceneObject& parent);
+
+		/** Triggered once per frame. Allows the component to handle input and move. */
+		void fixedUpdate() override;
+
+	private:
+		HCharacterController mController;
+
+		float mCurrentSpeed = 0.0f; /**< Current speed of the camera. */
+
+		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. */
+	};
+
+	using HFPSWalker = GameObjectHandle<FPSWalker>;
+}

+ 83 - 0
Examples/Source/Common/BsObjectRotator.cpp

@@ -0,0 +1,83 @@
+#include "BsObjectRotator.h"
+#include "Math/BsVector3.h"
+#include "Utility/BsTime.h"
+#include "Math/BsMath.h"
+#include "Scene/BsSceneObject.h"
+#include "Platform/BsCursor.h"
+
+namespace bs
+{
+	const float ObjectRotator::ROTATION_SPEED = 1.0f;
+
+	/** Wraps an angle so it always stays in [0, 360) range. */
+	Degree wrapAngle2(Degree angle)
+	{
+		if (angle.valueDegrees() < -360.0f)
+			angle += Degree(360.0f);
+
+		if (angle.valueDegrees() > 360.0f)
+			angle -= Degree(360.0f);
+
+		return angle;
+	}
+
+	ObjectRotator::ObjectRotator(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("ObjectRotator");
+
+		// Get handles for key bindings. Actual keys attached to these bindings will be registered during app start-up.
+		mRotateObj = VirtualButton("RotateObj");
+		mHorizontalAxis = VirtualAxis("Horizontal");
+		mVerticalAxis = VirtualAxis("Vertical");
+
+		// Determine initial yaw and pitch
+		Quaternion rotation = SO()->getTransform().getRotation();
+
+		Radian pitch, yaw, roll;
+		(void)rotation.toEulerAngles(pitch, yaw, roll);
+
+		mPitch = pitch;
+		mYaw = yaw;
+	}
+
+	void ObjectRotator::update()
+	{
+		// Check if any movement or rotation keys are being held
+		bool isRotating = gVirtualInput().isButtonHeld(mRotateObj);
+
+		// If switch to or from rotation mode, hide or show the cursor
+		if (isRotating != mLastButtonState)
+		{
+			if (isRotating)
+				Cursor::instance().hide();
+			else
+				Cursor::instance().show();
+
+			mLastButtonState = isRotating;
+		}
+
+		// If we're rotating, apply new pitch/yaw rotation values depending on the amount of rotation from the
+		// vertical/horizontal axes.
+		if (isRotating)
+		{
+			mYaw -= Degree(gVirtualInput().getAxisValue(mHorizontalAxis) * ROTATION_SPEED);
+			mPitch -= Degree(gVirtualInput().getAxisValue(mVerticalAxis) * ROTATION_SPEED);
+
+			mYaw = wrapAngle2(mYaw);
+			mPitch = wrapAngle2(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);
+		}
+	}
+}

+ 30 - 0
Examples/Source/Common/BsObjectRotator.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "Scene/BsComponent.h"
+#include "Math/BsMath.h"
+#include "Input/BsVirtualInput.h"
+
+namespace bs
+{
+	/** Component that controls rotation of its scene object. */
+	class ObjectRotator : public Component
+	{
+	public:
+		ObjectRotator(const HSceneObject& parent);
+
+		/** Triggered once per frame. Allows the component to handle input and move. */
+		void update() override;
+
+	private:
+		Degree mPitch; /**< Current pitch rotation of the object (up or down). */
+		Degree mYaw; /**< Current yar rotation of the object (left or right). */
+		bool mLastButtonState; /**< Determines was the user rotating the object last frame. */
+
+		VirtualButton mRotateObj; /**< Key that allows object to be rotated while held. */
+		VirtualAxis mVerticalAxis; /**< Input device axis used for controlling object's pitch rotation (up/down). */
+		VirtualAxis mHorizontalAxis; /**< Input device axis used for controlling object's yaw rotation (left/right). */
+
+		static const float ROTATION_SPEED; /**< Determines speed for rotation, in degrees per second. */
+	};
+}

+ 21 - 0
Examples/Source/Common/CMakeLists.txt

@@ -0,0 +1,21 @@
+# Source files and their filters
+include(CMakeSources.cmake)
+
+# Target
+add_library(Common STATIC ${BS_COMMON_SRC})
+
+# Includes
+target_include_directories(Common PUBLIC "./")
+	
+# Libraries
+## Local libs
+target_link_libraries(Common bsf)
+
+# IDE specific
+set_property(TARGET Common PROPERTY FOLDER Examples)
+
+# Copy bsf binaries
+copyBsfBinaries(Common ${BSF_DIRECTORY})
+
+# Precompiled header & Unity build
+conditional_cotire(Common)

+ 19 - 0
Examples/Source/Common/CMakeSources.cmake

@@ -0,0 +1,19 @@
+set(BS_COMMON_INC_NOFILTER
+	"BsExampleFramework.h"
+	"BsCameraFlyer.h"
+	"BsObjectRotator.h"
+	"BsFPSWalker.h"
+	"BsFPSCamera.h"
+)
+
+set(BS_COMMON_SRC_NOFILTER
+	"BsCameraFlyer.cpp"
+	"BsObjectRotator.cpp"
+	"BsFPSWalker.cpp"
+	"BsFPSCamera.cpp"
+)
+
+set(BS_COMMON_SRC
+	${BS_COMMON_INC_NOFILTER}
+	${BS_COMMON_SRC_NOFILTER}
+)

+ 23 - 0
Examples/Source/CustomMaterials/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(CustomMaterials WIN32 "Main.cpp")
+else()
+	add_executable(CustomMaterials "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(CustomMaterials PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(CustomMaterials Common)
+
+# Plugin dependencies
+add_engine_dependencies(CustomMaterials)
+add_dependencies(CustomMaterials bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET CustomMaterials PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(CustomMaterials)

+ 363 - 0
Examples/Source/CustomMaterials/Main.cpp

@@ -0,0 +1,363 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCSkybox.h"
+#include "Components/BsCLight.h"
+#include "GUI/BsCGUIWidget.h"
+#include "GUI/BsGUIPanel.h"
+#include "GUI/BsGUILayoutY.h"
+#include "GUI/BsGUILabel.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "Renderer/BsRenderer.h"
+#include "Material/BsShader.h"
+
+// Example includes
+#include "BsCameraFlyer.h"
+#include "BsObjectRotator.h"
+#include "BsExampleFramework.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example renders an object using a variety of custom materials, showing you how you can customize the rendering of
+// your objects if the built-in materials are not adequate. The example is structuraly very similar to the 
+// PhysicallyBasedShading example, with the addition of custom materials. The most important part of this example are in
+// fact the shaders that it uses, so make sure to also study the BSL code of the shaders we import below.
+//
+// The example first loads necessary resources, including a mesh and textures to use for rendering. Then it imports a set
+// of custom shaders and creates a set of materials based on those shaders. It then proceeds to register the relevant keys 
+// used for controling the camera and the rendered object, as well as a key to switch between different materials. It then
+// sets up the 3D scene using the mesh, textures, and the initial material, as well as a camera, along with CameraFlyer and
+// ObjectRotator components that allow the user to fly around the scene and rotate the 3D model. Finally it hooks up a 
+// callback that switches between the materials when the user presses the relevant key, and adds a bit of GUI to let the
+// user know which key to press.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Container for all resources used by the example. */
+	struct Assets
+	{
+		HMesh sphere;
+		HTexture exampleAlbedoTex;
+		HTexture exampleNormalsTex;
+		HTexture exampleRoughnessTex;
+		HTexture exampleMetalnessTex;
+		HTexture skyTex;
+
+		HMaterial standardMaterial;
+		HMaterial vertexMaterial;
+		HMaterial deferredSurfaceMaterial;
+		HMaterial forwardMaterial;
+
+		HShader deferredLightingShader;
+	};
+
+	Assets gAssets;
+
+	/** Helper method that creates a material from the provided shader, and assigns the relevant PBR textures. */
+	HMaterial createPBRMaterial(const HShader& shader, const Assets& assets)
+	{
+		HMaterial material = Material::create(shader);
+
+		material->setTexture("gAlbedoTex", assets.exampleAlbedoTex);
+		material->setTexture("gNormalTex", assets.exampleNormalsTex);
+		material->setTexture("gRoughnessTex", assets.exampleRoughnessTex);
+		material->setTexture("gMetalnessTex", assets.exampleMetalnessTex);
+
+		return material;
+	}
+
+	/** Load the resources we'll be using throughout the example. */
+	Assets loadAssets()
+	{
+		Assets assets;
+
+		// Load a 3D model
+		assets.sphere = ExampleFramework::loadMesh(ExampleMesh::Pistol, 10.0f);
+
+		// Load PBR textures for the 3D model
+		assets.exampleAlbedoTex = ExampleFramework::loadTexture(ExampleTexture::PistolAlbedo);
+		assets.exampleNormalsTex = ExampleFramework::loadTexture(ExampleTexture::PistolNormal, false);
+		assets.exampleRoughnessTex = ExampleFramework::loadTexture(ExampleTexture::PistolRoughness, false);
+		assets.exampleMetalnessTex = ExampleFramework::loadTexture(ExampleTexture::PistolMetalness, false);
+
+		// Create a set of materials we'll be using for rendering the object
+		//// Create a standard PBR material
+		HShader standardShader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		assets.standardMaterial = createPBRMaterial(standardShader, assets);
+
+		//// Create a material that overrides the vertex transform of the rendered model. This creates a wobble in the model
+		//// geometry, but doesn't otherwise change the lighting properties (i.e. it still uses the PBR lighting model).
+		HShader vertexShader = ExampleFramework::loadShader(ExampleShader::CustomVertex);
+		assets.vertexMaterial = createPBRMaterial(vertexShader, assets);
+
+		//// Create a material that overrides the surface data that gets used by the lighting evaluation. The material
+		//// ignores the albedo texture provided, and instead uses a noise function to generate the albedo values.
+		HShader deferredSurfaceShader = ExampleFramework::loadShader(ExampleShader::CustomDeferredSurface);
+		assets.deferredSurfaceMaterial = createPBRMaterial(deferredSurfaceShader, assets);
+
+		//// Create a material that overrides the lighting calculation by implementing a custom BRDF function, in this case
+		//// using a basic Lambert BRDF. Note that lighting calculations for the deferred pipeline are done globally, so
+		//// this material is created and used differently than others in this example. Instead of being assigned to 
+		//// Renderable it is instead applied globally and will affect all objects using the deferred pipeline.
+		assets.deferredLightingShader = ExampleFramework::loadShader(ExampleShader::CustomDeferredLighting);
+
+		//// Creates a material that uses the forward rendering pipeline, while all previous materials have used the
+		//// deferred rendering pipeline. Forward rendering is required when the shader is used for rendering transparent
+		//// geometry, as this is not supported by the deferred pipeline. Forward rendering shader contains both the surface
+		//// and lighting portions in a single shader (unlike with deferred). This custom shader overrides both, using a
+		//// noise function for generating the surface albedo, and overriding the PBR BRDF with a basic Lambert BRDF.
+		HShader forwardSurfaceAndLighting = ExampleFramework::loadShader(ExampleShader::CustomForward);
+		assets.forwardMaterial = createPBRMaterial(forwardSurfaceAndLighting, assets);
+
+		// Load an environment map
+		assets.skyTex = ExampleFramework::loadTexture(ExampleTexture::EnvironmentPaperMill, false, true, true);
+
+		return assets;
+	}
+
+	HRenderable gRenderable;
+	HGUIWidget gGUI;
+	UINT32 gMaterialIdx = 0;
+
+	/** Set up the 3D object used by the example, and the camera to view the world through. */
+	void setUp3DScene(const Assets& assets)
+	{
+		/************************************************************************/
+		/* 									RENDERABLE                  		*/
+		/************************************************************************/
+
+		// 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 loaded, and the material we created.
+		gRenderable = pistolSO->addComponent<CRenderable>();
+		gRenderable->setMesh(assets.sphere);
+		gRenderable->setMaterial(assets.standardMaterial);
+
+		// Add a rotator component so we can rotate the object during runtime
+		pistolSO->addComponent<ObjectRotator>();
+
+		/************************************************************************/
+		/* 									LIGHT		                  		*/
+		/************************************************************************/
+
+		// Add a light so we can actually see the object
+		HSceneObject lightSO = SceneObject::create("Light");
+
+		HLight light = lightSO->addComponent<CLight>();
+		light->setIntensity(100.0f);
+
+		lightSO->setPosition(Vector3(1.0f, 0.5f, 0.0f));
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(assets.skyTex);
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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);
+
+		// 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(2.0f, 1.0f, 2.0f));
+		sceneCameraSO->lookAt(Vector3(-0.4f, 0, 0));
+
+		/************************************************************************/
+		/* 									GUI		                     		*/
+		/************************************************************************/
+
+		// Add a GUIWidget component we will use for rendering the GUI
+		HSceneObject guiSO = SceneObject::create("GUI");
+		gGUI = guiSO->addComponent<CGUIWidget>(sceneCamera);
+	}
+	
+	/** Sets up or rebuilds any GUI elements used by the example. */
+	void updateGUI()
+	{
+		GUIPanel* mainPanel = gGUI->getPanel();
+
+		// Clear any existing elements, in case this is not the first time we're calling this function
+		mainPanel->clear();
+
+		// Set up strings to display
+		String materialNameLookup[] =
+		{
+			"Standard",
+			"Vertex wobble (Deferred)",
+			"Surface noise (Deferred)",
+			"Lambert BRDF (Deferred)",
+			"Surface noise & Lambert BRDF (Forward)"
+		};
+
+		HString toggleString("Press Q to toggle between materials");
+		HString currentMaterialString("Current material: {0}");
+
+		currentMaterialString.setParameter(0, materialNameLookup[gMaterialIdx]);
+
+		// Create a vertical GUI layout to align the two labels one below each other
+		GUILayoutY* vertLayout = GUILayoutY::create();
+
+		// Create a couple of GUI labels displaying the two strings we created above
+		vertLayout->addNewElement<GUILabel>(toggleString);
+		vertLayout->addNewElement<GUILabel>(currentMaterialString);
+
+		// Register the layout with the main GUI panel, placing the layout in top left corner of the screen by default
+		mainPanel->addElement(vertLayout);
+	}
+
+	/** Switches the material used for rendering the renderable object. */
+	void switchMaterial()
+	{
+		HMaterial materialLookup[] =
+		{
+			gAssets.standardMaterial,
+			gAssets.vertexMaterial,
+			gAssets.deferredSurfaceMaterial,
+			gAssets.forwardMaterial
+		};
+
+		gMaterialIdx = (gMaterialIdx + 1) % 5;
+
+		// Apply the newly selected material
+		switch(gMaterialIdx)
+		{
+		case 0:
+			// Standard material, simply apply to renderable
+			gRenderable->setMaterial(gAssets.standardMaterial);
+			break;
+		case 1:
+			// Deferred vertex material, simply apply to renderable
+			gRenderable->setMaterial(gAssets.vertexMaterial);
+			break;
+		case 2:
+			// Deferred surface material, simply apply to renderable
+			gRenderable->setMaterial(gAssets.deferredSurfaceMaterial);
+			break;
+		case 3:
+			// Deferred lighting material. Apply it globally and reset the surface material back to standard.
+			gRenderable->setMaterial(gAssets.standardMaterial);
+			ct::gRenderer()->setGlobalShaderOverride(gAssets.deferredLightingShader.getInternalPtr());
+			break;
+		case 4:
+			// Forward surface/lighting material. Simply apply to renderable. Also clear the deferred lighting material
+			// override from the last material.
+			gRenderable->setMaterial(gAssets.forwardMaterial);
+
+			// Clear previous overrides
+			const Vector<SubShader>& subShaders = gAssets.deferredLightingShader->getSubShaders();
+			for(auto& entry : subShaders)
+				ct::gRenderer()->setGlobalShaderOverride(entry.name, nullptr);
+
+			break;
+		}
+
+		// Update GUI with current material name
+		updateGUI();
+	}
+
+	/** Register relevant mouse/keyboard buttons used for controlling the example. */
+	void setupInput()
+	{
+		// Registers a default set of input controls
+		ExampleFramework::setupInputConfig();
+	
+		static VirtualButton SwitchMaterialButton("SwitchMaterial");
+
+		// Register a key for toggling between different materials
+		auto inputConfig = gVirtualInput().getConfiguration();
+		inputConfig->registerButton("SwitchMaterial", BC_Q);
+
+		gVirtualInput().onButtonUp.connect(
+			[](const VirtualButton& btn, UINT32 deviceIdx)
+		{
+			if(btn == SwitchMaterialButton)
+				switchMaterial();
+		});
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Register buttons for controlling the example 
+	setupInput();
+
+	// Load a model and textures, create materials
+	gAssets = loadAssets();
+
+	// Set up the scene with an object to render and a camera
+	setUp3DScene(gAssets);
+
+	// Sets up any GUI elements used by the example.
+	updateGUI();
+	
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 20 - 0
Examples/Source/Decals/CMakeLists.txt

@@ -0,0 +1,20 @@
+# Target
+if(WIN32)
+	add_executable(Decals WIN32 "Main.cpp")
+else()
+	add_executable(Decals "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(Decals PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(Decals Common)
+
+# Plugin dependencies
+add_engine_dependencies(Decals)
+add_dependencies(Decals bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET Decals PROPERTY FOLDER Examples)

+ 264 - 0
Examples/Source/Decals/Main.cpp

@@ -0,0 +1,264 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCSkybox.h"
+#include "Components/BsCPlaneCollider.h"
+#include "Components/BsCBoxCollider.h"
+#include "Components/BsCCharacterController.h"
+#include "Components/BsCRigidbody.h"
+#include "Physics/BsPhysicsMaterial.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "Input/BsInput.h"
+#include "Components/BsCDecal.h"
+
+// Example includes
+#include "BsExampleFramework.h"
+#include "BsFPSWalker.h"
+#include "BsFPSCamera.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example sets up a simple environment consisting of a floor and cube, and a decal projecting on both surfaces. The
+// example demonstrates how to set up decals, how decals are not shown on surfaces perpendicular to the decal direction,
+// and optionally how to use masking to only project a decal onto a certain set of surfaces.
+//
+// It also sets up necessary physical objects for collision, as well as the character collider and necessary components
+// for walking around the environment.
+//
+// The example first sets up the scene consisting of a floor, box and a skybox. Character controller is created next, 
+// as well as the camera. Components for moving the character controller and the camera are attached to allow the user to
+// control the character. It then loads the required decal textures, sets up a decal material and initializes the actual 
+// decal component. Finally the cursor is hidden and quit on Esc key press hooked up.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	constexpr float GROUND_PLANE_SCALE = 50.0f;
+
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	SPtr<Decal> decal;
+
+	/** Set up the scene used by the example, and the camera to view the world through. */
+	void setUpScene()
+	{
+		/************************************************************************/
+		/* 									ASSETS	                    		*/
+		/************************************************************************/
+
+		// Prepare all the resources we'll be using throughout this example
+
+		// Grab a couple of test textures that we'll apply to the rendered objects
+		HTexture gridPattern = ExampleFramework::loadTexture(ExampleTexture::GridPattern);
+		HTexture gridPattern2 = ExampleFramework::loadTexture(ExampleTexture::GridPattern2);
+
+		// Grab the default PBR shader
+		HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		
+		// Create a set of materials to apply to renderables used
+		HMaterial planeMaterial = Material::create(shader);
+		planeMaterial->setTexture("gAlbedoTex", gridPattern2);
+
+		// Tile the texture so every tile covers a 2x2m area
+		planeMaterial->setVec2("gUVTile", Vector2::ONE * GROUND_PLANE_SCALE * 0.5f);
+
+		// Load meshes we'll used for our rendered objects
+		HMesh planeMesh = gBuiltinResources().getMesh(BuiltinMesh::Quad);
+
+		/************************************************************************/
+		/* 									FLOOR	                    		*/
+		/************************************************************************/
+
+		// Set up renderable geometry for the floor plane
+		HSceneObject floorSO = SceneObject::create("Floor");
+		HRenderable floorRenderable = floorSO->addComponent<CRenderable>();
+		floorRenderable->setMesh(planeMesh);
+		floorRenderable->setMaterial(planeMaterial);
+
+		floorSO->setScale(Vector3(GROUND_PLANE_SCALE, 1.0f, GROUND_PLANE_SCALE));
+
+		// Add a plane collider that will prevent physical objects going through the floor
+		HPlaneCollider planeCollider = floorSO->addComponent<CPlaneCollider>();
+
+		HMaterial boxMaterial = Material::create(shader);
+		boxMaterial->setTexture("gAlbedoTex", gridPattern);
+
+		HMaterial sphereMaterial = Material::create(shader);
+
+		// Load meshes we'll used for our rendered objects
+		HMesh boxMesh = gBuiltinResources().getMesh(BuiltinMesh::Box);
+		HSceneObject boxSO = SceneObject::create("Box");
+
+		HRenderable boxRenderable = boxSO->addComponent<CRenderable>();
+		boxRenderable->setMesh(boxMesh);
+		boxRenderable->setMaterial(boxMaterial);
+
+		// Set a non-default layer for the box, so we can use it for masking on which surfaces should the decal be 
+		// projected onto
+		boxRenderable->setLayer(1 << 1);
+
+		boxSO->setPosition(Vector3(0.0f, 0.5f, 0.5f));
+
+		/************************************************************************/
+		/* 									CHARACTER                    		*/
+		/************************************************************************/
+
+		// Add physics geometry and components for character movement and physics interaction
+		HSceneObject characterSO = SceneObject::create("Character");
+		characterSO->setPosition(Vector3(0.0f, 1.0f, 5.0f));
+
+		// Add a character controller, representing the physical geometry of the character
+		HCharacterController charController = characterSO->addComponent<CCharacterController>();
+
+		// Make the character about 1.8m high, with 0.4m radius (controller represents a capsule)
+		charController->setHeight(1.0f); // + 0.4 * 2 radius = 1.8m height
+		charController->setRadius(0.4f);
+
+		// FPS walker uses default input controls to move the character controller attached to the same object
+		characterSO->addComponent<FPSWalker>();
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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);
+
+		// Add a component that allows the camera to be rotated using the mouse
+		sceneCameraSO->setRotation(Quaternion(Degree(-10.0f), Degree(0.0f), Degree(0.0f)));
+		HFPSCamera fpsCamera = sceneCameraSO->addComponent<FPSCamera>();
+
+		// Set the character controller on the FPS camera, so the component can apply yaw rotation to it
+		fpsCamera->setCharacter(characterSO);
+
+		// Make the camera a child of the character scene object, and position it roughly at eye level
+		sceneCameraSO->setParent(characterSO);
+		sceneCameraSO->setPosition(Vector3(0.0f, 1.8f * 0.5f - 0.1f, -2.0f));
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Load a skybox texture
+		HTexture skyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentDaytime, false, true, true);
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(skyCubemap);
+
+		/************************************************************************/
+		/* 									DECAL                       		*/
+		/************************************************************************/
+
+		// Load the decal textures
+		HTexture decalAlbedoTex = ExampleFramework::loadTexture(ExampleTexture::DecalAlbedo);
+		HTexture decalNormalTex = ExampleFramework::loadTexture(ExampleTexture::DecalNormal, false);
+
+		// Create a material using the built-in decal shader and assign the textures
+		HShader decalShader = gBuiltinResources().getBuiltinShader(BuiltinShader::Decal);
+		HMaterial decalMaterial = Material::create(decalShader);
+		decalMaterial->setTexture("gAlbedoTex", decalAlbedoTex);
+		decalMaterial->setTexture("gNormalTex", decalNormalTex);
+
+		decalMaterial->setVariation(ShaderVariation(
+			{
+				// Use the default, transparent blend mode that uses traditional PBR textures to project. Normally no need
+				// to set the default explicitly but it's done here for example purposes. See the manual for all available 
+				// modes
+				ShaderVariation::Param("BLEND_MODE", 0)
+			})
+		);
+
+		// Create the decal scene object, position and orient it, facing down
+		HSceneObject decalSO = SceneObject::create("Decal");
+		decalSO->setPosition(Vector3(0.0f, 6.0f, 1.0f));
+		decalSO->lookAt(Vector3(0.0f, 0.0f, 1.0f));
+
+		// Set the material to project
+		HDecal decal = decalSO->addComponent<CDecal>();
+		decal->setMaterial(decalMaterial);
+
+		// Optionally set a mask to only project onto elements with layer 1 set (in this case this is the floor since we
+		// changed the default layer for the box)
+		// decal->setLayerMask(1);
+
+		/************************************************************************/
+		/* 									INPUT                       		*/
+		/************************************************************************/
+
+		// Hook up input that launches a sphere when user clicks the mouse, and Esc key to quit
+		gInput().onButtonUp.connect([=](const ButtonEvent& ev)
+		{
+			if(ev.buttonCode == BC_ESCAPE)
+			{
+				// Quit the application when Escape key is pressed
+				gApplication().quitRequested();
+			}
+		});
+
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Registers a default set of input controls
+	ExampleFramework::setupInputConfig();
+
+	// Set up the scene with an object to render and a camera
+	setUpScene();
+
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 23 - 0
Examples/Source/GUI/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(GUI WIN32 "Main.cpp")
+else()
+	add_executable(GUI "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(GUI PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(GUI Common)
+
+# Plugin dependencies
+add_engine_dependencies(GUI)
+add_dependencies(GUI bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET GUI PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(GUI)

+ 324 - 0
Examples/Source/GUI/Main.cpp

@@ -0,0 +1,324 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "GUI/BsCGUIWidget.h"
+#include "GUI/BsGUIPanel.h"
+#include "GUI/BsGUILayoutX.h"
+#include "GUI/BsGUILayoutY.h"
+#include "GUI/BsGUILabel.h"
+#include "GUI/BsGUIButton.h"
+#include "GUI/BsGUIInputBox.h"
+#include "GUI/BsGUIListBox.h"
+#include "GUI/BsGUIToggle.h"
+#include "GUI/BsGUIScrollArea.h"
+#include "GUI/BsGUISpace.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "BsExampleFramework.h"
+#include "Image/BsSpriteTexture.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example demonstrates how to set up a graphical user interface. It demoes a variety of common GUI elements, as well
+// as demonstrating the capability of layouts. It also shows how to customize the look of GUI elements. 
+//
+// The example starts off by setting up a camera, which is required for any kind of rendering, including GUI. It then
+// proceeds to demonstrate a set of basic controls, while using manual positioning. It then shows how to create a custom
+// style and apply it to a GUI element. It follows to demonstrate the concept of layouts that automatically position
+// and size elements, as well as scroll areas. Finally, it demonstrates a more complex example of creating a custom style,
+// by creating a button with custom textures and font.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Set up the GUI elements and the camera. */
+	void setUpGUI()
+	{
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(window);
+
+		// Pick a prettier background color
+		Color gray = Color(51/255.0f, 51/255.0f, 51/255.0f);
+		sceneCamera->getViewport()->setClearColorValue(gray);
+
+		// Let the camera know it will be used for overlay rendering only. This stops the renderer from running potentially
+		// expensive effects that ultimately don't effect anything. It also allows us to use a linear-space color for the
+		// camera background (normal rendering expects colors in gamma space, which is unintuitive for aspects such as 
+		// GUI).
+		const SPtr<RenderSettings>& renderSettings = sceneCamera->getRenderSettings();
+		renderSettings->overlayOnly = true;
+		sceneCamera->setRenderSettings(renderSettings);
+
+		/************************************************************************/
+		/* 									GUI		                     		*/
+		/************************************************************************/
+		// Add a GUIWidget component we will use for rendering the GUI
+		HSceneObject guiSO = SceneObject::create("GUI");
+		HGUIWidget gui = guiSO->addComponent<CGUIWidget>(sceneCamera);
+
+		// Retrieve the primary panel onto which to attach GUI elements to. Panels allow free placement of elements in
+		// them (unlike layouts), and can also have depth, meaning you can overlay multiple panels over one another.
+		GUIPanel* mainPanel = gui->getPanel();
+
+		////////////////////// Add a variety of GUI controls /////////////////////
+		// Clickable button with a textual label
+		GUIButton* button = mainPanel->addNewElement<GUIButton>(HString("Click me!"));
+		button->onClick.connect([]()
+		{
+			// Log a message when the user clicks the button
+			LOGDBG("Button clicked!");
+		});
+
+		button->setPosition(10, 50);
+		button->setSize(100, 30);
+
+		// Toggleable button
+		GUIToggle* toggle = mainPanel->addNewElement<GUIToggle>(HString(""));
+		toggle->onToggled.connect([](bool enabled)
+		{
+			// Log a message when the user toggles the button
+			if(enabled)
+			{
+				LOGDBG("Toggle turned on");
+			}
+			else
+			{
+				LOGDBG("Toggle turned off");
+			}
+		});
+
+		toggle->setPosition(10, 90);
+
+		// Add non-interactable label next to the toggle
+		GUILabel* toggleLabel = mainPanel->addNewElement<GUILabel>(HString("Toggle me!"));
+		toggleLabel->setPosition(30, 92);
+
+		// Single-line box in which user can input text into
+		GUIInputBox* inputBox = mainPanel->addNewElement<GUIInputBox>();
+		inputBox->onValueChanged.connect([](const String& value)
+		{
+			// Log a message when the user enters new text in the input box
+			LOGDBG("User entered: \"" + value + "\"");
+		});
+
+		inputBox->setText("Type in me...");
+		inputBox->setPosition(10, 115);
+		inputBox->setWidth(100);
+
+		// List box allowing you to select one of the specified elements
+		Vector<HString> listBoxElements =
+		{
+			HString("Blue"),
+			HString("Black"),
+			HString("Green"),
+			HString("Orange")
+		};
+
+		GUIListBox* listBox = mainPanel->addNewElement<GUIListBox>(listBoxElements);
+		listBox->onSelectionToggled.connect([listBoxElements](UINT32 idx, bool enabled)
+		{
+			// Log a message when the user selects a new element
+			LOGDBG("User selected element: \"" + listBoxElements[idx].getValue() + "\"");
+			
+		});
+
+		listBox->setPosition(10, 140);
+		listBox->setWidth(100);
+
+		// Add a button with an image
+		HTexture icon = ExampleFramework::loadTexture(ExampleTexture::GUIBansheeIcon, false, false, false, false);
+		HSpriteTexture iconSprite = SpriteTexture::create(icon);
+
+		// Create a GUI content object that contains an icon to display on the button. Also an optional text and tooltip.
+		GUIContent buttonContent(iconSprite);
+		GUIButton* iconButton = mainPanel->addNewElement<GUIButton>(buttonContent);
+
+		iconButton->setPosition(10, 170);
+		iconButton->setSize(70, 70);
+
+		/////////////////////////// Header label /////////////////////////////////
+		// Create a custom style for a label we'll used for headers. Then add a header
+		// for the controls we added in the previous section.
+
+		// Create a new style
+		GUIElementStyle headerLabelStyle;
+
+		// Make it use a custom font with size 30
+		headerLabelStyle.font = ExampleFramework::loadFont(ExampleFont::SegoeUISemiBold, { 24 });
+		headerLabelStyle.fontSize = 24;
+
+		// Set the default text color
+		headerLabelStyle.normal.textColor = Color::White;
+
+		// Grab the default GUI skin to which we'll append the new style to. You could also create a new GUI skin and
+		// add the style to it, but that would also require adding default styles for all the GUI element types.
+		HGUISkin skin = gBuiltinResources().getGUISkin();
+		skin->setStyle("HeaderLabelStyle", headerLabelStyle);
+
+		// Create and position the label
+		GUILabel* basicControlsLbl = mainPanel->addNewElement<GUILabel>(HString("Basic controls"), "HeaderLabelStyle");
+		basicControlsLbl->setPosition(10, 10);
+
+		///////////////////////////  vertical layout /////////////////////////
+		// Use a vertical layout to automatically position GUI elements. This is unlike above where we position and
+		// sized all elements manually.
+		GUILayoutY* vertLayout = mainPanel->addNewElement<GUILayoutY>();
+
+		// Add five buttons to the layout
+		for(UINT32 i = 0; i < 5; i++)
+		{
+			vertLayout->addNewElement<GUIButton>(HString("Click me!"));
+
+			// Add a 10 pixel spacing between each button
+			vertLayout->addNewElement<GUIFixedSpace>(10);
+		}
+
+		// Add a flexible space ensuring all the elements get pushed to the top of the layout
+		vertLayout->addNewElement<GUIFlexibleSpace>();
+
+		// Position the layout relative to the main panel, and limit width to 100 pixels
+		vertLayout->setPosition(350, 50);
+		vertLayout->setWidth(100);
+
+		// Add a header
+		GUILabel* vertLayoutLbl = mainPanel->addNewElement<GUILabel>(HString("Vertical layout"), "HeaderLabelStyle");
+		vertLayoutLbl->setPosition(300, 10);
+
+		////////////////////////// Horizontal layout ///////////////////////
+		// Use a horizontal layout to automatically position GUI elements
+		GUILayoutX* horzLayout = mainPanel->addNewElement<GUILayoutX>();
+		horzLayout->addNewElement<GUIFlexibleSpace>();
+
+		// Add vive buttons to the layout
+		for(UINT32 i = 0; i < 5; i++)
+		{
+			horzLayout->addNewElement<GUIButton>(HString("Click me!"));
+			horzLayout->addNewElement<GUIFlexibleSpace>();
+		}
+
+		// Position the layout relative to the main panel, and limit the height to 30 pixels
+		horzLayout->setPosition(0, 340);
+		horzLayout->setHeight(30);
+
+		// Add a header
+		GUILabel* horzLayoutLbl = mainPanel->addNewElement<GUILabel>(HString("Horizontal layout"), "HeaderLabelStyle");
+		horzLayoutLbl->setPosition(10, 300);
+
+		//////////////////////////// Scroll area ///////////////////////
+		// Container GUI element that allows scrolling if the number of elements inside the area are larger than the visible
+		// area
+		GUIScrollArea* scrollArea = mainPanel->addNewElement<GUIScrollArea>();
+
+		// Scroll areas have a vertical layout we can append elements to, same as with a normal layout
+		GUILayout& scrollLayout = scrollArea->getLayout();
+
+		for(UINT32 i = 0; i < 15; i++)
+			scrollLayout.addNewElement<GUIButton>(HString("Click me!"));
+
+		scrollArea->setPosition(565, 50);
+		scrollArea->setSize(130, 200);
+
+		// Add a header
+		GUILabel* scrollAreaLbl = mainPanel->addNewElement<GUILabel>(HString("Scroll area"), "HeaderLabelStyle");
+		scrollAreaLbl->setPosition(550, 10);
+
+		///////////////////////////// Button using a custom style ///////////////////
+		HTexture buttonNormalTex = ExampleFramework::loadTexture(ExampleTexture::GUIExampleButtonNormal, false, false, 
+			false, false);
+		HTexture buttonHoverTex = ExampleFramework::loadTexture(ExampleTexture::GUIExampleButtonHover, false, false,  
+			false, false);
+		HTexture buttonActiveTex = ExampleFramework::loadTexture(ExampleTexture::GUIExampleButtonActive, false, false, 
+			false, false);
+
+		// Create a new style
+		GUIElementStyle customBtnStyle;
+
+		// Set custom textures for 'normal', 'hover' and 'active' states of the button
+		customBtnStyle.normal.texture = SpriteTexture::create(buttonNormalTex);
+		customBtnStyle.hover.texture = SpriteTexture::create(buttonHoverTex);
+		customBtnStyle.active.texture = SpriteTexture::create(buttonActiveTex);
+
+		// Button size is fixed, and should match the size of the texture's we're using
+		customBtnStyle.fixedHeight = true;
+		customBtnStyle.fixedWidth = true;
+		customBtnStyle.width = buttonNormalTex->getProperties().getWidth();
+		customBtnStyle.height = buttonNormalTex->getProperties().getHeight();
+
+		// Make the button use a custom font for text
+		customBtnStyle.font = ExampleFramework::loadFont(ExampleFont::SegoeUILight, { 24 });
+		customBtnStyle.fontSize = 24;
+
+		// Offset the position of the text within the button, to match the texture
+		customBtnStyle.contentOffset.top = 20;
+		customBtnStyle.contentOffset.left = 15;
+		customBtnStyle.contentOffset.right = 65;
+
+		skin->setStyle("CustomButtonStyle", customBtnStyle);
+
+		// Create the button that uses the custom style
+		GUIButton* customButton = mainPanel->addNewElement<GUIButton>(HString("Click me!"), "CustomButtonStyle");
+		customButton->setPosition(800, 50);
+
+		// Add a header
+		GUILabel* customButtonLbl = mainPanel->addNewElement<GUILabel>(HString("Custom button"), "HeaderLabelStyle");
+		customButtonLbl->setPosition(800, 10);
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Load a resource manifest so previously saved Fonts can find their child Texture resources
+	ExampleFramework::loadResourceManifest();
+
+	// Set up the GUI elements
+	setUpGUI();
+
+	// Save the manifest, in case we did any asset importing during the setup stage
+	ExampleFramework::saveResourceManifest();
+
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 23 - 0
Examples/Source/LowLevelRendering/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(LowLevelRendering WIN32 "Main.cpp")
+else()
+	add_executable(LowLevelRendering "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(LowLevelRendering PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")	
+	
+# Libraries
+## Local libs
+target_link_libraries(LowLevelRendering Common)
+
+# Plugin dependencies
+add_engine_dependencies(LowLevelRendering)
+add_dependencies(LowLevelRendering bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET LowLevelRendering PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(LowLevelRendering)

+ 616 - 0
Examples/Source/LowLevelRendering/Main.cpp

@@ -0,0 +1,616 @@
+#include "BsApplication.h"
+#include "Material/BsMaterial.h"
+#include "CoreThread/BsCoreThread.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "RenderAPI/BsCommandBuffer.h"
+#include "RenderAPI/BsGpuProgram.h"
+#include "RenderAPI/BsGpuPipelineState.h"
+#include "RenderAPI/BsBlendState.h"
+#include "RenderAPI/BsDepthStencilState.h"
+#include "RenderAPI/BsGpuParamBlockBuffer.h"
+#include "RenderAPI/BsIndexBuffer.h"
+#include "RenderAPI/BsVertexDataDesc.h"
+#include "Mesh/BsMeshData.h"
+#include "Math/BsQuaternion.h"
+#include "Utility/BsTime.h"
+#include "Renderer/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
+// bsf'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 destination 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
+		{
+			// Queue the method for execution on the core thread
+			gCoreThread().queueCommand(&ct::shutdown);
+
+			// Shut-down engine components
+			Application::onShutDown();
+		}
+
+		// Called every frame, before any other engine system (optionally use postUpdate())
+		void preUpdate() override
+		{
+			// Queue the method for execution on the core thread
+			gCoreThread().queueCommand(&ct::render);
+
+			// Call the default version of this method to handle normal functionality
+			Application::preUpdate();
+		}
+	};
+}
+
+// Main entry point into the application
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// 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, "bsf 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();
+
+	// Clean up when done
+	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;
+	bool gUseHLSL = true;
+	bool gUseVKSL = false;
+
+	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
+	};
+
+	// Initializes any resources required for rendering
+	void setup(const SPtr<RenderWindow>& renderWindow)
+	{
+		// Determine which shading language to use (depending on the RenderAPI chosen during build)
+		gUseHLSL = strcmp(BS_RENDER_API_MODULE, "bsfD3D11RenderAPI") == 0;
+		gUseVKSL = strcmp(BS_RENDER_API_MODULE, "bsfVulkanRenderAPI") == 0;
+
+		// This will be the primary output for our rendering (created by the main thread on start-up)
+		gRenderWindow = renderWindow;
+
+		// Create a vertex GPU program
+		const char* vertProgSrc = getVertexProgSource();
+
+		GPU_PROGRAM_DESC vertProgDesc;
+		vertProgDesc.type = GPT_VERTEX_PROGRAM;
+		vertProgDesc.entryPoint = "main";
+		vertProgDesc.language = gUseHLSL ? "hlsl" : gUseVKSL ? "vksl" : "glsl4_1";
+		vertProgDesc.source = vertProgSrc;
+
+		SPtr<GpuProgram> vertProg = GpuProgram::create(vertProgDesc);
+
+		// Create a fragment GPU program
+		const char* fragProgSrc = getFragmentProgSource();
+
+		GPU_PROGRAM_DESC fragProgDesc;
+		fragProgDesc.type = GPT_FRAGMENT_PROGRAM;
+		fragProgDesc.entryPoint = "main";
+		fragProgDesc.language = gUseHLSL ? "hlsl" : gUseVKSL ? "vksl" : "glsl4_1";
+		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_RGBA8);
+		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_RGBA8;
+		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(gUseHLSL)
+			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, 0, RT_NONE, cmds);
+		rapi.clearRenderTarget(FBT_COLOR | FBT_DEPTH, Color::Blue, 1, 0, 0xFF, cmds);
+
+		// Bind the pipeline state
+		rapi.setGraphicsPipeline(gPipelineState, 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);
+
+		// Bind the GPU program parameters (i.e. resource descriptors)
+		rapi.setGpuParams(gGpuParams, cmds);
+
+		// Draw
+		rapi.drawIndexed(0, NUM_INDICES, 0, NUM_VERTICES, 1, cmds);
+
+		// Submit the command buffer
+		rapi.submitCommandBuffer(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);
+
+		// 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::Corner 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(gUseHLSL)
+		{
+			static const 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 if(gUseVKSL)
+		{
+			static const 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;
+		}
+		else
+		{
+			static const char* src = R"(
+layout (std140) uniform Params
+{
+	mat4 gMatWVP;
+	vec4 gTint;
+};
+
+in vec3 bs_position;
+in vec2 bs_texcoord0;
+
+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 (gUseHLSL)
+		{
+			static const 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 if(gUseVKSL)
+		{
+			static const 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;
+		}
+		else
+		{
+
+			static const char* src = R"(
+layout (std140) uniform Params
+{
+	mat4 gMatWVP;
+	vec4 gTint;
+};
+
+uniform sampler2D gMainTexture;
+
+in vec2 texcoord0;
+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(!gUseHLSL)
+			viewProj = viewProj.transpose();
+
+		return viewProj;
+	}
+}}

+ 20 - 0
Examples/Source/Particles/CMakeLists.txt

@@ -0,0 +1,20 @@
+# Target
+if(WIN32)
+	add_executable(Particles WIN32 "Main.cpp")
+else()
+	add_executable(Particles "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(Particles PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(Particles Common)
+
+# Plugin dependencies
+add_engine_dependencies(Particles)
+add_dependencies(Particles bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET Particles PROPERTY FOLDER Examples)

+ 666 - 0
Examples/Source/Particles/Main.cpp

@@ -0,0 +1,666 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCLight.h"
+#include "Components/BsCSkybox.h"
+#include "Components/BsCPlaneCollider.h"
+#include "Components/BsCCharacterController.h"
+#include "Components/BsCParticleSystem.h"
+#include "Image/BsSpriteTexture.h"
+#include "Particles/BsParticleSystem.h"
+#include "Particles/BsParticleEmitter.h"
+#include "Particles/BsParticleEvolver.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "Platform/BsCursor.h"
+#include "Input/BsInput.h"
+#include "Utility/BsTime.h"
+
+// Example includes
+#include "BsExampleFramework.h"
+#include "BsFPSWalker.h"
+#include "BsFPSCamera.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example sets up an environment with three particle systems:
+// - Smoke effect using traditional billboard particles
+// - 3D particles with support for world collisions and lighting
+// - GPU particle simulation with a vector field
+//
+// It also sets up necessary physical objects for collision, as well as the character collider and necessary components
+// for walking around the environment.
+//
+// The example first loads necessary resources, including textures and materials. Then it set up the scene, consisting of a
+// floor and a skybox. Character controller is created next, as well as the camera. Components for moving the character 
+// controller and the camera are attached to allow the user to control the character. Finally it sets up three separate
+// particle systems, their creation wrapped in their own creation methods. Finally the cursor is hidden and quit on Esc
+// key press hooked up.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	constexpr float GROUND_PLANE_SCALE = 50.0f;
+
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	// Set up a helper component that makes the object its attached to orbit a point. This is used by the 3D particle
+	// system for moving its light.
+	class LightOrbit : public Component
+	{
+	public:
+		LightOrbit(const HSceneObject& parent, float radius)
+			:Component(parent), mRadius(radius)
+		{ }
+
+		void onInitialized() override
+		{
+			mCenter = SO()->getTransform().getPosition();
+		}
+
+		void update() override
+		{
+			Vector3 position = mCenter + mRadius * Vector3(Math::cos(mAngle), 0.0f, Math::sin(mAngle));
+
+			mAngle += Degree(gTime().getFrameDelta() * 90.0f);
+
+			SO()->setWorldPosition(position);
+		}
+
+	private:
+		Degree mAngle = Degree(0.0f);
+		Vector3 mCenter;
+		float mRadius;
+	};
+
+	/** Container for all assets used by the particles systems in this example. */
+	struct ParticleSystemAssets
+	{
+		// Smoke particle system assets
+		HSpriteTexture smokeTex;
+		HMaterial smokeMat;
+
+		// 3D particle system assets
+		HMesh sphereMesh;
+		HMaterial particles3DMat;
+		HMaterial lightMat;
+
+		// GPU particle system assets
+		HMaterial litParticleEmissiveMat;
+		HVectorField vectorField;
+	};
+
+	/** Load the assets used by the particle systems. */
+	ParticleSystemAssets loadParticleSystemAssets()
+	{
+		ParticleSystemAssets assets;
+
+		// Smoke particle system assets
+		//// Import the texture and set up a sprite texture so we can animate it
+		HTexture smokeTex = ExampleFramework::loadTexture(ExampleTexture::ParticleSmoke);
+		assets.smokeTex = SpriteTexture::create(smokeTex);
+
+		//// Set up sprite sheet animation on the sprite texture
+		SpriteSheetGridAnimation smokeGridAnim(5, 6, 30, 30);
+		assets.smokeTex->setAnimation(smokeGridAnim);
+		assets.smokeTex->setAnimationPlayback(SpriteAnimationPlayback::None);
+
+		//// Set up a shader without lighting and enable soft particle rendering
+		HShader particleUnlitShader = gBuiltinResources().getBuiltinShader(BuiltinShader::ParticlesUnlit);
+		assets.smokeMat = Material::create(particleUnlitShader);
+		assets.smokeMat->setVariation(ShaderVariation(
+			{
+				ShaderVariation::Param("SOFT", true)
+			})
+		);
+
+		//// Fade over the range of 2m (used for soft particle blending)
+		assets.smokeMat->setFloat("gInvDepthRange", 1.0f / 2.0f);
+		assets.smokeMat->setSpriteTexture("gTexture", assets.smokeTex);
+
+		// Set up an emissive material used in the GPU vector field example
+		HShader particleLitShader = gBuiltinResources().getBuiltinShader(BuiltinShader::ParticlesLitOpaque);
+		assets.litParticleEmissiveMat = Material::create(particleLitShader);
+		assets.litParticleEmissiveMat->setTexture("gEmissiveMaskTex", gBuiltinResources().getTexture(BuiltinTexture::White));
+		assets.litParticleEmissiveMat->setColor("gEmissiveColor", Color::White * 10.0f);
+
+		// 3D particle system assets
+		//// Create another lit material using a plain white albedo texture
+		assets.particles3DMat = Material::create(particleLitShader);
+		assets.particles3DMat->setTexture("gAlbedoTex", gBuiltinResources().getTexture(BuiltinTexture::White));
+
+		//// Create a material used for rendering the light sphere itself
+		HShader standardShader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		assets.lightMat = Material::create(standardShader);
+		assets.lightMat->setTexture("gEmissiveMaskTex", gBuiltinResources().getTexture(BuiltinTexture::White));
+		assets.lightMat->setColor("gEmissiveColor", Color::Red * 5.0f);
+
+		//// Import a vector field used in the GPU simulation
+		assets.vectorField = ExampleFramework::loadResource<VectorField>(ExampleResource::VectorField);
+
+		//// Import a sphere mesh used for the 3D particles and the light sphere
+		assets.sphereMesh = gBuiltinResources().getMesh(BuiltinMesh::Sphere);
+
+		return assets;
+	}
+
+	void setupGPUParticleEffect(const Vector3& pos, const ParticleSystemAssets& assets);
+	void setup3DParticleEffect(const Vector3& pos, const ParticleSystemAssets& assets);
+	void setupSmokeEffect(const Vector3& pos, const ParticleSystemAssets& assets);
+
+	/** Set up the scene used by the example, and the camera to view the world through. */
+	void setUpScene()
+	{
+		/************************************************************************/
+		/* 									ASSETS	                    		*/
+		/************************************************************************/
+
+		// Prepare the assets required for the scene and background
+
+		// Grab a texture used for rendering the ground
+		HTexture gridPattern = ExampleFramework::loadTexture(ExampleTexture::GridPattern2);
+
+		// Grab the default PBR shader
+		HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		
+		// Create a material for rendering the ground and apply the ground texture
+		HMaterial planeMaterial = Material::create(shader);
+		planeMaterial->setTexture("gAlbedoTex", gridPattern);
+
+		// Tile the texture so every tile covers a 2x2m area
+		planeMaterial->setVec2("gUVTile", Vector2::ONE * GROUND_PLANE_SCALE * 0.5f);
+
+		// Load the floor mesh
+		HMesh planeMesh = gBuiltinResources().getMesh(BuiltinMesh::Quad);
+
+		// Load assets used by the particle systems
+		ParticleSystemAssets assets = loadParticleSystemAssets();
+
+		/************************************************************************/
+		/* 									FLOOR	                    		*/
+		/************************************************************************/
+
+		// Set up renderable geometry for the floor plane
+		HSceneObject floorSO = SceneObject::create("Floor");
+		HRenderable floorRenderable = floorSO->addComponent<CRenderable>();
+		floorRenderable->setMesh(planeMesh);
+		floorRenderable->setMaterial(planeMaterial);
+
+		floorSO->setScale(Vector3(GROUND_PLANE_SCALE, 1.0f, GROUND_PLANE_SCALE));
+
+		// Add a plane collider that will prevent physical objects going through the floor
+		HPlaneCollider planeCollider = floorSO->addComponent<CPlaneCollider>();
+
+		/************************************************************************/
+		/* 									CHARACTER                    		*/
+		/************************************************************************/
+
+		// Add physics geometry and components for character movement and physics interaction
+		HSceneObject characterSO = SceneObject::create("Character");
+		characterSO->setPosition(Vector3(0.0f, 1.0f, 5.0f));
+
+		// Add a character controller, representing the physical geometry of the character
+		HCharacterController charController = characterSO->addComponent<CCharacterController>();
+
+		// Make the character about 1.8m high, with 0.4m radius (controller represents a capsule)
+		charController->setHeight(1.0f); // + 0.4 * 2 radius = 1.8m height
+		charController->setRadius(0.4f);
+
+		// FPS walker uses default input controls to move the character controller attached to the same object
+		characterSO->addComponent<FPSWalker>();
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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);
+
+		// Add a component that allows the camera to be rotated using the mouse
+		sceneCameraSO->setRotation(Quaternion(Degree(-10.0f), Degree(0.0f), Degree(0.0f)));
+		HFPSCamera fpsCamera = sceneCameraSO->addComponent<FPSCamera>();
+
+		// Set the character controller on the FPS camera, so the component can apply yaw rotation to it
+		fpsCamera->setCharacter(characterSO);
+
+		// Make the camera a child of the character scene object, and position it roughly at eye level
+		sceneCameraSO->setParent(characterSO);
+		sceneCameraSO->setPosition(Vector3(0.0f, 1.8f * 0.5f - 0.1f, -2.0f));
+
+		// Enable Bloom effect so that emissive materials look better
+		auto rs = sceneCamera->getRenderSettings();
+		rs->bloom.enabled = true;
+		rs->bloom.intensity = 0.1f;
+		rs->bloom.threshold = 5.0f;
+		rs->bloom.quality = 3;
+
+		sceneCamera->setRenderSettings(rs);
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Load a skybox texture
+		HTexture skyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentDaytime, false, true, true);
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(skyCubemap);
+
+		/************************************************************************/
+		/* 								PARTICLES                       		*/
+		/************************************************************************/
+
+		// Set up different particle systems
+		setup3DParticleEffect(Vector3(-5.0f, 1.0f, 0.0f), assets);
+		setupGPUParticleEffect(Vector3(0.0f, 1.0f, 0.0f), assets);
+		setupSmokeEffect(Vector3(5.0f, 0.0f, 0.0f), assets);
+
+		/************************************************************************/
+		/* 									CURSOR                       		*/
+		/************************************************************************/
+
+		// Hide and clip the cursor, since we only use the mouse movement for camera rotation
+		Cursor::instance().hide();
+		Cursor::instance().clipToWindow(*window);
+
+		/************************************************************************/
+		/* 									INPUT                       		*/
+		/************************************************************************/
+
+		// Hook up Esc key to quit
+		gInput().onButtonUp.connect([=](const ButtonEvent& ev)
+		{
+			if(ev.buttonCode == BC_ESCAPE)
+			{
+				// Quit the application when Escape key is pressed
+				gApplication().quitRequested();
+			}
+		});
+	}
+
+	/** 
+	 * Sets up a particle system using traditional billboard particles to render a smoke effect. The particles are emitted 
+	 * from the base and distributed towards a cone shape. After emission particle color, size and velocity is modified
+	 * through particle evolvers.
+	 */
+	void setupSmokeEffect(const Vector3& pos, const ParticleSystemAssets& assets)
+	{
+		// Create the particle system scene object and position/orient it
+		HSceneObject particleSystemSO = SceneObject::create("Smoke");
+		particleSystemSO->setPosition(pos);
+		particleSystemSO->setRotation(Quaternion(Degree(0), Degree(90), Degree(90)));
+
+		// Add a particle system component
+		HParticleSystem particleSystem = particleSystemSO->addComponent<CParticleSystem>();
+
+		// Set up the emitter
+		SPtr<ParticleEmitter> emitter = bs_shared_ptr_new<ParticleEmitter>();
+
+		// All newly spawned particles will have the size of 1m
+		emitter->setInitialSize(1.0f);
+
+		// 20 particles will be emitted per second
+		emitter->setEmissionRate(20.0f);
+
+		// Particles will initially move at a rate of 1m/s
+		emitter->setInitialSpeed(1.0f);
+
+		// Particles will live for exactly 5 seconds
+		emitter->setInitialLifetime(5.0f);
+
+		// Particles will initially have no tint
+		emitter->setInitialColor(Color::White);
+
+		// Set up a shape that determines the position and initial travel direction of newly spawned particles. In this
+		// case we're using a cone shape.
+		PARTICLE_CONE_SHAPE_DESC coneShape;
+
+		// All particles will spawn at the narrow point in the cone (position doesn't vary)
+		coneShape.type = ParticleEmitterConeType::Base;
+
+		// The particle travel direction will be in the 10 degrees spawned by the cone
+		coneShape.angle = Degree(10.0f);
+
+		// Assign the shape to the emitter
+		emitter->setShape(ParticleEmitterConeShape::create(coneShape));
+
+		// Assign the emitter to the particle system
+		particleSystem->setEmitters({emitter});
+
+		// Set up evolvers that will modify the particle systems over its lifetime
+		Vector<SPtr<ParticleEvolver>> evolvers;
+
+		// Animate particle texture - this uses the sprite sheet animation set up during the asset loading step
+		PARTICLE_TEXTURE_ANIMATION_DESC texAnimDesc;
+
+		// Perform one animation cycle during the particle lifetime
+		texAnimDesc.numCycles = 1;
+
+		// Create and add the texture animation evolver
+		SPtr<ParticleEvolver> texAnimEvolver = bs_shared_ptr_new<ParticleTextureAnimation>(texAnimDesc);
+		evolvers.push_back(texAnimEvolver);
+
+		// Scale particles from size 1 to size 4 over their lifetime
+		PARTICLE_SIZE_DESC sizeDesc;
+		sizeDesc.size = TAnimationCurve<float>(
+			{
+				TKeyframe<float>{1.0f, 0.0f, 1.0f, 0.0f},
+				TKeyframe<float>{4.0f, 1.0f, 0.0f, 1.0f},
+			});
+
+		// Create and add the size evolver
+		SPtr<ParticleEvolver> sizeEvolver = bs_shared_ptr_new<ParticleSize>(sizeDesc);
+		evolvers.push_back(sizeEvolver);
+
+		// Modify particle tint from white (no tint) to dark gray over first 40% of their lifetime
+		PARTICLE_COLOR_DESC colorDesc;
+		colorDesc.color = ColorGradient(
+			{
+				ColorGradientKey(Color::White, 0.0f),
+				ColorGradientKey(Color(0.1f, 0.1f, 0.1f, 1.0f), 0.4f)
+			}
+		);
+
+		// Create and add the color evolver
+		SPtr<ParticleEvolver> colorEvolver = bs_shared_ptr_new<ParticleColor>(colorDesc);
+		evolvers.push_back(colorEvolver);
+
+		// Apply force moving the particles to the right
+		PARTICLE_FORCE_DESC forceDesc;
+		forceDesc.force = TAnimationCurve<Vector3>(
+			{
+				TKeyframe<Vector3>{Vector3::ZERO, Vector3::ZERO, Vector3::ONE, 0.0f},
+				TKeyframe<Vector3>{Vector3(100.0f, 0.0f, 0.0f), -Vector3::ONE, Vector3::ZERO, 0.5f},
+			});
+
+		// Lets the system know the provided force direction is in world space
+		forceDesc.worldSpace = true;
+
+		// Create and add the force evolver
+		SPtr<ParticleEvolver> forceEvolver = bs_shared_ptr_new<ParticleForce>(forceDesc);
+		evolvers.push_back(forceEvolver);
+
+		// Register all the evolvers with the particle system
+		particleSystem->setEvolvers(evolvers);
+
+		// Set up general particle system settings
+		ParticleSystemSettings psSettings;
+
+		// Orient the particles towards the camera plane (standard for billboard particles)
+		psSettings.orientation = ParticleOrientation::ViewPlane;
+
+		// But lock the Y orientation
+		psSettings.orientationLockY = true;
+
+		// Sort based on distance from the camera so that transparency looks appropriate
+		psSettings.sortMode = ParticleSortMode::Distance;
+
+		// Assign the material we created earlier
+		psSettings.material = assets.smokeMat;
+
+		// And actually apply the settings
+		particleSystem->setSettings(psSettings);
+	}
+
+	/** 
+	 * Sets up a particle system using 3D mesh particles. The particles support lighting which is demonstrated via an
+	 * addition of an orbiting point light. Once emitted the particles are evolved through the gravity evolver, ensuring
+	 * they fall down. After which they collide with the ground plane by using the collider evolver.
+	 */
+	void setup3DParticleEffect(const Vector3& pos, const ParticleSystemAssets& assets)
+	{
+		// Create the particle system scene object and position/orient it
+		HSceneObject particleSystemSO = SceneObject::create("3D particles");
+		particleSystemSO->setPosition(pos);
+		particleSystemSO->setRotation(Quaternion(Degree(0), Degree(90), Degree(0)));
+
+		// Add a particle system component
+		HParticleSystem particleSystem = particleSystemSO->addComponent<CParticleSystem>();
+
+		// Set up the emitter
+		SPtr<ParticleEmitter> emitter = bs_shared_ptr_new<ParticleEmitter>();
+
+		// All newly spawned particles will have the size of 2cm
+		emitter->setInitialSize(0.02f);
+
+		// 50 particles will be emitted per second
+		emitter->setEmissionRate(50.0f);
+
+		// Particles will initially move at a rate of 1m/s
+		emitter->setInitialSpeed(1.0f);
+
+		// Particles will live for exactly 5 seconds
+		emitter->setInitialLifetime(5.0f);
+
+		// Set up a shape that determines the position and initial travel direction of newly spawned particles. In this
+		// case we're using a cone shape.
+		PARTICLE_CONE_SHAPE_DESC coneShape;
+
+		// All particles will spawn at the narrow point in the cone (position doesn't vary)
+		coneShape.type = ParticleEmitterConeType::Base;
+
+		// The particle travel direction will be in the 45 degrees spawned by the cone
+		coneShape.angle = Degree(45.0f);
+
+		// Assign the shape to the emitter
+		emitter->setShape(ParticleEmitterConeShape::create(coneShape));
+
+		// Assign the emitter to the particle system
+		particleSystem->setEmitters({emitter});
+
+		// Set up evolvers that will modify the particle systems over its lifetime
+		Vector<SPtr<ParticleEvolver>> evolvers;
+
+		// Set up an evolver at applies gravity to the particles. The gravity as set by the physics system is used, but
+		// can be scaled as needed
+		PARTICLE_GRAVITY_DESC gravityDesc;
+		gravityDesc.scale = 1.0f;
+
+		// Create and add the gravity evolver
+		SPtr<ParticleGravity> gravityEvolver = bs_shared_ptr_new<ParticleGravity>(gravityDesc);
+		evolvers.push_back(gravityEvolver);
+
+		// Set up an evolver that allows the particles to collide with the ground.
+		PARTICLE_COLLISIONS_DESC collisionsDesc;
+
+		// We use plane collisions but we could have also used world collisions (which are more expensive, but perform
+		// general purpose collisions with all physical objects)
+		collisionsDesc.mode = ParticleCollisionMode::Plane;
+
+		// Set up the particle radius used for collisions (2cm, same as visible size)
+		collisionsDesc.radius = 0.02f;
+
+		// Create the collision evolver
+		SPtr<ParticleCollisions> collisionEvolver = bs_shared_ptr_new<ParticleCollisions>(collisionsDesc);
+
+		// Assign the plane the particles will collide with
+		collisionEvolver->setPlanes( { Plane(Vector3::UNIT_Y, 0.0f)});
+
+		// Register the collision evolver
+		evolvers.push_back(collisionEvolver);
+
+		// Register all evolvers with the particle system
+		particleSystem->setEvolvers(evolvers);
+
+		// Set up general particle system settings
+		ParticleSystemSettings psSettings;
+
+		// Specify that we want to render 3D meshes instead of billboards
+		psSettings.renderMode = ParticleRenderMode::Mesh;
+
+		// Specify the mesh to use for particles
+		psSettings.mesh = assets.sphereMesh;
+
+		// Set up a plain white diffuse material
+		psSettings.material = assets.particles3DMat;
+
+		// And actually apply the settings
+		particleSystem->setSettings(psSettings);
+
+		// Set up an orbiting light
+		//// Create the scene object, position and scale it
+		HSceneObject lightSO = SceneObject::create("Radial light");
+		lightSO->setPosition(pos - Vector3(0.0f, 0.8f, 0.0f));
+		lightSO->setScale(Vector3::ONE * 0.02f);
+
+		//// Add the light component, emitting a red light
+		HLight light = lightSO->addComponent<CLight>();
+		light->setIntensity(30.0f);
+		light->setColor(Color::Red);
+		light->setUseAutoAttenuation(false);
+		light->setAttenuationRadius(20.0f);
+
+		//// Add a sphere using an emissive material to represent the light
+		HRenderable lightSphere = lightSO->addComponent<CRenderable>();
+		lightSphere->setMesh(assets.sphereMesh);
+		lightSphere->setMaterial(assets.lightMat);
+
+		//// Add a component that orbits the light at 1m of its original position
+		lightSO->addComponent<LightOrbit>(1.0f);
+	}
+
+	/** 
+	 * Sets up a particle system that uses the GPU particle simulation. Particles are spawned on a surface of a sphere and
+	 * a vector field is used for evolving the particles during their lifetime.
+	 */
+	void setupGPUParticleEffect(const Vector3& pos, const ParticleSystemAssets& assets)
+	{
+		// Create the particle system scene object and position/orient it
+		HSceneObject particleSystemSO = SceneObject::create("Vector field");
+		particleSystemSO->setPosition(pos);
+
+		// Add a particle system component
+		HParticleSystem particleSystem = particleSystemSO->addComponent<CParticleSystem>();
+
+		// Set up the emitter
+		SPtr<ParticleEmitter> emitter = bs_shared_ptr_new<ParticleEmitter>();
+
+		// All newly spawned particles will have the size of 1cm
+		emitter->setInitialSize(0.01f);
+
+		// 400 particles will be emitted per second
+		emitter->setEmissionRate(400.0f);
+
+		// No initial speed, we'll rely purely on the vector field force to move the particles
+		emitter->setInitialSpeed(0.0f);
+
+		// Particles will live for exactly 5 seconds
+		emitter->setInitialLifetime(5.0f);
+
+		// Set up a shape that determines the position of newly spawned particles. In this case spawn particles randomly
+		// on a surface of a sphere.
+		PARTICLE_SPHERE_SHAPE_DESC sphereShape;
+
+		// Spawn on a sphere with radius of 30 cm
+		sphereShape.radius = 0.3f;
+
+		// Assign the shape to the emitter
+		emitter->setShape(ParticleEmitterSphereShape::create(sphereShape));
+
+		// Assign the emitter to the particle system
+		particleSystem->setEmitters({emitter});
+
+		// Set up general particle system settings
+		ParticleSystemSettings psSettings;
+
+		// Orient the particles towards the camera plane (standard for billboard particles)
+		psSettings.orientation = ParticleOrientation::ViewPlane;
+
+		// But lock the Y orientation
+		psSettings.orientationLockY = true;
+
+		// Sort by distance from camera so that transparency renders properly
+		psSettings.sortMode = ParticleSortMode::Distance;
+
+		// Use an emissive material to render the particles
+		psSettings.material = assets.litParticleEmissiveMat;
+
+		// Actually enable the GPU simulation
+		psSettings.gpuSimulation = true;
+
+		// Increase the maximum particle count since we'll be emitting them quickly
+		psSettings.maxParticles = 10000;
+
+		// And actually apply the general settings
+		particleSystem->setSettings(psSettings);
+
+		// Set up settings specific to the GPU simulation
+		ParticleGpuSimulationSettings gpuSimSettings;
+
+		// Set up a vector field. Use the vector field resource we imported earlier
+		gpuSimSettings.vectorField.vectorField = assets.vectorField;
+
+		// Increase the intensity of the forces in the vector field
+		gpuSimSettings.vectorField.intensity = 3.0f;
+
+		// Setting this to zero ensures the vector field only applies forces, not velocities, to the particles
+		gpuSimSettings.vectorField.tightness = 0.0f;
+
+		// And actually apply the GPU simulation settings
+		particleSystem->setGpuSimulationSettings(gpuSimSettings);
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Registers a default set of input controls
+	ExampleFramework::setupInputConfig();
+
+	// Set up the scene with an object to render and a camera
+	setUpScene();
+
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 23 - 0
Examples/Source/PhysicallyBasedShading/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(PhysicallyBasedShading WIN32 "Main.cpp")
+else()
+	add_executable(PhysicallyBasedShading "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(PhysicallyBasedShading PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(PhysicallyBasedShading Common)
+
+# Plugin dependencies
+add_engine_dependencies(PhysicallyBasedShading)
+add_dependencies(PhysicallyBasedShading bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET PhysicallyBasedShading PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(PhysicallyBasedShading)

+ 177 - 0
Examples/Source/PhysicallyBasedShading/Main.cpp

@@ -0,0 +1,177 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCSkybox.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+
+// Example includes
+#include "BsObjectRotator.h"
+#include "BsExampleFramework.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example renders an object using the standard built-in physically based material. 
+//
+// The example first loads necessary resources, including a mesh and textures to use for rendering. Then it creates a
+// material using the standard PBR shader. It then proceeds to register the relevant keys used for controling the camera
+// and the rendered object. Finally it sets up the 3D scene using the mesh, textures, material and sets up a camera, along
+// with CameraFlyer and ObjectRotator components that allow the user to fly around the scene and rotate the 3D model.
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Container for all resources used by the example. */
+	struct Assets 
+	{
+		HMesh exampleModel;
+		HTexture exampleAlbedoTex;
+		HTexture exampleNormalsTex;
+		HTexture exampleRoughnessTex;
+		HTexture exampleMetalnessTex;
+		HTexture exampleSkyCubemap;
+		HMaterial exampleMaterial;
+	};
+
+	/** Load the resources we'll be using throughout the example. */
+	Assets loadAssets()
+	{
+		Assets assets;
+
+		// Load a 3D model
+		assets.exampleModel = ExampleFramework::loadMesh(ExampleMesh::Cerberus);
+
+		// Load PBR textures for the 3D model
+		assets.exampleAlbedoTex = ExampleFramework::loadTexture(ExampleTexture::CerberusAlbedo);
+		assets.exampleNormalsTex = ExampleFramework::loadTexture(ExampleTexture::CerberusNormal, false);
+		assets.exampleRoughnessTex = ExampleFramework::loadTexture(ExampleTexture::CerberusRoughness, false);
+		assets.exampleMetalnessTex = ExampleFramework::loadTexture(ExampleTexture::CerberusMetalness, false);
+
+		// Create a material using the default physically based shader, and apply the PBR textures we just loaded
+		HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		assets.exampleMaterial = Material::create(shader);
+
+		assets.exampleMaterial->setTexture("gAlbedoTex", assets.exampleAlbedoTex);
+		assets.exampleMaterial->setTexture("gNormalTex", assets.exampleNormalsTex);
+		assets.exampleMaterial->setTexture("gRoughnessTex", assets.exampleRoughnessTex);
+		assets.exampleMaterial->setTexture("gMetalnessTex", assets.exampleMetalnessTex);
+
+		// Load an environment map
+		assets.exampleSkyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentPaperMill, false, true, true);
+
+		return assets;
+	}
+
+	/** Set up the 3D object used by the example, and the camera to view the world through. */
+	void setUp3DScene(const Assets& assets)
+	{
+		/************************************************************************/
+		/* 									RENDERABLE                  		*/
+		/************************************************************************/
+
+		// 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 loaded, and the material we created.
+		HRenderable renderable = pistolSO->addComponent<CRenderable>();
+		renderable->setMesh(assets.exampleModel);
+		renderable->setMaterial(assets.exampleMaterial);
+
+		pistolSO->setRotation(Quaternion(Degree(0.0f), Degree(-160.0f), Degree(0.0f)));
+
+		// Add a rotator component so we can rotate the object during runtime
+		pistolSO->addComponent<ObjectRotator>();
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(assets.exampleSkyCubemap);
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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);
+
+		// Position and orient the camera scene object
+		sceneCameraSO->setPosition(Vector3(0.2f, 0.05f, 1.4f));
+		sceneCameraSO->lookAt(Vector3(0.2f, 0.05f, 0.0f));
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Registers a default set of input controls
+	ExampleFramework::setupInputConfig();
+
+	// Load a model and textures, create materials
+	Assets assets = loadAssets();
+
+	// Set up the scene with an object to render and a camera
+	setUp3DScene(assets);
+	
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 23 - 0
Examples/Source/Physics/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(Physics WIN32 "Main.cpp")
+else()
+	add_executable(Physics "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(Physics PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(Physics Common)
+
+# Plugin dependencies
+add_engine_dependencies(Physics)
+add_dependencies(Physics bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET Physics PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(Physics)

+ 346 - 0
Examples/Source/Physics/Main.cpp

@@ -0,0 +1,346 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCSkybox.h"
+#include "Components/BsCPlaneCollider.h"
+#include "Components/BsCBoxCollider.h"
+#include "Components/BsCSphereCollider.h"
+#include "Components/BsCCharacterController.h"
+#include "Components/BsCRigidbody.h"
+#include "GUI/BsCGUIWidget.h"
+#include "GUI/BsGUIPanel.h"
+#include "GUI/BsGUILayoutY.h"
+#include "GUI/BsGUILabel.h"
+#include "Physics/BsPhysicsMaterial.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+#include "Platform/BsCursor.h"
+#include "Input/BsInput.h"
+
+// Example includes
+#include "BsExampleFramework.h"
+#include "BsFPSWalker.h"
+#include "BsFPSCamera.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example sets up a physical environment in which the user can walk around using the character controller component,
+// and shoot the placed geometry demonstrating various aspects of the physics system. This includes a demonstration of
+// static colliders, dynamic rigidbodies, physical materials, character controller and manual application of forces.
+//
+// The example first loads necessary resources, including textures, materialss and physical materials. Then it sets up the 
+// scene, consisting of a floor, and multiple stacks of boxes that can be knocked down. Character controller is created 
+// next, as well as the camera. Components for moving the character controller and the camera are attached to allow the 
+// user to control the character. Finally an input callback is hooked up that shoots spheres when user presses the left 
+// mouse button. 
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	constexpr float GROUND_PLANE_SCALE = 50.0f;
+
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Set up the scene used by the example, and the camera to view the world through. */
+	void setUpScene()
+	{
+		/************************************************************************/
+		/* 									ASSETS	                    		*/
+		/************************************************************************/
+
+		// Prepare all the resources we'll be using throughout this example
+
+		// Grab a couple of test textures that we'll apply to the rendered objects
+		HTexture gridPattern = ExampleFramework::loadTexture(ExampleTexture::GridPattern);
+		HTexture gridPattern2 = ExampleFramework::loadTexture(ExampleTexture::GridPattern2);
+
+		// Grab the default PBR shader
+		HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		
+		// Create a set of materials to apply to renderables used
+		HMaterial planeMaterial = Material::create(shader);
+		planeMaterial->setTexture("gAlbedoTex", gridPattern2);
+
+		// Tile the texture so every tile covers a 2x2m area
+		planeMaterial->setVec2("gUVTile", Vector2::ONE * GROUND_PLANE_SCALE * 0.5f);
+
+		HMaterial boxMaterial = Material::create(shader);
+		boxMaterial->setTexture("gAlbedoTex", gridPattern);
+
+		HMaterial sphereMaterial = Material::create(shader);
+
+		// Load meshes we'll used for our rendered objects
+		HMesh boxMesh = gBuiltinResources().getMesh(BuiltinMesh::Box);
+		HMesh planeMesh = gBuiltinResources().getMesh(BuiltinMesh::Quad);
+		HMesh sphereMesh = gBuiltinResources().getMesh(BuiltinMesh::Sphere);
+
+		// Create a physics material we'll use for the box geometry, as well as the floor. The material has high
+		// static and dynamic friction, with low restitution (low bounciness). Simulates a harder, rough, solid surface.
+		HPhysicsMaterial boxPhysicsMaterial = PhysicsMaterial::create(1.0f, 1.0f, 0.0f);
+
+		// Create a physics material for the sphere geometry, with higher bounciness. Simulates elasticity.
+		HPhysicsMaterial spherePhysicsMaterial = PhysicsMaterial::create(1.0f, 1.0f, 0.5f);
+
+		/************************************************************************/
+		/* 									FLOOR	                    		*/
+		/************************************************************************/
+
+		// Set up renderable geometry for the floor plane
+		HSceneObject floorSO = SceneObject::create("Floor");
+		HRenderable floorRenderable = floorSO->addComponent<CRenderable>();
+		floorRenderable->setMesh(planeMesh);
+		floorRenderable->setMaterial(planeMaterial);
+
+		floorSO->setScale(Vector3(GROUND_PLANE_SCALE, 1.0f, GROUND_PLANE_SCALE));
+
+		// Add a plane collider that will prevent physical objects going through the floor
+		HPlaneCollider planeCollider = floorSO->addComponent<CPlaneCollider>();
+
+		// Apply the non-bouncy material
+		planeCollider->setMaterial(boxPhysicsMaterial);
+
+		/************************************************************************/
+		/* 									BOXES	                    		*/
+		/************************************************************************/
+
+		// Helper method that creates a pyramid of six boxes that can be physically manipulated
+		auto createBoxStack = [=](const Vector3& position, const Quaternion& rotation = Quaternion::IDENTITY)
+		{
+			HSceneObject boxSO[6];
+			for (auto& entry : boxSO)
+			{
+				// Create a scene object and a renderable
+				entry = SceneObject::create("Box");
+
+				HRenderable boxRenderable = entry->addComponent<CRenderable>();
+				boxRenderable->setMesh(boxMesh);
+				boxRenderable->setMaterial(boxMaterial);
+
+				// Add a plane collider that represent's the physical geometry of the box
+				HBoxCollider boxCollider = entry->addComponent<CBoxCollider>();
+
+				// Apply the non-bouncy material
+				boxCollider->setMaterial(boxPhysicsMaterial);
+
+				// Set the mass of a box to 25 kilograms
+				boxCollider->setMass(25.0f);
+
+				// Add a rigidbody, making the box geometry able to react to interactions with other physical objects
+				HRigidbody boxRigidbody = entry->addComponent<CRigidbody>();
+			}
+
+			// Stack the boxes in a pyramid
+			Vector3 positions[] =
+			{
+				// First row
+				Vector3(-1.25f, 0.55f, 0.0f),
+				Vector3(0.0f, 0.55f, 0.0f),
+				Vector3(1.25f, 0.55f, 0.0f),
+				// Second row
+				Vector3(-0.65f, 1.6f, 0.0f),
+				Vector3(0.65f, 1.6f, 0.0f),
+				// Third row
+				Vector3(0.0f, 2.65f, 0.0f),
+			};
+
+			for(UINT32 i = 0; i < 6; i++)
+			{
+				Vector3 pos = rotation.rotate(positions[i]) + position;
+				boxSO[i]->setPosition(pos);
+			}
+		};
+
+		createBoxStack(Vector3::ZERO);
+		createBoxStack(Vector3(6.0f, 0.0f, 3.0f), Quaternion(Degree(0.0f), Degree(-45.0f), Degree(0.0f)));
+		createBoxStack(Vector3(-6.0f, 0.0f, 3.0f), Quaternion(Degree(0.0f), Degree(45.0f), Degree(0.0f)));
+
+		/************************************************************************/
+		/* 									CHARACTER                    		*/
+		/************************************************************************/
+
+		// Add physics geometry and components for character movement and physics interaction
+		HSceneObject characterSO = SceneObject::create("Character");
+		characterSO->setPosition(Vector3(0.0f, 1.0f, 5.0f));
+
+		// Add a character controller, representing the physical geometry of the character
+		HCharacterController charController = characterSO->addComponent<CCharacterController>();
+
+		// Make the character about 1.8m high, with 0.4m radius (controller represents a capsule)
+		charController->setHeight(1.0f); // + 0.4 * 2 radius = 1.8m height
+		charController->setRadius(0.4f);
+
+		// FPS walker uses default input controls to move the character controller attached to the same object
+		characterSO->addComponent<FPSWalker>();
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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);
+
+		// Add a component that allows the camera to be rotated using the mouse
+		HFPSCamera fpsCamera = sceneCameraSO->addComponent<FPSCamera>();
+
+		// Set the character controller on the FPS camera, so the component can apply yaw rotation to it
+		fpsCamera->setCharacter(characterSO);
+
+		// Make the camera a child of the character scene object, and position it roughly at eye level
+		sceneCameraSO->setParent(characterSO);
+		sceneCameraSO->setPosition(Vector3(0.0f, 1.8f * 0.5f - 0.1f, 0.0f));
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Load a skybox texture
+		HTexture skyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentDaytime, false, true, true);
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(skyCubemap);
+
+		/************************************************************************/
+		/* 									CURSOR                       		*/
+		/************************************************************************/
+
+		// Hide and clip the cursor, since we only use the mouse movement for camera rotation
+		Cursor::instance().hide();
+		Cursor::instance().clipToWindow(*window);
+
+		/************************************************************************/
+		/* 									INPUT                       		*/
+		/************************************************************************/
+
+		// Hook up input that launches a sphere when user clicks the mouse, and Esc key to quit
+		gInput().onButtonUp.connect([=](const ButtonEvent& ev)
+		{
+			if(ev.buttonCode == BC_MOUSE_LEFT)
+			{
+				// Create the scene object and renderable geometry of the sphere
+				HSceneObject sphereSO = SceneObject::create("Sphere");
+
+				HRenderable sphereRenderable = sphereSO->addComponent<CRenderable>();
+				sphereRenderable->setMesh(sphereMesh);
+				sphereRenderable->setMaterial(sphereMaterial);
+
+				// Create a spherical collider, represting physical geometry
+				HSphereCollider sphereCollider = sphereSO->addComponent<CSphereCollider>();
+
+				// Apply the bouncy material
+				sphereCollider->setMaterial(spherePhysicsMaterial);
+
+				// Set mass to 25kg
+				sphereCollider->setMass(25.0f);
+
+				// Add a rigidbody, making the object interactable
+				HRigidbody sphereRigidbody = sphereSO->addComponent<CRigidbody>();
+				
+				// Position the sphere in front of the character, and scale it down a bit
+				Vector3 spawnPos = characterSO->getTransform().getPosition();
+				spawnPos += sceneCameraSO->getTransform().getForward() * 0.5f;
+				spawnPos.y += 0.5f;
+
+				sphereSO->setPosition(spawnPos);
+				sphereSO->setScale(Vector3(0.3f, 0.3f, 0.3f));
+
+				// Apply force to the sphere, launching it forward in the camera's view direction
+				sphereRigidbody->addForce(sceneCameraSO->getTransform().getForward() * 40.0f, ForceMode::Velocity);
+			}
+			else if(ev.buttonCode == BC_ESCAPE)
+			{
+				// Quit the application when Escape key is pressed
+				gApplication().quitRequested();
+			}
+		});
+
+		/************************************************************************/
+		/* 									GUI		                     		*/
+		/************************************************************************/
+
+		// Display GUI elements indicating to the user which input keys are available
+
+		// Add a GUIWidget component we will use for rendering the GUI
+		HSceneObject guiSO = SceneObject::create("GUI");
+		HGUIWidget gui = guiSO->addComponent<CGUIWidget>(sceneCamera);
+
+		// Grab the main panel onto which to attach the GUI elements to
+		GUIPanel* mainPanel = gui->getPanel();
+
+		// Create a vertical GUI layout to align the labels one below each other
+		GUILayoutY* vertLayout = GUILayoutY::create();
+
+		// Create the GUI labels displaying the available input commands
+		HString shootString(u8"Press left mouse button to shoot");
+		HString quitString(u8"Press the Escape key to quit");
+
+		vertLayout->addNewElement<GUILabel>(shootString);
+		vertLayout->addNewElement<GUILabel>(quitString);
+
+		// Register the layout with the main GUI panel, placing the layout in top left corner of the screen by default
+		mainPanel->addElement(vertLayout);
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Registers a default set of input controls
+	ExampleFramework::setupInputConfig();
+
+	// Set up the scene with an object to render and a camera
+	setUpScene();
+
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 23 - 0
Examples/Source/SkeletalAnimation/CMakeLists.txt

@@ -0,0 +1,23 @@
+# Target
+if(WIN32)
+	add_executable(SkeletalAnimation WIN32 "Main.cpp")
+else()
+	add_executable(SkeletalAnimation "Main.cpp")
+endif()
+	
+# Working directory
+set_target_properties(SkeletalAnimation PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)")		
+	
+# Libraries
+## Local libs
+target_link_libraries(SkeletalAnimation Common)
+
+# Plugin dependencies
+add_engine_dependencies(SkeletalAnimation)
+add_dependencies(SkeletalAnimation bsfFBXImporter bsfFontImporter bsfFreeImgImporter)
+
+# IDE specific
+set_property(TARGET SkeletalAnimation PROPERTY FOLDER Examples)
+
+# Precompiled header & Unity build
+conditional_cotire(SkeletalAnimation)

+ 215 - 0
Examples/Source/SkeletalAnimation/Main.cpp

@@ -0,0 +1,215 @@
+// Framework includes
+#include "BsApplication.h"
+#include "Resources/BsResources.h"
+#include "Resources/BsBuiltinResources.h"
+#include "Material/BsMaterial.h"
+#include "Components/BsCCamera.h"
+#include "Components/BsCRenderable.h"
+#include "Components/BsCAnimation.h"
+#include "Components/BsCSkybox.h"
+#include "RenderAPI/BsRenderAPI.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "Scene/BsSceneObject.h"
+
+// Example includes
+#include "BsCameraFlyer.h"
+#include "BsExampleFramework.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// This example demonstrates how to animate a 3D model using skeletal animation. Aside from animation this example is
+// structurally similar to PhysicallyBasedShading example.
+//
+// The example first loads necessary resources, including a mesh and textures to use for rendering, as well as an animation
+// clip. The animation clip is imported from the same file as the 3D model. Special import options are used to tell the
+// importer to import data required for skeletal animation. It then proceeds to register the relevant keys used for
+// controling the camera. Next it sets up the 3D scene using the mesh, textures, material and adds an animation
+// component. The animation component start playing the animation clip we imported earlier. Finally it sets up a camera,
+// along with CameraFlyer component that allows the user to fly around the scene.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace bs
+{
+	UINT32 windowResWidth = 1280;
+	UINT32 windowResHeight = 720;
+
+	/** Container for all resources used by the example. */
+	struct Assets
+	{
+		HMesh exampleModel;
+		HAnimationClip exampleAnimClip;
+		HTexture exampleAlbedoTex;
+		HTexture exampleNormalsTex;
+		HTexture exampleRoughnessTex;
+		HTexture exampleMetalnessTex;
+		HTexture exampleSkyCubemap;
+		HMaterial exampleMaterial;
+	};
+
+	/** Load the resources we'll be using throughout the example. */
+	Assets loadAssets()
+	{
+		Assets assets;
+
+		// Load the 3D model and the animation clip
+
+		// Set up a path to the model resource
+		const Path exampleDataPath = EXAMPLE_DATA_PATH;
+		const Path modelPath = exampleDataPath + "MechDrone/Drone.FBX";
+
+		// Set up mesh import options so that we import information about the skeleton and the skin, as well as any
+		// animation clips the model might have.
+		SPtr<MeshImportOptions> meshImportOptions = MeshImportOptions::create();
+		meshImportOptions->setImportSkin(true);
+		meshImportOptions->setImportAnimation(true);
+
+		// The FBX file contains multiple resources (a mesh and an animation clip), therefore we use importAll() method,
+		// which imports all resources in a file.
+		Vector<SubResource> modelResources = gImporter().importAll(modelPath, meshImportOptions);
+		for(auto& entry : modelResources)
+		{
+			if(rtti_is_of_type<Mesh>(entry.value.get()))
+				assets.exampleModel = static_resource_cast<Mesh>(entry.value);
+			else if(rtti_is_of_type<AnimationClip>(entry.value.get()))
+				assets.exampleAnimClip = static_resource_cast<AnimationClip>(entry.value);
+		}
+
+		// Load PBR textures for the 3D model
+		assets.exampleAlbedoTex = ExampleFramework::loadTexture(ExampleTexture::DroneAlbedo);
+		assets.exampleNormalsTex = ExampleFramework::loadTexture(ExampleTexture::DroneNormal, false);
+		assets.exampleRoughnessTex = ExampleFramework::loadTexture(ExampleTexture::DroneRoughness, false);
+		assets.exampleMetalnessTex = ExampleFramework::loadTexture(ExampleTexture::DroneMetalness, false);
+
+		// Create a material using the default physically based shader, and apply the PBR textures we just loaded
+		HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard);
+		assets.exampleMaterial = Material::create(shader);
+
+		assets.exampleMaterial->setTexture("gAlbedoTex", assets.exampleAlbedoTex);
+		assets.exampleMaterial->setTexture("gNormalTex", assets.exampleNormalsTex);
+		assets.exampleMaterial->setTexture("gRoughnessTex", assets.exampleRoughnessTex);
+		assets.exampleMaterial->setTexture("gMetalnessTex", assets.exampleMetalnessTex);
+
+		// Load an environment map
+		assets.exampleSkyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentRathaus, false, true, true);
+
+		return assets;
+	}
+
+	/** Set up the 3D object used by the example, and the camera to view the world through. */
+	void setUp3DScene(const Assets& assets)
+	{
+		/************************************************************************/
+		/* 									RENDERABLE                  		*/
+		/************************************************************************/
+
+		// 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 droneSO = SceneObject::create("Drone");
+		
+		// Attach the Renderable component and hook up the mesh we loaded, and the material we created.
+		HRenderable renderable = droneSO->addComponent<CRenderable>();
+		renderable->setMesh(assets.exampleModel);
+		renderable->setMaterial(assets.exampleMaterial);
+
+		/************************************************************************/
+		/* 									ANIMATION	                  		*/
+		/************************************************************************/
+
+		// Add an animation component to the same scene object we added Renderable to.
+		HAnimation animation = droneSO->addComponent<CAnimation>();
+
+		// Start playing the animation clip we imported
+		animation->play(assets.exampleAnimClip);
+
+		/************************************************************************/
+		/* 									SKYBOX                       		*/
+		/************************************************************************/
+
+		// Add a skybox texture for sky reflections
+		HSceneObject skyboxSO = SceneObject::create("Skybox");
+
+		HSkybox skybox = skyboxSO->addComponent<CSkybox>();
+		skybox->setTexture(assets.exampleSkyCubemap);
+
+		/************************************************************************/
+		/* 									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. 
+		SPtr<RenderWindow> window = gApplication().getPrimaryWindow();
+
+		// 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).
+		HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>();
+		sceneCamera->getViewport()->setTarget(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 indirect lighting so we get accurate diffuse lighting from the skybox environment map
+		const SPtr<RenderSettings>& renderSettings = sceneCamera->getRenderSettings();
+		renderSettings->enableIndirectLighting = true;
+
+		sceneCamera->setRenderSettings(renderSettings);
+
+		// 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.0f, 2.5f, -4.0f) * 0.65f);
+		sceneCameraSO->lookAt(Vector3(0, 1.5f, 0));
+	}
+}
+
+/** Main entry point into the application. */
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include <windows.h>
+
+int CALLBACK WinMain(
+	_In_  HINSTANCE hInstance,
+	_In_  HINSTANCE hPrevInstance,
+	_In_  LPSTR lpCmdLine,
+	_In_  int nCmdShow
+	)
+#else
+int main()
+#endif
+{
+	using namespace bs;
+
+	// Initializes the application and creates a window with the specified properties
+	VideoMode videoMode(windowResWidth, windowResHeight);
+	Application::startUp(videoMode, "Example", false);
+
+	// Registers a default set of input controls
+	ExampleFramework::setupInputConfig();
+
+	// Load a model and textures, create materials
+	Assets assets = loadAssets();
+
+	// Set up the scene with an object to render and a camera
+	setUp3DScene(assets);
+	
+	// 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();
+
+	// When done, clean up
+	Application::shutDown();
+
+	return 0;
+}

+ 12 - 0
Examples/appveyor.yml

@@ -0,0 +1,12 @@
+image: Visual Studio 2017
+platform: x64
+configuration: Release
+clone_folder: C:\Projects\bsfExamples
+
+build_script:
+  - cmd: set INSTALL_DIR=C:/Projects/bsfExamples/install/Release
+  - cmd: mkdir "%INSTALL_DIR%"
+  - cmd: mkdir Build
+  - cmd: cd Build
+  - cmd: cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX:STRING=%INSTALL_DIR% ..
+  - cmd: cmake --build ./ --config Release

BIN
Game/Internal/Assemblies/MScriptGame.dll


BIN
Game/Internal/Assemblies/MScriptGame.dll.mdb


BIN
Game/Internal/BuildData.asset


BIN
Game/Internal/Layout.asset


BIN
Game/Internal/ProjectLibrary.asset


BIN
Game/Internal/ResourceManifest.asset


BIN
Game/Internal/Resources/12cb8106-456a-0df9-9db4-456a83a83e85.asset


BIN
Game/Internal/Resources/8a4ee213-4880-6950-5c8a-4880fdf2dc7d.asset


BIN
Game/Internal/Settings.asset


+ 31 - 0
Game/Resources/New Shader.bsl

@@ -0,0 +1,31 @@
+#include "$ENGINE$\BasePass.bslinc"
+#include "$ENGINE$\GBufferOutput.bslinc"
+
+shader Surface
+{
+	mixin BasePass;
+	mixin GBufferOutput;
+
+	code
+	{
+		void fsmain(
+			in VStoFS input, 
+			out float3 OutSceneColor : SV_Target0,
+			out float4 OutGBufferA : SV_Target1,
+			out float4 OutGBufferB : SV_Target2,
+			out float2 OutGBufferC : SV_Target3,
+			out float OutGBufferD : SV_Target4)
+		{
+			SurfaceData surfaceData;
+			surfaceData.albedo = float4(0.05f, 0.05f, 0.05f, 1.0f);
+			surfaceData.worldNormal.xyz = input.tangentToWorldZ;
+			surfaceData.roughness = 1.0f;
+			surfaceData.metalness = 0.0f;
+			surfaceData.mask = gLayer;
+			
+			encodeGBuffer(surfaceData, OutGBufferA, OutGBufferB, OutGBufferC, OutGBufferD);
+			
+			OutSceneColor = 0.0f;
+		}	
+	};
+};

BIN
Game/Resources/New Shader.bsl.meta


+ 20 - 0
Game/Resources/test.cs

@@ -0,0 +1,20 @@
+namespace bs
+{
+	public class NewComponent : ManagedComponent
+	{
+		private void OnInitialize()
+		{
+			// TODO: Add code that needs to be called when Component is created
+		}
+		
+		private void OnUpdate()
+		{
+			// TODO: Add code that needs to be called every frame
+		}
+		
+		private void OnDestroy()
+		{
+			// TODO: Add code that needs to be called when Component is destroyed
+		}
+	}
+}

BIN
Game/Resources/test.cs.meta


+ 8 - 2
README.md

@@ -1,7 +1,13 @@
 [![Latest version](https://img.shields.io/badge/latest-v0.4--dev-red.svg)](https://img.shields.io/badge/latest-v0.4--dev-red.svg) [![Build status](https://ci.appveyor.com/api/projects/status/v043naykgplkj42s?svg=true)](https://ci.appveyor.com/project/BearishSun/bansheeengine) [![Discord](https://img.shields.io/discord/572359664528916490.svg?logo=discord)](https://discord.gg/8Xyf5gF)
 
-# What is Banshee? 
-Banshee is a high-quality and modern game development toolkit. It provides a **high-performance, multi-threaded game engine** written in C++14. The engine includes math and utility libraries, Vulkan, DirectX 11 and OpenGL support, handles common tasks such as input, GUI, physics, audio, animation and scripting, and supports many popular resource formats (e.g. FBX, PNG, PSD, TTF, OGG, WAV).
+# Preserving Banshee3D...
+This repository is a fork of the latest public release from Banshee 3D, which was removed from the original author.
+
+### Major Changes
+Integrated bsf, bsf examples and made the project compilable.
+
+# What was Banshee? 
+Banshee was a high-quality and modern game development toolkit. It provides a **high-performance, multi-threaded game engine** written in C++14. The engine includes math and utility libraries, Vulkan, DirectX 11 and OpenGL support, handles common tasks such as input, GUI, physics, audio, animation and scripting, and supports many popular resource formats (e.g. FBX, PNG, PSD, TTF, OGG, WAV).
 
 Banshee provides an intuitive and customizable **editor** that can be used to manage assets, build levels, compile scripts, and to test and publish your game. The editor can be extended or customized with scripts to meet the exact needs of your project.
 

BIN
Source/Banshee3D/RCa09888


BIN
Source/Banshee3D/RCa12528


BIN
Source/Banshee3D/RCa21224


+ 1 - 1
Source/EditorManaged/MBansheeEditor.csproj.in

@@ -9,7 +9,7 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>${BS_SHARP_ROOT_NS}</RootNamespace>
     <AssemblyName>${BS_SHARP_ASSEMBLY_NAME}</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
 	<Deterministic>true</Deterministic>
   </PropertyGroup>

+ 1 - 0
Source/bsf/.gitignore

@@ -3,6 +3,7 @@
 *.suo
 *.sdf
 *.opensdf
+.vs/*
 bin
 obj
 lib