Prechádzať zdrojové kódy

Readded bsf framework (everything should be in this repository)

larioteo 2 rokov pred
rodič
commit
a59ae61ffe
100 zmenil súbory, kde vykonal 13842 pridanie a 0 odobranie
  1. 14 0
      Source/bsf/.editorconfig
  2. 36 0
      Source/bsf/.gitignore
  3. 1 0
      Source/bsf/.gitmodules
  4. 132 0
      Source/bsf/.travis.yml
  5. 31 0
      Source/bsf/CMakeLists.txt
  6. 69 0
      Source/bsf/CONTRIBUTING.md
  7. 61 0
      Source/bsf/CONTRIBUTORS.md
  8. 1 0
      Source/bsf/Data/.reqversion
  9. 954 0
      Source/bsf/Data/Raw/DataList.json
  10. 13 0
      Source/bsf/Data/Raw/DataPackageContents.txt
  11. 1281 0
      Source/bsf/Data/Raw/GUISkin.json
  12. 966 0
      Source/bsf/Data/Raw/ShaderDependencies.json
  13. 151 0
      Source/bsf/Data/Raw/Shaders/BicubicUpsample.bsl
  14. 105 0
      Source/bsf/Data/Raw/Shaders/Blit.bsl
  15. 39 0
      Source/bsf/Data/Raw/Shaders/Clear.bsl
  16. 110 0
      Source/bsf/Data/Raw/Shaders/ClearLoadStore.bsl
  17. 59 0
      Source/bsf/Data/Raw/Shaders/Composite.bsl
  18. 79 0
      Source/bsf/Data/Raw/Shaders/DebugDraw.bsl
  19. 342 0
      Source/bsf/Data/Raw/Shaders/Decal.bsl
  20. 28 0
      Source/bsf/Data/Raw/Shaders/Default.bsl
  21. 116 0
      Source/bsf/Data/Raw/Shaders/DeferredDirectionalLight.bsl
  22. 102 0
      Source/bsf/Data/Raw/Shaders/DeferredIBLFinalize.bsl
  23. 157 0
      Source/bsf/Data/Raw/Shaders/DeferredIBLProbe.bsl
  24. 96 0
      Source/bsf/Data/Raw/Shaders/DeferredIBLSetup.bsl
  25. 92 0
      Source/bsf/Data/Raw/Shaders/DeferredIBLSky.bsl
  26. 177 0
      Source/bsf/Data/Raw/Shaders/DeferredPointLight.bsl
  27. 75 0
      Source/bsf/Data/Raw/Shaders/Diffuse.bsl
  28. 144 0
      Source/bsf/Data/Raw/Shaders/GpuParticleBounds.bsl
  29. 25 0
      Source/bsf/Data/Raw/Shaders/GpuParticleClear.bsl
  30. 45 0
      Source/bsf/Data/Raw/Shaders/GpuParticleCurveInject.bsl
  31. 57 0
      Source/bsf/Data/Raw/Shaders/GpuParticleInject.bsl
  32. 257 0
      Source/bsf/Data/Raw/Shaders/GpuParticleSimulate.bsl
  33. 71 0
      Source/bsf/Data/Raw/Shaders/GpuParticleSortPrepare.bsl
  34. 45 0
      Source/bsf/Data/Raw/Shaders/Includes/BasePass.bslinc
  35. 93 0
      Source/bsf/Data/Raw/Shaders/Includes/ColorSpace.bslinc
  36. 87 0
      Source/bsf/Data/Raw/Shaders/Includes/DeferredLightCommon.bslinc
  37. 20 0
      Source/bsf/Data/Raw/Shaders/Includes/DepthInput.bslinc
  38. 60 0
      Source/bsf/Data/Raw/Shaders/Includes/DepthOfFieldCommon.bslinc
  39. 90 0
      Source/bsf/Data/Raw/Shaders/Includes/DirectLightAccumulator.bslinc
  40. 387 0
      Source/bsf/Data/Raw/Shaders/Includes/DirectLighting.bslinc
  41. 90 0
      Source/bsf/Data/Raw/Shaders/Includes/ForwardLighting.bslinc
  42. 85 0
      Source/bsf/Data/Raw/Shaders/Includes/GBufferInput.bslinc
  43. 45 0
      Source/bsf/Data/Raw/Shaders/Includes/GBufferOutput.bslinc
  44. 43 0
      Source/bsf/Data/Raw/Shaders/Includes/GpuParticleTileVertex.bslinc
  45. 199 0
      Source/bsf/Data/Raw/Shaders/Includes/ImageBasedLighting.bslinc
  46. 71 0
      Source/bsf/Data/Raw/Shaders/Includes/ImportanceSampling.bslinc
  47. 63 0
      Source/bsf/Data/Raw/Shaders/Includes/LightGridCommon.bslinc
  48. 17 0
      Source/bsf/Data/Raw/Shaders/Includes/MaskInput.bslinc
  49. 35 0
      Source/bsf/Data/Raw/Shaders/Includes/PPBase.bslinc
  50. 35 0
      Source/bsf/Data/Raw/Shaders/Includes/PPEyeAdaptationCommon.bslinc
  51. 67 0
      Source/bsf/Data/Raw/Shaders/Includes/PPGaussianBlurCommon.bslinc
  52. 29 0
      Source/bsf/Data/Raw/Shaders/Includes/PPGaussianDOFCommon.bslinc
  53. 111 0
      Source/bsf/Data/Raw/Shaders/Includes/PPTonemapCommon.bslinc
  54. 207 0
      Source/bsf/Data/Raw/Shaders/Includes/PPWhiteBalance.bslinc
  55. 342 0
      Source/bsf/Data/Raw/Shaders/Includes/ParticleVertex.bslinc
  56. 115 0
      Source/bsf/Data/Raw/Shaders/Includes/PerCameraData.bslinc
  57. 11 0
      Source/bsf/Data/Raw/Shaders/Includes/PerFrameData.bslinc
  58. 23 0
      Source/bsf/Data/Raw/Shaders/Includes/PerObjectData.bslinc
  59. 21 0
      Source/bsf/Data/Raw/Shaders/Includes/RadixSortCommon.bslinc
  60. 223 0
      Source/bsf/Data/Raw/Shaders/Includes/RayMarch.bslinc
  61. 101 0
      Source/bsf/Data/Raw/Shaders/Includes/ReflProbeAccumulator.bslinc
  62. 89 0
      Source/bsf/Data/Raw/Shaders/Includes/ReflectionCubemapCommon.bslinc
  63. 184 0
      Source/bsf/Data/Raw/Shaders/Includes/SHCommon.bslinc
  64. 97 0
      Source/bsf/Data/Raw/Shaders/Includes/ShadowDepthBase.bslinc
  65. 44 0
      Source/bsf/Data/Raw/Shaders/Includes/ShadowProjectionCommon.bslinc
  66. 451 0
      Source/bsf/Data/Raw/Shaders/Includes/ShapingCommon.bslinc
  67. 93 0
      Source/bsf/Data/Raw/Shaders/Includes/SpriteCommon.bslinc
  68. 30 0
      Source/bsf/Data/Raw/Shaders/Includes/SurfaceData.bslinc
  69. 567 0
      Source/bsf/Data/Raw/Shaders/Includes/TemporalResolve.bslinc
  70. 215 0
      Source/bsf/Data/Raw/Shaders/Includes/VertexCommon.bslinc
  71. 67 0
      Source/bsf/Data/Raw/Shaders/Includes/VertexInput.bslinc
  72. 66 0
      Source/bsf/Data/Raw/Shaders/Includes/VolumeRenderBase.bslinc
  73. 42 0
      Source/bsf/Data/Raw/Shaders/IrradianceAccumulateCubeSH.bsl
  74. 48 0
      Source/bsf/Data/Raw/Shaders/IrradianceAccumulateSH.bsl
  75. 116 0
      Source/bsf/Data/Raw/Shaders/IrradianceComputeSH.bsl
  76. 61 0
      Source/bsf/Data/Raw/Shaders/IrradianceComputeSHFrag.bsl
  77. 325 0
      Source/bsf/Data/Raw/Shaders/IrradianceEvaluate.bsl
  78. 73 0
      Source/bsf/Data/Raw/Shaders/IrradianceProjectSH.bsl
  79. 76 0
      Source/bsf/Data/Raw/Shaders/IrradianceReduceSH.bsl
  80. 161 0
      Source/bsf/Data/Raw/Shaders/LightGridLLCreation.bsl
  81. 106 0
      Source/bsf/Data/Raw/Shaders/LightGridLLReduction.bsl
  82. 49 0
      Source/bsf/Data/Raw/Shaders/MSAACoverage.bsl
  83. 32 0
      Source/bsf/Data/Raw/Shaders/MSAACoverageStencil.bsl
  84. 72 0
      Source/bsf/Data/Raw/Shaders/PPBloomClip.bsl
  85. 199 0
      Source/bsf/Data/Raw/Shaders/PPBokehDOF.bsl
  86. 96 0
      Source/bsf/Data/Raw/Shaders/PPBokehDOFCombine.bsl
  87. 83 0
      Source/bsf/Data/Raw/Shaders/PPBokehDOFPrepare.bsl
  88. 45 0
      Source/bsf/Data/Raw/Shaders/PPBuildHiZ.bsl
  89. 77 0
      Source/bsf/Data/Raw/Shaders/PPChromaticAberration.bsl
  90. 161 0
      Source/bsf/Data/Raw/Shaders/PPCreateTonemapLUT.bsl
  91. 73 0
      Source/bsf/Data/Raw/Shaders/PPDownsample.bsl
  92. 38 0
      Source/bsf/Data/Raw/Shaders/PPEncodeDepth.bsl
  93. 108 0
      Source/bsf/Data/Raw/Shaders/PPEyeAdaptHistogram.bsl
  94. 39 0
      Source/bsf/Data/Raw/Shaders/PPEyeAdaptHistogramReduce.bsl
  95. 133 0
      Source/bsf/Data/Raw/Shaders/PPEyeAdaptation.bsl
  96. 80 0
      Source/bsf/Data/Raw/Shaders/PPEyeAdaptationBasic.bsl
  97. 30 0
      Source/bsf/Data/Raw/Shaders/PPEyeAdaptationBasicSetup.bsl
  98. 934 0
      Source/bsf/Data/Raw/Shaders/PPFXAA.bsl
  99. 32 0
      Source/bsf/Data/Raw/Shaders/PPFilmGrain.bsl
  100. 49 0
      Source/bsf/Data/Raw/Shaders/PPGaussianBlur.bsl

+ 14 - 0
Source/bsf/.editorconfig

@@ -0,0 +1,14 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+max_line_length=125
+insert_final_newline=true
+trim_trailing_whitespace=true
+
+[*.cs]
+indent_style = space
+
+[*.cs.in]
+indent_style = space

+ 36 - 0
Source/bsf/.gitignore

@@ -0,0 +1,36 @@
+# Ignored files
+*~
+*.suo
+*.sdf
+*.opensdf
+bin
+obj
+lib
+Dependencies
+Builds
+Data/.version
+Data/*
+!Data/.reqversion
+!Data/Raw/
+Data/Raw/*
+!Data/Raw/ShaderDependencies.json
+!Data/Raw/DataList.json
+!Data/Raw/GUISkin.json
+!Data/Raw/Shaders/
+!Data/Raw/Shaders/Includes/
+!Data/Raw/.reqversion
+!Data/Raw/DataPackageContents.txt
+Documentation/Generated
+Documentation/Manuals/docs/Images
+Documentation/Manuals/static
+Documentation/.version
+Documentation/Timestamp.asset
+/Build
+*.aps
+*.opendb
+BsEngineConfig.h
+BsFrameworkConfig.h
+Source/Scripting/bsfSharp/bsfSharpCore.csproj
+Source/Scripting/bsfSharp/MBansheeEngine.csproj
+Source/Scripting/bsfSharp/EngineVersion.cs
+Source/Scripting/bsfSharp/FrameworkVersion.cs

+ 1 - 0
Source/bsf/.gitmodules

@@ -0,0 +1 @@
+

+ 132 - 0
Source/bsf/.travis.yml

@@ -0,0 +1,132 @@
+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
+  - make install
+  
+after_success:
+  - cd ..
+  - today=`date +%Y.%m.%d`
+  - |
+    if [ -n "$TRAVIS_TAG" ]; then
+      buildName=${TRAVIS_TAG}
+    else
+      buildName=${today}
+    fi
+  - filename=bsf_${buildName}_${TRAVIS_OS_NAME}.tar.gz
+  - symbolsFilename=bsf_${buildName}_${TRAVIS_OS_NAME}_symbols.tar.gz
+  - cd ${INSTALL_DIR}
+  - cd ..
+  - |
+    if [[ "$TRAVIS_EVENT_TYPE" == "cron" ]] || [[ -n "$TRAVIS_TAG" ]]; then
+      find ./Install -not -name "*.dbg" -not -name ".dwarf" -not -type d | cut -sd / -f 3- | tar -C ./Install -czvf ${filename} -T -
+      find ./Install -name "*.dbg" -or -name "*.dwarf" -not -type d | cut -sd / -f 3- | tar -C ./Install -czvf ${symbolsFilename} -T -
+
+      mkdir artifacts
+      mv ${filename} artifacts
+      mv ${symbolsFilename} artifacts
+    fi
+deploy:
+  - provider: s3
+    access_key_id: ${S3_ACCESS}
+    secret_access_key: ${S3_SECRET}
+    bucket: ${S3_BUCKET}
+    skip_cleanup: true
+    region: ${S3_REGION}
+    local_dir: ${TRAVIS_BUILD_DIR}/artifacts
+    on:
+      condition: $TRAVIS_EVENT_TYPE = cron
+  - provider: s3
+    access_key_id: ${S3_ACCESS}
+    secret_access_key: ${S3_SECRET}
+    bucket: ${S3_BUCKET}
+    skip_cleanup: true
+    region: ${S3_REGION}
+    local_dir: ${TRAVIS_BUILD_DIR}/artifacts
+    on:
+      tags: true

+ 31 - 0
Source/bsf/CMakeLists.txt

@@ -0,0 +1,31 @@
+cmake_minimum_required (VERSION 3.12.0)
+project (bsf)
+
+set (BSF_SOURCE_DIR ${PROJECT_SOURCE_DIR}/Source)
+set (APP_ROOT_DIR ${PROJECT_SOURCE_DIR})
+set (BS_IS_BANSHEE3D 0)
+
+# Default install dir
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+	if(LINUX)
+		set(CMAKE_INSTALL_PREFIX /usr/local/opt/bsf CACHE PATH "..." FORCE)
+	endif()
+endif()
+
+include(Source/CMake/Properties.cmake)
+include(Source/CMake/FindPackageOrBuild.cmake)
+include(Source/CMake/HelperMethods.cmake)
+
+add_subdirectory(Source)
+
+# Options
+set(SCRIPT_API "None" CACHE STRING "Which scripting API/language to use, if any. If no scripting API is chosen only native C++ bsf core will be built. If a scripting API is chosen, then a separate scripting layer on top of the core will be included in the build as well.")
+set_property(CACHE SCRIPT_API PROPERTY STRINGS "None" "C#")
+
+set(SCRIPT_BINDING_GENERATION OFF CACHE BOOL "If true, script binding generation will be supported through a specialized build target. Enable this if you plan on modifying the scripting API. Requires the SBGen tool dependency. Only relevant if you have selected a SCRIPT_API other than \"None\".")
+
+set(BSF_STRIP_DEBUG_INFO ON CACHE BOOL "If true debug info will be stripped into a separate file in release builds.")
+
+set(BSF_ENABLE_EXCEPTIONS OFF CACHE BOOL "If true C++ exceptions will be enabled when compiling.")
+
+set(BSF_ENABLE_RTTI OFF CACHE BOOL "If true C++ RTTI will be enabled when compiling.")

+ 69 - 0
Source/bsf/CONTRIBUTING.md

@@ -0,0 +1,69 @@
+# Contributing to this project
+
+Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
+
+## Using the issue tracker
+
+The issue tracker is the preferred channel for [bug reports](#bugs), [features requests](#features) and [submitting pull requests](#pull-requests), but please respect the following restrictions:
+
+* Please **do not** use the issue tracker for personal support requests or questions. Use the [community board](https://discourse.bsframework.io) instead. You can sign in using your GitHub credentials, making the process as simple as submitting an issue on GitHub itself.
+
+* Please **do not** derail issues. Keep the discussion on topic and respect the opinions of others. Start a new issue for a new topic (bug, feature) instead of using the same thread for multiple topics.
+
+<a name="bugs"></a>
+## Bug reports
+
+A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - thank you!
+
+Guidelines for bug reports:
+
+1. **Use the GitHub issue search** &mdash; check if the issue has already been reported.
+
+2. **Check if the issue has been fixed** &mdash; try to reproduce it using the latest `master` branch.
+
+3. **Submit a crash log** &mdash; If your bug is causing a crash, `bsf` will generate a crash log in the `CrashReports` directory. Please submit this log along your report.
+
+A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report.
+
+<a name="features"></a>
+## Feature requests
+
+Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible.
+
+If a feature is already listed in the [upcoming feature tracker](https://github.com/GameFoundry/bsf/issues?q=is%3Aopen+is%3Aissue+label%3AOFFICIAL) use that thread to discuss the feature and ask to be assigned to that particular task if you are interested in developing it.
+
+<a name="pull-requests"></a>
+## Pull requests
+
+Good pull requests - patches, improvements, new features - are a fantastic help. They should remain focused in scope and avoid containing unrelated commits.
+
+**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.
+
+Please adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.). See the [code style](https://www.bsframework.io/docs/code_style.html) guide and respect the style of surrounding code.
+
+<a id="developers-certificate-of-origin"></a>
+## Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+* (a) The contribution was created in whole or in part by me and I
+  have the right to submit it under the open source license
+  indicated in the file; or
+
+* (b) The contribution is based upon previous work that, to the best
+  of my knowledge, is covered under an appropriate open source
+  license and I have the right under that license to submit that
+  work with modifications, whether created in whole or in part
+  by me, under the same open source license (unless I am
+  permitted to submit under a different license), as indicated
+  in the file; or
+
+* (c) The contribution was provided directly to me by some other
+  person who certified (a), (b) or (c) and I have not modified
+  it.
+
+* (d) I understand and agree that this project and the contribution
+  are public and that a record of the contribution (including all
+  personal information I submit with it, including my sign-off) is
+  maintained indefinitely and may be redistributed consistent with
+  this project or the open source license(s) involved.

+ 61 - 0
Source/bsf/CONTRIBUTORS.md

@@ -0,0 +1,61 @@
+Huge thanks to all the patrons, contributors and donators!
+
+# Code contributions
+ - @almightykiwi - Early Linux fixes, Unix filesystem & crash handler implementation.
+ - @w-flo - XInput implementation on Linux. Various help with Mesa rendering issues.
+ - @MarcoROG - Range/step values for sliders. Various memory leaks resolved.
+ - @nemerle - Various bugfixes and code improvements.
+ - @jonesmz - Various refactors and code modernization. 
+ - @cwfitzgerald - Added cotire support and various build system improvements.
+ - @paolopaoletto - SmallVector, DynArray, MinHeap implementation, math library improvements, modernizing code.
+ - @guestnone - Logging system improvements.
+ - @Meumeu - Modernizing & clean-up of the build system.
+ - @ataulien - Added distance culling.
+ - @remoe - Initial work on MoltenVK render backend.
+ 
+# Commercial Tier 2 Patron
+- Chance McDonald [Founder] [Inactive]
+
+# Commercial Tier 1 Patrons
+- Nathan Warden [Founder] [Inactive]
+ 
+# Tier X Patrons
+- Henri Hyyryläinen
+- Dreyton Brereton
+- Christian Clavet [Founder]
+- jeaninde [Founder]
+- Galnart
+----------------------------------------
+- Paul Gruenbacher [Inactive]
+- Robert Campbell [Founder] [Inactive]
+- John Lagerquist [Founder] [Inactive]
+- Andreas Schüle [Founder] [Inactive]
+
+
+# Tier 3 Patrons
+
+# Tier 2 Patrons
+- Adisibo
+- Andre Taulien
+- Nemerle [Founder]
+- Thomas Tang [Founder]
+----------------------------------------
+- Andrzej Oraniewicz [Founder] [Inactive]
+
+# Tier 1 Patrons
+- Andreas Schüle
+- Timothy Green
+- Marko Kovačić
+-----------------------------------------
+- Nekith [Founder] [Inactive]
+- Vladimir Zakharov [Founder] [Inactive]
+- Akaoray [Founder] [Inactive]
+- Danijel Ribić [Founder] [Inactive]
+- Summer Softleigh [Founder] [Inactive]
+
+# Donators
+- Andre Taulien
+- Žan Strahija [Founder]
+- Michael Jones [Founder]
+- Patrick Meyer [Founder]
+- Paolo Francesco

+ 1 - 0
Source/bsf/Data/.reqversion

@@ -0,0 +1 @@
+35

+ 954 - 0
Source/bsf/Data/Raw/DataList.json

@@ -0,0 +1,954 @@
+{
+    "AnimatedSprites": null,
+    "Cursors": [
+        {
+            "Path": "Arrow.psd",
+            "UUID": "4c1c6cf4-7d37-46f1-9263-a8f0d498b7e0"
+        },
+        {
+            "Path": "ArrowDrag.psd",
+            "UUID": "2eebb0e7-4d21-43d5-9349-d2b0c71aabbe"
+        },
+        {
+            "Path": "ArrowLeftRight.psd",
+            "UUID": "23830d8f-f81f-4141-9b61-e0414b5e3f58"
+        },
+        {
+            "Path": "Deny.psd",
+            "UUID": "caba0f11-d080-4876-b832-9d664adac056"
+        },
+        {
+            "Path": "IBeam.psd",
+            "UUID": "38e6a064-8225-4126-8198-7892af7fedd0"
+        },
+        {
+            "Path": "SizeNESW.psd",
+            "UUID": "a7ac5600-acc8-4692-b149-a52cd4116af7"
+        },
+        {
+            "Path": "SizeNS.psd",
+            "UUID": "1f8930d0-953c-4575-b453-5a3bd62b4873"
+        },
+        {
+            "Path": "SizeNWSE.psd",
+            "UUID": "7ff92ce5-cba0-4846-a1b2-1249e67c371a"
+        },
+        {
+            "Path": "SizeWE.psd",
+            "UUID": "dfcb4a8a-f213-4697-9b61-4cc4368f530f"
+        },
+        {
+            "Path": "Wait.psd",
+            "UUID": "7ad1fc24-5e08-489c-8cd4-02b4e452a412"
+        }
+    ],
+    "Fonts": [
+        {
+            "Antialiasing": false,
+            "Name": "arial.ttf",
+            "Path": "arial.ttf",
+            "Sizes": [
+                8
+            ],
+            "UUID": "c9f08cab-f9c9-47c4-96e0-1066a8d4455b"
+        }
+    ],
+    "GUISkin": {
+        "Path": "GUISkin.json",
+        "UUID": "c1bf9a9d-4355-4841-a538-25e67730ec4b"
+    },
+    "Icons": [
+        {
+            "Path": "bsfIcon.png",
+            "UUID": "fc1a1c8f-4242-c2e9-728b-42429f5b7cd2"
+        }
+    ],
+    "Includes": [
+        {
+            "Path": "PerCameraData.bslinc",
+            "UUID": "c1e4d309-8c20-4fd4-bf14-eb129af39d4e"
+        },
+        {
+            "Path": "PerObjectData.bslinc",
+            "UUID": "ed07f3ce-3abd-4379-b8f0-4c884af524a7"
+        },
+        {
+            "Path": "PPBase.bslinc",
+            "UUID": "44dc42de-410a-4949-a2e8-cb451a416481"
+        },
+        {
+            "Path": "PPTonemapCommon.bslinc",
+            "UUID": "a5d1c354-a05d-45b4-bccf-2f88db9aecb0"
+        },
+        {
+            "Path": "PPWhiteBalance.bslinc",
+            "UUID": "b46d1cee-75f7-4387-8b32-7eb646f099f4"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc",
+            "UUID": "eef92578-b8ed-45d6-8eed-d85697ade3c5"
+        },
+        {
+            "Path": "VolumeRenderBase.bslinc",
+            "UUID": "6d0dfe4e-45ef-4fe1-9344-33b8b9e69048"
+        },
+        {
+            "Path": "SurfaceData.bslinc",
+            "UUID": "3364cb35-9140-4571-8fae-d5648ae7386f"
+        },
+        {
+            "Path": "BasePass.bslinc",
+            "UUID": "ec1d3d37-5509-4bcb-9be2-93208d6ae224"
+        },
+        {
+            "Path": "LightGridCommon.bslinc",
+            "UUID": "26caf86e-433c-4ade-9bc5-55c136d82912"
+        },
+        {
+            "Path": "SHCommon.bslinc",
+            "UUID": "b2a3ecfd-77d7-4a4c-900b-91c3804f4a58"
+        },
+        {
+            "Path": "ImageBasedLighting.bslinc",
+            "UUID": "ff162afd-ed4d-4460-8982-ad6c0f7be19c"
+        },
+        {
+            "Path": "GBufferInput.bslinc",
+            "UUID": "d481b49b-a60b-4d0d-bd8c-c3ed9c53fc6c"
+        },
+        {
+            "Path": "GBufferOutput.bslinc",
+            "UUID": "46df32e8-6400-46d5-9ac2-beefdc9016ad"
+        },
+        {
+            "Path": "ShadowDepthBase.bslinc",
+            "UUID": "ff6f2a9d-6766-4f80-984b-c159a5fd3c5e"
+        },
+        {
+            "Path": "DeferredLightCommon.bslinc",
+            "UUID": "8ffd1df9-3de4-442d-adca-95e8766fa81a"
+        },
+        {
+            "Path": "ShadowProjectionCommon.bslinc",
+            "UUID": "36da8807-b201-482b-b320-96f5300d1751"
+        },
+        {
+            "Path": "PPGaussianDOFCommon.bslinc",
+            "UUID": "78521453-a352-4fd8-b997-8e58942dee2d"
+        },
+        {
+            "Path": "RayMarch.bslinc",
+            "UUID": "c64f9bee-c0c2-442e-9f59-35da0494e827"
+        },
+        {
+            "Path": "ColorSpace.bslinc",
+            "UUID": "9fc44e5a-b23f-4002-bd2e-793cf0d03c32"
+        },
+        {
+            "Path": "TemporalResolve.bslinc",
+            "UUID": "b0ce01c2-3326-445e-bf2b-35cf8d9d6e1b"
+        },
+        {
+            "Path": "ImportanceSampling.bslinc",
+            "UUID": "52a46920-860e-4d05-a7c3-34e926b02664"
+        },
+        {
+            "Path": "PPEyeAdaptationCommon.bslinc",
+            "UUID": "1a0c4fb5-49b6-7eb8-28b8-49b698a6b7fe"
+        },
+        {
+            "Path": "VertexInput.bslinc",
+            "UUID": "2d0dc7e6-46bf-03bb-f48d-46bf25dda973"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc",
+            "UUID": "71fa057c-4f77-8bb0-acb3-4f77cb467414"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc",
+            "UUID": "72a07be3-4949-b48c-449b-49491e752f8a"
+        },
+        {
+            "Path": "ForwardLighting.bslinc",
+            "UUID": "bf35f9b3-4c48-30bc-d480-4c48cb444517"
+        },
+        {
+            "Path": "DirectLighting.bslinc",
+            "UUID": "efdae4c9-40bd-5983-ada9-40bda7a45f55"
+        },
+        {
+            "Path": "PerFrameData.bslinc",
+            "UUID": "64225149-43b6-698e-809e-43b647d8d09d"
+        },
+        {
+            "Path": "ParticleVertex.bslinc",
+            "UUID": "c830bfdf-4e45-56a3-46b2-4e4598d3b312"
+        },
+        {
+            "Path": "RadixSortCommon.bslinc",
+            "UUID": "944603e9-4407-32cb-69a8-44078adaeceb"
+        },
+        {
+            "Path": "GpuParticleTileVertex.bslinc",
+            "UUID": "a6943ff6-4123-4bbc-d397-4123245a6cae"
+        },
+        {
+            "Path": "VertexCommon.bslinc",
+            "UUID": "7f6cf98d-48da-efc9-c1a2-48da18703e92"
+        },
+        {
+            "Path": "DepthInput.bslinc",
+            "UUID": "06b077c9-432d-43b1-8884-432d5ddfe878"
+        },
+        {
+            "Path": "MaskInput.bslinc",
+            "UUID": "e69b8e15-4301-f0fb-11ad-430140d3a7d6"
+        },
+        {
+            "Path": "ShapingCommon.bslinc",
+            "UUID": "3d40728a-454c-0618-cb8d-454c0d8b9d07"
+        },
+        {
+            "Path": "PPGaussianBlurCommon.bslinc",
+            "UUID": "5fcd03a8-489b-a141-a2b6-489b4995ad59"
+        },
+        {
+            "Path": "DepthOfFieldCommon.bslinc",
+            "UUID": "54902d51-41df-28f2-a9ad-41dfa3f28758"
+        },
+        {
+            "Path": "SpriteCommon.bslinc",
+            "UUID": "4249d0c0-4edd-6c00-2b82-4eddf29ba6f0"
+        }
+    ],
+    "Shaders": [
+        {
+            "Path": "Blit.bsl",
+            "UUID": "7953ba8a-6fc6-43ad-a472-cce558105cde"
+        },
+        {
+            "Path": "Default.bsl",
+            "UUID": "a8e36d37-d6f7-4117-bbba-31dca716c8a3"
+        },
+        {
+            "Path": "Diffuse.bsl",
+            "UUID": "4e2b2437-0a02-456c-8d98-71df27ccf697"
+        },
+        {
+            "Path": "PPCreateTonemapLUT.bsl",
+            "UUID": "766adb8c-1302-4f2d-a26f-6a0f8ff6b147"
+        },
+        {
+            "Path": "PPDownsample.bsl",
+            "UUID": "c1a11fc5-f7b4-422d-a359-70be6dac05ef"
+        },
+        {
+            "Path": "PPEyeAdaptation.bsl",
+            "UUID": "6baa01c8-fe61-4613-9fa9-a9ae045dfc64"
+        },
+        {
+            "Path": "PPEyeAdaptHistogram.bsl",
+            "UUID": "5414fffc-a96c-4a8a-86e3-c24050d14363"
+        },
+        {
+            "Path": "PPEyeAdaptHistogramReduce.bsl",
+            "UUID": "4c98431b-bd58-4160-8ff0-7d12add946fa"
+        },
+        {
+            "Path": "PPTonemapping.bsl",
+            "UUID": "cbe752ea-301a-47aa-b8ae-2f35d103595c"
+        },
+        {
+            "Path": "PPTonemapping.bsl",
+            "UUID": "36366f4c-3584-4dbf-9c2b-bb2638438b33"
+        },
+        {
+            "Path": "PPTonemapping.bsl",
+            "UUID": "520d9228-c7b5-492e-bd08-1fdc7e033ea8"
+        },
+        {
+            "Path": "PPTonemapping.bsl",
+            "UUID": "a8aa01e7-7e72-4f84-89f8-a0097250b0d3"
+        },
+        {
+            "Path": "Skybox.bsl",
+            "UUID": "b1c191fa-0c24-4987-8d3b-72b17511621d"
+        },
+        {
+            "Path": "SpriteLine.bsl",
+            "UUID": "3a9e31a8-cbef-49ac-af2a-2dc200372d36"
+        },
+        {
+            "Path": "SpriteText.bsl",
+            "UUID": "25df2c87-c206-4c2f-ab2b-3aad9e7f90f1"
+        },
+        {
+            "Path": "TiledDeferredLighting.bsl",
+            "UUID": "787d7293-f335-4eda-a897-c706e6b5c818"
+        },
+        {
+            "Path": "LightGridLLCreation.bsl",
+            "UUID": "097acd4f-9eeb-4a25-bbd8-e3ee31118a0f"
+        },
+        {
+            "Path": "LightGridLLReduction.bsl",
+            "UUID": "d190e4e1-5ea9-4961-9ee3-b5df218cb5e6"
+        },
+        {
+            "Path": "Transparent.bsl",
+            "UUID": "10db2029-145c-424e-8399-2be48aa66680"
+        },
+        {
+            "Path": "ReflectionCubeDownsample.bsl",
+            "UUID": "0efe7fe5-f7da-4ac8-a634-522bf6791dad"
+        },
+        {
+            "Path": "ReflectionCubeImportanceSample.bsl",
+            "UUID": "ed16fdd9-7982-4f5c-ae63-8303c786e9ad"
+        },
+        {
+            "Path": "IrradianceComputeSH.bsl",
+            "UUID": "5b431ac7-c97a-407e-9ca3-6845d6cd0d6c"
+        },
+        {
+            "Path": "IrradianceProjectSH.bsl",
+            "UUID": "64d9535e-b5e7-42f5-8dc4-c0b15a5c48b9"
+        },
+        {
+            "Path": "IrradianceReduceSH.bsl",
+            "UUID": "aa40f2be-00e4-4322-a4bf-e435528f1e6e"
+        },
+        {
+            "Path": "TiledDeferredImageBasedLighting.bsl",
+            "UUID": "6029db14-107f-43df-9a33-7105c56aa0fd"
+        },
+        {
+            "Path": "ShadowDepthCube.bsl",
+            "UUID": "285c7b78-053a-4763-899e-8e11b5d4dac7"
+        },
+        {
+            "Path": "ShadowDepthNormal.bsl",
+            "UUID": "c9b64475-375d-410e-8cd4-e1b1181318d4"
+        },
+        {
+            "Path": "ShadowDepthDirectional.bsl",
+            "UUID": "acd0f016-8084-4b32-806c-66a85b34ee5a"
+        },
+        {
+            "Path": "DeferredDirectionalLight.bsl",
+            "UUID": "17d573f8-1142-4257-9e32-90038d3786f3"
+        },
+        {
+            "Path": "DeferredPointLight.bsl",
+            "UUID": "428d9fde-9838-4367-9857-57bd80c8f599"
+        },
+        {
+            "Path": "ShadowProject.bsl",
+            "UUID": "93667cf9-c699-4d92-ba20-6e2e912f76fa"
+        },
+        {
+            "Path": "ShadowProjectOmni.bsl",
+            "UUID": "50447773-98e9-410c-8f64-eacbb9622c52"
+        },
+        {
+            "Path": "ShadowProjectStencil.bsl",
+            "UUID": "c8625547-f5e7-43df-85cf-4da6e1806cf9"
+        },
+        {
+            "Path": "PPGaussianBlur.bsl",
+            "UUID": "14b16378-6282-4d2d-bc4c-2d662cc98066"
+        },
+        {
+            "Path": "PPGaussianDOFSeparate.bsl",
+            "UUID": "78285aaa-a862-46f3-837c-d13abbe8b351"
+        },
+        {
+            "Path": "PPGaussianDOFCombine.bsl",
+            "UUID": "2743e8f1-97cf-4441-b9e4-aad280343901"
+        },
+        {
+            "Path": "PPBuildHiZ.bsl",
+            "UUID": "1b314248-1965-49c5-968b-ae923c2af717"
+        },
+        {
+            "Path": "PPFXAA.bsl",
+            "UUID": "f064b202-dbc0-440f-bd9f-37f094b2c521"
+        },
+        {
+            "Path": "PPSSAO.bsl",
+            "UUID": "7a65b0f1-9a37-452e-ba3f-2a1fb58362cb"
+        },
+        {
+            "Path": "PPSSAODownsample.bsl",
+            "UUID": "c06a649a-646e-48cf-89fc-f97e46f75264"
+        },
+        {
+            "Path": "PPSSAOBlur.bsl",
+            "UUID": "46c7f742-a02d-4e1f-b121-047cd62a6d4d"
+        },
+        {
+            "Path": "PPSSRTrace.bsl",
+            "UUID": "daadcf73-1ae5-4f8f-90e5-a6e538832304"
+        },
+        {
+            "Path": "PPSSRStencil.bsl",
+            "UUID": "a9eb5ddf-1196-48d8-9403-a80a9595cde0"
+        },
+        {
+            "Path": "TetrahedraRender.bsl",
+            "UUID": "5912b0ab-b2be-4c0e-a039-91fcc2d3e536"
+        },
+        {
+            "Path": "IrradianceEvaluate.bsl",
+            "UUID": "6f6ed59d-b94b-428e-91bc-82c94ed48892"
+        },
+        {
+            "Path": "Clear.bsl",
+            "UUID": "5a325167-5bab-4925-8b50-95434448930f"
+        },
+        {
+            "Path": "DebugDraw.bsl",
+            "UUID": "6d3eb44e-b7e9-4c5a-8f8c-e5695607ca13"
+        },
+        {
+            "Path": "PPEncodeDepth.bsl",
+            "UUID": "8bbf989a-9f8d-4864-8332-7f8c499685ba"
+        },
+        {
+            "Path": "MSAACoverage.bsl",
+            "UUID": "151465e1-e123-475d-97f5-77bc04bc15ce"
+        },
+        {
+            "Path": "MSAACoverageStencil.bsl",
+            "UUID": "f4344aa8-d286-4f10-b25d-a76302487fa8"
+        },
+        {
+            "Path": "IrradianceAccumulateCubeSH.bsl",
+            "UUID": "92c3b1a4-4f21-6566-7cbe-4f21bcc8e0aa"
+        },
+        {
+            "Path": "IrradianceAccumulateSH.bsl",
+            "UUID": "c52783b0-4974-f349-0fae-4974f81eb485"
+        },
+        {
+            "Path": "IrradianceComputeSHFrag.bsl",
+            "UUID": "cc8139f7-4a48-4a74-2691-4a48e288b8dd"
+        },
+        {
+            "Path": "PPEyeAdaptationBasic.bsl",
+            "UUID": "b00f745f-4d40-5ebf-bcae-4d40161ceb6b"
+        },
+        {
+            "Path": "PPEyeAdaptationBasicSetup.bsl",
+            "UUID": "86cda288-4fd5-4d0f-a5a2-4fd56d3b92ea"
+        },
+        {
+            "Path": "DeferredIBLFinalize.bsl",
+            "UUID": "5973a208-4956-8ea4-85af-495668744158"
+        },
+        {
+            "Path": "DeferredIBLProbe.bsl",
+            "UUID": "76dd3c87-4cea-7066-9182-4ceade228448"
+        },
+        {
+            "Path": "DeferredIBLSetup.bsl",
+            "UUID": "115c9291-4260-606d-1098-426042ea073a"
+        },
+        {
+            "Path": "DeferredIBLSky.bsl",
+            "UUID": "e3c4e19e-4d65-dd20-bca1-4d65a0b9980b"
+        },
+        {
+            "Path": "ShadowDepthNormalNoPS.bsl",
+            "UUID": "5335edda-c14c-0158-d73e-f880d58d0596"
+        },
+        {
+            "Path": "ParticlesUnlit.bsl",
+            "UUID": "cf6ba550-40b3-6e4e-55ab-40b3abd077f1"
+        },
+        {
+            "Path": "RadixSortCount.bsl",
+            "UUID": "63499594-43a3-71da-64bf-43a3a5efe7ac"
+        },
+        {
+            "Path": "RadixSortPrefixScan.bsl",
+            "UUID": "6c0c0b72-4e01-6863-fa8d-4e01a4df8046"
+        },
+        {
+            "Path": "RadixSortReorder.bsl",
+            "UUID": "0dd1ac91-402a-9e2e-2ba1-402a77f5efda"
+        },
+        {
+            "Path": "ClearLoadStore.bsl",
+            "UUID": "1e87bcc7-4477-7234-1482-4477d48e52d4"
+        },
+        {
+            "Path": "TextureArrayToMSAATexture.bsl",
+            "UUID": "a99d5d8d-4daa-3ced-a99b-4daa6fe91297"
+        },
+        {
+            "Path": "GpuParticleClear.bsl",
+            "UUID": "75174c83-4231-d406-ffbe-423122141e61"
+        },
+        {
+            "Path": "GpuParticleInject.bsl",
+            "UUID": "d9df7b26-4eec-3e02-2f8c-4eec7782b461"
+        },
+        {
+            "Path": "GpuParticleSimulate.bsl",
+            "UUID": "95e03c75-4b53-ec71-4686-4b53d4367cd4"
+        },
+        {
+            "Path": "GpuParticleBounds.bsl",
+            "UUID": "ba7c69fd-4b9b-3fc4-888a-4b9b29180376"
+        },
+        {
+            "Path": "GpuParticleCurveInject.bsl",
+            "UUID": "65877027-40ba-0789-cd8a-40ba393eec73"
+        },
+        {
+            "Path": "GpuParticleSortPrepare.bsl",
+            "UUID": "a7de4e11-4de4-8ddc-8585-4de4d39a91b9"
+        },
+        {
+            "Path": "RadixSortClear.bsl",
+            "UUID": "26e580ba-4b0c-5872-dcb6-4b0c47c5eda8"
+        },
+        {
+            "Path": "ParticlesLit.bsl",
+            "UUID": "dca4932c-472b-bf22-ecbe-472bb581143d"
+        },
+        {
+            "Path": "ParticlesLitOpaque.bsl",
+            "UUID": "520813e4-4e15-b067-72ae-4e15d603d443"
+        },
+        {
+            "Path": "PPBloomClip.bsl",
+            "UUID": "0a832da9-47df-8c7d-268a-47dfcd36cc48"
+        },
+        {
+            "Path": "Decal.bsl",
+            "UUID": "8d448d61-463b-84fe-d0a1-463b897090a3"
+        },
+        {
+            "Path": "SpriteImage.bsl",
+            "UUID": "430d9f0c-491b-d499-a994-491bfb3a1907"
+        },
+        {
+            "Path": "PPScreenSpaceLensFlare.bsl",
+            "UUID": "77a9c58f-4f0c-b1db-6294-4f0c85c504db"
+        },
+        {
+            "Path": "Composite.bsl",
+            "UUID": "9035d668-46c0-ba4a-75b0-46c0cf3c64e7"
+        },
+        {
+            "Path": "BicubicUpsample.bsl",
+            "UUID": "b690e22c-45a1-6cda-3cb1-45a1a944984e"
+        },
+        {
+            "Path": "PPBokehDOF.bsl",
+            "UUID": "3c4f084d-4a50-502e-3cad-4a501d10d663"
+        },
+        {
+            "Path": "PPBokehDOFCombine.bsl",
+            "UUID": "a0b378f9-491d-b2d8-359f-491de29726a9"
+        },
+        {
+            "Path": "PPBokehDOFPrepare.bsl",
+            "UUID": "0a6293a4-465b-7b01-e8ae-465be083fc8e"
+        },
+        {
+            "Path": "PPMotionBlur.bsl",
+            "UUID": "041e3400-4192-d889-a781-41923dbdd11a"
+        },
+        {
+            "Path": "PPChromaticAberration.bsl",
+            "UUID": "628e06c1-4fec-0620-309a-4fec04df32fd"
+        },
+        {
+            "Path": "PPFilmGrain.bsl",
+            "UUID": "2c006eb5-4977-3f27-75aa-49770fb3abe6"
+        },
+        {
+            "Path": "TemporalFiltering.bsl",
+            "UUID": "2e904b94-43db-a066-d1ac-43db9b91d61e"
+        }
+    ],
+    "Skin": [
+        {
+            "Path": "ButtonActive.png",
+            "SpriteUUID": "4b1deba9-af84-43ab-a9eb-d9a79f52962c",
+            "TextureUUID": "51c3a4c6-3060-402d-9412-ba686b7907d5"
+        },
+        {
+            "Path": "ButtonHover.png",
+            "SpriteUUID": "710ab76c-702c-402f-a1bb-e3c180105a3c",
+            "TextureUUID": "a6748d8f-5310-4732-a8e6-10ab948bce7b"
+        },
+        {
+            "Path": "ButtonNormal.png",
+            "SpriteUUID": "094326be-9c04-4031-90d1-c2b8ac3b2a9e",
+            "TextureUUID": "910b7a3e-115f-4c46-888e-f0b642c07613"
+        },
+        {
+            "Path": "DropDownBoxArrowDownHover.png",
+            "SpriteUUID": "3096fd0b-a964-428a-a220-0a8a4f3f6264",
+            "TextureUUID": "303020d5-6af6-4e92-81d6-4574e237e7ee"
+        },
+        {
+            "Path": "DropDownBoxArrowDownNormal.png",
+            "SpriteUUID": "71e5136e-ea1b-4297-aed9-891678e3c0e7",
+            "TextureUUID": "fec60788-a47f-46ee-90a9-29a5ac71b899"
+        },
+        {
+            "Path": "DropDownBoxArrowUpHover.png",
+            "SpriteUUID": "0bbf87d4-c412-4336-bef3-744771594648",
+            "TextureUUID": "bd78ac65-6ec7-4d80-b414-31992e0f1b9d"
+        },
+        {
+            "Path": "DropDownBoxArrowUpNormal.png",
+            "SpriteUUID": "7a4f1a4e-c07a-4283-809c-13599a4fdabf",
+            "TextureUUID": "2010e471-a623-4e1c-8b7e-107d2926b0dd"
+        },
+        {
+            "Path": "DropDownBoxBg.png",
+            "SpriteUUID": "40a8dd33-26bc-42a5-899b-962fcd8f5bfa",
+            "TextureUUID": "a380dde4-e929-4e09-8eb3-3c9f4d1bdee6"
+        },
+        {
+            "Path": "DropDownBoxEntryHover.png",
+            "SpriteUUID": "c0dc00b7-ba21-45f4-a9cc-acb923b1da7c",
+            "TextureUUID": "190d5968-64be-460a-9861-e8f4f9cae68d"
+        },
+        {
+            "Path": "DropDownBoxEntryNormal.png",
+            "SpriteUUID": "8aad937c-499d-4935-bd7d-d9389aed4f33",
+            "TextureUUID": "1070d714-83a2-4d15-860b-c3607ff5a3e0"
+        },
+        {
+            "Path": "DropDownBoxEntryToggleHover.png",
+            "SpriteUUID": "4f0bfa04-ccfb-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "4f0bfd7e-ccfb-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "DropDownBoxEntryToggleHoverOn.png",
+            "SpriteUUID": "4f0c0292-ccfb-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "4f0c04fe-ccfb-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "DropDownBoxEntryToggleNormal.png",
+            "SpriteUUID": "4f0c06b6-ccfb-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "4f0c0864-ccfb-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "DropDownBoxEntryToggleNormalOn.png",
+            "SpriteUUID": "4f0c0a12-ccfb-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "4f0c0bca-ccfb-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "DropDownBoxExpandBtnHover.png",
+            "SpriteUUID": "94748c7b-dc27-44dd-8798-22f76ea69b36",
+            "TextureUUID": "a079167a-d28f-4c28-a75c-be6ffca2f39a"
+        },
+        {
+            "Path": "DropDownBoxExpandBtnNormal.png",
+            "SpriteUUID": "aa18bb97-3393-47e9-88b5-5460c3076b83",
+            "TextureUUID": "0b2417af-281d-46b1-8f76-66875c04eb98"
+        },
+        {
+            "Path": "DropDownBoxScrollHandle.png",
+            "SpriteUUID": "7f29b25d-2615-4a68-8085-5da7484b6dab",
+            "TextureUUID": "74fbf74b-f296-4c2d-9ec6-15a29120372a"
+        },
+        {
+            "Path": "DropDownBoxSeparator.png",
+            "SpriteUUID": "7a91e73d-7982-40c9-a2f3-356b1b1356b4",
+            "TextureUUID": "0dee08fe-3f2e-452b-8ec4-26923148b365"
+        },
+        {
+            "Path": "DropDownBoxSideBg.png",
+            "SpriteUUID": "9cc6ecda-5535-4e2d-9a31-7548552fb85d",
+            "TextureUUID": "041cacb8-4368-4c37-acf3-e5ad50394cfe"
+        },
+        {
+            "Path": "DropDownButtonActive.png",
+            "SpriteUUID": "6ea86ad8-7fa1-49c9-bd8d-fd8b751840d3",
+            "TextureUUID": "5b90999e-dcab-40b7-9081-ab770b590090"
+        },
+        {
+            "Path": "DropDownButtonHover.png",
+            "SpriteUUID": "c6e974fc-f7e7-4b7a-a7f8-c7ca319bb40d",
+            "TextureUUID": "809762ea-337b-4eae-bc5e-a135266c7a4d"
+        },
+        {
+            "Path": "DropDownButtonNormal.png",
+            "SpriteUUID": "b897e03d-78fc-43b5-a41b-98b8d20deeab",
+            "TextureUUID": "627102cf-ad03-4766-bf56-82231a17fbb8"
+        },
+        {
+            "Path": "InputBoxActive.png",
+            "SpriteUUID": "44aaada3-05c6-45d1-bb56-56c379c74f5c",
+            "TextureUUID": "a249502e-258d-4202-98f9-448193478d0f"
+        },
+        {
+            "Path": "InputBoxHover.png",
+            "SpriteUUID": "a75bbffd-cf3a-47f5-8d0c-3bc55171d8d0",
+            "TextureUUID": "b03edeb6-4e22-4296-aad2-9816a7ba0d4d"
+        },
+        {
+            "Path": "InputBoxNormal.png",
+            "SpriteUUID": "e4b076be-1527-49b9-9b1d-7aa20f8c3e04",
+            "TextureUUID": "21887ed4-19c6-4748-81c6-ae86737e53f9"
+        },
+        {
+            "Path": "ScrollArrowDownActive.png",
+            "SpriteUUID": "597b9cbf-ae13-458e-bb49-f58d1a03dc34",
+            "TextureUUID": "f7fa3bf5-019a-4e97-ba98-2e064a76504b"
+        },
+        {
+            "Path": "ScrollArrowDownHover.png",
+            "SpriteUUID": "778f44b2-6838-4b27-94ea-e2758e3ba607",
+            "TextureUUID": "499885b7-03ac-4e57-b985-b024e567e497"
+        },
+        {
+            "Path": "ScrollArrowDownNormal.png",
+            "SpriteUUID": "bf29509d-42e7-4419-9c2f-d13e724fcad5",
+            "TextureUUID": "e847475e-e12c-4c04-a0f9-2ac6a4ac4e3b"
+        },
+        {
+            "Path": "ScrollArrowLeftActive.png",
+            "SpriteUUID": "e0b1eb29-e30e-4b17-9070-10fa3385b6f3",
+            "TextureUUID": "2838ea6d-dcf8-41b1-8cd5-e17685c8d539"
+        },
+        {
+            "Path": "ScrollArrowLeftHover.png",
+            "SpriteUUID": "62ddc954-5d60-40e7-ba68-487266e18acc",
+            "TextureUUID": "6c0b8f12-d250-4988-b380-bc253b9408a6"
+        },
+        {
+            "Path": "ScrollArrowLeftNormal.png",
+            "SpriteUUID": "cdf5ee62-8466-4527-a5be-01c39cf758a9",
+            "TextureUUID": "bcbf65ba-58af-41b7-a6e5-b18f2ccb806e"
+        },
+        {
+            "Path": "ScrollArrowRightActive.png",
+            "SpriteUUID": "9fa51060-439f-43b3-bf88-f217a25175af",
+            "TextureUUID": "9331b7be-ad3e-4422-81d3-f04dfdd9fc6e"
+        },
+        {
+            "Path": "ScrollArrowRightHover.png",
+            "SpriteUUID": "854a447f-459f-49e5-bbc5-594bab7dec60",
+            "TextureUUID": "0350fba6-773c-4410-b9e1-4840c88210c2"
+        },
+        {
+            "Path": "ScrollArrowRightNormal.png",
+            "SpriteUUID": "dc3853f7-d852-42ab-87c3-5152273e6f22",
+            "TextureUUID": "0327097a-e372-41e4-8061-1de4f1e59c2c"
+        },
+        {
+            "Path": "ScrollArrowUpActive.png",
+            "SpriteUUID": "4f2746c8-ca7c-45ea-9ded-c6175103a202",
+            "TextureUUID": "29f4be13-6b63-4409-a9ad-2d67896468ce"
+        },
+        {
+            "Path": "ScrollArrowUpHover.png",
+            "SpriteUUID": "73ba26b4-9006-47f5-97e3-e1ec13412e65",
+            "TextureUUID": "da6b0a0f-2c15-4439-815d-626b7e245704"
+        },
+        {
+            "Path": "ScrollArrowUpNormal.png",
+            "SpriteUUID": "0fecba6f-f25b-40f4-9d25-44ed6d49b5b2",
+            "TextureUUID": "a7055810-cf81-4d8c-9617-14cdcb3f1dd1"
+        },
+        {
+            "Path": "ScrollBarHBackground.png",
+            "SpriteUUID": "4f1e25e0-a813-46b0-b658-ef43055e5906",
+            "TextureUUID": "9f0c2a63-6977-477c-86e7-cc4c0f86649d"
+        },
+        {
+            "Path": "ScrollBarHHandleActive.png",
+            "SpriteUUID": "92fa9af8-a061-4c95-b8dd-0551854ec8de",
+            "TextureUUID": "6d3d7024-507a-47c6-a9e2-db4af5bf21b0"
+        },
+        {
+            "Path": "ScrollBarHHandleHover.png",
+            "SpriteUUID": "f460a868-9dff-4147-ac4e-caeaa4187e05",
+            "TextureUUID": "fa4ea09d-bf35-4c92-8324-770e84e7c071"
+        },
+        {
+            "Path": "ScrollBarHHandleNormal.png",
+            "SpriteUUID": "6e2f9f25-88ad-4256-bf68-5ca80ecd2db1",
+            "TextureUUID": "7b0c9474-d0ea-4370-894c-bc4386f6fa8b"
+        },
+        {
+            "Path": "ScrollBarHHandleResizeableActive.png",
+            "SpriteUUID": "1828219c-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "18282476-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ScrollBarHHandleResizeableHover.png",
+            "SpriteUUID": "1828278c-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "18282b24-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ScrollBarHHandleResizeableNormal.png",
+            "SpriteUUID": "18282c00-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "18282cd2-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ScrollBarVBackground.png",
+            "SpriteUUID": "b37d3e5b-891a-4950-a4f9-3f8fa820d40a",
+            "TextureUUID": "18bd26ea-dc4e-4277-bef7-83a6ba57a137"
+        },
+        {
+            "Path": "ScrollBarVHandleActive.png",
+            "SpriteUUID": "40125cc7-3b3d-454c-b1ed-6e3d0b3ffe0c",
+            "TextureUUID": "2d6c3301-55bd-4dd2-8f78-9099c8167b9e"
+        },
+        {
+            "Path": "ScrollBarVHandleHover.png",
+            "SpriteUUID": "cb559a44-4b34-47e2-9308-344af1957a99",
+            "TextureUUID": "6728125e-b299-4b80-a788-c80d3671724f"
+        },
+        {
+            "Path": "ScrollBarVHandleNormal.png",
+            "SpriteUUID": "3d44c9c9-9bdd-48ab-97b1-4a7e929edb46",
+            "TextureUUID": "98f70a3b-8da7-44b0-85d1-6aae294df9dc"
+        },
+        {
+            "Path": "ScrollBarVHandleResizeableActive.png",
+            "SpriteUUID": "3ae0a6a0-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "3ae0aa60-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ScrollBarVHandleResizeableHover.png",
+            "SpriteUUID": "3ae0ae0c-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "3ae0afd8-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ScrollBarVHandleResizeableNormal.png",
+            "SpriteUUID": "3ae0b230-cd06-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "3ae0b3f2-cd06-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderHandleActive.png",
+            "SpriteUUID": "1ede5e0e-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede6138-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderHandleHover.png",
+            "SpriteUUID": "1ede62aa-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede6430-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderHandleNormal.png",
+            "SpriteUUID": "1ede650c-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede6958-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderHBackground.png",
+            "SpriteUUID": "1ede6b24-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede6cb4-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderHFill.png",
+            "SpriteUUID": "1ede6d90-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede6ef8-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderVBackground.png",
+            "SpriteUUID": "1ede70ce-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede71b4-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "SliderVFill.png",
+            "SpriteUUID": "1ede736c-ccff-11e6-9d9d-cec0c932ce01",
+            "TextureUUID": "1ede7448-ccff-11e6-9d9d-cec0c932ce01"
+        },
+        {
+            "Path": "ToggleHover.png",
+            "SpriteUUID": "daa9fd29-4228-47ca-8b9e-39636f0b566c",
+            "TextureUUID": "df341eda-d4b5-4940-9871-d39e4f90fbfc"
+        },
+        {
+            "Path": "ToggleHoverOn.png",
+            "SpriteUUID": "820ec0f3-4129-4db2-9042-13bf11c09fce",
+            "TextureUUID": "9d66b8d0-9469-4e72-90bd-a705373211a8"
+        },
+        {
+            "Path": "ToggleNormal.png",
+            "SpriteUUID": "381c8648-595b-4533-9861-e8b38ea671e2",
+            "TextureUUID": "5f947c87-5636-493d-9348-bfdbcf5a6edc"
+        },
+        {
+            "Path": "ToggleNormalOn.png",
+            "SpriteUUID": "15c41e2f-8f91-477d-af73-8085cd6699c9",
+            "TextureUUID": "34751e54-1376-410b-b7b7-958f8770f8e9"
+        },
+        {
+            "Path": "White.psd",
+            "SpriteUUID": "46bc0d6b-5cf0-4edc-bc5f-e0a06b8b04c2",
+            "TextureUUID": "723670c8-f634-435f-8887-001a6791226a"
+        },
+        {
+            "Path": "ButtonFocused.png",
+            "SpriteUUID": "57ce5eab-4310-79ad-3496-43107442c389",
+            "TextureUUID": "9dd24478-446e-8e10-82a8-446eca04620b"
+        },
+        {
+            "Path": "DropDownButtonFocused.png",
+            "SpriteUUID": "42d60331-4169-b6cc-41b2-4169ae0b6a85",
+            "TextureUUID": "7a3671b1-4ccb-e6ee-05a5-4ccb9ac57a06"
+        },
+        {
+            "Path": "SliderHBackgroundFocused.png",
+            "SpriteUUID": "ae02917b-436c-90f4-f584-436cb29932ac",
+            "TextureUUID": "346fef92-4524-dcdc-879c-4524dec1d093"
+        },
+        {
+            "Path": "SliderVBackgroundFocused.png",
+            "SpriteUUID": "9030b1e8-4d48-5ff9-d298-4d48b86e4632",
+            "TextureUUID": "8c887b69-4f8d-6a65-8ab5-4f8dd2247c26"
+        },
+        {
+            "Path": "ToggleFocused.png",
+            "SpriteUUID": "530f1b5d-410c-ba94-b095-410c4b4e6da1",
+            "TextureUUID": "06795729-437c-24f7-9088-437c1feea5aa"
+        },
+        {
+            "Path": "ToggleFocusedOn.png",
+            "SpriteUUID": "69a7b28d-4a9a-fa1d-dab6-4a9a881a91f8",
+            "TextureUUID": "e0d4afc2-4ba6-e6a5-57aa-4ba69061df71"
+        },
+        {
+            "Path": "ButtonFocusedHover.png",
+            "SpriteUUID": "89e494c9-4d6a-ae0e-0b96-4d6adec391ef",
+            "TextureUUID": "8bf1494e-4943-01d0-2a90-4943001f3f69"
+        },
+        {
+            "Path": "ToggleFocusedHover.png",
+            "SpriteUUID": "e9224fb0-4a2c-ab11-a4ab-4a2c9536bc40",
+            "TextureUUID": "7964c3a7-4366-a859-d09c-4366a8874f0a"
+        },
+        {
+            "Path": "ToggleFocusedHoverOn.png",
+            "SpriteUUID": "22a9c0b4-4d53-2113-fb97-4d53f1c43859",
+            "TextureUUID": "106417e8-4dc3-ab1f-efad-4dc35feb6df3"
+        },
+        {
+            "Path": "DropDownButtonFocusedHover.png",
+            "SpriteUUID": "6ff54b8e-4f01-bc8c-fd8d-4f01a316065b",
+            "TextureUUID": "138dc6d2-497e-8376-bc87-497e0cafc1d1"
+        }
+    ],
+    "SplashScreen": null,
+    "SpriteIcons": null,
+    "SpriteIcons3D": null,
+    "Textures": [
+        {
+            "Path": "BokehHex.png",
+            "UUID": "1f39006f-4092-f3c0-fe82-4092ec7b3957"
+        }
+    ]
+}

+ 13 - 0
Source/bsf/Data/Raw/DataPackageContents.txt

@@ -0,0 +1,13 @@
+Data/Cursors
+Data/Icons
+Data/Meshes
+Data/Shaders
+Data/Skin
+Data/Textures
+Data/.version
+Data/arial.ttf.asset
+Data/arial.ttf_8_texpage_0.asset
+Data/GUISkin.json.asset
+Data/SplashScreen.png.asset
+Data/Timestamp.asset
+Data/ResourceManifest.asset

+ 1281 - 0
Source/bsf/Data/Raw/GUISkin.json

@@ -0,0 +1,1281 @@
+[
+    {
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 14,
+        "minWidth": 10,
+        "name": "Label",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 1.0,
+                "g": 1.0,
+                "r": 1.0
+            }
+        }
+    },
+    {
+        "name": "Texture"
+    },	
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonActive.png"
+        },
+        "activeOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonActive.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "contentOffset": {
+            "bottom": 0,
+            "left": 3,
+            "right": 3,
+            "top": 2
+        },
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 21,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "ButtonHover.png"
+        },
+        "hoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonActive.png"
+        },
+        "margins": {
+            "bottom": 2,
+            "left": 0,
+            "right": 0,
+            "top": 0
+        },
+        "minWidth": 20,
+        "name": "Button",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "ButtonNormal.png"
+        },
+        "normalOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonActive.png"
+        },
+        "focused": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "ButtonFocused.png"
+        },
+        "focusedOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonFocused.png"
+        },
+        "focusedHover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "ButtonFocusedHover.png"
+        },
+        "focusedHoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.0,
+                "g": 0.0,
+                "r": 0.0
+            },
+            "texture": "ButtonFocusedHover.png"
+        },
+        "textHorzAlign": 1,
+        "textVertAlign": 1
+    },
+    {
+        "active": {
+            "texture": "ToggleHover.png"
+        },
+        "activeOn": {
+            "texture": "ToggleHoverOn.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 15,
+        "hover": {
+            "texture": "ToggleHover.png"
+        },
+        "hoverOn": {
+            "texture": "ToggleHoverOn.png"
+        },
+        "focusedHover": {
+            "texture": "ToggleFocusedHover.png"
+        },
+        "focusedHoverOn": {
+            "texture": "ToggleFocusedHoverOn.png"
+        },
+        "margins": {
+            "bottom": 2,
+            "left": 0,
+            "right": 0,
+            "top": 0
+        },
+        "name": "Toggle",
+        "normal": {
+            "texture": "ToggleNormal.png"
+        },
+        "normalOn": {
+            "texture": "ToggleNormalOn.png"
+        },
+		"focused": {
+            "texture": "ToggleFocused.png"
+        },
+        "focusedOn": {
+            "texture": "ToggleFocusedOn.png"
+        },
+        "width": 13
+    },
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "InputBoxNormal.png"
+        },
+        "border": {
+            "bottom": 1,
+            "left": 1,
+            "right": 3,
+            "top": 4
+        },
+        "contentOffset": {
+            "bottom": 4,
+            "left": 4,
+            "right": 4,
+            "top": 4
+        },
+        "fixedHeight": true,
+        "focused": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "InputBoxActive.png"
+        },
+        "font": "arial.ttf",
+        "height": 19,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "InputBoxHover.png"
+        },
+        "minWidth": 10,
+        "name": "InputBox",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "InputBoxNormal.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "ScrollArrowUpActive.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 11,
+        "hover": {
+            "texture": "ScrollArrowUpHover.png"
+        },
+        "name": "ScrollUpBtn",
+        "normal": {
+            "texture": "ScrollArrowUpNormal.png"
+        },
+        "width": 13
+    },
+    {
+        "active": {
+            "texture": "ScrollArrowDownActive.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 11,
+        "hover": {
+            "texture": "ScrollArrowDownHover.png"
+        },
+        "name": "ScrollDownBtn",
+        "normal": {
+            "texture": "ScrollArrowDownNormal.png"
+        },
+        "width": 13
+    },
+    {
+        "active": {
+            "texture": "ScrollArrowLeftActive.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 13,
+        "hover": {
+            "texture": "ScrollArrowLeftHover.png"
+        },
+        "name": "ScrollLeftBtn",
+        "normal": {
+            "texture": "ScrollArrowLeftNormal.png"
+        },
+        "width": 11
+    },
+    {
+        "active": {
+            "texture": "ScrollArrowRightActive.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 13,
+        "hover": {
+            "texture": "ScrollArrowRightHover.png"
+        },
+        "name": "ScrollRightBtn",
+        "normal": {
+            "texture": "ScrollArrowRightNormal.png"
+        },
+        "width": 11
+    },
+    {
+        "active": {
+            "texture": "ScrollBarHHandleActive.png"
+        },
+        "border": {
+            "bottom": 0,
+            "left": 4,
+            "right": 4,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "height": 13,
+        "hover": {
+            "texture": "ScrollBarHHandleHover.png"
+        },
+        "name": "ScrollBarHorzBtn",
+        "normal": {
+            "texture": "ScrollBarHHandleNormal.png"
+        },
+        "width": 10
+    },
+    {
+        "active": {
+            "texture": "ScrollBarVHandleActive.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 0,
+            "right": 0,
+            "top": 4
+        },
+        "fixedWidth": true,
+        "height": 10,
+        "hover": {
+            "texture": "ScrollBarVHandleHover.png"
+        },
+        "name": "ScrollBarVertBtn",
+        "normal": {
+            "texture": "ScrollBarVHandleNormal.png"
+        },
+        "width": 13
+    },
+    {
+        "active": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "fixedWidth": true,
+        "hover": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "minHeight": 8,
+        "name": "ScrollBarVert",
+        "normal": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "subStyles": [
+            {
+                "name": "UIScrollBarHHandle",
+                "style": "ScrollBarHorzBtn"
+            },
+            {
+                "name": "UIScrollBarVHandle",
+                "style": "ScrollBarVertBtn"
+            }
+        ],
+        "width": 16
+    },
+    {
+        "active": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "fixedHeight": true,
+        "height": 16,
+        "hover": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "minWidth": 8,
+        "name": "ScrollBarHorz",
+        "normal": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "subStyles": [
+            {
+                "name": "UIScrollBarHHandle",
+                "style": "ScrollBarHorzBtn"
+            },
+            {
+                "name": "UIScrollBarVHandle",
+                "style": "ScrollBarVertBtn"
+            }
+        ]
+    },
+    {
+        "active": {
+            "texture": "ScrollBarHHandleResizeableActive.png"
+        },
+        "border": {
+            "bottom": 0,
+            "left": 7,
+            "right": 7,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "height": 13,
+        "hover": {
+            "texture": "ScrollBarHHandleResizeableHover.png"
+        },
+        "minWidth": 15,
+        "name": "ScrollBarResizeableHorzBtn",
+        "normal": {
+            "texture": "ScrollBarHHandleResizeableNormal.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "ScrollBarVHandleResizeableActive.png"
+        },
+        "border": {
+            "bottom": 7,
+            "left": 0,
+            "right": 0,
+            "top": 7
+        },
+        "fixedWidth": true,
+        "hover": {
+            "texture": "ScrollBarVHandleResizeableHover.png"
+        },
+        "minHeight": 15,
+        "name": "ScrollBarResizeableVertBtn",
+        "normal": {
+            "texture": "ScrollBarVHandleResizeableNormal.png"
+        },
+        "width": 13
+    },
+    {
+        "active": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "fixedWidth": true,
+        "hover": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "minHeight": 15,
+        "name": "ResizeableScrollBarVert",
+        "normal": {
+            "texture": "ScrollBarVBackground.png"
+        },
+        "subStyles": [
+            {
+                "name": "UIScrollBarHHandle",
+                "style": "ScrollBarResizeableHorzBtn"
+            },
+            {
+                "name": "UIScrollBarVHandle",
+                "style": "ScrollBarResizeableVertBtn"
+            }
+        ],
+        "width": 16
+    },
+    {
+        "active": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "fixedHeight": true,
+        "height": 16,
+        "hover": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "minWidth": 15,
+        "name": "ResizeableScrollBarHorz",
+        "normal": {
+            "texture": "ScrollBarHBackground.png"
+        },
+        "subStyles": [
+            {
+                "name": "UIScrollBarHHandle",
+                "style": "ScrollBarResizeableHorzBtn"
+            },
+            {
+                "name": "UIScrollBarVHandle",
+                "style": "ScrollBarResizeableVertBtn"
+            }
+        ]
+    },
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonActive.png"
+        },
+        "activeOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonActive.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 16,
+            "top": 2
+        },
+        "contentOffset": {
+            "bottom": 2,
+            "left": 3,
+            "right": 18,
+            "top": 2
+        },
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 21,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonHover.png"
+        },
+        "hoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonActive.png"
+        },
+		"focusedHover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonFocusedHover.png"
+        },
+        "focusedHoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonFocusedHover.png"
+        },
+        "margins": {
+            "bottom": 2,
+            "left": 0,
+            "right": 0,
+            "top": 0
+        },
+        "minWidth": 20,
+        "name": "ListBox",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonNormal.png"
+        },
+        "normalOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonActive.png"
+        },
+		"focused": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonFocused.png"
+        },
+        "focusedOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownButtonFocused.png"
+        },
+        "textVertAlign": 1
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "name": "ListBoxScrollUpBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowUpNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "name": "MenuBarScrollUpBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowUpNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowUpHover.png"
+        },
+        "name": "ContextMenuScrollUpBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowUpNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "name": "ListBoxScrollDownBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowDownNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "name": "MenuBarScrollDownBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowDownNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 12,
+        "hover": {
+            "texture": "DropDownBoxArrowDownHover.png"
+        },
+        "name": "ContextMenuScrollDownBtn",
+        "normal": {
+            "texture": "DropDownBoxArrowDownNormal.png"
+        },
+        "width": 8
+    },
+    {
+        "fixedWidth": true,
+        "height": 8,
+        "name": "ListBoxHandle",
+        "normal": {
+            "texture": "DropDownBoxScrollHandle.png"
+        },
+        "width": 8
+    },
+    {
+        "fixedWidth": true,
+        "height": 8,
+        "name": "MenuBarHandle",
+        "normal": {
+            "texture": "DropDownBoxScrollHandle.png"
+        },
+        "width": 8
+    },
+    {
+        "fixedWidth": true,
+        "height": 8,
+        "name": "ContextMenuHandle",
+        "normal": {
+            "texture": "DropDownBoxScrollHandle.png"
+        },
+        "width": 8
+    },
+    {
+        "border": {
+            "bottom": 1,
+            "left": 1,
+            "right": 0,
+            "top": 1
+        },
+        "fixedWidth": true,
+        "height": 8,
+        "name": "ListBoxSidebarBg",
+        "normal": {
+            "texture": "DropDownBoxSideBg.png"
+        },
+        "width": 9
+    },
+    {
+        "border": {
+            "bottom": 1,
+            "left": 1,
+            "right": 0,
+            "top": 1
+        },
+        "fixedWidth": true,
+        "height": 8,
+        "name": "MenuBarSidebarBg",
+        "normal": {
+            "texture": "DropDownBoxSideBg.png"
+        },
+        "width": 9
+    },
+    {
+        "border": {
+            "bottom": 1,
+            "left": 1,
+            "right": 0,
+            "top": 1
+        },
+        "fixedWidth": true,
+        "height": 8,
+        "name": "ContextMenuSidebarBg",
+        "normal": {
+            "texture": "DropDownBoxSideBg.png"
+        },
+        "width": 9
+    },
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryHover.png"
+        },
+        "activeOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryHover.png"
+        },
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 16,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryHover.png"
+        },
+        "hoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryHover.png"
+        },
+        "name": "DropDownEntryBtn",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryNormal.png"
+        },
+        "normalOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryHover.png"
+        },
+        "textVertAlign": 1,
+        "width": 30
+    },
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleHover.png"
+        },
+        "activeOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleHoverOn.png"
+        },
+        "border": {
+            "bottom": 0,
+            "left": 17,
+            "right": 0,
+            "top": 0
+        },
+        "contentOffset": {
+            "bottom": 0,
+            "left": 17,
+            "right": 0,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 18,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleHover.png"
+        },
+        "hoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleHoverOn.png"
+        },
+        "name": "DropDownEntryToggleBtn",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleNormal.png"
+        },
+        "normalOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxEntryToggleNormalOn.png"
+        },
+        "textVertAlign": 1,
+        "width": 30
+    },
+    {
+        "active": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnHover.png"
+        },
+        "activeOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnHover.png"
+        },
+        "border": {
+            "bottom": 0,
+            "left": 0,
+            "right": 13,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 16,
+        "hover": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnHover.png"
+        },
+        "hoverOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnHover.png"
+        },
+        "name": "DropDownEntryExpBtn",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnNormal.png"
+        },
+        "normalOn": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            },
+            "texture": "DropDownBoxExpandBtnHover.png"
+        },
+        "textVertAlign": 1,
+        "width": 30
+    },
+    {
+        "fixedHeight": true,
+        "height": 3,
+        "name": "DropDownSeparator",
+        "normal": {
+            "texture": "DropDownBoxSeparator.png"
+        },
+        "width": 30
+    },
+    {
+        "minHeight": 20,
+        "minWidth": 50,
+        "name": "ListBoxContent",
+        "subStyles": [
+            {
+                "name": "DropDownEntryBtn",
+                "style": "DropDownEntryBtn"
+            },
+            {
+                "name": "DropDownEntryExpBtn",
+                "style": "DropDownEntryExpBtn"
+            },
+            {
+                "name": "DropDownEntryToggleBtn",
+                "style": "DropDownEntryToggleBtn"
+            },
+            {
+                "name": "DropDownSeparator",
+                "style": "DropDownSeparator"
+            }
+        ]
+    },
+    {
+        "minHeight": 20,
+        "minWidth": 50,
+        "name": "MenuBarContent",
+        "subStyles": [
+            {
+                "name": "DropDownEntryBtn",
+                "style": "DropDownEntryBtn"
+            },
+            {
+                "name": "DropDownEntryExpBtn",
+                "style": "DropDownEntryExpBtn"
+            },
+            {
+                "name": "DropDownEntryToggleBtn",
+                "style": "DropDownEntryToggleBtn"
+            },
+            {
+                "name": "DropDownSeparator",
+                "style": "DropDownSeparator"
+            }
+        ]
+    },
+    {
+        "minHeight": 20,
+        "minWidth": 50,
+        "name": "ContextMenuContent",
+        "subStyles": [
+            {
+                "name": "DropDownEntryBtn",
+                "style": "DropDownEntryBtn"
+            },
+            {
+                "name": "DropDownEntryExpBtn",
+                "style": "DropDownEntryExpBtn"
+            },
+            {
+                "name": "DropDownEntryToggleBtn",
+                "style": "DropDownEntryToggleBtn"
+            },
+            {
+                "name": "DropDownSeparator",
+                "style": "DropDownSeparator"
+            }
+        ]
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "hover": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "margins": {
+            "bottom": 6,
+            "left": 6,
+            "right": 6,
+            "top": 4
+        },
+        "name": "ListBoxFrame",
+        "normal": {
+            "texture": "DropDownBoxBg.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "hover": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "margins": {
+            "bottom": 6,
+            "left": 6,
+            "right": 6,
+            "top": 4
+        },
+        "name": "MenuBarFrame",
+        "normal": {
+            "texture": "DropDownBoxBg.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "hover": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "margins": {
+            "bottom": 6,
+            "left": 6,
+            "right": 6,
+            "top": 4
+        },
+        "name": "ContextMenuFrame",
+        "normal": {
+            "texture": "DropDownBoxBg.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "border": {
+            "bottom": 4,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "hover": {
+            "texture": "DropDownBoxBg.png"
+        },
+        "margins": {
+            "bottom": 6,
+            "left": 6,
+            "right": 6,
+            "top": 4
+        },
+        "name": "TooltipFrame",
+        "normal": {
+            "texture": "DropDownBoxBg.png"
+        }
+    },
+    {
+        "active": {
+            "texture": "SliderHandleActive.png"
+        },
+        "fixedHeight": true,
+        "fixedWidth": true,
+        "height": 13,
+        "hover": {
+            "texture": "SliderHandleHover.png"
+        },
+        "name": "SliderHandle",
+        "normal": {
+            "texture": "SliderHandleNormal.png"
+        },
+        "width": 12
+    },
+    {
+        "border": {
+            "bottom": 0,
+            "left": 4,
+            "right": 4,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "height": 10,
+        "name": "SliderHorzBg",
+        "normal": {
+            "texture": "SliderHBackground.png"
+        },
+		"focused": {
+            "texture": "SliderHBackgroundFocused.png"
+        }
+    },
+    {
+        "border": {
+            "bottom": 0,
+            "left": 6,
+            "right": 4,
+            "top": 0
+        },
+        "fixedHeight": true,
+        "height": 10,
+        "name": "SliderHorzFill",
+        "normal": {
+            "texture": "SliderHFill.png"
+        }
+    },
+    {
+        "fixedHeight": true,
+        "height": 13,
+        "minWidth": 10,
+        "name": "SliderHorz",
+        "subStyles": [
+            {
+                "name": "SliderBackground",
+                "style": "SliderHorzBg"
+            },
+            {
+                "name": "SliderFill",
+                "style": "SliderHorzFill"
+            },
+            {
+                "name": "SliderHandle",
+                "style": "SliderHandle"
+            }
+        ],
+        "width": 150
+    },
+    {
+        "border": {
+            "bottom": 4,
+            "left": 0,
+            "right": 0,
+            "top": 4
+        },
+        "fixedWidth": true,
+        "name": "SliderVertBg",
+        "normal": {
+            "texture": "SliderVBackground.png"
+        },
+		"focused": {
+            "texture": "SliderVBackgroundFocused.png"
+        },
+        "width": 10
+    },
+    {
+        "border": {
+            "bottom": 4,
+            "left": 0,
+            "right": 0,
+            "top": 6
+        },
+        "fixedWidth": true,
+        "name": "SliderVertFill",
+        "normal": {
+            "texture": "SliderVFill.png"
+        },
+        "width": 10
+    },
+    {
+        "fixedWidth": true,
+        "height": 150,
+        "minHeight": 10,
+        "name": "SliderVert",
+        "subStyles": [
+            {
+                "name": "SliderBackground",
+                "style": "SliderVertBg"
+            },
+            {
+                "name": "SliderFill",
+                "style": "SliderVertFill"
+            },
+            {
+                "name": "SliderHandle",
+                "style": "SliderHandle"
+            }
+        ],
+        "width": 13
+    },
+    {
+        "fixedHeight": true,
+        "font": "arial.ttf",
+        "height": 11,
+        "minWidth": 10,
+        "name": "RightAlignedLabel",
+        "normal": {
+            "textColor": {
+                "a": 1.0,
+                "b": 0.699999988079071,
+                "g": 0.699999988079071,
+                "r": 0.699999988079071
+            }
+        },
+        "textHorzAlign": 2
+    }
+]

+ 966 - 0
Source/bsf/Data/Raw/ShaderDependencies.json

@@ -0,0 +1,966 @@
+{
+    "BicubicUpsample.bsl": null,
+    "Blit.bsl": null,
+    "Clear.bsl": null,
+    "ClearLoadStore.bsl": null,
+    "Composite.bsl": null,
+    "DebugDraw.bsl": null,
+    "Decal.bsl": [
+        {
+            "Path": "GBufferOutput.bslinc"
+        },
+        {
+            "Path": "MaskInput.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "DepthInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "Default.bsl": [
+        {
+            "Path": "BasePass.bslinc"
+        },
+        {
+            "Path": "GBufferOutput.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "DeferredDirectionalLight.bsl": [
+        {
+            "Path": "DeferredLightCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "DeferredIBLFinalize.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "DeferredIBLProbe.bsl": [
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "DeferredIBLSetup.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "DeferredIBLSky.bsl": [
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "DeferredPointLight.bsl": [
+        {
+            "Path": "DeferredLightCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "Diffuse.bsl": [
+        {
+            "Path": "BasePass.bslinc"
+        },
+        {
+            "Path": "GBufferOutput.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "GpuParticleBounds.bsl": null,
+    "GpuParticleClear.bsl": [
+        {
+            "Path": "GpuParticleTileVertex.bslinc"
+        }
+    ],
+    "GpuParticleCurveInject.bsl": null,
+    "GpuParticleInject.bsl": null,
+    "GpuParticleSimulate.bsl": [
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "GpuParticleTileVertex.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        }
+    ],
+    "GpuParticleSortPrepare.bsl": null,
+    "IrradianceAccumulateCubeSH.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "IrradianceAccumulateSH.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "IrradianceComputeSH.bsl": [
+        {
+            "Path": "SHCommon.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "IrradianceComputeSHFrag.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "SHCommon.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "IrradianceEvaluate.bsl": [
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "SHCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "IrradianceProjectSH.bsl": [
+        {
+            "Path": "SHCommon.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "IrradianceReduceSH.bsl": [
+        {
+            "Path": "SHCommon.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        }
+    ],
+    "LightGridLLCreation.bsl": [
+        {
+            "Path": "LightGridCommon.bslinc"
+        },
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "LightGridLLReduction.bsl": [
+        {
+            "Path": "LightGridCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        }
+    ],
+    "MSAACoverage.bsl": [
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "MSAACoverageStencil.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPBloomClip.bsl": [
+        {
+            "Path": "ColorSpace.bslinc"
+        }
+    ],
+    "PPBokehDOF.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DepthOfFieldCommon.bslinc"
+        }
+    ],
+    "PPBokehDOFCombine.bsl": [
+        {
+            "Path": "DepthInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "DepthOfFieldCommon.bslinc"
+        }
+    ],
+    "PPBokehDOFPrepare.bsl": [
+        {
+            "Path": "DepthInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "DepthOfFieldCommon.bslinc"
+        }
+    ],
+    "PPBuildHiZ.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPChromaticAberration.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPCreateTonemapLUT.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "PPWhiteBalance.bslinc"
+        },
+        {
+            "Path": "PPTonemapCommon.bslinc"
+        }
+    ],
+    "PPDownsample.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPEncodeDepth.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPEyeAdaptHistogram.bsl": null,
+    "PPEyeAdaptHistogramReduce.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPEyeAdaptation.bsl": [
+        {
+            "Path": "PPEyeAdaptationCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPEyeAdaptationBasic.bsl": [
+        {
+            "Path": "PPEyeAdaptationCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPEyeAdaptationBasicSetup.bsl": [
+        {
+            "Path": "PPEyeAdaptationCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPFXAA.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPFilmGrain.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPGaussianBlur.bsl": [
+        {
+            "Path": "PPGaussianBlurCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPGaussianDOFCombine.bsl": [
+        {
+            "Path": "PPGaussianDOFCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPGaussianDOFSeparate.bsl": [
+        {
+            "Path": "PPGaussianDOFCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPMotionBlur.bsl": [
+        {
+            "Path": "DepthInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPSSAO.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPSSAOBlur.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPSSAODownsample.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPSSRResolve.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "TemporalResolve.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ColorSpace.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "PPSSRStencil.bsl": [
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPSSRTrace.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "RayMarch.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ColorSpace.bslinc"
+        },
+        {
+            "Path": "ImportanceSampling.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPScreenSpaceLensFlare.bsl": [
+        {
+            "Path": "ColorSpace.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "PPTonemapping.bsl": [
+        {
+            "Path": "PPTonemapCommon.bslinc"
+        }
+    ],
+    "ParticlesLit.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ForwardLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "LightGridCommon.bslinc"
+        },
+        {
+            "Path": "ParticleVertex.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        }
+    ],
+    "ParticlesLitOpaque.bsl": [
+        {
+            "Path": "GBufferOutput.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ParticleVertex.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        }
+    ],
+    "ParticlesUnlit.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "DepthInput.bslinc"
+        },
+        {
+            "Path": "ParticleVertex.bslinc"
+        }
+    ],
+    "RadixSortClear.bsl": [
+        {
+            "Path": "RadixSortCommon.bslinc"
+        }
+    ],
+    "RadixSortCount.bsl": [
+        {
+            "Path": "RadixSortCommon.bslinc"
+        }
+    ],
+    "RadixSortPrefixScan.bsl": [
+        {
+            "Path": "RadixSortCommon.bslinc"
+        }
+    ],
+    "RadixSortReorder.bsl": [
+        {
+            "Path": "RadixSortCommon.bslinc"
+        }
+    ],
+    "ReflectionCubeDownsample.bsl": [
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "ReflectionCubeImportanceSample.bsl": [
+        {
+            "Path": "ImportanceSampling.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PPBase.bslinc"
+        }
+    ],
+    "ShadowDepthCube.bsl": [
+        {
+            "Path": "ShadowDepthBase.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "ShadowDepthDirectional.bsl": [
+        {
+            "Path": "ShadowDepthBase.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "ShadowDepthNormal.bsl": [
+        {
+            "Path": "ShadowDepthBase.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "ShadowDepthNormalNoPS.bsl": [
+        {
+            "Path": "ShadowDepthBase.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        }
+    ],
+    "ShadowProject.bsl": [
+        {
+            "Path": "ShadowProjectionCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        }
+    ],
+    "ShadowProjectOmni.bsl": [
+        {
+            "Path": "ShadowProjectionCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        }
+    ],
+    "ShadowProjectStencil.bsl": [
+        {
+            "Path": "ShadowProjectionCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        }
+    ],
+    "Skybox.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        }
+    ],
+    "SpriteImage.bsl": [
+        {
+            "Path": "SpriteCommon.bslinc"
+        }
+    ],
+    "SpriteLine.bsl": [
+        {
+            "Path": "SpriteCommon.bslinc"
+        }
+    ],
+    "SpriteText.bsl": [
+        {
+            "Path": "SpriteCommon.bslinc"
+        }
+    ],
+    "TemporalFiltering.bsl": [
+        {
+            "Path": "PPBase.bslinc"
+        },
+        {
+            "Path": "TemporalResolve.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "ColorSpace.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "TetrahedraRender.bsl": [
+        {
+            "Path": "PerCameraData.bslinc"
+        }
+    ],
+    "TextureArrayToMSAATexture.bsl": null,
+    "TiledDeferredImageBasedLighting.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "TiledDeferredLighting.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "GBufferInput.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        }
+    ],
+    "Transparent.bsl": [
+        {
+            "Path": "ImageBasedLighting.bslinc"
+        },
+        {
+            "Path": "ForwardLighting.bslinc"
+        },
+        {
+            "Path": "ReflProbeAccumulator.bslinc"
+        },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc"
+        },
+        {
+            "Path": "PerCameraData.bslinc"
+        },
+        {
+            "Path": "DirectLightAccumulator.bslinc"
+        },
+        {
+            "Path": "DirectLighting.bslinc"
+        },
+        {
+            "Path": "SurfaceData.bslinc"
+        },
+        {
+            "Path": "BasePass.bslinc"
+        },
+        {
+            "Path": "LightGridCommon.bslinc"
+        },
+        {
+            "Path": "VertexInput.bslinc"
+        },
+        {
+            "Path": "VertexCommon.bslinc"
+        },
+        {
+            "Path": "PerObjectData.bslinc"
+        }
+    ]
+}

+ 151 - 0
Source/bsf/Data/Raw/Shaders/BicubicUpsample.bsl

@@ -0,0 +1,151 @@
+shader BicubicUpsample
+{
+	depth
+	{	
+		read = false;
+		write = false;	
+	};
+	
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			color = { one, one, add };
+		};
+	};
+	
+	variations
+	{
+		HERMITE = { true, false };
+	};
+	
+
+	code
+	{
+		#if HERMITE
+			#define CUBIC_FUNC cubicHermite
+		#else
+			#define CUBIC_FUNC cubicLagrange
+		#endif
+	
+		[internal]
+		cbuffer Input
+		{
+			[color]
+			float4 gTint;
+			uint2 gTextureSize;
+			float2 gInvPixel;
+			float2 gInvTwoPixels;
+		}		
+
+		struct VStoFS
+		{
+			noperspective float4 position : SV_POSITION;
+			noperspective float2 uv0 : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+
+			return output;
+		}			
+
+		Texture2D<float4> gSource;
+		
+		[alias(gSource)]
+		SamplerState gSampler
+		{
+			Filter = MIN_MAG_MIP_POINT;
+		};
+		
+		static float4 OFFSET = float4(-1.0f, 0.0f, 1.0f, 2.0f);
+		
+		float3 cubicLagrange(float3 A, float3 B, float3 C, float3 D, float t)
+		{
+			return
+				A * 
+				(
+					(t - OFFSET.y) / (OFFSET.x - OFFSET.y) * 
+					(t - OFFSET.z) / (OFFSET.x - OFFSET.z) *
+					(t - OFFSET.w) / (OFFSET.x - OFFSET.w)
+				) +
+				B * 
+				(
+					(t - OFFSET.x) / (OFFSET.y - OFFSET.x) * 
+					(t - OFFSET.z) / (OFFSET.y - OFFSET.z) *
+					(t - OFFSET.w) / (OFFSET.y - OFFSET.w)
+				) +
+				C * 
+				(
+					(t - OFFSET.x) / (OFFSET.z - OFFSET.x) * 
+					(t - OFFSET.y) / (OFFSET.z - OFFSET.y) *
+					(t - OFFSET.w) / (OFFSET.z - OFFSET.w)
+				) +       
+				D * 
+				(
+					(t - OFFSET.x) / (OFFSET.w - OFFSET.x) * 
+					(t - OFFSET.y) / (OFFSET.w - OFFSET.y) *
+					(t - OFFSET.z) / (OFFSET.w - OFFSET.z)
+				);
+		}
+		
+		float3 cubicHermite(float3 A, float3 B, float3 C, float3 D, float t)
+		{
+			float t2 = t*t;
+			float t3 = t*t*t;
+			float3 a = -A/2.0f + (3.0f*B)/2.0f - (3.0f*C)/2.0f + D/2.0f;
+			float3 b = A - (5.0f*B)/2.0f + 2.0f*C - D / 2.0f;
+			float3 c = -A/2.0f + C/2.0f;
+			float3 d = B;
+			
+			return a*t3 + b*t2 + c*t + d;
+		}
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float2 pixel = input.uv0 * gTextureSize + 0.5f;
+    
+			float2 fractional = frac(pixel);
+			pixel = floor(pixel) / gTextureSize - gInvPixel/2.0f;
+	
+			// Note: Investigate a version requiring less taps: http://vec3.ca/bicubic-filtering-in-fewer-taps/
+			float3 C00 = gSource.Sample(gSampler, pixel + float2(-gInvPixel.x    	, -gInvPixel.y)).rgb;
+			float3 C10 = gSource.Sample(gSampler, pixel + float2( 0.0f         		, -gInvPixel.y)).rgb;
+			float3 C20 = gSource.Sample(gSampler, pixel + float2( gInvPixel.x    	, -gInvPixel.y)).rgb;
+			float3 C30 = gSource.Sample(gSampler, pixel + float2( gInvTwoPixels.x	, -gInvPixel.y)).rgb;
+			
+			float3 C01 = gSource.Sample(gSampler, pixel + float2(-gInvPixel.x 		, 0.0f)).rgb;
+			float3 C11 = gSource.Sample(gSampler, pixel + float2( 0.0f    			, 0.0f)).rgb;
+			float3 C21 = gSource.Sample(gSampler, pixel + float2( gInvPixel.x 		, 0.0f)).rgb;
+			float3 C31 = gSource.Sample(gSampler, pixel + float2( gInvTwoPixels.x	, 0.0f)).rgb;    
+			
+			float3 C02 = gSource.Sample(gSampler, pixel + float2(-gInvPixel.x 		, gInvPixel.y)).rgb;
+			float3 C12 = gSource.Sample(gSampler, pixel + float2( 0.0f        		, gInvPixel.y)).rgb;
+			float3 C22 = gSource.Sample(gSampler, pixel + float2( gInvPixel.x 		, gInvPixel.y)).rgb;
+			float3 C32 = gSource.Sample(gSampler, pixel + float2( gInvTwoPixels.x	, gInvPixel.y)).rgb;    
+			
+			float3 C03 = gSource.Sample(gSampler, pixel + float2(-gInvPixel.x 		, gInvTwoPixels.y)).rgb;
+			float3 C13 = gSource.Sample(gSampler, pixel + float2( 0.0f        		, gInvTwoPixels.y)).rgb;
+			float3 C23 = gSource.Sample(gSampler, pixel + float2( gInvPixel.x 		, gInvTwoPixels.y)).rgb;
+			float3 C33 = gSource.Sample(gSampler, pixel + float2( gInvTwoPixels.x	, gInvTwoPixels.y)).rgb;    
+			
+			float3 CP0X = CUBIC_FUNC(C00, C10, C20, C30, fractional.x);
+			float3 CP1X = CUBIC_FUNC(C01, C11, C21, C31, fractional.x);
+			float3 CP2X = CUBIC_FUNC(C02, C12, C22, C32, fractional.x);
+			float3 CP3X = CUBIC_FUNC(C03, C13, C23, C33, fractional.x);
+		
+			return float4(CUBIC_FUNC(CP0X, CP1X, CP2X, CP3X, fractional.y), 1.0f) * gTint;
+		}
+	};
+};

+ 105 - 0
Source/bsf/Data/Raw/Shaders/Blit.bsl

@@ -0,0 +1,105 @@
+shader Blit
+{
+	variations
+	{
+		MODE = 
+			{ 
+				0, // Color (point filtering) 
+				1, // Color (bilinear filtering)
+				2, // Depth
+			};
+		MSAA_COUNT = { 1, 2, 4, 8 };
+	};
+
+	depth
+	{	
+		#if MODE != 2
+		read = false;
+		write = false;	
+		#else
+		// Cannot use read = false because that disables gl_FragDepth writes on OpenGL
+		compare = always;	
+		#endif
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			noperspective float4 position : SV_POSITION;
+			noperspective float2 uv0 : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+
+			return output;
+		}			
+
+		#if MSAA_COUNT > 1
+		
+		#if MODE != 2
+		Texture2DMS<float4> gSource;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		#else // MODE
+		// Depth
+		Texture2DMS<float> gSource;
+		
+		float fsmain(VStoFS input, out float depth : SV_Depth) : SV_Target0
+		#endif // MODE
+		{
+			int2 iUV = trunc(input.uv0);
+		
+			#if MODE != 2
+				float4 sum = float4(0, 0, 0, 0);
+				
+				[unroll]
+				for(int i = 0; i < MSAA_COUNT; i++)
+					sum += gSource.Load(iUV, i);
+					
+				return sum / MSAA_COUNT;
+			#else // MODE 
+				// Depth
+				float minVal = gSource.Load(iUV, 0);
+				
+				[unroll]
+				for(int i = 1; i < MSAA_COUNT; i++)
+					minVal = min(minVal, gSource.Load(iUV, i));
+					
+				depth = minVal;
+				return 0.0f;
+			#endif // MODE
+		}
+		
+		#else // MSAA_COUNT
+		
+		Texture2D<float4> gSource;
+		
+		#if MODE == 1
+			SamplerState gSampler;
+		#endif
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			#if MODE == 1
+				return gSource.Sample(gSampler, input.uv0);
+			#else // MODE
+				int2 iUV = trunc(input.uv0);
+				return gSource.Load(int3(iUV.xy, 0));
+			#endif // MODE
+		}
+		
+		#endif // MSAA_COUNT
+	};
+};

+ 39 - 0
Source/bsf/Data/Raw/Shaders/Clear.bsl

@@ -0,0 +1,39 @@
+shader Clear
+{
+	depth
+	{
+		read = false;
+		write = false;
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+		};
+		
+		cbuffer Params
+		{
+			uint gClearValue;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+			output.position = float4(input.screenPos, 0, 1);
+
+			return output;
+		}			
+
+		uint4 fsmain(VStoFS input) : SV_Target0
+		{
+			return uint4(gClearValue, gClearValue, gClearValue, gClearValue);
+		}
+	};
+};

+ 110 - 0
Source/bsf/Data/Raw/Shaders/ClearLoadStore.bsl

@@ -0,0 +1,110 @@
+shader ClearLoadStore
+{
+	featureset = HighEnd;
+
+	variations
+	{
+	    // 0 - Texture, 1 - Texture array, 2 - Buffer, 3 - StructuredBuffer
+		OBJ_TYPE = { 0, 1, 2, 3 };
+
+		// 0 - Float, 1 - Integer
+		DATA_TYPE = { 0, 1 };
+
+		NUM_COMPONENTS = { 0, 1, 2, 3 };
+	};
+	
+	code
+	{
+	    #if DATA_TYPE == 0
+	        #if NUM_COMPONENTS == 0
+                #define TYPE float
+                #define CLEAR_VAL gFloatClearVal.x
+            #elif NUM_COMPONENTS == 1
+                #define TYPE float2
+                #define CLEAR_VAL gFloatClearVal.xy
+            #elif NUM_COMPONENTS == 2
+                #define TYPE float3
+                #define CLEAR_VAL gFloatClearVal.xyz
+            #elif NUM_COMPONENTS == 3
+                #define TYPE float4
+                #define CLEAR_VAL gFloatClearVal
+            #endif
+	    #else
+	        #if NUM_COMPONENTS == 0
+                #define TYPE uint
+                #define CLEAR_VAL gIntClearVal.x
+            #elif NUM_COMPONENTS == 1
+                #define TYPE uint2
+                #define CLEAR_VAL gIntClearVal.xy
+            #elif NUM_COMPONENTS == 2
+                #define TYPE uint3
+                #define CLEAR_VAL gIntClearVal.xyz
+            #elif NUM_COMPONENTS == 3
+                #define TYPE uint4
+                #define CLEAR_VAL gIntClearVal
+            #endif
+	    #endif
+
+	    #if OBJ_TYPE == 0
+			RWTexture2D<TYPE> gOutput;
+	    #elif OBJ_TYPE == 1
+			RWTexture2DArray<TYPE> gOutput;
+	    #elif OBJ_TYPE == 2
+		    RWBuffer<TYPE> gOutput;
+		#else
+			RWStructuredBuffer<TYPE> gOutput;
+	    #endif
+
+		[internal]
+		cbuffer Params
+		{
+			uint2 gSize;
+			float4 gFloatClearVal;
+			uint4 gIntClearVal;
+		}
+
+		[numthreads(NUM_THREADS, 1, 1)]
+		void csmain(uint3 groupThreadId : SV_GroupThreadID, uint3 groupId : SV_GroupID)
+		{
+			#if OBJ_TYPE == 0 || OBJ_TYPE == 1
+                uint2 tilePos;
+                tilePos.x = groupId.x * NUM_THREADS + groupThreadId.x;
+                tilePos.y = groupId.y * NUM_THREADS + groupThreadId.y;
+
+                if(tilePos.x >= gSize.x || tilePos.y >= gSize.y)
+                    return;
+
+                for (uint y = 0; y < TILE_SIZE; ++y)
+                {
+                    for (uint x = 0; x < TILE_SIZE; ++x)
+                    {
+                        uint2 pixelPos = tilePos + uint2(x, y);
+                        if(pixelPos.x >= gSize.x || pixelPos.y >= gSize.y)
+                            continue;
+
+                        #if OBJ_TYPE == 1
+                            gOutput[uint3(pixelPos, 0)] = CLEAR_VAL;
+                        #else
+                            gOutput[pixelPos] = CLEAR_VAL;
+                        #endif
+                    }
+                }
+			#else
+                uint threadIdx = groupId.x * NUM_THREADS + groupThreadId.x;
+                uint pos = threadIdx * (TILE_SIZE * TILE_SIZE);
+
+                if(pos >= gSize.x)
+                    return;
+
+                for (uint x = 0; x < (TILE_SIZE * TILE_SIZE); ++x)
+                {
+                    uint curPos = pos + x;
+                    if(curPos.x >= gSize.x)
+                        continue;
+
+                    gOutput[curPos] = CLEAR_VAL;
+                }
+			#endif
+		}
+	};
+};

+ 59 - 0
Source/bsf/Data/Raw/Shaders/Composite.bsl

@@ -0,0 +1,59 @@
+shader Composite
+{
+	depth
+	{	
+		read = false;
+		write = false;	
+	};
+	
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			color = { one, one, add };
+		};
+	};
+
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			[color]
+			float4 gTint;
+		}		
+
+		struct VStoFS
+		{
+			noperspective float4 position : SV_POSITION;
+			noperspective float2 uv0 : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+
+			return output;
+		}			
+
+		Texture2D<float4> gSource;
+		
+		[alias(gSource)]
+		SamplerState gSampler;
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			return gSource.Sample(gSampler, input.uv0) * gTint;
+		}
+	};
+};

+ 79 - 0
Source/bsf/Data/Raw/Shaders/DebugDraw.bsl

@@ -0,0 +1,79 @@
+shader DebugDraw
+{
+	variations
+	{
+		LINE = { 0, 1 };
+		WIRE = { 0, 1 };
+		SOLID = { 0, 1 };
+	};
+
+	#if LINE
+	raster
+	{
+		multisample = false; // This controls line rendering algorithm
+		lineaa = true;
+	};
+
+	blend
+	{
+		target
+		{
+			enabled = true;
+			color = { srcA, srcIA, add };
+		};
+	};
+	#endif
+	
+	#if WIRE
+	raster
+	{
+		fill = wire;
+	};
+	#endif
+	
+	code
+	{
+		cbuffer Params
+		{
+			float4x4 	gMatViewProj;
+			float4		gViewDir;
+		}
+		
+		#if LINE || WIRE
+		void vsmain(
+			in float3 inPos : POSITION,
+			in float4 color : COLOR0,
+			out float4 oPosition : SV_Position,
+			out float4 oColor : COLOR0)
+		{
+			oPosition = mul(gMatViewProj, float4(inPos.xyz, 1));
+			oColor = color;
+		}
+
+		float4 fsmain(in float4 inPos : SV_Position, in float4 color : COLOR0) : SV_Target
+		{
+			return color;
+		}
+		#else
+		void vsmain(
+			in float3 inPos : POSITION,
+			in float3 inNormal : NORMAL,
+			in float4 color : COLOR0,
+			out float4 oPosition : SV_Position,
+			out float3 oNormal : NORMAL,
+			out float4 oColor : COLOR0)
+		{
+			oPosition = mul(gMatViewProj, float4(inPos.xyz, 1));
+			oNormal = inNormal;
+			oColor = color;
+		}
+		float4 fsmain(in float4 inPos : SV_Position, in float3 normal : NORMAL, in float4 color : COLOR0) : SV_Target
+		{
+			float4 outColor = color * dot(normalize(normal), -gViewDir);
+			outColor.a = color.a;
+			
+			return outColor;
+		}
+		#endif
+	};
+};

+ 342 - 0
Source/bsf/Data/Raw/Shaders/Decal.bsl

@@ -0,0 +1,342 @@
+#define CLIP_POS 1
+#define NO_ANIMATION 1
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\PerObjectData.bslinc"
+#include "$ENGINE$\VertexInput.bslinc"
+
+#if MSAA_MODE != 0
+	#define MSAA_COUNT 2
+#endif
+#include "$ENGINE$\DepthInput.bslinc"
+#include "$ENGINE$\MaskInput.bslinc"
+
+#include "$ENGINE$\GBufferOutput.bslinc"
+
+shader Surface
+{
+	mixin PerCameraData;
+	mixin PerObjectData;
+	mixin VertexInput;
+	mixin GBufferOutput;
+	mixin DepthInput;
+	mixin MaskInput;
+
+	variations
+	{
+		[name("Blend mode"),show]
+		BLEND_MODE = 
+		{ 
+			[name("Transparent")] 0, 
+			[name("Stain")] 1, 
+			[name("Normals")] 2, 
+			[name("Emissive")] 3 
+		};
+		INSIDE_GEOMETRY = { true, false };
+		// 0 - None
+		// 1 - Resolve single sample only
+		// 2 - Resolve all samples
+		MSAA_MODE = { 0, 1, 2 };
+	};
+	
+	blend
+	{
+		independant = true;
+	
+		// Scene color
+		target	
+		{
+			enabled = true;
+			color = { srcA, srcIA, add };
+			alpha = { zero, one, add };
+			
+			#if BLEND_MODE != 3
+				writemask = empty;
+			#endif
+		};
+		
+		// Albedo
+		target	
+		{
+			enabled = true;
+			
+			#if BLEND_MODE == 1
+				color = { dstRGB, zero, add };
+			#else
+				color = { srcA, srcIA, add };
+			#endif
+			
+			alpha = { zero, one, add };
+			
+			#if BLEND_MODE != 0
+			#if BLEND_MODE != 1
+				writemask = empty;
+			#endif
+			#endif
+		};
+		
+		// Normal
+		target	
+		{
+			enabled = true;
+			color = { srcA, srcIA, add };
+			alpha = { zero, one, add };
+			
+			#if BLEND_MODE == 3
+				writemask = empty;
+			#endif
+		};
+		
+		// Metallic & roughness
+		target	
+		{
+			enabled = true;
+			color = { srcA, srcIA, add };
+			alpha = { zero, one, add };
+			
+			#if BLEND_MODE != 0
+			#if BLEND_MODE != 1
+				writemask = empty;
+			#endif
+			#endif
+		};
+		
+		// Velocity
+		target	
+		{
+			enabled = true;
+			writemask = empty;
+		};
+	};
+	
+	depth
+	{
+		write = false;
+		
+		#if INSIDE_GEOMETRY
+		read = false;
+		#else
+		read = true;
+		#endif
+	};
+	
+	raster
+	{
+		#if INSIDE_GEOMETRY
+		cull = cw;
+		#else
+		cull = ccw;
+		#endif
+	};
+	
+	#if MSAA_MODE > 0
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_MODE == 1
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		[alias(gOpacityTex)]
+		SamplerState gOpacitySamp;	
+	
+		Texture2D gOpacityTex = white;
+	
+		#if BLEND_MODE == 3
+			[alias(gEmissiveMaskTex)]
+			SamplerState gEmissiveMaskSamp;	
+		
+			Texture2D gEmissiveMaskTex = black;
+		#endif
+		
+		#if BLEND_MODE != 3
+			[alias(gNormalTex)]
+			SamplerState gNormalSamp;
+			
+			Texture2D gNormalTex = normal;
+		#endif
+	
+		#if BLEND_MODE == 0 || BLEND_MODE == 1
+			[alias(gAlbedoTex)]
+			SamplerState gAlbedoSamp;
+		
+			[alias(gRoughnessTex)]
+			SamplerState gRoughnessSamp;
+			
+			[alias(gMetalnessTex)]
+			SamplerState gMetalnessSamp;
+		
+			Texture2D gAlbedoTex = white;
+			Texture2D gRoughnessTex = white;
+			Texture2D gMetalnessTex = black;
+		#endif
+
+		cbuffer MaterialParams
+		{
+			float2 gUVOffset = { 0.0f, 0.0f };
+			float2 gUVTile = { 1.0f, 1.0f };
+			
+			#if BLEND_MODE == 3
+				[color][hdr]
+				float3 gEmissiveColor = { 1.0f, 1.0f, 1.0f };
+			#endif
+			
+			#if BLEND_MODE == 0 || BLEND_MODE == 1
+				[spriteuv(gAlbedoTex)]
+				float4 gSpriteUV;
+			#endif
+		};
+	
+		[internal]
+		cbuffer DecalParams
+		{
+			float4x4 gWorldToDecal;
+			float3 gDecalNormal;
+			float gNormalTolerance;
+			float gFlipDerivatives;
+			uint gLayerMask;
+		}
+		
+		float3x3 getWorldToTangent(float3 N, float3 p, float2 uv)
+		{
+			float3 dp1 = ddx(p);
+			float3 dp2 = ddy(p);
+			float2 duv1 = ddx(uv);
+			float2 duv2 = ddy(uv);
+
+			float3 dp2perp = cross(dp2, N);
+			float3 dp1perp = cross(N, dp1);
+			float3 T = dp2perp * duv1.x + dp1perp * duv2.x;
+			float3 B = dp2perp * duv1.y + dp1perp * duv2.y;
+
+			float invmax = rsqrt(max(dot(T,T), dot(B,B)));
+			return float3x3(T * invmax, B * invmax, N);
+		}
+
+		struct DecalVStoFS
+		{
+			float4 position : SV_Position;
+			float4 clipPos : TEXCOORD0;
+		};
+		
+		DecalVStoFS vsmain(VertexInput_PO input)
+		{
+			DecalVStoFS output;
+		
+			float4 worldPosition = getVertexWorldPosition(input);
+			
+			output.position = mul(gMatViewProj, worldPosition);
+			output.clipPos = output.position;
+						
+			return output;
+		}
+		
+		void fsmain(
+			in DecalVStoFS input, 
+			in float4 screenPos : SV_Position,
+			#if MSAA_MODE == 2
+			uint sampleIdx : SV_SampleIndex,
+			#endif
+			out float4 OutSceneColor : SV_Target0,
+			out float4 OutGBufferA : SV_Target1,
+			out float4 OutGBufferB : SV_Target2,
+			out float4 OutGBufferC : SV_Target3)
+		{		
+			#if MSAA_MODE == 0
+				float deviceZ = gDepthBufferTex.Load(int3(screenPos.xy, 0)).r;
+				uint layer = (uint)(gMaskTex.Load(int3(screenPos.xy, 0)).r * 256.0f);
+			#elif MSAA_MODE == 1
+				float deviceZ = gDepthBufferTex.Load(screenPos.xy, 0).r;
+				uint layer = (uint)(gMaskTex.Load(screenPos.xy, 0).r * 256.0f);
+			#else
+				float deviceZ = gDepthBufferTex.Load(screenPos.xy, sampleIdx).r;
+				uint layer = (uint)(gMaskTex.Load(screenPos.xy, sampleIdx).r * 256.0f);
+			#endif
+			
+			if(layer < 32 && (gLayerMask & (1 << layer)) == 0)
+				discard;
+		
+			float depth = convertFromDeviceZ(deviceZ);
+			float2 ndcPos = input.clipPos.xy / input.clipPos.w;
+		
+			// x, y are now in clip space, z, w are in view space
+			// We multiply them by a special inverse view-projection matrix, that had the projection entries that effect
+			// z, w eliminated (since they are already in view space)
+			// Note: Multiply by depth should be avoided if using ortographic projection
+			float4 mixedSpacePos = float4(ndcPos.xy * -depth, depth, 1);
+			float4 worldPosition4D = mul(gMatScreenToWorld, mixedSpacePos);
+			float3 worldPosition = worldPosition4D.xyz / worldPosition4D.w;
+			
+			float4 decalPos = mul(gWorldToDecal, float4(worldPosition, 1.0f));
+			float3 decalUV = (decalPos.xyz + 1.0f) * 0.5f;
+						
+			float alpha = 0.0f;
+			if(any(decalUV < 0.0f) || any(decalUV > 1.0f))
+			{
+#ifdef METAL
+				// 'discard' is causing artifacts on Metal
+				OutSceneColor = 0;
+				OutGBufferA = 0;
+				OutGBufferB = 0;
+				OutGBufferC = 0;
+
+				return;
+#else
+				discard;
+#endif
+			}
+
+			float3 worldNormal = normalize(cross(ddy(worldPosition), ddx(worldPosition))) * gFlipDerivatives;
+			if(dot(worldNormal, gDecalNormal) > gNormalTolerance)
+				discard;
+				
+			float3x3 worldToTangent = getWorldToTangent(worldNormal, worldPosition, decalUV.xy);
+				
+			float2 uv = (decalUV.xy * gUVTile + gUVOffset);
+
+			#if BLEND_MODE == 0 || BLEND_MODE == 1
+				uv = uv * gSpriteUV.zw + gSpriteUV.xy;
+			#endif
+
+			float opacity = gOpacityTex.Sample(gOpacitySamp, uv);
+
+			#if BLEND_MODE == 3
+				OutSceneColor = float4(gEmissiveColor * gEmissiveMaskTex.Sample(gEmissiveMaskSamp, uv).x, opacity);
+			#elif BLEND_MODE == 2
+				float3 normal = normalize(gNormalTex.Sample(gNormalSamp, uv) * 2.0f - float3(1, 1, 1));
+
+				// Flip multiplication order since we need to transform with tangentToWorld, which is the transpose
+				worldNormal = mul(normal, worldToTangent);
+				OutGBufferB = float4(worldNormal * 0.5f + 0.5f, opacity);
+			#else
+				float4 albedo = gAlbedoTex.Sample(gAlbedoSamp, uv);
+				opacity *= albedo.a;
+				OutGBufferA = float4(albedo.xyz, opacity);
+
+				float3 normal = normalize(gNormalTex.Sample(gNormalSamp, uv) * 2.0f - float3(1, 1, 1));
+
+				// Flip multiplication order since we need to transform with tangentToWorld, which is the transpose
+				worldNormal = mul(normal, worldToTangent);
+				OutGBufferB = float4(worldNormal * 0.5f + 0.5f, opacity);
+
+				float roughness = gRoughnessTex.Sample(gRoughnessSamp, uv).x;
+				float metalness = gMetalnessTex.Sample(gMetalnessSamp, uv).x;
+
+				OutGBufferC = float4(roughness, metalness, 0.0f, opacity);
+			#endif
+		}	
+	};
+};

+ 28 - 0
Source/bsf/Data/Raw/Shaders/Default.bsl

@@ -0,0 +1,28 @@
+#include "$ENGINE$\BasePass.bslinc"
+#include "$ENGINE$\GBufferOutput.bslinc"
+
+shader Surface
+{
+	mixin BasePass;
+	mixin GBufferOutput;
+
+	code
+	{
+		void fsmain(
+			in VStoFS input, 
+			out float4 OutSceneColor : SV_Target0,
+			out GBufferData OutGBuffer)
+		{
+			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;
+			surfaceData.velocity = 0.0f;
+			
+			OutSceneColor = 0.0f;
+			OutGBuffer = encodeGBuffer(surfaceData);
+		}	
+	};
+};

+ 116 - 0
Source/bsf/Data/Raw/Shaders/DeferredDirectionalLight.bsl

@@ -0,0 +1,116 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\DeferredLightCommon.bslinc"
+
+shader DeferredDirectionalLight
+{
+	mixin DeferredLightCommon;
+
+	variations
+	{
+		MSAA = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};	
+	
+	depth
+	{
+		read = false;
+	};
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		front = { keep, keep, keep, eq };
+		readmask = 0x80;
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code 
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float2 uv0 : TEXCOORD0;
+			float4 screenPos : TEXCOORD1;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.screenPos = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+		
+			return output;
+		}
+
+		float4 fsmain(VStoFS input
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			uint2 pixelPos = UVToScreen(input.uv0);
+			
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData(pixelPos, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#endif
+			#else
+			SurfaceData surfaceData = getGBufferData(pixelPos);
+			#endif			
+		
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				float2 ndcPos = input.screenPos.xy / input.screenPos.w;
+				float3 worldPosition = NDCToWorld(ndcPos, surfaceData.depth);
+
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 R = 2 * dot(V, N) * N - V;
+				float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
+								
+				LightData lightData = getLightData();
+				
+				#if MSAA_COUNT > 1
+					#if MSAA_RESOLVE_0TH
+						float occlusion = gLightOcclusionTex.Load(pixelPos, 0).r;
+					#else
+						float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#endif
+				#else
+				float occlusion = gLightOcclusionTex.Load(int3(pixelPos, 0)).r;
+				#endif
+				
+				// Reverse the sqrt we did when storing it
+				occlusion *= occlusion;
+				
+				occlusion = 1.0f - occlusion;
+				
+				return float4(getLuminanceDirectional(lightData, worldPosition, V, specR, surfaceData) * occlusion, 1.0f);
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 102 - 0
Source/bsf/Data/Raw/Shaders/DeferredIBLFinalize.bsl

@@ -0,0 +1,102 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+
+shader DeferredIBLFinalize
+{
+	mixin PPBase;
+	mixin GBufferInput;
+	mixin PerCameraData;
+	mixin ImageBasedLighting;
+
+	blend
+	{
+		target
+		{
+			enabled = true;
+			color = { one, one, add };		
+		};
+	};
+	
+	variations
+	{
+		MSAA = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};		
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		#if MSAA_COUNT > 1
+			Texture2DMS<float4>	gIBLRadianceTex;
+		#else
+			Texture2D gIBLRadianceTex;
+		#endif
+	
+		float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{		
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, 0);
+					float3 radiance = gIBLRadianceTex.Load((uint2)pixelPos.xy, 0).rgb;
+				#else
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, sampleIdx);
+					float3 radiance = gIBLRadianceTex.Load((uint2)pixelPos.xy, sampleIdx).rgb;
+				#endif
+			#else
+				SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy);
+				float3 radiance = gIBLRadianceTex.Load(int3((int2)pixelPos.xy, 0)).rgb;
+			#endif	
+
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				// See C++ code for generation of gPreintegratedEnvBRDF to see why this code works as is
+				float3 worldPosition = NDCToWorld(input.screenPos, surfaceData.depth);
+				
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float NoV = saturate(dot(N, V));
+				
+				// Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
+				// For more customizability allow the user to provide separate albedo/specular colors for both types.
+				float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
+								
+				float2 envBRDF = gPreintegratedEnvBRDF.SampleLevel(gPreintegratedEnvBRDFSamp, float2(NoV, surfaceData.roughness), 0).rg;
+				
+				return float4(radiance * (specularColor * envBRDF.x + envBRDF.y), 0.0f);
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 157 - 0
Source/bsf/Data/Raw/Shaders/DeferredIBLProbe.bsl

@@ -0,0 +1,157 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+#include "$ENGINE$\DirectLighting.bslinc"
+
+shader DeferredIBLProbe
+{
+	mixin GBufferInput;
+	mixin PerCameraData;
+	mixin ImageBasedLighting;
+	mixin DirectLighting;
+
+	depth
+	{
+		write = false;
+		
+		#if INSIDE_GEOMETRY
+		read = false;
+		#else
+		read = true;
+		#endif
+	};
+	
+	blend
+	{
+		target
+		{
+			enabled = true;
+			color = { dstA, one, add };
+			alpha = { dstA, zero, add };			
+		};
+	};
+	
+	raster
+	{
+		#if INSIDE_GEOMETRY
+		cull = cw;
+		#else
+		cull = ccw;
+		#endif
+	};
+
+	variations
+	{
+		MSAA = { true, false };
+		INSIDE_GEOMETRY = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};		
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float4 screenPos : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float3 position : POSITION;
+			uint vertexIdx : SV_VERTEXID;
+		};
+		
+		cbuffer PerProbe
+		{
+			float3 gPosition;
+			float3 gExtents;
+			float gTransitionDistance;
+			float4x4 gInvBoxTransform;
+			uint gCubemapIdx;
+			uint gType; // 0 - Sphere, 1 - Box
+		}			
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+			
+			// Position & scale geometry
+			float3 worldPosition = input.position * gExtents + gPosition;
+			
+			output.screenPos = mul(gMatViewProj, float4(worldPosition, 1));
+			output.position = output.screenPos;
+			
+			return output;
+		}			
+
+		float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, sampleIdx);
+				#endif
+			#else
+				SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy);
+			#endif			
+		
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				ReflProbeData probeData;
+				probeData.position = gPosition;
+				probeData.radius = gExtents.x;
+				probeData.boxExtents = gExtents;
+				probeData.transitionDistance = gTransitionDistance;
+				probeData.invBoxTransform = gInvBoxTransform;
+				probeData.cubemapIdx = gCubemapIdx;
+				probeData.type = gType;
+				probeData.padding = float2(0, 0);
+				
+				float2 ndcPos = input.screenPos.xy / input.screenPos.w;
+				float3 worldPosition = NDCToWorld(ndcPos, surfaceData.depth);			
+
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 R = 2 * dot(V, N) * N - V;
+				float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);				
+			
+				float mipLevel = mapRoughnessToMipLevel(surfaceData.roughness, gReflCubemapNumMips);			
+				
+				return evaluateProbe(worldPosition, specR, mipLevel, probeData);
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 96 - 0
Source/bsf/Data/Raw/Shaders/DeferredIBLSetup.bsl

@@ -0,0 +1,96 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+
+shader DeferredIBLSetup
+{
+	mixin PPBase;
+	mixin GBufferInput;
+	mixin PerCameraData;
+	mixin ImageBasedLighting;
+	
+	variations
+	{
+		MSAA = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};		
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{		
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, sampleIdx);
+				#endif
+			#else
+				SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy);
+			#endif	
+
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				float3 worldPosition = NDCToWorld(input.screenPos, surfaceData.depth);
+
+				float ao = gAmbientOcclusionTex.SampleLevel(gAmbientOcclusionSamp, input.uv0, 0.0f).r;
+				float4 ssr = gSSRTex.SampleLevel(gSSRSamp, input.uv0, 0.0f);
+				
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float NoV = saturate(dot(N, V));
+				
+				float3 radiance = ssr.rgb;
+				float alpha = 1.0f - ssr.a; // Determines how much to blend in reflection probes & skybox
+				
+				// Generate an approximate spec. occlusion value from AO. This doesn't need to be applied to SSR since it accounts
+				// for occlusion by tracing rays.
+				float specOcclusion = getSpecularOcclusion(NoV, surfaceData.roughness * surfaceData.roughness, ao);
+				alpha *= specOcclusion;
+				
+				if(gUseReflectionMaps == 0)
+				{
+					// Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
+					// For more customizability allow the user to provide separate albedo/specular colors for both types.
+					float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);								
+					radiance += specularColor;
+				}
+
+				return float4(radiance, alpha);
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 92 - 0
Source/bsf/Data/Raw/Shaders/DeferredIBLSky.bsl

@@ -0,0 +1,92 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+#include "$ENGINE$\DirectLighting.bslinc"
+
+shader DeferredIBLFinalize
+{
+	mixin PPBase;
+	mixin GBufferInput;
+	mixin PerCameraData;
+	mixin ImageBasedLighting;
+	mixin DirectLighting;
+
+	blend
+	{
+		target
+		{
+			enabled = true;
+			color = { dstA, one, add };		
+		};
+	};
+	
+	variations
+	{
+		MSAA = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};		
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		float4 fsmain(VStoFS input, float4 pixelPos : SV_Position
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{		
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy, sampleIdx);
+				#endif
+			#else
+				SurfaceData surfaceData = getGBufferData((uint2)pixelPos.xy);
+			#endif	
+
+			if(surfaceData.worldNormal.w > 0.0f && gSkyCubemapAvailable > 0 && gUseReflectionMaps != 0)
+			{
+				float3 worldPosition = NDCToWorld(input.screenPos, surfaceData.depth);
+				
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 R = 2 * dot(V, N) * N - V;
+				float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
+			
+				float skyMipLevel = mapRoughnessToMipLevel(surfaceData.roughness, gSkyCubemapNumMips);
+				float4 skySample = gSkyReflectionTex.SampleLevel(gSkyReflectionSamp, specR, skyMipLevel) * gSkyBrightness;
+				
+				return float4(skySample.rgb, 1.0f); 
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 177 - 0
Source/bsf/Data/Raw/Shaders/DeferredPointLight.bsl

@@ -0,0 +1,177 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\DeferredLightCommon.bslinc"
+
+shader DeferredPointLight
+{
+	mixin DeferredLightCommon;
+
+	variations
+	{
+		MSAA = { true, false };
+		INSIDE_GEOMETRY = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+	};		
+	
+	#if MSAA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x80;
+		
+		#if INSIDE_GEOMETRY
+		back = { keep, keep, keep, eq };
+		#else
+		front = { keep, keep, keep, eq };
+		#endif
+		
+		#if MSAA_RESOLVE_0TH
+		reference = 0;
+		#else
+		reference = 0x80;
+		#endif
+	};
+	#endif
+	
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float4 screenPos : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float3 position : POSITION;
+			uint vertexIdx : SV_VERTEXID;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+			
+			float3 worldPosition;
+			uint numSides = gLightGeometry.x;
+			if(numSides > 0) // Generate spot light geometry
+			{
+				uint numSlices = gLightGeometry.y;
+				float radius = gLightGeometry.w;
+				float angle = gLightSpotAnglesAndSqrdInvAttRadius.x;
+			
+				// Extra scale to ensure edges lie on the circle, not inside it
+				// TODO - These can be precomputed
+				float extraRadiusScale = 1.0f / cos(PI / (float)numSides);
+				float angleTan = tan(angle);
+				float height = radius / angleTan;
+	
+				uint sphereStartIdx = numSides * numSlices;
+				// Cone vertices
+				if (input.vertexIdx < sphereStartIdx)
+				{
+					uint sliceIdx = input.vertexIdx / numSides;
+					uint sideIdx = input.vertexIdx % numSides;
+
+					float curAngle = sideIdx * 2 * PI / (float)numSides;
+					float sliceOffset = height * sliceIdx / (float)(numSlices - 1);
+					float sliceRadius = sliceOffset * angleTan * extraRadiusScale;
+
+					float4 localPos = float4(sliceRadius * cos(curAngle), 
+						sliceRadius * sin(curAngle), -sliceOffset, 1.0f);
+					worldPosition = (mul(gMatConeTransform, localPos)).xyz;
+				}
+				else // Sphere cap vertices
+				{
+					uint sphereVertexIdx = input.vertexIdx - sphereStartIdx;
+					uint sliceIdx = sphereVertexIdx / numSides;
+					uint sideIdx = sphereVertexIdx % numSides;
+
+					float curAngle = sideIdx * 2 * PI / (float)numSides;
+					float sliceOffset = radius * sliceIdx / (float)(numSlices - 1);
+					float sliceRadius = sqrt(max(0.0f, radius * radius - sliceOffset * sliceOffset)) * extraRadiusScale;
+
+					float4 localPos = float4(sliceRadius * cos(curAngle), 
+						sliceRadius * sin(curAngle), -height - sliceOffset, 1.0f);
+					worldPosition = (mul(gMatConeTransform, localPos)).xyz;
+				}
+			}
+			else // Scale and position pre-generated sphere geometry
+			{
+				worldPosition = input.position * gLightGeometry.z + gLightPositionAndSrcRadius.xyz;
+			}
+			
+			output.screenPos = mul(gMatViewProj, float4(worldPosition, 1));
+			output.position = output.screenPos;
+			
+			return output;
+		}			
+
+		float4 fsmain(VStoFS input
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			float2 ndcPos = input.screenPos.xy / input.screenPos.w;
+			uint2 pixelPos = NDCToScreen(ndcPos);
+			
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					SurfaceData surfaceData = getGBufferData(pixelPos, 0);
+				#else
+					SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#endif
+			#else
+			SurfaceData surfaceData = getGBufferData(pixelPos);
+			#endif	
+
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				// x, y are now in clip space, z, w are in view space
+				// We multiply them by a special inverse view-projection matrix, that had the projection entries that effect
+				// z, w eliminated (since they are already in view space)
+				// Note: Multiply by depth should be avoided if using ortographic projection
+				float4 mixedSpacePos = float4(ndcPos.xy * -surfaceData.depth, surfaceData.depth, 1);
+				float4 worldPosition4D = mul(gMatScreenToWorld, mixedSpacePos);
+				float3 worldPosition = worldPosition4D.xyz / worldPosition4D.w;
+
+				float3 V = normalize(gViewOrigin - worldPosition);
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 R = 2 * dot(V, N) * N - V;
+				float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
+				
+				float roughness2 = max(surfaceData.roughness, 0.08f);
+				roughness2 *= roughness2;
+				
+				LightData lightData = getLightData();
+				
+				#if MSAA_COUNT > 1
+					#if MSAA_RESOLVE_0TH
+						float occlusion = gLightOcclusionTex.Load(pixelPos, 0).r;
+					#else
+						float occlusion = gLightOcclusionTex.Load(pixelPos, sampleIdx).r;
+					#endif
+				#else
+				float occlusion = gLightOcclusionTex.Load(int3(pixelPos, 0)).r;
+				#endif
+				
+				// Reverse the sqrt we did when storing it
+				occlusion *= occlusion;
+				
+				occlusion = 1.0f - occlusion;
+				
+				bool isSpot = gShiftedLightPositionAndType.w > 0.5f;
+				if(isSpot)
+					return float4(getLuminanceSpot(lightData, worldPosition, V, R, roughness2, surfaceData) * occlusion, 1.0f);
+				else // Radial
+					return float4(getLuminanceRadial(lightData, worldPosition, V, R, roughness2, surfaceData) * occlusion, 1.0f);
+			}
+			else
+				return float4(0.0f, 0.0f, 0.0f, 0.0f);
+		}
+	};
+};

+ 75 - 0
Source/bsf/Data/Raw/Shaders/Diffuse.bsl

@@ -0,0 +1,75 @@
+#include "$ENGINE$\BasePass.bslinc"
+#include "$ENGINE$\GBufferOutput.bslinc"
+
+shader Surface
+{
+	mixin BasePass;
+	mixin GBufferOutput;
+
+	variations
+	{
+		WRITE_VELOCITY = { false, true };
+	};
+	
+	code
+	{
+		[alias(gAlbedoTex)]
+		SamplerState gAlbedoSamp;
+		
+		[alias(gNormalTex)]
+		SamplerState gNormalSamp;
+		
+		[alias(gRoughnessTex)]
+		SamplerState gRoughnessSamp;
+		
+		[alias(gMetalnessTex)]
+		SamplerState gMetalnessSamp;
+		
+		[alias(gEmissiveMaskTex)]
+		SamplerState gEmissiveMaskSamp;		
+		
+		Texture2D gAlbedoTex = white;
+		Texture2D gNormalTex = normal;
+		Texture2D gRoughnessTex = white;
+		Texture2D gMetalnessTex = black;
+		Texture2D gEmissiveMaskTex = black;
+		
+		cbuffer MaterialParams
+		{
+			float2 gUVOffset = { 0.0f, 0.0f };
+			float2 gUVTile = { 1.0f, 1.0f };
+			[color][hdr]
+			float3 gEmissiveColor = { 1.0f, 1.0f, 1.0f };
+		};
+		
+		void fsmain(
+			in VStoFS input, 
+			out float4 OutSceneColor : SV_Target0,
+			out GBufferData OutGBuffer)
+		{
+			float2 uv = input.uv0 * gUVTile + gUVOffset;
+		
+			float3 normal = normalize(gNormalTex.Sample(gNormalSamp, uv) * 2.0f - float3(1, 1, 1));
+			float3 worldNormal = calcWorldNormal(input, normal);
+		
+			SurfaceData surfaceData;
+			surfaceData.albedo = gAlbedoTex.Sample(gAlbedoSamp, uv);
+			surfaceData.worldNormal.xyz = worldNormal;
+			surfaceData.roughness = gRoughnessTex.Sample(gRoughnessSamp, uv).x;
+			surfaceData.metalness = gMetalnessTex.Sample(gMetalnessSamp, uv).x;
+			surfaceData.mask = gLayer;
+			
+			#if WRITE_VELOCITY
+			float2 ndcPos = input.clipPos.xy / input.clipPos.w;
+			float2 prevNdcPos = input.prevClipPos.xy / input.prevClipPos.w;
+			
+			surfaceData.velocity = ndcPos - prevNdcPos;
+			#else
+			surfaceData.velocity = 0.0f;
+			#endif
+			
+			OutSceneColor = float4(gEmissiveColor * gEmissiveMaskTex.Sample(gEmissiveMaskSamp, uv).x, 1.0f);
+			OutGBuffer = encodeGBuffer(surfaceData);
+		}	
+	};
+};

+ 144 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleBounds.bsl

@@ -0,0 +1,144 @@
+shader GpuParticleBounds
+{
+	featureset = HighEnd;
+
+	code
+	{
+		#define NUM_REDUCE_THREADS 32
+		#define NUM_SERIAL_REDUCTIONS NUM_THREADS / NUM_REDUCE_THREADS
+		
+		#define FLT_MAX 3.402823466e+38f
+		#define FLT_MIN -3.402823466e+38f
+
+		Buffer<uint2> gParticleIndices;
+		Texture2D gPosAndTimeTex;
+		
+		RWBuffer<float3> gOutput;
+		
+		cbuffer Input
+		{
+			uint gIterationsPerGroup;
+			uint gNumExtraIterations;
+			uint gNumParticles;
+		};	
+		
+		groupshared float3 sGroupMin[NUM_THREADS];
+		groupshared float3 sGroupMax[NUM_THREADS];
+
+		[numthreads(NUM_THREADS, 1, 1)]
+		void csmain(uint3 groupThreadId : SV_GroupThreadID, uint3 groupId : SV_GroupID)
+		{
+			uint threadId = groupThreadId.x;
+		
+			uint particleIdx;
+			uint numIterations;
+			
+			if(groupId.x < gNumExtraIterations)
+			{
+				particleIdx = groupId.x * (gIterationsPerGroup + 1);
+				numIterations = gIterationsPerGroup + 1;
+			}
+			else
+			{
+				particleIdx = groupId.x * gIterationsPerGroup + gNumExtraIterations;
+				numIterations = gIterationsPerGroup;
+			}
+					
+			particleIdx = particleIdx * NUM_THREADS + threadId;
+		
+			float3 localMin = FLT_MAX;
+			float3 localMax = FLT_MIN;
+		
+			for(uint i = 0; i < numIterations; ++i)
+			{
+				if(particleIdx >= gNumParticles)
+					break;
+					
+				uint3 pixelPos;
+				pixelPos.xy = gParticleIndices[particleIdx];
+				pixelPos.z = 0;
+				
+				float4 positionAndTime = gPosAndTimeTex.Load(pixelPos);
+				
+				// Check if particle is dead
+				if(positionAndTime.w > 1.0f)
+					continue;
+					
+				localMin = min(localMin, positionAndTime.xyz);
+				localMax = max(localMax, positionAndTime.xyz);
+				
+				particleIdx += NUM_THREADS;
+			}
+		
+			sGroupMin[threadId] = localMin;
+			sGroupMax[threadId] = localMax;
+		
+			GroupMemoryBarrierWithGroupSync();
+			
+			// Reduce serially first
+			uint reduceThreadId = threadId & (NUM_REDUCE_THREADS - 1);
+			if(threadId < NUM_REDUCE_THREADS)
+			{
+				// Reduce minimum
+				localMin = sGroupMin[reduceThreadId];
+				
+				[unroll]
+				for(int i = 0; i < NUM_SERIAL_REDUCTIONS; ++i)
+					localMin = min(localMin, sGroupMin[i * NUM_REDUCE_THREADS + reduceThreadId]);
+				
+			}
+			else if(threadId < (NUM_REDUCE_THREADS * 2))
+			{
+				// Reduce maximum
+				localMax = sGroupMax[reduceThreadId];
+				
+				[unroll]
+				for(int i = 0; i < NUM_SERIAL_REDUCTIONS; ++i)
+					localMax = max(localMax, sGroupMax[i * NUM_REDUCE_THREADS + reduceThreadId]);
+			}
+			
+			GroupMemoryBarrierWithGroupSync();
+			
+			// Store serial reduction results
+			if(threadId < NUM_REDUCE_THREADS)
+				sGroupMin[reduceThreadId] = localMin;
+			else if(threadId < (NUM_REDUCE_THREADS * 2))
+				sGroupMax[reduceThreadId] = localMax;
+		
+			GroupMemoryBarrierWithGroupSync();
+			
+			// Do parallel reduction within a warp
+			if(threadId < NUM_REDUCE_THREADS)
+			{
+				// Reduce minimum
+				localMin = sGroupMin[reduceThreadId];
+
+				[unroll]
+				for(uint i = 1; i < NUM_REDUCE_THREADS; i <<= 1)
+				{
+					// Note: Bank conflicts with i = 2, 4, etc.
+					localMin = min(localMin, sGroupMin[reduceThreadId + i]);
+					sGroupMin[reduceThreadId] = localMin;
+				}
+			}
+			else if(threadId < (NUM_REDUCE_THREADS * 2))
+			{
+				// Reduce maximum
+				localMax = sGroupMax[reduceThreadId];
+
+				[unroll]
+				for(uint i = 1; i < NUM_REDUCE_THREADS; i <<= 1)
+				{
+					// Note: Bank conflicts with i = 2, 4, etc.
+					localMax = min(localMax, sGroupMax[reduceThreadId + i]);
+					sGroupMax[reduceThreadId] = localMax;
+				}
+			}
+			
+			GroupMemoryBarrierWithGroupSync();
+			
+			if(threadId == 0) gOutput[groupId.x * 2 + 0] = sGroupMin[0];
+			if(threadId == 1) gOutput[groupId.x * 2 + 1] = sGroupMax[0];
+		}
+	};
+};

+ 25 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleClear.bsl

@@ -0,0 +1,25 @@
+#include "$ENGINE$/GpuParticleTileVertex.bslinc"
+
+shader GpuParticleClear
+{
+	mixin GpuParticleTileVertex;
+	
+	code
+	{	
+		void fsmain(VStoFS input, 
+			out float4 outPosAndTime : SV_Target0, 
+			out float4 outVel : SV_Target1,
+			out float4 outSizeAndRotation : SV_Target2)
+		{
+			// Time > 1.0f means the particle is dead
+			float time = 10.0f;
+		
+			outPosAndTime.xyz = float3(1.0f, 0.0f, 0.0f);
+			outPosAndTime.w = time;
+			
+			outVel = 0.0f;
+			
+			outSizeAndRotation = 0.0f;
+		}
+	};
+};

+ 45 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleCurveInject.bsl

@@ -0,0 +1,45 @@
+shader GpuParticleCurveInject
+{
+	depth
+	{
+		write = false;
+		read = false;
+	};
+
+	code
+	{	
+		struct VertexInput
+		{
+			float4 color : TEXCOORD0;
+			float2 dataUV : TEXCOORD1;
+			float2 uv0 : TEXCOORD2;
+		};	
+	
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float4 color : TEXCOORD0;
+		};
+
+		cbuffer Input
+		{
+			float4 gUVToNDC;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			float2 uv = input.uv0 + input.dataUV;
+		
+			VStoFS output;
+			output.position = float4(uv * gUVToNDC.xy + gUVToNDC.zw, 0.0f, 1.0f);
+			output.color = input.color;
+		
+			return output;
+		}			
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			return input.color;
+		}	
+	};
+};

+ 57 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleInject.bsl

@@ -0,0 +1,57 @@
+shader GpuParticleInject
+{
+	depth
+	{
+		write = false;
+		read = false;
+	};
+
+	code
+	{	
+		struct VertexInput
+		{
+			float4 posAndTime : TEXCOORD0;
+			float4 velocity : TEXCOORD1;
+			float2 size : TEXCOORD2;
+			float rotation : TEXCOORD3;
+			float2 dataUV : TEXCOORD4;
+			float2 uv0 : TEXCOORD5;
+		};	
+	
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float4 posAndTime : TEXCOORD0;
+			float4 velocity : TEXCOORD1;
+			float3 sizeAndRotation : TEXCOORD2;
+		};
+
+		cbuffer Input
+		{
+			float4 gUVToNDC;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			float2 uv = input.uv0 + input.dataUV;
+		
+			VStoFS output;
+			output.position = float4(uv * gUVToNDC.xy + gUVToNDC.zw, 0.0f, 1.0f);
+			output.posAndTime = input.posAndTime;
+			output.velocity = input.velocity;
+			output.sizeAndRotation = float3(input.size, input.rotation);
+		
+			return output;
+		}			
+	
+		void fsmain(VStoFS input, 
+			out float4 outPosAndTime : SV_Target0, 
+			out float4 outVel : SV_Target1,
+			out float4 outSizeAndRotation : SV_Target2)
+		{
+			outPosAndTime = input.posAndTime;
+			outVel = input.velocity;
+			outSizeAndRotation = float4(input.sizeAndRotation, 0.0f);
+		}	
+	};
+};

+ 257 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleSimulate.bsl

@@ -0,0 +1,257 @@
+#include "$ENGINE$/GpuParticleTileVertex.bslinc"
+#include "$ENGINE$/PerCameraData.bslinc"
+#include "$ENGINE$/PerObjectData.bslinc"
+
+shader GpuParticleSimulate
+{
+	variations
+	{
+		DEPTH_COLLISIONS = { 0, 1, 2 };
+	};
+
+	mixin GpuParticleTileVertex;
+	
+	#if DEPTH_COLLISIONS
+		mixin PerCameraData;
+		mixin PerObjectData;
+	#endif
+	
+	code
+	{
+		Texture2D gPosAndTimeTex;
+		[alias(gPosAndTimeTex)]
+		SamplerState gPosAndTimeSampler
+		{
+            Filter = MIN_MAG_MIP_POINT;
+        };
+
+		Texture2D gVelocityTex;
+		[alias(gVelocityTex)]
+		SamplerState gVelocitySampler
+		{
+            Filter = MIN_MAG_MIP_POINT;
+        };
+		
+		Texture3D gVectorFieldTex;
+		[alias(gVectorFieldTex)]
+		SamplerState gVectorFieldSampler
+		{
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+			AddressW = CLAMP;
+		};
+		
+		#if DEPTH_COLLISIONS
+		Texture2D gDepthTex;
+		Texture2D gNormalsTex;
+		
+		[alias(gDepthTex)]
+		SamplerState gDepthSampler
+		{
+            Filter = MIN_MAG_MIP_POINT;
+        };
+		
+		[alias(gNormalsTex)]
+		SamplerState gNormalsSampler;
+		
+		Texture2D gSizeRotationTex;
+		[alias(gSizeRotationTex)]
+		SamplerState gSizeRotationSampler
+		{
+            Filter = MIN_MAG_MIP_POINT;
+        };
+		
+		Texture2D gCurvesTex;
+		
+		[alias(gCurvesTex)]
+		SamplerState gCurvesSampler;
+		
+		#endif
+		
+		cbuffer VectorFieldParams
+		{
+			float3 gFieldBounds;
+			float gFieldIntensity;
+			float3 gFieldTiling;
+			float gFieldTightness;
+			float4x4 gWorldToField;
+			float3x3 gFieldToWorld;
+		};
+		
+		cbuffer Params
+		{
+			uint gNumVectorFields;
+			uint gNumIterations;
+			float gDT;
+			float gDrag;
+			float3 gAcceleration;
+		};
+		
+		#if DEPTH_COLLISIONS
+		cbuffer DepthCollisionParams
+		{
+			float gCollisionRange;
+			float gRestitution;
+			float gDampening;
+			float gCollisionRadiusScale;
+			
+			float2 gSizeScaleCurveOffset;
+			float2 gSizeScaleCurveScale;
+		};
+		
+		void integrateWithDepthCollisions(
+			float3 inPosition, float3 inVelocity, 
+			out float3 outPosition, out float3 outVelocity,
+			float dt, 
+			float3 acceleration, float radius)
+		{
+			// Integrate as normal
+			float3 deltaPosPerSec = inVelocity + acceleration * 0.5f;
+			float3 deltaPos = deltaPosPerSec * dt;
+			
+			outPosition = inPosition + deltaPos;
+			outVelocity = inVelocity + acceleration;
+			
+			// Determine potential collision point (edge of the particle sphere in the direction of travel)
+			float3 dirOffset = normalize(deltaPos) * radius;
+			float3 offsetPointWrld = inPosition + dirOffset;
+			float4 offsetPointClip = mul(gMatViewProj, float4(offsetPointWrld, 1.0f));
+			float2 offsetPointNdc = offsetPointClip.xy / offsetPointClip.w;
+			
+			// Potential collision point out of view
+			if(any(abs(offsetPointNdc.xy) > float2(1.0f, 1.0f)))
+				return;
+				
+			float2 offsetPointUV = NDCToUV(offsetPointNdc);
+			float sampledDepth = convertFromDeviceZ(gDepthTex.Sample(gDepthSampler, offsetPointUV).r);
+			
+			// Potential collision point too far away from sampled depth
+			if(abs(-offsetPointClip.w - sampledDepth) >= gCollisionRange)
+				return;
+				
+			float3 hitPointWrld = NDCToWorld(offsetPointNdc.xy, sampledDepth);
+			float3 hitPointNormal = gNormalsTex.Sample(gNormalsSampler, offsetPointUV).xyz * 2.0f - 1.0f;
+			
+			float4 hitPlane = float4(hitPointNormal, dot(hitPointNormal, hitPointWrld));
+			float speedToPlane = dot(hitPlane.xyz, deltaPos);
+			
+			// Moving away from the collision plane
+			if(speedToPlane >= 0.0f)
+				return;
+			
+			// Check if capsule (sweeped sphere) intersects the plane
+			float distToOldPos = dot(inPosition, hitPlane.xyz) - hitPlane.w;
+			float distToNewPos = dot(outPosition, hitPlane.xyz) - hitPlane.w;
+			
+			if(distToOldPos >= -radius && distToNewPos <= radius)
+			{
+				// Separate velocity into perpendicular and tangent velocity
+				// Note: This is using mid-velocity for both position and velocity integration. Velocity integration should
+				// should use the full acceleration value, but we'll keep it imprecise to avoid another calculation.
+				float3 perpVelocity = dot(deltaPosPerSec, hitPlane.xyz) * hitPlane.xyz;
+				float3 tanVelocity = deltaPosPerSec - perpVelocity;
+				
+				outVelocity = (1.0f - gDampening) * tanVelocity - gRestitution * perpVelocity;
+				
+				float t = (distToOldPos - radius) / -speedToPlane;
+				outPosition = inPosition + deltaPos * t + outVelocity * (1.0f - t) * dt;
+			}
+		}		
+		
+		#endif
+		
+		float3 evaluateVectorField(float3 pos, float scale, out float3 velocity, out float tightness)
+		{
+			if(gNumVectorFields == 0)
+			{
+				velocity = 0.0f;
+				tightness = 0.0f;
+				return 0.0f;
+			}
+		
+			float3 uv = mul(gWorldToField, float4(pos, 1.0f)).xyz;
+			uv -= floor(uv * gFieldTiling);
+			
+			float3 smp = gVectorFieldTex.Sample(gVectorFieldSampler, uv);
+			smp = mul(gFieldToWorld, smp);
+			
+			velocity = gFieldIntensity * gFieldTightness * smp;
+			tightness = gFieldTightness;
+			return gFieldIntensity * smp;
+		}
+	
+		void fsmain(VStoFS input, 
+			out float4 outPosAndTime : SV_Target0, 
+			out float4 outVelocityAndTimeScale : SV_Target1)
+		{
+			float4 posAndTime = gPosAndTimeTex.Sample(gPosAndTimeSampler, input.uv0);
+			float4 velocityAndTimeScale = gVelocityTex.Sample(gVelocitySampler, input.uv0);
+			
+			float3 position = posAndTime.xyz;
+			float time = posAndTime.w;
+			float3 velocity = velocityAndTimeScale.xyz;
+			float timeScale = velocityAndTimeScale.w;
+			
+			for(uint i = 0; i < gNumIterations; i++)
+			{
+				time += gDT * timeScale;
+			
+				// Constant acceleration
+				float3 totalForce = gAcceleration;
+			
+				// Evaluate vector field
+				float3 fieldVelocity;
+				float fieldTightness;
+				totalForce += evaluateVectorField(posAndTime.xyz, 1.0f, fieldVelocity, fieldTightness);
+				
+				velocity = lerp(velocity, fieldVelocity, fieldTightness);
+				
+				// Drag
+				totalForce -= gDrag * velocity;
+				
+				// Integrate
+				float3 acceleration = totalForce * gDT;
+				
+#if DEPTH_COLLISIONS
+				float2 size = gSizeRotationTex.Sample(gSizeRotationSampler, input.uv0).xy;
+			
+				float2 sizeScaleCurveUV = gSizeScaleCurveOffset + time * gSizeScaleCurveScale;
+				float2 sizeScale = gCurvesTex.Sample(gCurvesSampler, sizeScaleCurveUV, 0).xy;
+			
+				// TODO - Apply world transform scale
+				size *= 0.5f * sizeScale;
+				float collisionRadius = min(size.x, size.y) * gCollisionRadiusScale;
+
+			#if DEPTH_COLLISIONS == 2
+				position = mul(gMatWorld, float4(position, 1.0f)).xyz;
+				velocity = mul(gMatWorld, float4(velocity, 0.0f)).xyz;			
+			#endif
+				
+				float3 outPosition;
+				float3 outVelocity;
+				integrateWithDepthCollisions(
+					position, velocity, 
+					outPosition, outVelocity,
+					gDT, 
+					acceleration, collisionRadius);
+					
+			#if DEPTH_COLLISIONS == 2
+				position = mul(gMatInvWorld, float4(outPosition, 1.0f)).xyz;
+				velocity = mul(gMatInvWorld, float4(outVelocity, 0.0f)).xyz;
+			#else
+				position = outPosition;
+				velocity = outVelocity;
+			#endif
+#else
+				position += (velocity + acceleration * 0.5f) * gDT;
+				velocity += acceleration;
+#endif
+			}
+
+			outPosAndTime.xyz = position;
+			outPosAndTime.w = time;
+			outVelocityAndTimeScale.xyz = velocity;
+			outVelocityAndTimeScale.w = timeScale;			
+		}	
+	};
+};

+ 71 - 0
Source/bsf/Data/Raw/Shaders/GpuParticleSortPrepare.bsl

@@ -0,0 +1,71 @@
+shader GpuParticleSortPrepare
+{
+	featureset = HighEnd;
+	
+	code
+	{
+		Texture2D gPosAndTimeTex;
+		Buffer<uint2> gInputIndices;
+		
+		RWBuffer<uint> gOutputKeys;
+		RWBuffer<uint2> gOutputIndices;
+		
+		cbuffer Input
+		{
+			uint gIterationsPerGroup;
+			uint gNumExtraIterations;
+			uint gNumParticles;
+			uint gOutputOffset;
+			uint gSystemKey;
+			float3 gLocalViewOrigin;
+		};	
+		
+		[numthreads(NUM_THREADS, 1, 1)]
+		void csmain(uint3 groupThreadId : SV_GroupThreadID, uint3 groupId : SV_GroupID)
+		{
+			uint threadId = groupThreadId.x;
+		
+			uint inputIdx;
+			uint numIterations;
+			
+			if(groupId.x < gNumExtraIterations)
+			{
+				inputIdx = groupId.x * (gIterationsPerGroup + 1);
+				numIterations = gIterationsPerGroup + 1;
+			}
+			else
+			{
+				inputIdx = groupId.x * gIterationsPerGroup + gNumExtraIterations;
+				numIterations = gIterationsPerGroup;
+			}
+					
+			inputIdx = inputIdx * NUM_THREADS + threadId;
+			uint outputIdx = gOutputOffset + inputIdx;
+		
+			for(uint i = 0; i < numIterations; ++i)
+			{
+				if(inputIdx >= gNumParticles)
+					break;
+					
+				uint3 pixelPos;
+				pixelPos.xy = gInputIndices[inputIdx];
+				pixelPos.z = 0;
+				
+				float3 position = gPosAndTimeTex.Load(pixelPos).xyz;
+				float dist = length(position - gLocalViewOrigin);
+				
+				uint key = f32tof16(dist);
+				
+				// Flip the sign to sort from furthest to nearest. This works because positive float bits
+				// will still compare properly. Negative ones won't be those are invisible behind the camera anyway.
+				key = ((~key) & 0xFFFF) | gSystemKey;
+
+				gOutputKeys[outputIdx] = key;
+				gOutputIndices[outputIdx] = pixelPos.xy;
+					
+				inputIdx += NUM_THREADS;
+				outputIdx += NUM_THREADS;
+			}	
+		}	
+	};
+};

+ 45 - 0
Source/bsf/Data/Raw/Shaders/Includes/BasePass.bslinc

@@ -0,0 +1,45 @@
+#ifndef WRITE_VELOCITY
+	#define WRITE_VELOCITY 0
+#endif
+
+#if WRITE_VELOCITY
+	#define CLIP_POS 1
+	#define PREV_CLIP_POS 1
+#else
+	#define CLIP_POS 0
+	#define PREV_CLIP_POS 0
+#endif
+
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\PerObjectData.bslinc"
+#include "$ENGINE$\VertexInput.bslinc"
+
+mixin BasePass
+{
+	mixin PerCameraData;
+	mixin PerObjectData;
+	mixin VertexInput;
+
+	code
+	{			
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			VertexIntermediate intermediate = getVertexIntermediate(input);
+			float4 worldPosition = getVertexWorldPosition(input, intermediate);
+			
+			output.worldPosition = worldPosition.xyz;
+			output.position = mul(gMatViewProj, worldPosition);
+			
+			#if PREV_CLIP_POS
+				float4 prevWorldPosition = getPrevVertexWorldPosition(input, intermediate);
+				output.prevClipPos = mul(gMatPrevViewProj, prevWorldPosition);
+			#endif
+			
+			populateVertexOutput(input, intermediate, output);
+						
+			return output;
+		}
+	};
+};

+ 93 - 0
Source/bsf/Data/Raw/Shaders/Includes/ColorSpace.bslinc

@@ -0,0 +1,93 @@
+mixin ColorSpace
+{
+	code
+	{
+		/**
+		 * Converts a color in RGB space into YCoCg color space. Note that Co and Cg
+		 * components are in [-0.5, 0.5] range and therefore cannot be stored as-is
+		 * in a normal color texture.
+		 */
+		float3 RGBToYCoCg(float3 input)
+		{
+			float Y = dot(input, float3(0.25f, 0.5f, 0.25f));
+			float Co = input.r * 0.5f + input.b * -0.5f;
+			float Cg = dot(input, float3(-0.25f, 0.5f, -0.25f));
+			
+			return float3(Y, Co, Cg);
+		}
+		
+		/**
+		 * Converts a color in YCoCg color space into RGB color space.
+		 */
+		float3 YCoCgToRGB(float3 input)
+		{
+			float R = input.r + input.g - input.b;
+			float G = input.r + input.b;
+			float B = input.r - input.g - input.b;
+			
+			return float3(R, G, B);
+		}
+		
+		/** Calculates luminance from a color in RGB color space. */
+		float LuminanceRGB(float3 input)
+		{
+			return 0.299f * input.r + 0.587f * input.g + 0.114f * input.b;
+		}
+		
+		////////////////////// HDR SCALING HELPER FUNCTIONS ///////////////////
+		// HDR scaling methods use luminance based scaling, instead of a tonemap operator, as
+		// described here:
+		// http://graphicrants.blogspot.hr/2013/12/tone-mapping.html
+		float HDRScaleWeight(float luma, float exposureScale)
+		{
+			return rcp(1 + luma * exposureScale);
+		}
+		
+		float HDRScaleWeightInv(float luma, float exposureScale)
+		{
+			return rcp(1 - luma * exposureScale);
+		}		
+		
+		float3 HDRScaleRGB(float3 v, float exposureScale)
+		{
+			float luma = LuminanceRGB(v);
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+		
+		float3 HDRScaleY(float3 v, float exposureScale)
+		{
+			float luma = v.r;
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+		
+		float3 HDRScaleG(float3 v, float exposureScale)
+		{
+			float luma = v.g;
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+
+		float3 HDRScaleRGBInv(float3 v, float exposureScale)
+		{
+			float luma = LuminanceRGB(v);
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}
+		
+		float3 HDRScaleYInv(float3 v, float exposureScale)
+		{
+			float luma = v.r;
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}
+		
+		float3 HDRScaleGInv(float3 v, float exposureScale)
+		{
+			float luma = v.g;
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}				
+	};
+};

+ 87 - 0
Source/bsf/Data/Raw/Shaders/Includes/DeferredLightCommon.bslinc

@@ -0,0 +1,87 @@
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\DirectLighting.bslinc"
+
+mixin DeferredLightCommon
+{
+	mixin GBufferInput;
+	mixin PerCameraData;
+	mixin DirectLighting;
+
+	depth
+	{
+		write = false;
+		
+		#if INSIDE_GEOMETRY
+		read = false;
+		#else
+		read = true;
+		#endif
+	};
+	
+	blend
+	{
+		target
+		{
+			enabled = true;
+			color = { one, one, add };
+			writemask = RGB;			
+		};
+	};
+	
+	raster
+	{
+		#if INSIDE_GEOMETRY
+		cull = cw;
+		#else
+		cull = ccw;
+		#endif
+	};
+
+	code
+	{
+		cbuffer PerLight
+		{
+			// x, y, z - World position of the light
+			// w - Radius of the area light source, zero if not an area light
+			float4 gLightPositionAndSrcRadius;
+			float4 gLightColorAndLuminance;
+			// x - outerAngle in radians, y - cos(outerAngle), z - 1.0f/(cos(innerAngle) - cos(outerAngle)), w - inverse light attenuation radius
+			float4 gLightSpotAnglesAndSqrdInvAttRadius;
+			float4 gLightDirectionAndBoundRadius;
+			// xyz - light position shifted in order to adjust for area spot lights
+			// w - light type -> Directional = 0, Radial = >0, Spot = >0.5
+			float4 gShiftedLightPositionAndType;
+			
+			// x - Num sides (zero for radial lights)
+			// y - Num slices (zero for radial lights)
+			// z - Sphere radius for radial lights
+			// w - Cone radius for spot lights
+			float4 gLightGeometry; 
+			float4x4 gMatConeTransform;
+		}		
+
+		LightData getLightData()
+		{
+			LightData output;
+			
+			output.position = gLightPositionAndSrcRadius.xyz;
+			output.boundRadius = gLightDirectionAndBoundRadius.w;
+			output.direction = gLightDirectionAndBoundRadius.xyz;
+			output.luminance = gLightColorAndLuminance.w;
+			output.spotAngles = gLightSpotAnglesAndSqrdInvAttRadius.xyz;
+			output.attRadiusSqrdInv = gLightSpotAnglesAndSqrdInvAttRadius.w;
+			output.color = gLightColorAndLuminance.rgb;
+			output.srcRadius = gLightPositionAndSrcRadius.w;
+			output.shiftedLightPosition = gShiftedLightPositionAndType.rgb;
+
+			return output;
+		}
+		
+		#if MSAA_COUNT > 1
+		Texture2DMS<float4> gLightOcclusionTex;
+		#else
+		Texture2D<float4> gLightOcclusionTex;
+		#endif
+	};
+};

+ 20 - 0
Source/bsf/Data/Raw/Shaders/Includes/DepthInput.bslinc

@@ -0,0 +1,20 @@
+mixin DepthInput
+{
+	code 
+	{
+		[alias(gDepthBufferTex)]
+		SamplerState gDepthBufferSamp;
+		
+		#ifndef MSAA_COUNT
+			#define MSAA_COUNT 1
+		#endif
+
+		#if MSAA_COUNT > 1
+		[internal]
+		Texture2DMS<float4> gDepthBufferTex;
+		#else
+		[internal]
+		Texture2D gDepthBufferTex;
+		#endif	
+	};
+};

+ 60 - 0
Source/bsf/Data/Raw/Shaders/Includes/DepthOfFieldCommon.bslinc

@@ -0,0 +1,60 @@
+mixin DepthOfFieldCommon
+{
+	code
+	{
+		cbuffer DepthOfFieldParams
+		{
+			float gFocalPlaneDistance; // Distance to the in-focus plane, in world units
+			float gApertureSize; // In meters, e.g. 50mm = 0.05m
+			float gFocalLength; // In meters, e.g. 75mm = 0.075m
+			float gInFocusRange; // Region totally in focus, starting at the in-focus plane (0 for physically based)
+			float gSensorSize; // Camera sensor size in mm (shorter side)
+			float gImageSize; // View size in pixels (side corresponding to shorter sensor side)
+			float gMaxBokehSize; // Maximum size of the Bokeh shape in pixels
+			float gNearTransitionRegion; // Region before the in-focus plane used to transition from in-focus to out-of-focus (non-physically based)
+			float gFarTransitionRegion; // Region after the in-focus region used to transition from in-focus to out-of-focus (non-physically based)
+		};	
+
+		// Computes for how in-focus should an object at a specific distance be. 
+		// Returns 0 for in-focus, and 1 for out-of-focus
+		float circleOfConfusionPhysical(float distance)
+		{
+			// Force a region after the in-focus plane to be completely in focus (non-physically based)
+			[flatten]
+			if(distance > gFocalPlaneDistance)
+				distance = gFocalPlaneDistance + max(0, distance - gFocalPlaneDistance - gInFocusRange);
+			
+			// Note: Simplify this equation to reduce the number of operations. Terms can probably cancel out,
+			// and some terms can probably be pre-calculated.
+			float imagePlaneFocus = gFocalPlaneDistance * gFocalLength / (gFocalPlaneDistance - gFocalLength);
+			float imagePlaneObject = distance * gFocalLength / (distance - gFocalLength);
+			
+			float cocInMeters = gApertureSize * abs(imagePlaneObject - imagePlaneFocus) / imagePlaneFocus;
+			float cocInMM = cocInMeters * 1000.0f;
+			float cocInPixels = gImageSize * (cocInMM / gSensorSize);
+			
+			return saturate(cocInPixels / gMaxBokehSize);
+		}
+		
+		// Has the same purpose as circleOfConfusionPhysical, but uses the explictly set near, far and in-focus regions for determining
+		// the circle of confusion, rather than using physically based camera parameters.
+		float circleOfConfusionArtistic(float distance)
+		{
+			// Force a region after the in-focus plane to be completely in focus
+			[flatten]
+			if(distance > gFocalPlaneDistance)
+				distance = gFocalPlaneDistance + max(0, distance - gFocalPlaneDistance - gInFocusRange);
+				
+			float transitionRegion = (distance < gFocalPlaneDistance) ? gNearTransitionRegion : gFarTransitionRegion;
+			return saturate(abs(distance - gFocalPlaneDistance) / transitionRegion);
+		}
+				
+		float2 computeLayerContributions(float depth)
+		{
+			float near = saturate((gFocalPlaneDistance - depth) / gNearTransitionRegion);
+			float far = saturate((depth - gFocalPlaneDistance - gInFocusRange) / gFarTransitionRegion);
+
+			return float2(near, far);
+		}
+	};
+};

+ 90 - 0
Source/bsf/Data/Raw/Shaders/Includes/DirectLightAccumulator.bslinc

@@ -0,0 +1,90 @@
+// 
+// Contains helper mixin used for initializing different forms of light accumulation. Can be removed 
+// when template/specialization support for mixins is added to BSL.
+//
+
+#ifdef USE_UNIFORM_BUFFER
+mixin LightAccumulatorDirect
+#else
+mixin LightAccumulatorIndexed
+#endif
+{
+	code
+	{
+		#ifdef USE_UNIFORM_BUFFER
+			#define MAX_LIGHTS 8
+			
+			[internal]
+			cbuffer Lights
+			{
+				LightData gLights[MAX_LIGHTS];
+			}
+		#else
+			#define MAX_LIGHTS 512 // Arbitrary limit, increase if needed
+		
+			#ifdef USE_COMPUTE_INDICES
+				groupshared uint gLightIndices[MAX_LIGHTS];
+				StructuredBuffer<LightData> gLights;
+			#endif
+			
+			#ifdef USE_LIGHT_GRID_INDICES
+				Buffer<uint> gLightIndices;
+				StructuredBuffer<LightData> gLights;
+			#endif
+		#endif
+		
+		float4 getDirectLighting(float3 worldPos, float3 V, float3 R, SurfaceData surfaceData, uint4 lightOffsets)
+		{
+			float3 N = surfaceData.worldNormal.xyz;
+			float roughness2 = max(surfaceData.roughness, 0.08f);
+			roughness2 *= roughness2;
+			
+			float3 outLuminance = 0;
+			float alpha = 0.0f;
+			if(surfaceData.worldNormal.w > 0.0f)
+			{
+				// Handle directional lights
+				[loop]
+				for(uint i = 0; i < lightOffsets.x; ++i)
+				{
+					LightData lightData = gLights[i];
+					outLuminance += getLuminanceDirectional(lightData, worldPos, V, R, surfaceData);
+				}
+				
+				// Handle radial lights
+				[loop]
+				for (uint j = lightOffsets.y; j < lightOffsets.z; ++j)
+				{
+					#ifdef USE_UNIFORM_BUFFER
+					uint lightIdx = j;
+					#else
+					uint lightIdx = gLightIndices[j];
+					#endif
+					
+					LightData lightData = gLights[lightIdx];
+					outLuminance += getLuminanceRadial(lightData, worldPos, V, R, roughness2, surfaceData);
+				}
+
+				// Handle spot lights
+				[loop]
+				for(uint k = lightOffsets.z; k < lightOffsets.w; ++k)
+				{
+					#ifdef USE_UNIFORM_BUFFER
+					uint lightIdx = k;
+					#else
+					uint lightIdx = gLightIndices[k];
+					#endif
+					
+					LightData lightData = gLights[lightIdx];
+					outLuminance += getLuminanceSpot(lightData, worldPos, V, R, roughness2, surfaceData);
+				}
+				
+				// Ambient term for in-editor visualization, not used in actual lighting
+				outLuminance += surfaceData.albedo.rgb * gAmbientFactor / PI;
+				alpha = 1.0f;
+			}
+			
+			return float4(outLuminance, alpha);
+		}
+	};
+};

+ 387 - 0
Source/bsf/Data/Raw/Shaders/Includes/DirectLighting.bslinc

@@ -0,0 +1,387 @@
+#include "$ENGINE$\SurfaceData.bslinc"
+
+mixin LightData
+{
+	code
+	{
+		// Note: Size must be multiple of largest element, because of std430 rules
+		struct LightData
+		{
+			float3 position;
+			float boundRadius;
+			float3 direction;
+			float luminance;
+			float3 spotAngles;
+			float attRadiusSqrdInv;
+			float3 color;
+			float srcRadius;
+			float3 shiftedLightPosition;
+			float padding;
+		};
+	};
+};
+
+mixin DirectLightingCommon
+{
+	mixin LightData;
+	mixin SurfaceData;
+	
+	code
+	{
+		#define PI 3.1415926
+		#define HALF_PI 1.5707963
+		
+		float3 calcMicrofacetFresnelShlick(float3 F0, float LoH)
+		{
+			return F0 + (1.0f - F0) * pow(1.0f - LoH, 5.0f);
+		}
+
+		float calcMicrofacetShadowingSmithGGX(float roughness4, float NoV, float NoL)
+		{
+			// Note: It's probably better to use the joint shadowing + masking version of this function
+
+			// Note: Original GGX G1 multiplied by NoV & NoL (respectively), so that the microfacet function divisor gets canceled out
+			// Original formula being (ignoring the factor for masking negative directions):
+			//   G1(v) = 2 / (1 + sqrt(1 + roughness^4 * tan^2(v)))
+			//
+			// Using trig identities: tan = sin/cos & sin^2 + cos^2 = 1
+			//   G1(v) = 2 / (1 + sqrt(1 + roughness^4 * (1 - cos^2(v))/cos^2(v)))
+			//
+			// Multiply by cos(v) so that we cancel out the (NoL * NoV) factor in the microfacet formula divisor
+			//   G1(v) = 2 * cos(v) / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
+			// 
+			// Actually do the cancellation:
+			//    G1(v) = 2 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
+			//
+			// Also cancel out the 2 and the 4:
+			//    G1(v) = 1 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
+			//
+			// Final equation being:
+			//    G(v, l) = G1(v) * G1(l)
+			//
+			// Where cos(v) is NoV or NoL
+			
+			float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
+			float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
+			return rcp(g1V * g1L);
+		}
+
+		float calcMicrofacetDistGGX(float roughness4, float NoH)
+		{
+			float d = (NoH * roughness4 - NoH) * NoH + 1.0f;
+			return roughness4 / (PI * d * d);
+		}
+
+		float3 calcDiffuseLambert(float3 color)
+		{
+			return color * (1.0f / PI);
+		}
+
+		float getSpotAttenuation(float3 toLight, LightData lightData)
+		{
+			float output = saturate((dot(toLight, -lightData.direction) - lightData.spotAngles.y) * lightData.spotAngles.z);
+			return output * output;
+		}
+
+		// Window function to ensure the light contribution fades out to 0 at attenuation radius
+		float getRadialAttenuation(float distance2, LightData lightData)
+		{
+			float radialAttenuation = distance2 * lightData.attRadiusSqrdInv;
+			radialAttenuation *= radialAttenuation;
+			radialAttenuation = saturate(1.0f - radialAttenuation);
+			radialAttenuation *= radialAttenuation;
+			
+			return radialAttenuation;
+		}			
+					
+		// Calculates illuminance from a non-area point light
+		float illuminancePointLight(float distance2, float NoL, LightData lightData)
+		{
+			return (lightData.luminance * NoL) / max(distance2, 0.01f*0.01f);
+		}
+
+		// Calculates illuminance scale for a sphere or a disc area light, while also handling the case when
+		// parts of the area light are below the horizon.
+		// Input NoL must be unclamped.
+		// Sphere solid angle = arcsin(r / d)
+		// Right disc solid angle = atan(r / d)
+		//   - To compensate for oriented discs, multiply by dot(diskNormal, -L)
+		float illuminanceScaleSphereDiskAreaLight(float unclampedNoL, float sinSolidAngleSqrd)
+		{
+			// Handles parts of the area light below the surface horizon
+			// See https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf for reference
+			float sinSolidAngle = sqrt(sinSolidAngleSqrd);
+			
+			// TODO - Below horizon handling disabled as it currently outputs incorrect values, need to find a better approximation or just use the reference implementation
+			//if(unclampedNoL < sinSolidAngle)
+			//{
+			//	// Hermite spline approximation (see reference for exact formula)
+			//	unclampedNoL = max(unclampedNoL, -sinSolidAngle);
+			//	return ((sinSolidAngle + unclampedNoL) * (sinSolidAngle + unclampedNoL)) / (4 * sinSolidAngle);
+			//}
+			//else
+				return PI * sinSolidAngleSqrd * saturate(unclampedNoL);
+		}
+
+		// Calculates illuminance from a sphere area light.
+		float illuminanceSphereAreaLight(float unclampedNoL, float distToLight2, LightData lightData)
+		{
+			float radius2 = lightData.srcRadius * lightData.srcRadius;
+			
+			// Squared sine of the sphere solid angle
+			float sinSolidAngle2 = radius2 / distToLight2;
+
+			// Prevent divide by zero
+			sinSolidAngle2 = min(sinSolidAngle2, 0.9999f);
+			
+			return lightData.luminance * illuminanceScaleSphereDiskAreaLight(unclampedNoL, sinSolidAngle2);	
+		}
+
+		// Calculates illuminance from a disc area light.
+		float illuminanceDiscAreaLight(float unclampedNoL, float distToLight2, float3 L, LightData lightData)
+		{
+			// Solid angle for right disk = atan (r / d)
+			//  atan (r / d) = asin((r / d)/sqrt((r / d)^2+1))
+			//  sinAngle = (r / d)/sqrt((r / d)^2 + 1)
+			//  sinAngle^2 = (r / d)^2 / (r / d)^2 + 1
+			//             = r^2 / (d^2 + r^2)
+
+			float radius2 = lightData.srcRadius * lightData.srcRadius;
+			
+			// max() to prevent light penetrating object
+			float sinSolidAngle2 = saturate(radius2 / (radius2 + max(radius2, distToLight2)));
+			
+			// Multiply by extra term to somewhat handle the case of the oriented disc (formula above only works
+			// for right discs).
+			return lightData.luminance * illuminanceScaleSphereDiskAreaLight(unclampedNoL, sinSolidAngle2 * saturate(dot(lightData.direction, -L)));	
+		}
+
+		// With microfacet BRDF the BRDF lobe is not centered around the reflected (mirror) direction.
+		// Because of NoL and shadow-masking terms the lobe gets shifted toward the normal as roughness
+		// increases. This is called the "off-specular peak". We approximate it using this function.
+		float3 getSpecularDominantDir(float3 N, float3 R, float roughness)
+		{
+			// Note: Try this formula as well:
+			//  float smoothness = 1 - roughness;
+			//  return lerp(N, R, smoothness * (sqrt(smoothness) + roughness));
+
+			float r2 = roughness * roughness;
+			return normalize(lerp(N, R, (1 - r2) * (sqrt(1 - r2) + r2)));
+		}		
+	};
+};
+
+mixin StandardBRDF
+{
+	code
+	{
+		float3 evaluateStandardBRDF(float3 V, float3 L, float specLobeEnergy, SurfaceData surfaceData)
+		{
+			float3 N = surfaceData.worldNormal.xyz;
+
+			float3 H = normalize(V + L);
+			float LoH = saturate(dot(L, H));
+			float NoH = saturate(dot(N, H));
+			float NoV = saturate(dot(N, V));
+			float NoL = saturate(dot(N, L));
+			
+			float3 diffuseColor = lerp(surfaceData.albedo.rgb, float3(0.0f, 0.0f, 0.0f), surfaceData.metalness);
+			
+			// Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
+			// For more customizability allow the user to provide separate albedo/specular colors for both types.
+			float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
+			
+			float3 diffuse = calcDiffuseLambert(diffuseColor);
+			
+			float roughness = max(surfaceData.roughness, 0.04f); // Prevent NaNs
+			float roughness2 = roughness * roughness;
+			float roughness4 = roughness2 * roughness2;
+			
+			float3 specular = calcMicrofacetFresnelShlick(specularColor, LoH) * 
+				calcMicrofacetDistGGX(roughness4, NoH) *
+				calcMicrofacetShadowingSmithGGX(roughness4, NoV, NoL);
+			
+			// Note: Need to add energy conservation between diffuse and specular terms?
+			return diffuse + specular * specLobeEnergy;
+		}
+	};
+};
+
+mixin LuminanceSpot
+{
+	code
+	{
+		float3 getLuminanceSpot(LightData lightData, float3 worldPos, float3 V, float3 R, float roughness2, SurfaceData surfaceData)
+		{
+			float3 N = surfaceData.worldNormal.xyz;
+			float3 toLight = lightData.position - worldPos;
+			float distToLightSqrd = dot(toLight, toLight);
+			float invDistToLight = rsqrt(distToLightSqrd);
+			
+			float3 L = toLight * invDistToLight;
+			float NoL = dot(N, L);
+			
+			float specEnergy = 1.0f;
+			float illuminance = 0.0f;
+			float spotAttenuation = 1.0f;
+			
+			// Disc area light. Calculate its contribution analytically by
+			// finding the most important (least error) point on the area light and
+			// use it as a form of importance sampling.
+			if(lightData.srcRadius > 0)
+			{
+				// Calculate illuminance depending on source size, distance and angle
+				illuminance = illuminanceDiscAreaLight(NoL, distToLightSqrd, L, lightData);	
+			
+				// Energy conservation: Similar case as with radial lights
+				float rightDiscAngle = saturate(lightData.srcRadius * invDistToLight);
+				
+				// Account for disc orientation somewhat
+				float discAngle = rightDiscAngle * saturate(dot(lightData.direction, -L));
+				
+				specEnergy = roughness2 / saturate(roughness2 + 0.5f * discAngle);
+				specEnergy *= specEnergy;							
+			
+				// Find closest point on disc to ray
+				float3 discNormal = -lightData.direction;
+				float distAlongLightDir = max(dot(R, discNormal), 1e-6f);
+				float t = dot(toLight, discNormal) / distAlongLightDir;
+				float3 closestPointOnPlane = R * t; // Relative to shaded world point
+				
+				float3 centerToRay = closestPointOnPlane - toLight;
+				float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
+				float3 closestPointOnDisc = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
+
+				toLight = closestPointOnDisc;
+				L = normalize(toLight);
+				
+				// Expand spot attenuation by disc radius (not physically based)
+				float3 toSpotEdge = normalize(lightData.shiftedLightPosition - worldPos);
+				spotAttenuation = getSpotAttenuation(toSpotEdge, lightData);
+				
+				// TODO - Spot attenuation fades out the specular highlight in a noticeable way
+			}
+			else
+			{
+				NoL = saturate(NoL);
+				illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
+				
+				spotAttenuation = getSpotAttenuation(L, lightData);
+			}
+			
+			float radialAttenuation = getRadialAttenuation(distToLightSqrd, lightData);
+			float attenuation = spotAttenuation * radialAttenuation;
+			float3 surfaceShading = evaluateStandardBRDF(V, L, specEnergy, surfaceData);
+				
+			return lightData.color * illuminance * attenuation * surfaceShading;
+		}
+	};
+};
+
+mixin LuminanceRadial
+{
+	code
+	{
+		float3 getLuminanceRadial(LightData lightData, float3 worldPos, float3 V, float3 R, float roughness2, SurfaceData surfaceData)
+		{
+			float3 N = surfaceData.worldNormal.xyz;
+			float3 toLight = lightData.position - worldPos;
+			float distToLightSqrd = dot(toLight, toLight);
+			float invDistToLight = rsqrt(distToLightSqrd);
+			
+			float3 L = toLight * invDistToLight;
+			float NoL = dot(N, L);
+			
+			float specEnergy = 1.0f;
+			float illuminance = 0.0f;
+
+			// Sphere area light. Calculate its contribution analytically by
+			// finding the most important (least error) point on the area light and
+			// use it as a form of importance sampling.
+			if(lightData.srcRadius > 0)
+			{
+				// Calculate illuminance depending on source size, distance and angle
+				illuminance = illuminanceSphereAreaLight(NoL, distToLightSqrd, lightData);	
+
+				// Energy conservation:
+				//    We are widening the specular distribution by the sphere's subtended angle, 
+				//    so we need to handle the increase in energy. It is not enough just to account
+				//    for the sphere solid angle, since the energy difference is highly dependent on
+				//    specular distribution. By accounting for this energy difference we ensure glossy
+				//    reflections have sharp edges, instead of being too blurry.
+				//    See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for reference
+				float sphereAngle = saturate(lightData.srcRadius * invDistToLight);
+				
+				specEnergy = roughness2 / saturate(roughness2 + 0.5f * sphereAngle);
+				specEnergy *= specEnergy;							
+			
+				// Find closest point on sphere to ray
+				float3 closestPointOnRay = dot(toLight, R) * R;
+				float3 centerToRay = closestPointOnRay - toLight;
+				float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
+				float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
+				
+				toLight = closestPointOnSphere;
+				L = normalize(toLight);
+			}
+			else
+			{
+				NoL = saturate(NoL);
+				illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
+			}
+			
+			float attenuation = getRadialAttenuation(distToLightSqrd, lightData);
+			float3 surfaceShading = evaluateStandardBRDF(V, L, specEnergy, surfaceData);
+				
+			return lightData.color * illuminance * attenuation * surfaceShading;
+		}
+	};
+};
+
+mixin LuminanceDirectional
+{
+	code
+	{
+		float3 getLuminanceDirectional(LightData lightData, float3 worldPos, float3 V, float3 R, SurfaceData surfaceData)
+		{
+			float3 N = surfaceData.worldNormal.xyz;
+			float3 L = -lightData.direction;
+			float NoL = saturate(dot(N, L));
+			float specEnergy = 1.0f;
+			
+			// Distant disk area light. Calculate its contribution analytically by
+			// finding the most important (least error) point on the area light and
+			// use it as a form of importance sampling.
+			if(lightData.srcRadius > 0)
+			{
+				float diskRadius = sin(lightData.srcRadius);
+				float distanceToDisk = cos(lightData.srcRadius);
+				
+				// Closest point to disk (approximation for distant disks)
+				float DoR = dot(L, R);
+				float3 S = normalize(R - DoR * L);
+				L = DoR < distanceToDisk ? normalize(distanceToDisk * L + S * diskRadius) : R;
+			}
+			
+			float3 surfaceShading = evaluateStandardBRDF(V, L, specEnergy, surfaceData);
+			float illuminance = lightData.luminance * NoL;
+			return lightData.color * illuminance * surfaceShading;
+		}
+	};
+};
+
+mixin DirectLighting
+{
+	mixin DirectLightingCommon;
+	mixin StandardBRDF;
+	mixin LuminanceDirectional;
+	mixin LuminanceRadial;
+	mixin LuminanceSpot;
+};
+
+// Hackish way of "instantiating" two versions of a mixin (to be removed when template/specialization support is added)
+#include "$ENGINE$\DirectLightAccumulator.bslinc"
+
+#define USE_UNIFORM_BUFFER
+#include "$ENGINE$\DirectLightAccumulator.bslinc"
+#undef USE_UNIFORM_BUFFER

+ 90 - 0
Source/bsf/Data/Raw/Shaders/Includes/ForwardLighting.bslinc

@@ -0,0 +1,90 @@
+#include "$ENGINE$\LightGridCommon.bslinc"
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#define USE_LIGHT_GRID_INDICES 1
+#include "$ENGINE$\DirectLighting.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+
+options
+{
+	forward = true;
+};
+
+mixin ForwardLighting
+{
+	mixin DirectLighting;
+	mixin ReflectionCubemapCommon;
+	mixin ImageBasedLighting;
+	
+	#if CLUSTERED
+	mixin LightGridCommon;
+	mixin LightAccumulatorIndexed;
+	mixin ReflProbeAccumulatorIndexed;
+	
+	featureset = HighEnd;
+	#else
+	mixin LightAccumulatorDirect;
+	mixin ReflProbeAccumulatorDirect;
+	#endif
+	
+	variations
+	{
+		CLUSTERED = { true, false };
+	};		
+
+	code
+	{
+		#if CLUSTERED
+		Buffer<uint4> gGridLightOffsetsAndSize;
+		Buffer<uint2> gGridProbeOffsetsAndSize;
+		#else
+		[internal]
+		cbuffer LightAndReflProbeParams
+		{
+			// Number of lights per type in the lights buffer
+			// x - number of directional lights
+			// y - offset to radial lights
+			// z - offset to spot lights
+			// w - total number of lights
+			uint4 gLightOffsets;
+			
+			uint gReflProbeCount;
+		}
+		#endif
+
+		float3 calcLighting(float3 worldPosition, float3 screenPosition, float2 uv, SurfaceData surfaceData)
+		{
+			#if CLUSTERED
+			uint2 pixelPos = (uint2)screenPosition.xy;
+			uint cellIdx = calcCellIdx(pixelPos, screenPosition.z);
+			uint3 lightOffsetAndSize = gGridLightOffsetsAndSize[cellIdx].rgb;
+			
+			uint4 lightOffsets;
+			lightOffsets.x = gLightCounts.x;
+			lightOffsets.y = lightOffsetAndSize.x;
+			lightOffsets.z = lightOffsets.y + lightOffsetAndSize.y;
+			lightOffsets.w = lightOffsets.z + lightOffsetAndSize.z;
+			
+			uint2 reflProbeOffsetAndSize = gGridProbeOffsetsAndSize[cellIdx];
+			#else
+			uint4 lightOffsets = gLightOffsets;
+			uint2 reflProbeOffsetAndSize = uint2(0, gReflProbeCount);			
+			#endif
+			
+			float3 V = normalize(gViewOrigin - worldPosition);
+			float3 N = surfaceData.worldNormal.xyz;
+			float3 R = 2 * dot(V, N) * N - V;
+			float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
+			
+			float4 directLighting = getDirectLighting(worldPosition, V, specR, surfaceData, lightOffsets);
+			float ao = gAmbientOcclusionTex.Sample(gAmbientOcclusionSamp, uv);
+			float4 ssr = gSSRTex.Sample(gSSRSamp, uv);
+			float3 imageBasedSpecular = getImageBasedSpecular(worldPosition, V, specR, surfaceData, ao, ssr,
+				reflProbeOffsetAndSize.x, reflProbeOffsetAndSize.y);
+
+			float3 totalLighting = directLighting.rgb;
+			totalLighting.rgb += imageBasedSpecular;
+
+			return totalLighting;
+		}	
+	};
+};

+ 85 - 0
Source/bsf/Data/Raw/Shaders/Includes/GBufferInput.bslinc

@@ -0,0 +1,85 @@
+#include "$ENGINE$\SurfaceData.bslinc"
+
+mixin GBufferInput
+{
+	mixin SurfaceData;
+	mixin PerCameraData;
+
+	code 
+	{
+		// Note: Only one or two sampler states are likely required, I can avoid wasting register space
+		SamplerState gGBufferASamp;
+		SamplerState gGBufferBSamp;
+		SamplerState gGBufferCSamp;
+		[alias(gDepthBufferTex)]
+		SamplerState gDepthBufferSamp;
+		
+		#ifndef MSAA_COUNT
+			#define MSAA_COUNT 1
+		#endif
+
+		#if MSAA_COUNT > 1
+		Texture2DMS<float4> gGBufferATex;
+		Texture2DMS<float4>	gGBufferBTex;
+		Texture2DMS<float2>	gGBufferCTex;
+		Texture2DMS<float4> gDepthBufferTex;
+		#else
+		Texture2D gGBufferATex;
+		Texture2D gGBufferBTex;
+		Texture2D gGBufferCTex;
+		Texture2D gDepthBufferTex;
+		#endif
+		
+		SurfaceData decodeGBuffer(float4 GBufferAData, float4 GBufferBData, float2 GBufferCData, float deviceZ)
+		{
+			SurfaceData output;
+			
+			output.albedo.xyz = GBufferAData.xyz;
+			output.albedo.w = 1.0f;
+			output.worldNormal = GBufferBData * float4(2, 2, 2, 1) - float4(1, 1, 1, 0);
+			output.worldNormal.xyz = normalize(output.worldNormal.xyz);
+			output.depth = convertFromDeviceZ(deviceZ);
+			output.roughness = GBufferCData.x;
+			output.metalness = GBufferCData.y;
+			
+			// Not being read at the moment, as these are only needed in specific circumstances
+			output.mask = 0; 
+			output.velocity = 0.0f;
+			
+			return output;
+		}
+		
+		#if MSAA_COUNT > 1
+		SurfaceData getGBufferData(uint2 pixelPos, uint sampleIndex)
+		{
+			float4 GBufferAData = gGBufferATex.Load(pixelPos, sampleIndex);
+			float4 GBufferBData = gGBufferBTex.Load(pixelPos, sampleIndex);
+			float2 GBufferCData = gGBufferCTex.Load(pixelPos, sampleIndex).rg;
+			float deviceZ = gDepthBufferTex.Load(pixelPos, sampleIndex).r;
+			
+			return decodeGBuffer(GBufferAData, GBufferBData, GBufferCData, deviceZ);
+		}
+		
+		#else
+		SurfaceData getGBufferData(uint2 pixelPos)
+		{
+			float4 GBufferAData = gGBufferATex.Load(int3(pixelPos, 0));
+			float4 GBufferBData = gGBufferBTex.Load(int3(pixelPos, 0));
+			float2 GBufferCData = gGBufferCTex.Load(int3(pixelPos, 0)).rg;
+			float deviceZ = gDepthBufferTex.Load(int3(pixelPos, 0)).r;
+			
+			return decodeGBuffer(GBufferAData, GBufferBData, GBufferCData, deviceZ);
+		}
+
+		SurfaceData getGBufferData(float2 uvPos)
+		{
+			float4 GBufferAData = gGBufferATex.Sample(gGBufferASamp, uvPos);
+			float4 GBufferBData = gGBufferBTex.Sample(gGBufferBSamp, uvPos);
+			float2 GBufferCData = gGBufferCTex.Sample(gGBufferCSamp, uvPos).rg;
+			float deviceZ = gDepthBufferTex.Sample(gDepthBufferSamp, uvPos).r;
+			
+			return decodeGBuffer(GBufferAData, GBufferBData, GBufferCData, deviceZ);
+		}			
+		#endif			
+	};
+};

+ 45 - 0
Source/bsf/Data/Raw/Shaders/Includes/GBufferOutput.bslinc

@@ -0,0 +1,45 @@
+#include "$ENGINE$\SurfaceData.bslinc"
+
+#ifndef WRITE_VELOCITY
+	#define WRITE_VELOCITY 0
+#endif
+
+mixin GBufferOutput 
+{
+	mixin SurfaceData;
+
+	code
+	{
+		struct GBufferData
+		{
+			float4 albedo : SV_Target1;
+			float4 normal : SV_Target2; 
+			float2 metalRoughness : SV_Target3;
+			
+			#if WRITE_VELOCITY
+			float2 velocity : SV_Target4;
+			float id : SV_Target5;
+			#else
+			float id : SV_Target4;
+			#endif
+		};
+	
+		GBufferData encodeGBuffer(SurfaceData input)
+		{
+			GBufferData output;
+			output.albedo = input.albedo;
+			output.normal.xyz = float3(input.worldNormal.xyz * 0.5f + 0.5f);
+			output.normal.w = 1.0f; // Marks that some deferred data was written
+			output.metalRoughness.x = input.roughness;
+			output.metalRoughness.y = input.metalness;
+			
+			#if WRITE_VELOCITY
+				output.velocity = encodeVelocity16SNORM(input.velocity);
+			#endif
+			
+			output.id = input.mask / 256.0f;
+			
+			return output;
+		}
+	};
+};

+ 43 - 0
Source/bsf/Data/Raw/Shaders/Includes/GpuParticleTileVertex.bslinc

@@ -0,0 +1,43 @@
+mixin GpuParticleTileVertex
+{
+	depth
+	{
+		write = false;
+		read = false;
+	};
+
+	code
+	{	
+		struct VertexInput
+		{
+			uint vertexId : SV_VertexID;
+			uint instanceId : SV_InstanceID;
+			float2 uv0 : TEXCOORD0;
+		};	
+	
+		struct VStoFS
+		{
+			noperspective float4 position : SV_POSITION;
+			noperspective float2 uv0 : TEXCOORD0;
+		};
+
+		Buffer<float2> gTileUVs;
+		
+		cbuffer Input
+		{
+			float4 gUVToNDC;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			uint tileId = input.instanceId * TILES_PER_INSTANCE + input.vertexId / 4;
+			float2 uv = input.uv0 + gTileUVs[tileId];
+		
+			VStoFS output;
+			output.position = float4(uv * gUVToNDC.xy + gUVToNDC.zw, 0.0f, 1.0f);
+			output.uv0 = uv;
+		
+			return output;
+		}	
+	};
+};

+ 199 - 0
Source/bsf/Data/Raw/Shaders/Includes/ImageBasedLighting.bslinc

@@ -0,0 +1,199 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+
+mixin ImageBasedLighting
+{
+	mixin ReflectionCubemapCommon;
+
+	code
+	{
+		// Note: Size must be multiple of largest element, because of std430 rules
+		struct ReflProbeData
+		{
+			float3 position;
+			float radius;
+			float3 boxExtents;
+			float transitionDistance;
+			float4x4 invBoxTransform;
+			uint cubemapIdx;
+			uint type; // 0 - Sphere, 1 - Box
+			float2 padding;
+		};
+	
+		[internal]
+		TextureCube gSkyReflectionTex;
+		SamplerState gSkyReflectionSamp;
+		
+		[internal]
+		TextureCubeArray gReflProbeCubemaps;
+		SamplerState gReflProbeSamp;
+		
+		[internal]
+		Texture2D gAmbientOcclusionTex;
+		
+		[internal] [alias(gAmbientOcclusionTex)]
+		SamplerState gAmbientOcclusionSamp
+		{
+			Filter = MIN_MAG_MIP_POINT;
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+		};
+		
+		[internal]
+		Texture2D gSSRTex;
+		
+		[internal] [alias(gSSRTex)]
+		SamplerState gSSRSamp
+		{
+			Filter = MIN_MAG_MIP_POINT;
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+		};
+		
+		[internal]
+		Texture2D gPreintegratedEnvBRDF;
+		
+		[internal] [alias(gPreintegratedEnvBRDF)]
+		SamplerState gPreintegratedEnvBRDFSamp
+		{
+			Filter = MIN_MAG_MIP_LINEAR;
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+		};
+		
+		[internal]
+		cbuffer ReflProbeParams
+		{
+			uint gReflCubemapNumMips;
+			uint gNumProbes;
+			uint gSkyCubemapAvailable;
+			uint gUseReflectionMaps;
+			uint gSkyCubemapNumMips;
+			float gSkyBrightness;
+		}	
+
+		float getSphereReflectionContribution(float normalizedDistance)
+		{			
+			// If closer than 60% to the probe radius, then full contribution is used.
+			// For the other 40% we smoothstep and return contribution lower than 1 so other
+			// reflection probes can be blended.			
+		
+			// smoothstep from 1 to 0.6:
+			//   float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
+			//   return t * t * (3.0 - 2.0 * t);
+			float t = saturate(2.5 - 2.5 * normalizedDistance);
+			return t * t * (3.0 - 2.0 * t);
+		}
+		
+		float3 getLookupForSphereProxy(float3 originWS, float3 dirWS, float3 centerWS, float radius)
+		{
+			float radius2 = radius * radius;
+			float3 originLS = originWS - centerWS;
+			
+			float a = dot(originLS, dirWS);
+			float dist2 = a * a - dot(originLS, originLS) + radius2;
+
+			float3 lookupDir = dirWS;
+			
+			[flatten]
+			if(dist2 >= 0)
+			{
+				float farDist = sqrt(dist2) - a;
+				lookupDir = originLS + farDist * dirWS;
+			}
+			
+			return lookupDir;
+		}
+		
+		float getDistBoxToPoint(float3 pt, float3 extents)
+		{
+			float3 d = max(max(-extents - pt, 0), pt - extents);
+			return length(d);
+		}
+		
+		float3 getLookupForBoxProxy(float3 originWS, float3 dirWS, float3 centerWS, float3 extents, float4x4 invBoxTransform, float transitionDistance, out float contribution)
+		{
+			// Transform origin and direction into box local space, where it is unit sized and axis aligned
+			float3 originLS = mul(invBoxTransform, float4(originWS, 1)).xyz;
+			float3 dirLS = mul(invBoxTransform, float4(dirWS, 0)).xyz;
+			
+			// Get distance from 3 min planes and 3 max planes of the unit AABB
+			//  float3 unitVec = float3(1.0f, 1.0f, 1.0f);
+			//  float3 intersectsMax = (unitVec - originLS) / dirLS;
+			//  float3 intersectsMin = (-unitVec - originLS) / dirLS;
+			
+			float3 invDirLS = rcp(dirLS);
+			float3 intersectsMax = invDirLS - originLS * invDirLS;
+			float3 intersectsMin = -invDirLS - originLS * invDirLS;
+			
+			// Find nearest positive (along ray direction) intersection
+			float3 positiveIntersections = max(intersectsMax, intersectsMin);
+			float intersectDist = min(positiveIntersections.x, min(positiveIntersections.y, positiveIntersections.z));
+			
+			float3 intersectPositionWS = originWS + intersectDist * dirWS;
+			float3 lookupDir = intersectPositionWS - centerWS;
+			
+			// Calculate contribution
+			//// Shrink the box so fade out happens within box extents
+			float3 reducedExtents = extents - float3(transitionDistance, transitionDistance, transitionDistance);
+			float distToBox = getDistBoxToPoint(originLS * extents, reducedExtents);
+			
+			float normalizedDistance = distToBox / transitionDistance;
+			
+			// If closer than 70% to the probe radius, then full contribution is used.
+			// For the other 30% we smoothstep and return contribution lower than 1 so other
+			// reflection probes can be blended.			
+		
+			// smoothstep from 1 to 0.7:
+			//   float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
+			//   return t * t * (3.0 - 2.0 * t);
+			
+			float t = saturate(3.3333 - 3.3333 * normalizedDistance);
+			contribution = t * t * (3.0 - 2.0 * t);
+			
+			return lookupDir;
+		}
+		
+		// rgb - probe color, a - probe contribution
+		float4 evaluateProbe(float3 worldPos, float3 dir, float mipLevel, ReflProbeData probeData)
+		{
+			float3 probeToPos = worldPos - probeData.position;
+			float distToProbe = length(probeToPos);
+			float normalizedDist = saturate(distToProbe / probeData.radius);
+						
+			if(distToProbe <= probeData.radius)
+			{
+				float3 correctedDir;
+				float contribution = 0;
+				if(probeData.type == 0) // Sphere
+				{
+					correctedDir = getLookupForSphereProxy(worldPos, dir, probeData.position, probeData.radius);
+					contribution = getSphereReflectionContribution(normalizedDist);
+				}
+				else if(probeData.type == 1) // Box
+				{
+					correctedDir = getLookupForBoxProxy(worldPos, dir, probeData.position, probeData.boxExtents, probeData.invBoxTransform, probeData.transitionDistance, contribution);
+				}
+				
+				float4 probeSample = gReflProbeCubemaps.SampleLevel(gReflProbeSamp, float4(correctedDir, probeData.cubemapIdx), mipLevel);
+				probeSample *= contribution;
+				
+				return float4(probeSample.rgb, (1.0f - contribution));
+			}
+			
+			return float4(0, 0, 0, 1.0f);
+		}
+		
+		float getSpecularOcclusion(float NoV, float r, float ao)
+		{
+			float r2 = r * r;
+			return saturate(pow(NoV + ao, r2) - 1.0f + ao);
+		}				
+	};
+};
+
+// Hackish way of "instantiating" two versions of a mixin (to be removed when template/specialization support is added)
+#include "$ENGINE$\ReflProbeAccumulator.bslinc"
+
+#define USE_UNIFORM_BUFFER
+#include "$ENGINE$\ReflProbeAccumulator.bslinc"
+#undef USE_UNIFORM_BUFFER

+ 71 - 0
Source/bsf/Data/Raw/Shaders/Includes/ImportanceSampling.bslinc

@@ -0,0 +1,71 @@
+mixin ImportanceSampling
+{
+	code
+	{
+		#define PI 3.1415926
+	
+		uint radicalInverse(uint bits)  
+		{
+			// Reverse bits. Algorithm from Hacker's Delight.
+			bits = (bits << 16u) | (bits >> 16u);
+			bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+			bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+			bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+			bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+			
+			return bits;
+		}
+		
+		float2 hammersleySequence(uint i, uint count)
+		{
+			float2 output;
+			output.x = i / (float)count;
+			uint y = radicalInverse(i);
+			
+			// Normalizes unsigned int in range [0, 4294967295] to [0, 1]
+			output.y = float(y) * 2.3283064365386963e-10;
+			
+			return output;
+		}
+		
+		float2 hammersleySequence(uint i, uint count, uint2 random)
+		{
+			float2 output;
+			output.x = frac(i / (float)count + float(random.x & 0xFFFF) / (1<<16));
+			uint y = radicalInverse(i) ^ random.y;
+			
+			// Normalizes unsigned int in range [0, 4294967295] to [0, 1]
+			output.y = float(y) * 2.3283064365386963e-10;
+			
+			return output;
+		}		
+		
+		// Returns cos(theta) in x and phi in y
+		float2 importanceSampleGGX(float2 e, float roughness4)
+		{
+			// See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it,
+			// generate PDF, split PDF into marginal probability for theta and conditional probability
+			// for phi. Plug those into the CDF, invert it.)				
+			float cosTheta = sqrt((1.0f - e.x) / (1.0f + (roughness4 - 1.0f) * e.x));
+			float phi = 2.0f * PI * e.y;
+			
+			return float2(cosTheta, phi);
+		}
+		
+		float3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
+		{
+			float3 output;
+			output.x = sinTheta * cos(phi);
+			output.y = sinTheta * sin(phi);
+			output.z = cosTheta;
+			
+			return output;
+		}
+		
+		float pdfGGX(float cosTheta, float sinTheta, float roughness4)
+		{
+			float d = (cosTheta*roughness4 - cosTheta) * cosTheta + 1;
+			return roughness4 * cosTheta * sinTheta / (d*d*PI);
+		}
+	};
+};

+ 63 - 0
Source/bsf/Data/Raw/Shaders/Includes/LightGridCommon.bslinc

@@ -0,0 +1,63 @@
+mixin LightGridCommon
+{
+	code
+	{
+		cbuffer GridParams : register(b4)
+		{
+			// Number of lights per type in the lights buffer
+			// x - directional lights, y - radial lights, z - spot lights, w - total number of lights
+			uint4 gLightCounts;
+			// Strides between different light types in the light buffer
+			// x - stride to radial lights, y - stride to spot lights. Directional lights are assumed to start at 0.
+			uint2 gLightStrides;			
+			uint gNumReflProbes;
+			uint gNumCells;
+			uint3 gGridSize;
+			uint gMaxNumLightsPerCell;
+			uint2 gGridPixelSize;
+		}
+					
+		float calcViewZFromCellZ(uint cellZ)
+		{
+			// We don't want to subdivide depth uniformly because XY sizes will be much
+			// smaller closer to the near plane, and larger towards far plane. We want 
+			// our cells to be as close to cube shape as possible, so that width/height/depth
+			// are all similar. Ideally we would use either width or height as calculated for
+			// purposes of the projection matrix, for the depth. But since we'll be splitting
+			// the depth range into multiple slices, in practice this ends up with many tiny
+			// cells close to the near plane. Instead we use a square function, which is
+			// somewhere between the two extremes:
+			//  view = slice^2
+			
+			// We need it in range [near, far] so we normalize and scale
+			//  view = slice^2 / maxSlices^2 * (far - near) + near
+			
+			// Note: Some of these calculations could be moved to CPU
+			float viewZ = (pow(cellZ, 2) / pow(gGridSize.z, 2)) * (gNearFar.y - gNearFar.x) + gNearFar.x; 
+			return -viewZ;
+		}
+		
+		uint calcCellZFromViewZ(float viewZ)
+		{
+			// Inverse of calculation in calcViewZFromCellZ
+			uint cellZ = min((uint)floor(sqrt(((-viewZ - gNearFar.x)*pow(gGridSize.z, 2))/(gNearFar.y - gNearFar.x))), gGridSize.z);
+			
+			return cellZ;
+		}
+		
+		uint calcCellIdx(uint2 pixelPos, float deviceZ)
+		{
+			// OpenGL uses lower left for window space origin
+			#ifdef OPENGL
+				pixelPos.y = gViewportRectangle.w - pixelPos.y;
+			#endif			
+		
+			// Note: Use bitshift to divide since gGridPixelSize will be a power of 2
+			uint2 cellXY = pixelPos / gGridPixelSize;
+			uint cellZ = calcCellZFromViewZ(convertFromDeviceZ(deviceZ));
+			
+			uint cellIdx = (cellZ * gGridSize.y + cellXY.y) * gGridSize.x + cellXY.x;
+			return cellIdx;
+		}
+	};
+};

+ 17 - 0
Source/bsf/Data/Raw/Shaders/Includes/MaskInput.bslinc

@@ -0,0 +1,17 @@
+mixin MaskInput
+{
+	code 
+	{
+		#ifndef MSAA_COUNT
+			#define MSAA_COUNT 1
+		#endif
+
+		#if MSAA_COUNT > 1
+		[internal]
+		Texture2DMS<float> gMaskTex;
+		#else
+		[internal]
+		Texture2D<float> gMaskTex;
+		#endif	
+	};
+};

+ 35 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPBase.bslinc

@@ -0,0 +1,35 @@
+mixin PPBase
+{
+	depth
+	{
+		write = false;
+		read = false;
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float2 uv0 : TEXCOORD0;
+			float2 screenPos : TEXCOORD1;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+			output.screenPos = input.screenPos;
+
+			return output;
+		}			
+	};
+};

+ 35 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPEyeAdaptationCommon.bslinc

@@ -0,0 +1,35 @@
+mixin PPEyeAdaptationParams
+{
+	code
+	{
+		[internal]
+		cbuffer EyeAdaptationParams
+		{
+			// [0]: x - histogram scale, y - histogram offset, z - histogram percent low, w - histogram percent high
+			// [1]: x - min adaptation, y - max adaptation, z - adaptation speed up, w - adaptation speed down
+			// [2]: x - exposure scale, y - frame time delta, z - min. allowed intensity, w - nothing
+			float4 gEyeAdaptationParams[3];
+		}
+		
+		/** 
+		 * Smooths out eye adaptation changes over multiple frames so they aren't as jarring.
+		 *
+		 * @param	old			Eye adaptation value from the previous frame.
+		 * @param	target		Ideal eye adaptation value for this frame.
+		 * @param	frameDelta	Time difference between this and last frame, in seconds.
+		 * @return				Smoothed eye adaptation.
+		 */
+		float smoothEyeAdaptation(float old, float target, float frameDelta)
+		{
+			float diff = target - old;
+
+			float speedUp = gEyeAdaptationParams[1].z;
+			float speedDown = gEyeAdaptationParams[1].w;
+
+			float adaptionSpeed = (diff > 0) ? speedUp : speedDown;
+			float scale = 1.0f - exp2(-frameDelta * adaptionSpeed);
+
+			return clamp(old + diff * scale, gEyeAdaptationParams[1].x, gEyeAdaptationParams[1].y);
+		}		
+	};
+};

+ 67 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPGaussianBlurCommon.bslinc

@@ -0,0 +1,67 @@
+mixin PPGaussianBlurCommon
+{
+	code
+	{
+		[internal]
+		cbuffer GaussianBlurParams
+		{
+			float4 gSampleOffsets[(MAX_NUM_SAMPLES + 1) / 2];
+			float4 gSampleWeights[MAX_NUM_SAMPLES];
+			int gNumSamples;
+		}		
+
+		float4 gaussianBlur(Texture2D source, SamplerState samp, float2 uv)
+		{
+			// Note: Consider adding a version of this shader with unrolled loop for small number of samples
+			float4 output = 0;
+			
+			int idx = 0;
+			for(; idx < (gNumSamples / 4); idx++)
+			{
+				{
+					float2 sampleUV = uv + gSampleOffsets[idx * 2 + 0].xy;
+					output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 0];
+				}
+				
+				{
+					float2 sampleUV = uv + gSampleOffsets[idx * 2 + 0].zw;
+					output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 1];
+				}
+				
+				{
+					float2 sampleUV = uv + gSampleOffsets[idx * 2 + 1].xy;
+					output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 2];
+				}
+				
+				{
+					float2 sampleUV = uv + gSampleOffsets[idx * 2 + 1].zw;
+					output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 3];
+				}
+			}
+			
+			int extraSamples = gNumSamples - idx * 4;
+			[branch]
+			if(extraSamples >= 1)
+			{
+				float2 sampleUV = uv + gSampleOffsets[idx * 2 + 0].xy;
+				output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 0];
+				
+				[branch]
+				if(extraSamples >= 2)
+				{
+					float2 sampleUV = uv + gSampleOffsets[idx * 2 + 0].zw;
+					output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 1];
+					
+					[branch]
+					if(extraSamples >= 3)
+					{
+						float2 sampleUV = uv + gSampleOffsets[idx * 2 + 1].xy;
+						output += source.SampleLevel(samp, sampleUV, 0) * gSampleWeights[idx * 4 + 2];
+					}
+				}				
+			}
+
+			return output;
+		}
+	};
+};

+ 29 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPGaussianDOFCommon.bslinc

@@ -0,0 +1,29 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+
+mixin PPGaussianDOFCommon
+{
+	mixin PerCameraData;
+
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			float gNearBlurPlane;
+			float gFarBlurPlane;
+			float gInvNearBlurRange;
+			float gInvFarBlurRange;
+			float2 gHalfPixelOffset;
+		}		
+		
+		float calcNearMask(float depth)
+		{
+			return saturate((gNearBlurPlane - depth) * gInvNearBlurRange);
+		}
+		
+		float calcFarMask(float depth)
+		{
+			return saturate((depth - gFarBlurPlane) * gInvFarBlurRange);
+		}
+	};
+};

+ 111 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPTonemapCommon.bslinc

@@ -0,0 +1,111 @@
+mixin PPTonemapCommon
+{
+	code
+	{
+		static const float3x3 sRGBToXYZMatrix =
+		{
+			0.4124564f, 0.3575761f, 0.1804375f,
+			0.2126729f, 0.7151522f, 0.0721750f,
+			0.0193339f, 0.1191920f, 0.9503041f,
+		};
+		
+		static const float3x3 XYZTosRGBMatrix =
+		{
+			 3.2409699419f, -1.5373831776f, -0.4986107603f,
+			-0.9692436363f,  1.8759675015f,  0.0415550574f,
+			 0.0556300797f, -0.2039769589f,  1.0569715142f,
+		};
+		
+		static const float3x3 D65ToD60Matrix =
+		{
+			 1.01303,    0.00610531, -0.014971,
+			 0.00769823, 0.998165,   -0.00503203,
+			-0.00284131, 0.00468516,  0.924507,
+		};
+
+		static const float3x3 D60ToD65Matrix =
+		{
+			 0.987224,   -0.00611327, 0.0159533,
+			-0.00759836,  1.00186,    0.00533002,
+			 0.00307257, -0.00509595, 1.08168,
+		};
+		
+		static const float3x3 XYZToACES2065Matrix =
+		{
+			 1.0498110175, 0.0000000000,-0.0000974845,
+			-0.4959030231, 1.3733130458, 0.0982400361,
+			 0.0000000000, 0.0000000000, 0.9912520182,
+		};
+
+		static const float3x3 XYZToACEScgMatrix =
+		{
+			 1.6410233797, -0.3248032942, -0.2364246952,
+			-0.6636628587,  1.6153315917,  0.0167563477,
+			 0.0117218943, -0.0082844420,  0.9883948585,
+		};
+
+		static const float3x3 ACEScgToXYZMatrix = 
+		{
+			 0.6624541811, 0.1340042065, 0.1561876870,
+			 0.2722287168, 0.6740817658, 0.0536895174,
+			-0.0055746495, 0.0040607335, 1.0103391003,
+		};
+
+		/**
+		 * Encodes a 10bit linear color into 8bits by converting it to log space.
+		 *
+		 * @param 	linearColor		Linear color.
+		 * @return					Encoded color in log space.
+		 */			
+		float3 LinearToLogColor(float3 linearColor)
+		{
+			float linearRange = 14.0f;
+			float linearGrey = 0.18f;
+			float exposureGrey = 444.0f;
+
+			float3 logColor = log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0f;
+			return saturate(logColor);
+		}
+
+		/**
+		 * Decodes a 8bit log encoded color back into linear space.
+		 *
+		 * @param 	logColor		Log space color.
+		 * @return					Color in linear space.
+		 */			
+		float3 LogToLinearColor(float3 logColor)
+		{
+			float linearRange = 14.0f;
+			float linearGrey = 0.18f;
+			float exposureGrey = 444.0f;
+
+			return exp2((logColor - exposureGrey / 1023.0f) * linearRange) * linearGrey;
+		}
+
+		/**
+		 * Converts a linear color value in sRGB/Rec.709 color space into gamma space (applies Rec.709 transfer function). 
+		 * Rec.709 values are suitable for HDTVs and projectors.
+		 *
+		 * @param 	linearColor		Linear color in sRGB/Rec.709 color space.
+		 * @return					Gamma corrected color.
+		 */				
+		float3 LinearToGammaRec709(float3 linearColor) 
+		{
+			// TODO: Clamp lower end of linear color so it isn't denormalized?
+			return min(linearColor * 4.5f, pow(max(linearColor, 0.018f), 0.45f) * 1.099f - 0.099f);
+		}
+
+		/**
+		 * Converts a linear color value in sRGB/Rec.709 color space into gamma space (applies sRGB transfer function). 
+		 * sRGB values are suitable for PC displays.
+		 *
+		 * @param 	linearColor		Linear color in sRGB/Rec.709 color space.
+		 * @return					Gamma corrected color.
+		 */		
+		float3 LinearToGammasRGB(float3 linearColor) 
+		{
+			// TODO: Clamp lower end of linear color so it isn't denormalized?
+			return min(linearColor * 12.92f, pow(max(linearColor, 0.00313067f), 1.0f/2.4f) * 1.055f - 0.055f);
+		}			
+	};
+};

+ 207 - 0
Source/bsf/Data/Raw/Shaders/Includes/PPWhiteBalance.bslinc

@@ -0,0 +1,207 @@
+mixin PPWhiteBalance
+{
+	code
+	{
+		/**
+		 * Calculates correlated color temperature from chomaticity coordinates using the McCamy's formula.
+		 * Coordinates should be near the Planckian locus otherwise the returned temperature becomes meaningless.
+		 *
+		 * @param 	coords	CIE 1931 x chomaticity coordinates.
+		 * @return			Correlated color temperature in degrees Kelvin.
+		 */
+		float CCT(float2 coords)
+		{
+			float n = (coords.x - 0.3320f) / (0.1858f - coords.y);
+			float n2 = n * n;
+			float n3 = n2 * n;
+			
+			return -449.0f * n3 + 3525.0f * n2 - 6823.3f * n + 5520.33f;
+		}
+
+		/**
+		 * Calculates chromaticity coordinates from a correlated color temperature. Uses the Planckian locus formula
+		 * which works for values in range [1000K, 15000K].
+		 *
+		 * @param	T	Correlated color temperature in degrees Kelvin.
+		 * @return		CIE 1960 UCS chomaticity coordinates.
+		 */
+		float2 PlanckianLocusChromaticity(float T)
+		{
+			float T2 = T * T;
+
+			// Calculates CIE 1960 UCS coordinates
+			float u = (0.860117757f + 1.54118254e-4f * T + 1.28641212e-7f * T2) / (1.0f + 8.42420235e-4f * T + 7.08145163e-7f * T2);
+			float v = (0.317398726f + 4.22806245e-5f * T + 4.20481691e-8f * T2) / (1.0f - 2.89741816e-5f * T + 1.61456053e-7f * T2);
+			
+			return float2(u, v);
+		}
+
+		/**
+		 * Calculates chromaticity coordinates from a correlated color temperature. Uses the formula for series
+		 * D standard illuminants (D55, D65, D75, etc.). Valid for values in range [4000K, 25000K].
+		 *
+		 * @param	T	Correlated color temperature in degrees Kelvin.
+		 * @return		CIE 1931 chomaticity coordinates.
+		 */
+		float2 DSeriesIlluminantChromaticity(float T)
+		{
+			float x = T <= 7000.0f 
+				? 0.244063f + (0.09911e3 + (2.9678e6 - 4.6070e9 / T) / T) / T 
+				: 0.237040f + (0.24748e3 + (1.9018e6 - 2.0064e9 / T) / T) / T;
+			
+			float y = -3.0f * x * x + 2.87f * x - 0.275f;
+
+			return float2(x, y);
+		}
+
+		/**
+		 * Converts chomaticity coordinates from CIE 1960 uniform color space to CIE 1931 color space.
+		 *
+		 * @param	uv	Chromaticity coordinates in CIE 1960 UCS.
+		 * @return		Chromaticity coordinates in CIE 1931.
+		 */
+		float2 CIE1960ToCIE1931(float2 uv)
+		{
+			float x = (3 * uv.x) / (2 * uv.x - 8 * uv.y + 4);
+			float y = (2 * uv.y) / (2 * uv.x - 8 * uv.y + 4);
+
+			return float2(x, y);
+		}
+
+		/**
+		 * Adds the specified offset along the Planckian isothermal line and returns the chromaticity coordinates for the offset position.
+		 *
+		 * @param	uv		Chromaticity coordiantes in CIE 1960 UCS for the correlated color temperature along the Planckian locus.
+		 * @param	offset	Offset to be added along the isothermal. In range [-1, 1]. The actual offset in chromaticity
+		 *					coordinates is scaled to |0.05| since values farther than that usually aren't useful.
+		 * @return			CIE 1931 chomaticity coordinates.
+		 */
+		float2 PlanckianIsothermalOffset(float2 uv, float offset)
+		{
+			// Rotate uv by 90 degrees and normalize it to get the isotherm line
+			float2 isotherm = normalize(float2(-uv.y, uv.x));
+			
+			uv += isotherm * offset * 0.05f;
+			return CIE1960ToCIE1931(uv);
+		}
+		
+		/**
+		 * Converts from CIE 1931 xyY color space to XYZ color space.
+		 *
+		 * @param	xyY		Coordinates in xyY color space.
+		 * @return			Coordinates in XYZ color space.
+		 */
+		float3 xyYToXYZ(float3 xyY)
+		{
+			float divisor = max(xyY.y, 1e-10f);
+		
+			float3 XYZ;
+			XYZ.x = (xyY.x * xyY.z) / divisor;
+			XYZ.y = xyY.z;  
+			XYZ.z = ((1.0 - xyY.x - xyY.y) * xyY.z) / divisor;
+
+			return XYZ;
+		}
+		
+		/**
+		 * Converts from CIE 1931 XYZ color space to xyY color space.
+		 *
+		 * @param	XYZ		Coordinates in XYZ color space.
+		 * @return			Coordinates in xyY color space.
+		 */
+		float3 XYZToxyY(float3 XYZ)
+		{
+			float3 xyY;
+			float divisor = XYZ.x + XYZ.y + XYZ.z;
+			if (divisor == 0.0f) 
+				divisor = 1e-10f;
+			
+			xyY.x = XYZ.x / divisor;
+			xyY.y = XYZ.y / divisor;  
+			xyY.z = XYZ.y;
+		  
+			return xyY;
+		}			
+		
+		/**
+		 * Returns a matrix that transform XYZ tristimulus values for a given white point to
+		 * a new white point.
+		 *
+		 * @param	orgWhite	Chromaticity coordinates in CIE 1931 for the original white point.
+		 * @param	newWhite	Chromaticity coordinates in CIE 1931 for the new white point.
+		 * @return				Matrix that transform from the original to new white point.
+		 */
+		float3x3 ChromaticAdaptation(float2 orgWhite, float2 newWhite)
+		{
+			// Convert xyY to XYZ
+			float3 orgWhite3 = xyYToXYZ(float3(orgWhite.xy, 1.0f));
+			float3 newWhite3 = xyYToXYZ(float3(newWhite.xy, 1.0f));
+			
+			// Convert to cone response domain using Bradford's matrix
+			const float3x3 coneResponse =
+			{
+				 0.8951f,  0.2664f, -0.1614f,
+				-0.7502f,  1.7135f,  0.0367f,
+				 0.0389f, -0.0685f,  1.0296f,
+			};
+			
+			const float3x3 invConeResponse =
+			{
+				 0.9870f, -0.1471f,  0.1600f,
+				 0.4323f,  0.5184f,  0.0493f,
+				-0.0085f,  0.0400f,  0.9685f,
+			};
+			
+			orgWhite3 = mul(coneResponse, orgWhite3);
+			newWhite3 = mul(coneResponse, newWhite3);
+			
+			// Generate transformation matrix
+			float3x3 adaptation =
+			{
+				newWhite3.x / orgWhite3.x, 0.0f, 0.0f,
+				0.0f, newWhite3.y / orgWhite3.y, 0.0f,
+				0.0f, 0.0f, newWhite3.z / orgWhite3.z
+			};
+			
+			return mul(invConeResponse, mul(adaptation, coneResponse));
+		}
+		
+		[internal]
+		cbuffer WhiteBalanceInput
+		{
+			float gWhiteTemp;
+			float gWhiteOffset;
+		}
+		
+		/**
+		 * Applies color balancing to the provided color. The color is transformed from its original white point
+		 * (provided by gWhiteTemp and gWhiteOffset) to a D65 white point.
+		 * 
+		 * @param	color 	Color in linear sRGB/Rec.709 color space.
+		 * @return			White balanced linear color.
+		 */
+		float3 WhiteBalance(float3 color)
+		{
+			float2 orgPlanckianUV = PlanckianLocusChromaticity(gWhiteTemp);
+			float2 orgWhiteXY;
+			if(gWhiteTemp < 4000)
+			{
+				orgWhiteXY = PlanckianIsothermalOffset(orgPlanckianUV, gWhiteOffset);
+			}
+			else
+			{
+				orgWhiteXY = DSeriesIlluminantChromaticity(gWhiteTemp);
+				float2 offsetXY = PlanckianIsothermalOffset(orgPlanckianUV, gWhiteOffset) - CIE1960ToCIE1931(orgPlanckianUV);
+				
+				orgWhiteXY += offsetXY;
+			}
+		
+			float2 newWhiteXY = float2(0.3128f, 0.3290f); // D65 white point
+			
+			float3x3 adaptation = ChromaticAdaptation(orgWhiteXY, newWhiteXY);
+			adaptation = mul(XYZTosRGBMatrix, mul(adaptation, sRGBToXYZMatrix));
+
+			return mul(adaptation, color);
+		}
+	};
+};

+ 342 - 0
Source/bsf/Data/Raw/Shaders/Includes/ParticleVertex.bslinc

@@ -0,0 +1,342 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\PerObjectData.bslinc"
+// Note: I should include VertexCommon.bslinc here, instead of redefining vertex input code below
+
+mixin ParticleVertex
+{
+	mixin PerCameraData;
+	mixin PerObjectData;
+
+	variations
+	{
+		ORIENT = { 0, 1, 2 }; // 0 - Camera plane, 1 - Camera position, 2 - Axis
+		LOCK_Y = { false, true };
+		GPU = { false, true };
+		IS_3D = { false, true };
+	};
+
+	code
+	{
+		#define RENDER_3D IS_3D && !GPU
+	
+		struct VertexInput
+		{
+			#if RENDER_3D
+			float3 position : POSITION;
+			#else
+			float2 position : POSITION;
+			#endif
+			
+			float2 uv0 : TEXCOORD0;
+			uint instanceId	: SV_InstanceID;
+
+			#ifdef LIGHTING_DATA
+			float3 normal : NORMAL; // Note: Half-precision could be used
+			float4 tangent : TANGENT; // Note: Half-precision could be used
+			#endif
+		};	
+	
+		#if RENDER_3D
+		struct ParticleInput
+		{
+			float3 position;
+			float3 rotation;
+			float3 size;
+			float3 velocity;
+			float4 color;
+		};
+		#else
+		struct ParticleInput
+		{
+			float3 position;
+			float rotation;
+			float2 uvflip;
+			float2 size;
+			float3 velocity;
+			float frame;
+			float4 color;
+		};
+		#endif
+		
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+			float2 uv0 : TEXCOORD0;
+			float4 color : COLOR0;
+			
+			#ifdef VIEW_DEPTH
+			float depth : TEXCOORD1;
+			#endif
+
+			#ifdef LIGHTING_DATA
+			float3 worldPosition : TEXCOORD2;
+			
+			float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
+			float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
+			#endif
+		};
+		
+		#if GPU
+		[internal]
+		Texture2D gPositionTimeTex; // position in .xyz, time (in [0, 1] range) in .w
+		
+		[internal]
+		Texture2D gSizeRotationTex; // size in .xy, rotation in .z
+		
+		[internal]
+		Texture2D gCurvesTex;
+		
+		[alias(gCurvesTex)]
+		SamplerState gCurvesSampler;
+		
+		[internal]
+		cbuffer GpuParticleParams
+		{
+			float2 gColorCurveOffset;
+			float2 gColorCurveScale;
+			float2 gSizeScaleFrameIdxCurveOffset;
+			float2 gSizeScaleFrameIdxCurveScale;
+		};
+		
+		ParticleInput getParticleInput(uint2 index)
+		{
+			uint3 pixelPos;
+			pixelPos.xy = index;
+			pixelPos.z = 0;
+			
+			ParticleInput pi;
+			
+			float4 posAndTime = gPositionTimeTex.Load(pixelPos);
+			pi.position = posAndTime.xyz;
+			
+			float time = posAndTime.w;
+			float aliveMask = step(time, 1.0f);
+			
+			float4 sizeAndRotation = gSizeRotationTex.Load(pixelPos);
+			
+			float2 sizeScaleFrameIdxCurveUV = gSizeScaleFrameIdxCurveOffset + time * gSizeScaleFrameIdxCurveScale;
+			float3 sizeScaleFrameIdx = gCurvesTex.SampleLevel(gCurvesSampler, sizeScaleFrameIdxCurveUV, 0).xyz;
+			
+			float2 sizeScale = sizeScaleFrameIdx.xy;
+			
+			pi.uvflip = sign(sizeAndRotation.xy);
+			pi.size = abs(sizeAndRotation.xy) * sizeScale.xy * aliveMask;
+			pi.rotation = sizeAndRotation.z;
+			
+			pi.frame = sizeScaleFrameIdx.z;
+			
+			float2 colorCurveUV = gColorCurveOffset + time * gColorCurveScale;
+			pi.color = gCurvesTex.SampleLevel(gCurvesSampler, colorCurveUV, 0);
+			
+			return pi;
+		}
+		#elif IS_3D
+		[internal]
+		Texture2D gPositionTex;
+		
+		[internal]
+		Texture2D gColorTex;
+		
+		[internal]
+		Texture2D gSizeTex;
+		
+		[internal]
+		Texture2D gRotationTex;
+		
+		ParticleInput getParticleInput(uint2 index)
+		{
+			uint3 pixelPos;
+			pixelPos.xy = index;
+			pixelPos.z = 0;
+			
+			ParticleInput pi;
+			
+			pi.position = gPositionTex.Load(pixelPos).xyz;
+			pi.rotation = gRotationTex.Load(pixelPos).xyz;
+			pi.color = gColorTex.Load(pixelPos);
+			pi.size = gSizeTex.Load(pixelPos).xyz;
+			
+			return pi;
+		}
+		
+		float3x3 getRotationScaleMatrix(float3 anglesRad, float3 scale)
+		{
+			float cx, sx, cy, sy, cz, sz;
+			sincos(anglesRad.x, sx, cx);
+			sincos(anglesRad.y, sy, cy);
+			sincos(anglesRad.z, sz, cz);
+			
+			float3x3 m;
+			m[0][0] = (cy * cz + sx * sy * sz) * scale.x;
+			m[0][1] = cz * sx * sy - cy * sz;
+			m[0][2] = cx * sy;
+
+			m[1][0] = cx * sz;
+			m[1][1] = cx * cz * scale.y;
+			m[1][2] = -sx;
+
+			m[2][0] = -cz * sy + cy * sx * sz;
+			m[2][1] = cy * cz * sx + sy * sz;
+			m[2][2] = cx * cy * scale.z;
+			
+			return m;
+		}
+		
+		#else
+		[internal]
+		Texture2D gPositionAndRotTex; // position in .xyz, rotation in radians in .w
+		[internal]
+		Texture2D gColorTex;
+		[internal]
+		Texture2D gSizeAndFrameIdxTex; // size in .xy, uv flip encoded in sign of .xy, frame index in .z, .w unused
+		
+		ParticleInput getParticleInput(uint2 index)
+		{
+			uint3 pixelPos;
+			pixelPos.xy = index;
+			pixelPos.z = 0;
+			
+			ParticleInput pi;
+			
+			float4 posAndRot = gPositionAndRotTex.Load(pixelPos);
+			pi.position = posAndRot.xyz;
+			pi.rotation = posAndRot.w;
+			
+			float4 sizeAndFrame = gSizeAndFrameIdxTex.Load(pixelPos);
+			pi.uvflip = sign(sizeAndFrame.xy);
+			pi.size = abs(sizeAndFrame.xy);
+			pi.frame = sizeAndFrame.z;
+			
+			pi.color = gColorTex.Load(pixelPos);
+			
+			return pi;
+		}
+		#endif
+		
+		[internal]
+		Buffer<uint2> gIndices;
+
+		[internal]
+		cbuffer ParticleParams
+		{
+			float4 gSubImageSize; // .xy column/row count, .zw inverse column/row count
+			float2 gParticleUVOffset;
+			float2 gParticleUVSize;
+			float3 gAxisUp;
+			uint gTexSize;
+			float3 gAxisRight;
+			uint gBufferOffset;
+		}
+		
+		#ifdef LIGHTING_DATA
+		float3x3 getTangentToLocal(VertexInput input, out float tangentSign)
+		{
+			float3 normal = input.normal * 2.0f - 1.0f;
+			float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
+			
+			tangentSign = input.tangent.w < 0.5f ? -1.0f : 1.0f;
+			float3 bitangent = cross(normal, tangent) * tangentSign;
+			tangentSign *= gWorldDeterminantSign;
+			
+			// Note: Maybe it's better to store everything in row vector format?
+			float3x3 result = float3x3(tangent, bitangent, normal);
+			result = transpose(result);
+											
+			return result;
+		}
+		
+		float3 calcWorldNormal(VStoFS input, float3 surfaceNormal)
+		{
+			float3 tangentToWorldX = input.tangentToWorldX.xyz;
+			float3 tangentToWorldZ = input.tangentToWorldZ;
+			float3 tangentToWorldY = cross(tangentToWorldZ, tangentToWorldX) * input.tangentToWorldX.w;
+			
+			float3x3 tangentToWorld = float3x3(tangentToWorldX, tangentToWorldY, tangentToWorldZ);
+			
+			// Multiplication order flipped because we stored basis vectors as rows
+			return normalize(mul(surfaceNormal, tangentToWorld));			
+		}	
+		#endif
+
+		VStoFS vsmain(VertexInput input)
+		{
+			ParticleInput pi = getParticleInput(gIndices[gBufferOffset + input.instanceId]);
+			
+			VStoFS output;
+			output.color = pi.color;
+			
+			float4 worldPosition = mul(gMatWorld, float4(pi.position, 1.0f));
+
+			#if RENDER_3D
+				float3x3 rotScale = getRotationScaleMatrix(pi.rotation, pi.size);
+				worldPosition.xyz += mul(rotScale, input.position);
+
+				output.uv0 = input.uv0;
+			#else // RENDER_3D
+				float3 axisRight, axisUp;
+				#if ORIENT == 2 // Axis
+					axisRight = gAxisRight;
+					axisUp = gAxisUp;
+				#elif ORIENT == 1 // Towards camera origin
+					float3 diff = gViewOrigin - worldPosition.xyz;
+					
+					float3 cameraUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
+					axisRight = normalize(cross(diff, cameraUp));
+					
+					#if LOCK_Y
+						axisUp = float3(0, 1, 0);
+					#else
+						axisUp = normalize(cross(axisRight, diff));
+					#endif
+				#else // Towards camera plane
+					axisRight = float3(gMatView[0].x, gMatView[0].y, gMatView[0].z);
+					
+					#if LOCK_Y
+						axisUp = float3(0, 1, 0);				
+					#else
+						axisUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
+					#endif
+				#endif
+
+				float rotSin, rotCos;
+				sincos(pi.rotation, rotSin, rotCos);
+				
+				float3 rotAxisRight = rotSin * axisUp + rotCos * axisRight;
+				float3 rotAxisUp = rotCos * axisUp - rotSin * axisRight;
+				
+				worldPosition.xyz += rotAxisRight * input.position.x * pi.size.x + rotAxisUp * input.position.y  * pi.size.y;	
+				
+				float2 uv;
+				uv.x = pi.uvflip.x >= 0.0f ? input.uv0.x : 1.0f - input.uv0.x;
+				uv.y = pi.uvflip.y >= 0.0f ? input.uv0.y : 1.0f - input.uv0.y;
+				
+				float frame = pi.frame - frac(pi.frame);
+				float row = floor(frame * gSubImageSize.z);
+				float column = fmod(frame, gSubImageSize.x);
+				
+				float2 subUV = (float2(row, column) + uv) * gSubImageSize.zw;
+				output.uv0 = gParticleUVOffset + subUV * gParticleUVSize;
+			#endif // RENDER_3D
+
+			#ifdef LIGHTING_DATA
+				float tangentSign;
+				float3x3 tangentToLocal = getTangentToLocal(input, tangentSign);
+				float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
+			
+				// Note: Consider transposing these externally, for easier reads
+				output.tangentToWorldZ = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
+				output.tangentToWorldX = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
+				
+				output.worldPosition = worldPosition.xyz;
+			#endif
+			
+			output.position = mul(gMatViewProj, worldPosition);
+			
+			#ifdef VIEW_DEPTH
+				output.depth = output.position.w;
+			#endif
+			
+			return output;
+		}
+	};
+};

+ 115 - 0
Source/bsf/Data/Raw/Shaders/Includes/PerCameraData.bslinc

@@ -0,0 +1,115 @@
+mixin PerCameraData
+{
+	code
+	{
+		[internal]
+		cbuffer PerCamera
+		{
+			float3	 gViewDir;
+			float3 	 gViewOrigin;
+			float4x4 gMatViewProj;
+			float4x4 gMatView;
+			float4x4 gMatProj;
+			float4x4 gMatInvProj;
+			float4x4 gMatInvViewProj;
+			float4x4 gMatPrevViewProj;
+			
+			// Special inverse view-projection matrix that had projection entries that affect z and w eliminated.
+			// Used to transform a vector(clip_x, clip_y, view_z, view_w), where clip_x/clip_y are in clip space, 
+			// and view_z/view_w in view space, into world space				
+			float4x4 gMatScreenToWorld;
+			
+			// Transforms a location in NDC, to the location of the same pixel on the previous frame. Used for
+			// determining camera movement for temporal filtering
+			float4x4 gNDCToPrevNDC;			
+			
+			// Converts device Z to world Z using this formula: worldZ = (1 / (deviceZ + y)) * x
+			float2 	 gDeviceZToWorldZ;
+			float2	 gNDCZToWorldZ;
+			float2 	 gNDCZToDeviceZ;
+			
+			// x - near plane distance, y - far plane distance
+			float2	 gNearFar;
+			
+			// xy - Viewport offset in pixels
+			// zw - Viewport width & height in pixels
+			int4 	 gViewportRectangle;
+			
+			// xy - (Viewport size in pixels / 2) / Target size in pixels
+			// zw - (Viewport offset in pixels + (Viewport size in pixels / 2) + Optional pixel center offset) / Target size in pixels
+			float4 	 gClipToUVScaleOffset;
+			float4 	 gUVToClipScaleOffset;	
+			float	gAmbientFactor;
+		}
+		
+		/** Converts Z value in range [0,1] into Z value in view space. */
+		float convertFromDeviceZ(float deviceZ)
+		{
+			// Note: Convert to MAD form
+			return gDeviceZToWorldZ.x / (deviceZ + gDeviceZToWorldZ.y);
+		}
+		
+		/** Converts Z value in range [0,1] into Z value in view space. */
+		float4 convertFromDeviceZ(float4 deviceZ)
+		{
+			// Note: Convert to MAD form
+			return gDeviceZToWorldZ.x / (deviceZ + gDeviceZToWorldZ.y);
+		}		
+		
+		/** Converts Z value from view space to NDC space. */
+		float convertToNDCZ(float viewZ)
+		{
+			return -gNDCZToWorldZ.y + (gNDCZToWorldZ.x / viewZ);
+		}
+				
+		/** Converts Z value from NDC space to device Z value in range [0, 1]. */
+		float NDCZToDeviceZ(float ndcZ)
+		{
+			return (ndcZ + gNDCZToDeviceZ.y) * gNDCZToDeviceZ.x;
+		}
+		
+		/** Converts Z value from device range ([0, 1]) to NDC space. */
+		float DeviceZToNDCZ(float deviceZ)
+		{
+			return deviceZ / gNDCZToDeviceZ.x - gNDCZToDeviceZ.y;
+		}
+		
+		/** Converts position in NDC to UV coordinates mapped to the screen rectangle. */ 
+		float2 NDCToUV(float2 ndcPos)
+		{
+			return ndcPos.xy * gClipToUVScaleOffset.xy + gClipToUVScaleOffset.zw;
+		}
+		
+		/** Converts position in UV coordinates mapped to screen rectangle to NDC coordinates. */
+		float2 UVToNDC(float2 uvPos)
+		{
+			return uvPos * gUVToClipScaleOffset.xy + gUVToClipScaleOffset.zw;
+		}
+		
+		/** Converts position in UV coordinates mapped to the screen, to screen coordinates in pixels. */
+		uint2 UVToScreen(float2 uv)
+		{
+			return (uint2)(uv * (float2)gViewportRectangle.zw - ((float2)gViewportRectangle.xy));
+		}
+		
+		/** Converts position in NDC to screen coordinates in pixels. */
+		uint2 NDCToScreen(float2 ndcPos)
+		{
+			float2 uv = NDCToUV(ndcPos);
+			return UVToScreen(uv);
+		}
+		
+		/** Converts position in NDC to world space. */
+		float3 NDCToWorld(float2 ndcPos, float depth)
+		{
+			// x, y are now in clip space, z, w are in view space
+			// We multiply them by a special inverse view-projection matrix, that had the projection entries that effect
+			// z, w eliminated (since they are already in view space)
+			// Note: Multiply by depth should be avoided if using ortographic projection
+			float4 mixedSpacePos = float4(ndcPos.xy * -depth, depth, 1);
+			float4 worldPosition4D = mul(gMatScreenToWorld, mixedSpacePos);
+			
+			return worldPosition4D.xyz / worldPosition4D.w;
+		}
+	};
+};

+ 11 - 0
Source/bsf/Data/Raw/Shaders/Includes/PerFrameData.bslinc

@@ -0,0 +1,11 @@
+mixin PerFrameData
+{
+	code
+	{
+		[internal]
+		cbuffer PerFrame
+		{
+			float gTime;
+		}
+	};
+};

+ 23 - 0
Source/bsf/Data/Raw/Shaders/Includes/PerObjectData.bslinc

@@ -0,0 +1,23 @@
+mixin PerObjectData
+{
+	code
+	{
+		[internal]
+		cbuffer PerObject
+		{
+			float4x4 gMatWorld;
+			float4x4 gMatInvWorld;
+			float4x4 gMatWorldNoScale;
+			float4x4 gMatInvWorldNoScale;
+			float4x4 gMatPrevWorld;
+			float gWorldDeterminantSign;
+			uint gLayer;
+		}	
+
+		[internal]
+		cbuffer PerCall
+		{
+			float4x4 gMatWorldViewProj;
+		}			
+	};
+};

+ 21 - 0
Source/bsf/Data/Raw/Shaders/Includes/RadixSortCommon.bslinc

@@ -0,0 +1,21 @@
+mixin RadixSortCommon
+{
+	featureset = HighEnd;
+
+	code
+	{
+		#define TILE_SIZE NUM_THREADS * KEYS_PER_LOOP
+	
+		#define NUM_DIGITS (1 << RADIX_NUM_BITS)
+		#define KEY_MASK (NUM_DIGITS - 1)
+		
+		cbuffer Params
+		{
+			uint gBitOffset;
+			uint gTilesPerGroup;
+			uint gNumGroups;
+			uint gNumExtraTiles;
+			uint gNumExtraKeys;
+		};		
+	};
+};

+ 223 - 0
Source/bsf/Data/Raw/Shaders/Includes/RayMarch.bslinc

@@ -0,0 +1,223 @@
+#include "$ENGINE$/PerCameraData.bslinc"
+
+mixin RayMarch
+{
+	mixin PerCameraData;
+
+	code
+	{
+		#ifndef NUM_STEPS
+			#define NUM_STEPS 16
+		#endif
+		
+		#ifndef HI_Z
+			#define HI_Z 0
+		#endif
+		
+		// Note this is a very high number of iterations, but it is expected only a small number of pixels will use
+		// this full range. Generally those are pixels that keep having false positives when intersecting a higher
+		// Z level.
+		#define MAX_HIZ_ITERATIONS 64
+		#define HIZ_START_LEVEL 2
+	
+		float3 viewToNDC(float3 view)
+		{
+			float4 projected = mul(gMatProj, float4(view, 1));
+			projected.xyz /= projected.w;
+			
+			return projected.xyz;
+		}
+		
+		bool linearSearch(Texture2D depth, SamplerState samp, float3 rayStart, float3 rayStep, int numSteps, float stepIncrement, float compareTolerance, inout float t)
+		{
+			float lastDiff = 0.0f;
+			
+			[unroll]
+			for(int i = 0; i < numSteps; ++i)
+			{
+				float3 rayPos = rayStart + rayStep * t;
+
+				#if HI_Z
+				float sampleDepth = depth.Sample(samp, rayPos.xy).r;
+				#else
+				float sampleDepth = depth.SampleLevel(samp, rayPos.xy, 0).r;
+				#endif
+				
+				// Check if ray is behind an object, but not too much behind otherwise we'll have false positives.
+				// Instead we treat "compareTolerance" as an approximate thickness of the object. Proper
+				// thickness should be calculated by rendering depth buffer for backfaces.
+ 				float depthDiff = rayPos.z - sampleDepth;
+				bool hit = abs(depthDiff - compareTolerance) < compareTolerance;
+				if(hit)
+				{
+					// Refine hit using line segment intersection
+					float tt = lastDiff / (depthDiff - lastDiff);
+					t += tt * stepIncrement + stepIncrement;
+					
+					return true;
+				}
+			
+				lastDiff = depthDiff;
+				t += stepIncrement;
+			}
+			
+			return false;
+		}
+
+		bool hiZSearch(Texture2D depth, SamplerState samp, int2 bufferSize, int maxMipLevel, float3 rayPos, float3 rayStep, out float3 hitPos)
+		{		
+			float iterationCount = 0.0f;
+			int mipLevel = HIZ_START_LEVEL;
+			
+			bufferSize >>= mipLevel;
+			
+			// Get the ray equation, in the form so that t == Z
+			float3 D = rayStep / rayStep.z; // Scale vector so Z is moved into [0, 1] range
+			float3 O = rayPos + D * -rayPos.z; // Get point where Z equals 0 (on near plane)
+			// Our ray is now O + D * t, where t = 0 = near plane, and t = 1 = far plane
+			
+			// Avoid division by zero
+			D.x = abs(D.x) < 0.00001f ? 0.00001f : D.x;
+			D.y = abs(D.y) < 0.00001f ? 0.00001f : D.y;
+
+			while(rayPos.z < 0.9999f && iterationCount < MAX_HIZ_ITERATIONS)
+			{
+				// Get depth of the current cell
+				float cellZ = depth.SampleLevel(samp, rayPos.xy, mipLevel).r;
+			
+				// Get pixel coordinates of the current cell
+				float2 curCellIdx = trunc(rayPos.xy * bufferSize);
+			
+				// Find intersection with the cell floor plane
+				float3 newRay = O + D * max(cellZ, rayPos.z); // max() so we can't hit the ceiling (ray going backwards)
+				
+				// Get pixel coordinates of the new ray's cell
+				float2 newCellIdx = trunc(newRay.xy * bufferSize);
+				
+				// If we moved to another cell, no intersection with floor
+				if(any(curCellIdx != newCellIdx))
+				{
+					float2 cellStart = (curCellIdx / bufferSize);
+					float2 cellEnd = ((curCellIdx + 1) / bufferSize);
+					
+					float2 intersectStart = (cellStart - rayPos.xy) / D.xy;
+					float2 intersectEnd = (cellEnd - rayPos.xy) / D.xy;
+					
+					// Only care about positive t
+					float maxIntersectX = max(intersectStart.x, intersectEnd.x);
+					float maxIntersectY = max(intersectStart.y, intersectEnd.y);
+					
+					// Closest t is the one at the boundary
+					float minIntersect = min(maxIntersectX, maxIntersectY);
+				
+					// Little extra to ensure the boundary is crossed. max() to ensure the value isn't too
+					// small to prevent it ever leaving a cell. Note that this clamping results in a quality loss,
+					// you want to keep the clamp value as low as possible, but not too low to avoid artifacts.
+					minIntersect = max(minIntersect * 1.05f, 1.0f / (bufferSize * 512)); // 1/512th of a pixel
+					
+					// Move the ray past the boundary
+					rayPos = O + D * (rayPos.z + minIntersect);
+				
+					if(mipLevel < maxMipLevel)
+					{
+						++mipLevel;
+						bufferSize >>= 1;
+					}
+				}
+				else // Intersection with floor, move to higher quality mip
+				{
+					rayPos = newRay;
+					--mipLevel;
+					
+					if(mipLevel < 0)
+					{
+						hitPos = rayPos;
+						return true;
+					}
+					
+					bufferSize <<= 1;
+				}
+
+				++iterationCount;
+			}
+			
+			hitPos = rayPos;			
+			return false;
+		}
+		
+		struct RayMarchParams
+		{
+			int2 bufferSize;
+			int numMips;
+			float4 NDCToHiZUV; // From NDC to HiZ UV. .xy - multiply, .zw - add
+			float2 HiZUVToScreenUV; // From HiZ UV to screen UV. .xy - multiply
+			float3 rayOrigin; // World space
+			float3 rayDir; // World space
+			float jitterOffset;
+		};
+	
+		float4 rayMarch(Texture2D depth, SamplerState samp, RayMarchParams params)
+		{
+			float3 viewOrigin = mul(gMatView, float4(params.rayOrigin, 1));
+			float3 viewDir = mul(gMatView, float4(params.rayDir, 0));
+		
+			float3 ndcStart = viewToNDC(viewOrigin);
+			float3 ndcEnd = viewToNDC(viewOrigin + viewDir);
+			float3 ndcStep = ndcEnd - ndcStart;
+			
+			// Resize ray so it reaches screen edge
+			//// We want: start + |step| * t = 1
+			//// Solve for t: t = (1 - start) / |step|
+			//// This has two solutions, but we can handle them both in a single equation by flipping sign depending on "step", on only one of the components:
+			//// t = 1/|step| - start/step
+			float epsilon = 0.00001f; // Handle div by zero
+			float2 stepScale = 1.0f / abs(ndcStep.xy + epsilon) - ndcStart.xy/(ndcStep.xy + epsilon);
+			ndcStep *= min(stepScale.x, stepScale.y);
+		
+			float deviceZStart = NDCZToDeviceZ(ndcStart.z);
+			float deviceZEnd = NDCZToDeviceZ(ndcStart.z + ndcStep.z);
+		
+			#if HI_Z
+			float3 uvStart;
+			uvStart.xy = ndcStart.xy * params.NDCToHiZUV.xy + params.NDCToHiZUV.zw;
+			uvStart.z = deviceZStart;
+			
+			float3 uvStep;
+			uvStep.xy = ndcStep.xy * params.NDCToHiZUV.xy;
+			uvStep.z = deviceZEnd - deviceZStart;
+		
+			#else
+			float3 uvStart = float3(NDCToUV(ndcStart.xy), deviceZStart);
+			float3 uvStep = float3(ndcStep.xy * gClipToUVScaleOffset.xy, deviceZEnd - deviceZStart);
+			#endif
+		
+			float stepIncrement = 1.0f / NUM_STEPS;
+			// Offset starting position to avoid self-intersection. Use random values to avoid
+			// staircase artifacts.
+			float t = stepIncrement + stepIncrement * params.jitterOffset;
+			
+			// Note: Perhaps tweak this value
+			float compareTolerance = uvStep.z * stepIncrement * 2.0f;
+						
+			#if HI_Z
+			
+			// Note: Perhaps do a few steps of linear search first to handle nearby surfaces
+			
+			// Hierarchical search
+			float3 rayPos = uvStart + uvStep * t;
+			float3 hitPos;
+			if(hiZSearch(depth, samp, params.bufferSize, params.numMips, rayPos, uvStep, hitPos))
+				return float4(hitPos.xy * params.HiZUVToScreenUV.xy, hitPos.z, 0);
+				
+			#else
+			
+			// Plain linear search
+			if(linearSearch(depth, samp, uvStart, uvStep, NUM_STEPS, stepIncrement, compareTolerance, t))
+				return float4(uvStart + uvStep * t, t);
+			#endif
+			
+			// Hit not found
+			return float4(0, 0, 0, 1);
+		}		
+	};
+};

+ 101 - 0
Source/bsf/Data/Raw/Shaders/Includes/ReflProbeAccumulator.bslinc

@@ -0,0 +1,101 @@
+// 
+// Contains helper mixin used for initializing different forms of refl. probe accumulation. Can be removed 
+// when template/specialization support for mixins is added to BSL.
+//
+
+#ifdef USE_UNIFORM_BUFFER
+mixin ReflProbeAccumulatorDirect
+#else
+mixin ReflProbeAccumulatorIndexed
+#endif
+{
+	code
+	{
+		#ifdef USE_UNIFORM_BUFFER
+			#define MAX_PROBES 8
+			
+			[internal]
+			cbuffer ReflProbes
+			{
+				ReflProbeData gReflectionProbes[MAX_PROBES];
+			}
+		#else
+			#define MAX_PROBES 512 // Arbitrary limit, increase if needed
+		
+			#ifdef USE_COMPUTE_INDICES
+				groupshared uint gReflectionProbeIndices[MAX_PROBES];
+				StructuredBuffer<ReflProbeData> gReflectionProbes;
+			#endif
+			
+			#ifdef USE_LIGHT_GRID_INDICES
+				Buffer<uint> gReflectionProbeIndices;
+				StructuredBuffer<ReflProbeData> gReflectionProbes;
+			#endif
+		#endif
+		
+		float3 gatherReflectionRadiance(float3 worldPos, float3 dir, float roughness, float alpha, float3 specularColor, uint probeOffset, uint numProbes)
+		{
+			if(gUseReflectionMaps == 0)
+				return specularColor;
+									
+			float mipLevel = mapRoughnessToMipLevel(roughness, gReflCubemapNumMips);
+			
+			float3 output = 0;
+			[loop]
+			for(uint i = 0; i < numProbes; i++)
+			{
+				if(alpha < 0.001f)
+					break;
+						
+				#ifdef USE_UNIFORM_BUFFER
+				uint probeIdx = probeOffset + i;
+				#else
+				uint probeIdx = gReflectionProbeIndices[probeOffset + i];
+				#endif
+				
+				ReflProbeData probeData = gReflectionProbes[probeIdx];
+				float4 probeValue = evaluateProbe(worldPos, dir, mipLevel, probeData);
+				
+				output += probeValue.rgb * alpha; 
+				alpha *= probeValue.w;
+			}
+				
+			if(gSkyCubemapAvailable > 0)
+			{
+				float skyMipLevel = mapRoughnessToMipLevel(roughness, gSkyCubemapNumMips);
+				float4 skySample = gSkyReflectionTex.SampleLevel(gSkyReflectionSamp, dir, skyMipLevel) * gSkyBrightness;
+				
+				output += skySample.rgb * alpha; 
+			}
+					
+			return output;
+		}
+		
+		float3 getImageBasedSpecular(float3 worldPos, float3 V, float3 R, SurfaceData surfaceData, float ao, float4 ssr, 
+			uint probeOffset, uint numProbes)
+		{
+			// See C++ code for generation of gPreintegratedEnvBRDF to see why this code works as is
+			float3 N = surfaceData.worldNormal.xyz;
+			float NoV = saturate(dot(N, V));
+			
+			// Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
+			// For more customizability allow the user to provide separate albedo/specular colors for both types.
+			float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
+			
+			// Get SSR
+			float3 radiance = ssr.rgb;
+			float alpha = 1.0f - ssr.a; // Determines how much to blend in reflection probes & skybox
+			
+			// Generate an approximate spec. occlusion value from AO. This doesn't need to be applied to SSR since it accounts
+			// for occlusion by tracing rays.
+			float specOcclusion = getSpecularOcclusion(NoV, surfaceData.roughness * surfaceData.roughness, ao);
+			alpha *= specOcclusion;
+			
+			// Get radiance from probes and skybox
+			radiance += gatherReflectionRadiance(worldPos, R, surfaceData.roughness, alpha, specularColor, probeOffset, numProbes);
+			
+			float2 envBRDF = gPreintegratedEnvBRDF.SampleLevel(gPreintegratedEnvBRDFSamp, float2(NoV, surfaceData.roughness), 0).rg;
+			return radiance * (specularColor * envBRDF.x + envBRDF.y);
+		}
+	};
+};

+ 89 - 0
Source/bsf/Data/Raw/Shaders/Includes/ReflectionCubemapCommon.bslinc

@@ -0,0 +1,89 @@
+mixin ReflectionCubemapCommon
+{
+	code
+	{
+		float3 getDirFromCubeFace(uint cubeFace, float2 uv)
+		{
+			float3 dir;
+			
+			if(cubeFace == 0)
+				dir = float3(1.0f, -uv.y, -uv.x);
+			else if(cubeFace == 1)
+				dir = float3(-1.0f, -uv.y, uv.x);
+			else if(cubeFace == 2)
+				dir = float3(uv.x, 1.0f, uv.y);
+			else if(cubeFace == 3)
+				dir = float3(uv.x, -1.0f, -uv.y);
+			else if(cubeFace == 4)
+				dir = float3(uv.x, -uv.y, 1.0f);
+			else
+				dir = float3(-uv.x, -uv.y, -1.0f);
+				
+			return dir;
+		}
+		
+		/** 
+		 * Integrates area of a cube face projected onto the surface of the sphere, from [0, 0] to [u, v]. 
+		 * u & v expected in [-1, -1] to [1, 1] range.
+		 *
+		 * See http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/ for derivation.
+		 */
+		float integrateProjectedCubeArea(float u, float v)
+		{
+			return atan2(u * v, sqrt(u * u + v * v + 1.0f));
+		}
+		
+		/** Calculates solid angle of a texel projected onto a sphere. */
+		float texelSolidAngle(float u, float v, float invFaceSize)
+		{
+			float x0 = u - invFaceSize;
+			float x1 = u + invFaceSize;
+			float y0 = v - invFaceSize;
+			float y1 = v + invFaceSize;
+
+			return   integrateProjectedCubeArea(x1, y1)
+				   - integrateProjectedCubeArea(x0, y1)
+				   - integrateProjectedCubeArea(x1, y0)
+				   + integrateProjectedCubeArea(x0, y0);
+		}		
+		
+		/**
+		 * Calculates a mip level to sample from based on roughness value.
+		 *
+		 * @param 	roughness	Roughness in range [0, 1]. Higher values yield more roughness.
+		 * @param	numMips		Total number of mip-map levels in the texture we'll be sampling from.
+		 * @return				Index of the mipmap level to sample.
+		 */					
+		float mapRoughnessToMipLevel(float roughness, int numMips)
+		{
+			// We use the following equation:
+			//    mipLevel = log10(1 - roughness) / log10(dropPercent)
+			//
+			// Where dropPercent represent by what % to drop the roughness with each mip level.
+			// We convert to log2 and a assume a drop percent value of 0.7. This gives us:
+			//    mipLevel = -2.8 * log2(1 - roughness);
+			
+			// Note: Another value that could be used is drop 0.6, which yields a multiply by -1.35692. 
+			// This more accurately covers the mip range, but early mip levels end up being too smooth,
+			// and benefits from our cubemap importance sampling strategy seem to be lost as most samples
+			// fall within one pixel, resulting in same effect as just trivially downsampling. With 0.7 drop
+			// the roughness increases too early and higher mip levels don't cover the full [0, 1] range. Which
+			// is better depends on what looks better.
+			
+			return max(0, -2.8f * log2(1.0f - roughness));
+		}
+		
+		/**
+		 * Calculates a roughness value from the provided mip level.
+		 *
+		 * @param 	mipLevel	Mip level to determine roughness for.
+		 * @param	numMips		Total number of mip-map levels in the texture we'll be sampling from.
+		 * @return				Roughness value for the specific mip level.
+		 */				
+		float mapMipLevelToRoughness(int mipLevel, int numMips)
+		{
+			// mapRoughnessToMipLevel() solved for roughness
+			return 1 - exp2((float)mipLevel / -2.8f);
+		}	
+	};
+};

+ 184 - 0
Source/bsf/Data/Raw/Shaders/Includes/SHCommon.bslinc

@@ -0,0 +1,184 @@
+mixin SHCommon
+{
+	code
+	{
+		#define SH_NUM_COEFFS SH_ORDER * SH_ORDER
+	
+		struct SHVector
+		{
+			float v[SH_NUM_COEFFS];
+		};
+
+		struct SHVectorRGB
+		{
+			SHVector R;
+			SHVector G;
+			SHVector B;
+		};
+		
+		SHVectorRGB SHLoad(Texture2D input, int2 offset)
+		{
+			SHVectorRGB output;
+						
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)
+			{
+				float3 coeff = input.Load(int3(offset.x + i, offset.y, 0)).rgb;
+			
+				output.R.v[i] = coeff.r;
+				output.G.v[i] = coeff.g;
+				output.B.v[i] = coeff.b;
+			}
+			
+			return output;
+		}
+		
+		void SHZero(inout SHVector v)
+		{
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)
+				v.v[i] = 0;
+		}
+		
+		void SHZero(inout SHVectorRGB v)
+		{
+			SHZero(v.R);
+			SHZero(v.G);
+			SHZero(v.B);
+		}				
+		
+		void SHMultiplyAdd(inout SHVector lhs, SHVector rhs, float c)
+		{
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)		
+				lhs.v[i] += rhs.v[i] * c;
+		}
+		
+		void SHMultiplyAdd(inout SHVectorRGB lhs, SHVectorRGB rhs, float c)
+		{
+			SHMultiplyAdd(lhs.R, rhs.R, c);
+			SHMultiplyAdd(lhs.G, rhs.G, c);
+			SHMultiplyAdd(lhs.B, rhs.B, c);
+		}
+		
+		void SHAdd(inout SHVector lhs, SHVector rhs)
+		{
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)		
+				lhs.v[i] += rhs.v[i];
+		}
+				
+		void SHAdd(inout SHVectorRGB lhs, SHVectorRGB rhs)
+		{
+			SHAdd(lhs.R, rhs.R);
+			SHAdd(lhs.G, rhs.G);
+			SHAdd(lhs.B, rhs.B);
+		}
+		
+		void SHMultiply(inout SHVector lhs, SHVector rhs)
+		{
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)
+				lhs.v[i] *= rhs.v[i];
+		}	
+		
+		void SHMultiply(inout SHVectorRGB lhs, SHVectorRGB rhs)
+		{
+			SHMultiply(lhs.R, rhs.R);
+			SHMultiply(lhs.G, rhs.G);
+			SHMultiply(lhs.B, rhs.B);
+		}	
+		
+		void SHMultiply(inout SHVector lhs, float rhs)
+		{
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)		
+				lhs.v[i] *= rhs;
+		}
+
+		void SHMultiply(inout SHVectorRGB lhs, float rhs)
+		{
+			SHMultiply(lhs.R, rhs);
+			SHMultiply(lhs.G, rhs);
+			SHMultiply(lhs.B, rhs);
+		}	
+				
+		#if SH_ORDER == 5
+		SHVector SHBasis(float3 dir)
+		{
+			float x = dir.x;
+			float y = dir.y;
+			float z = dir.z;
+
+			float x2 = x*x;
+			float y2 = y*y;
+			float z2 = z*z;
+
+			float z3 = z2 * z;
+
+			float x4 = x2 * x2;
+			float y4 = y2 * y2;
+			float z4 = z2 * z2;
+
+			SHVector o;
+			o.v[0] = 0.282095f;
+
+			o.v[1] = -0.488603f * y;
+			o.v[2] =  0.488603f * z;
+			o.v[3] = -0.488603f * x;
+
+			o.v[4] =  1.092548f * x * y;
+			o.v[5] = -1.092548f * y * z;
+			o.v[6] =  0.315392f * (3.0f * z2 - 1.0f);
+			o.v[7] = -1.092548f * x * z;
+			o.v[8] =  0.546274f * (x2 - y2);
+
+			o.v[9]  = -0.590043f * y * (3.0f * x2 - y2);
+			o.v[10] =  2.890611f * y * x * z;
+			o.v[11] = -0.646360f * y * (-1.0f + 5.0f * z2);
+			o.v[12] =  0.373176f *(5.0f * z3 - 3.0f * z);
+			o.v[13] = -0.457045f * x * (-1.0f + 5.0f * z2);
+			o.v[14] =  1.445306f *(x2 - y2) * z;
+			o.v[15] = -0.590043f * x * (x2 - 3.0f * y2);
+
+			o.v[16] =  2.503340f * x * y * (x2 - y2);
+			o.v[17] = -1.770130f * y * z * (3.0f * x2 - y2);
+			o.v[18] =  0.946175f * y * x * (-1.0f + 7.0f * z2);
+			o.v[19] = -0.669046f * y * z * (-3.0f + 7.0f * z2);
+			o.v[20] =  (105.0f * z4 - 90.0f * z2 + 9.0f) / 28.359261f;
+			o.v[21] = -0.669046f * x * z * (-3.0f + 7.0f * z2);
+			o.v[22] =  0.473087f * (x2 - y2) * (-1.0f + 7.0f * z2);
+			o.v[23] = -1.770130f * x * z * (x2 - 3.0f * y2);
+			o.v[24] =  0.625836f * (x4 - 6.0f * y2 * x2 + y4);
+			
+			return o;
+		}
+		#elif SH_ORDER == 3
+		SHVector SHBasis(float3 dir)
+		{
+			float x = dir.x;
+			float y = dir.y;
+			float z = dir.z;
+
+			float x2 = x*x;
+			float y2 = y*y;
+			float z2 = z*z;
+
+			SHVector o;
+			o.v[0] = 0.282095f;
+
+			o.v[1] = -0.488603f * y;
+			o.v[2] =  0.488603f * z;
+			o.v[3] = -0.488603f * x;
+
+			o.v[4] =  1.092548f * x * y;
+			o.v[5] = -1.092548f * y * z;
+			o.v[6] =  0.315392f * (3.0f * z2 - 1.0f);
+			o.v[7] = -1.092548f * x * z;
+			o.v[8] =  0.546274f * (x2 - y2);
+
+			return o;
+		}
+		#endif
+	};
+};

+ 97 - 0
Source/bsf/Data/Raw/Shaders/Includes/ShadowDepthBase.bslinc

@@ -0,0 +1,97 @@
+#include "$ENGINE$\PerObjectData.bslinc"
+#include "$ENGINE$\VertexInput.bslinc"
+
+mixin ShadowDepthBase
+{
+	mixin PerObjectData;
+	mixin VertexInput;
+
+	// Render back-faces to reduce shadow acne (assuming thick geometry everywhere)
+	raster
+	{
+		cull = cw;
+	};	
+	
+	code
+	{
+		struct ShadowVStoFS
+		{
+			float4 position : SV_Position;
+			
+			#ifdef USES_GS
+				float4 worldPos : TEXCOORD0;
+			#else
+			#ifdef USES_PS
+				float shadowPos : TEXCOORD0;
+			#endif
+			#endif
+		};
+		
+		cbuffer ShadowParams
+		{
+			float4x4 gMatViewProj;
+			float2 gNDCZToDeviceZ;
+			float gDepthBias;
+			float gInvDepthRange;
+		};
+		
+		/** Converts Z value from device range ([0, 1]) to NDC space. */
+		float DeviceZToNDCZ(float deviceZ)
+		{
+			return deviceZ / gNDCZToDeviceZ.x - gNDCZToDeviceZ.y;
+		}
+		
+		/** Converts Z value from NDC space to device Z value in range [0, 1]. */
+		float NDCZToDeviceZ(float ndcZ)
+		{
+			return (ndcZ + gNDCZToDeviceZ.y) * gNDCZToDeviceZ.x;
+		}		
+
+		ShadowVStoFS vsmain(VertexInput_PO input)
+		{
+			ShadowVStoFS output;
+		
+			float4 worldPosition = getVertexWorldPosition(input);
+			
+			// If using a geometry shader, just pass through the relevant information
+			// as the GS does the necessary transform and applies the depth bias
+			#ifdef USES_GS
+			output.worldPos = worldPosition;
+			output.position = worldPosition;
+			#else // USES_GS
+			
+			// Not using a geometry shader, transform to clip space
+			float4 clipPos = mul(gMatViewProj, worldPosition);
+			
+			// Clamp geometry behind the near plane
+			#ifdef CLAMP_TO_NEAR_PLANE
+				float ndcZ = clipPos.z / clipPos.w;
+				float deviceZ = NDCZToDeviceZ(ndcZ);
+			
+				#ifdef USES_PS
+				if (deviceZ < 0)
+				#else
+				if (deviceZ < -gDepthBias)
+				#endif
+				{
+					clipPos.z = DeviceZToNDCZ(0);
+					clipPos.w = 1.0f;
+				}
+			#endif // CLAMP_TO_NEAR_PLANE
+
+			// If using a pixel shader, output shadow depth in clip space, as
+			// we'll apply bias to it in PS (depth needs to be interpolated in
+			// a perspective correct way)
+			#ifdef USES_PS
+				output.shadowPos = clipPos.z;
+			#else // Otherwise apply bias immediately
+				clipPos.z = max(DeviceZToNDCZ(0), clipPos.z + gDepthBias);
+			#endif // USES_PS
+			
+			output.position = clipPos;
+			#endif // USES_GS
+			
+			return output;
+		}
+	};
+};

+ 44 - 0
Source/bsf/Data/Raw/Shaders/Includes/ShadowProjectionCommon.bslinc

@@ -0,0 +1,44 @@
+#include "$ENGINE$/PerCameraData.bslinc"
+
+mixin ShadowProjectionCommon
+{
+	mixin PerCameraData;
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float4 clipSpacePos : TEXCOORD0;
+		};
+
+		struct VertexInput
+		{
+			float3 position : POSITION;
+		};
+		
+		#if NEEDS_TRANSFORM
+		[internal]
+		cbuffer VertParams
+		{
+			float4 gPositionAndScale;
+		};
+		#endif
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+			
+			#if NEEDS_TRANSFORM
+				float3 worldPos = input.position.xyz * gPositionAndScale.w + gPositionAndScale.xyz;
+				output.clipSpacePos = mul(gMatViewProj, float4(worldPos, 1));
+				output.position = output.clipSpacePos;
+			#else
+				output.clipSpacePos = float4(input.position, 1);
+				output.position = float4(input.position, 1);
+			#endif
+		
+			return output;
+		}			
+	};
+};

+ 451 - 0
Source/bsf/Data/Raw/Shaders/Includes/ShapingCommon.bslinc

@@ -0,0 +1,451 @@
+mixin ShapingCommon 
+{
+	code 
+	{
+		#define EPSILON 0.00001f
+		#define PI 3.1415926f
+
+		float almostIdentity(float x, float m, float n)
+		{
+		    if(x > m) 
+				return x;
+		
+		    float a = 2.0f * n - m
+		    float b = 2.0f * m - 3.0f * n;
+		    float t = x / m;
+		
+		    return (a * t + b) * t * t + n;
+		}
+
+		float gain(float x, float k)
+		{
+			float a = 0;
+			float res = 0;
+
+			if(x < 0.5f)
+				a = 0.5f * pow(2.0f * x, k);
+			else
+				a = 0.5f * pow(2.0f * (1.0f - x), k);
+
+			if(x < 0.5f)
+				res = a;
+			else 
+				res = 1.0f - a;
+
+			return res;
+		}
+
+		float parabola(float x, float k)
+		{
+		    return pow(4.0f * x * (1.0f - x), k);
+		}
+
+		// Power curve
+		float pcurve(float x, float a, float b)
+		{
+		    float k = pow(a + b, a + b) / (pow(a, a) * pow(b, b));
+		    return k * pow(x, a) * pow(1.0f - x, b);
+		}
+
+		// Sinc Curve
+		float sinc(float x, float k)
+		{
+		    float a = PI * ((float(k) * x - 1.0f);
+		    return sin(a) / a;
+		}
+
+		// Blinn-Wyvill Approximation to the Raised Inverted Cosine
+		float cosApprox(float x)
+		{
+			float x2 = x * x;
+			float x4 = x2 * x2;
+			float x6 = x4 * x2;
+
+			float fa = (4.0f / 9.0f);
+			float fb = (17.0f / 9.0f);
+			float fc = (22.0f / 9.0f);
+			
+			return fa * x6 - fb * x4 + fc * x2;
+		}
+
+		// Seat shaped function formed by joining two 3rd order polynomial curves
+		// The curves meet with a horizontal inflection point at the control coordinate (a, b) in the unit square
+		float cubicSeat(float x, float a, float b)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+			float res = 0;
+
+			a = min(maxA, max(minA, a));
+			b = min(maxB, max(minB, b));
+
+			if (x <= a)
+				res = b - b * pow(1 - x / a, 3.0f);
+			else 
+				res = b + (1 - b) * pow((x - a) / (1 - a), 3.0f);
+
+			return res;
+		}
+
+		// Double cubic Seat function uses a single variable to control the location of its inflection point along the diagonal of the unit square
+		// The second parameter is used to blend the curve with the identity function (y = x)
+		// Uses the variable b to control the blending amount
+		float cubicSeatWithLinearBlend(float x, float a, float b)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+			float res = 0;
+
+			a = min(maxA, max(minA, a));  
+			b = min(maxB, max(minB, b)); 
+			b = 1.0 - b;
+			
+			if (x <= a)
+				res = b * x + (1 - b) * a * (1 - pow(1 - x / a, 3.0f));
+			else 
+				res = b * x + (1 - b) * (a + (1 - a) * pow((x - a) / (1 - a), 3.0f));
+
+			return res;
+		}
+
+		float oddPolynomialSeat(float x, float a, float b, int n)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+			float res = 0;
+
+			a = min(maxA, max(minA, a));  
+			b = min(maxB, max(minB, b)); 
+			int p = 2 * n + 1;
+
+			if (x <= a)
+				res = b - b * pow(1 - x / a, p);
+			else 
+				res = b + (1 - b) * pow((x - a) / (1 - a), p);
+
+			return res;
+		}
+
+		float polynomialSigmoid(float x, float a, float b, int n)
+		{
+			float res = 0;
+			if (n % 2 == 0)
+			{ 
+				// Even polynomial
+				if (x <= 0.5f)
+					res = pow(2.0f * x, n) / 2.0f;
+				else 
+					res = 1.0f - pow(2 * (x - 1), n) / 2.0f;
+			} 
+			else 
+			{ 
+				// Odd polynomial
+				if (x <= 0.5f)
+					res = pow(2.0f * x, n) / 2.0f;
+				else 
+					res = 1.0f + pow(2.0f * (x - 1), n) / 2.0f;
+			}
+
+			return res;
+		}
+
+		// Defines a parabola which passes through a user provided point (a, b) in the unit square
+		float quadraticThroughPoint(float x, float a, float b)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+
+			a = min(maxA, max(minA, a));
+			b = min(maxB, max(minB, b));
+			
+			float fA = (1 - b) / (1 - a) - (b / a);
+			float fB = (fA * (a * a) - b) / a;
+			float res = fA * (x * x) - fB * (x);
+
+			return min(1, max(0, res));
+		}
+
+		float exponentialEasing(float x, float a)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float res = 0;
+
+			a = max(minA, min(maxA, a));
+			
+			if (a < 0.5f)
+			{
+				a = 2.0f * a;
+				res = pow(x, a);
+			} 
+			else 
+			{
+				a = 2.0f * (a - 0.5f);
+				res = pow(x, 1.0f / (1 - a));
+			}
+
+			return res;
+		}
+
+		float exponentialSeat(float x, float a)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float res = 0;
+
+			a = min(maxA, max(minA, a)); 
+
+			if (x <= 0.5f)
+				res = (pow(2.0f * x, 1 - a)) / 2.0f;
+			else 
+				res = 1.0f - (pow(2.0f * (1.0f - x), 1 - a)) / 2.0f;
+
+			return res;
+		}
+
+		float exponentialSigmoid(float x, float a)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float res = 0;
+
+			a = min(maxA, max(minA, a));
+			a = 1.0f - a;
+			
+			if (x <= 0.5f)
+				res = (pow(2.0f * x, 1.0f / a)) / 2.0f;
+			else 
+				res = 1.0f - (pow(2.0f * (1.0f - x), 1.0f / a)) / 2.0f;
+
+			return res;
+		}
+
+		float logisticSigmoid(float x, float a)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+
+			a = max(minA, min(maxA, a));
+			a = (1 / (1 - a) - 1);
+
+			float fA = 1.0f / (1.0f + exp(0 - ((x - 0.5f) * a * 2.0f)));
+			float fB = 1.0f / (1.0f + exp(a));
+			float fC = 1.0f / (1.0f + exp(0 - a)); 
+			
+			return (fA - fB) / (fC - fB);
+		}
+
+		// A circular arc for easing in of the unit square.
+		float circularEaseIn(float x)
+		{
+			return 1 - sqrt(1 - x * x);
+		}
+
+		// A circular arc for easing out of the unit square.
+		float circularEaseOut(float x)
+		{
+			return sqrt(1 - pow(1 - x, 2));
+		}
+
+		float circleSeat(float x, float a)
+		{
+			float minA = 0.0f;
+			float maxA = 1.0f;
+			float res = 0;
+
+			a = max(minA, min(maxA, a)); 
+
+			if (x <= a)
+				res = sqrt(pow(a, 2) - pow(x - a, 2));
+			else 
+				res = 1 - sqrt(pow(1 - a, 2) - pow(x - a, 2));
+
+			return res;
+		}
+
+		float circleSigmoid(float x, float a)
+		{
+			float minA = 0.0f;
+			float maxA = 1.0f;
+			float res = 0;
+
+			a = max(minA, min(maxA, a));
+
+			if (x <= a)
+				res = a - sqrt(a * a - x * x);
+			else 
+				res = a + sqrt(pow(1 - a, 2) - pow(x - 1, 2));
+
+			return res;
+		}
+
+		float ellipticSeat(float x, float a, float b)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+			float res = 0;
+
+			a = max(minA, min(maxA, a)); 
+			b = max(minB, min(maxB, b)); 
+
+			if (x <= a)
+				res = (b / a) * sqrt(pow(a, 2) - pow(x - a, 2));
+			else 
+				res = 1 - ((1 - b) / (1 - a)) * sqrt(pow(1 - a, 2) - pow(x - a, 2));
+
+			return res;
+		}
+
+		float ellipticSigmoid(float x, float a, float b)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f;
+			float maxB = 1.0f;
+			float res = 0;
+
+			a = max(minA, min(maxA, a)); 
+			b = max(minB, min(maxB, b));
+ 
+			if (x <= a)
+				res = b * (1 - (sqrt(pow(a, 2) - pow(x, 2)) / a));
+			else 
+				res = b + ((1 - b) / (1 - a)) * sqrt(pow(1 - a, 2) - pow(x - 1));
+
+			return res;
+		}
+
+		// Defines a 2nd order Bezier curve with a single spline control point at the coordinate (a, b) in the unit square. 
+		float quadraticBezier(float x, float a, float b)
+		{
+			a = max(0, min(1, a)); 
+			b = max(0, min(1, b)); 
+
+			if (a == 0.5f)
+				a += EPSILON;
+			
+			// Solve t from x (an inverse operation)
+			float om2a = 1 - 2 * a;
+			float t = (sqrt(a * a + om2a * x) - a) / om2a;
+			float res = (1 - 2 * b) * (t * t) + (2 * b) * t;
+			
+			return res;
+		}
+
+		float slopeFromT(float t, float A, float B, float C)
+		{
+			return 1.0f / (3.0f* A * t * t + 2.0f* B * t + C); 
+		}
+		
+		float xFromT(float t, float A, float B, float C, float D)
+		{
+			return A * (t * t * t) + B * (t * t) + C * t + D;
+		}
+		
+		float yFromT(float t, float E, float F, float G, float H)
+		{
+			return E * (t * t * t) + F * (t * t) + G * t + H;
+		}
+
+		float cubicBezier(float x, float a, float b, float c, float d)
+		{
+			float y0a = 0.0f;
+			float x0a = 0.0f;
+			float y1a = b;   
+			float x1a = a; 
+			float y2a = d;
+			float x2a = c;
+			float y3a = 1.0f;
+			float x3a = 1.0f;
+
+			float A = x3a - 3 * x2a + 3 * x1a - x0a;
+			float B = 3 * x2a - 6 * x1a + 3 * x0a;
+			float C = 3 * x1a - 3 * x0a;   
+			float D = x0a;
+
+			float E = y3a - 3 * y2a + 3 * y1a - y0a;    
+			float F = 3 * y2a - 6 * y1a + 3 * y0a;             
+			float G = 3 * y1a - 3 * y0a;             
+			float H = y0a;
+
+			// Solve for t given x (using Newton-Raphelson), then solve for y given t.
+			// Assume for the first guess that t = x.
+			float currentt = x;
+			int iteration = 5;
+			for (int i = 0; i < iteration; i++)
+			{
+				float currentx = xFromT(currentt, A, B, C, D); 
+				float currentslope = slopeFromT(currentt, A, B, C);
+				currentt -= (currentx - x) * (currentslope);
+				currentt = clamp(currentt, 0, 1);
+			} 
+
+			float res = yFromT(currentt, E, F, G, H);
+			return res;
+		}
+
+		float cubicBezierThroughTwoPoints(float x, float a, float b, float c, float d)
+		{
+			float minA = 0.0f + EPSILON;
+			float maxA = 1.0f - EPSILON;
+			float minB = 0.0f + EPSILON;
+			float maxB = 1.0f - EPSILON;
+			float res = 0;
+
+			a = max(minA, min(maxA, a));
+			b = max(minB, min(maxB, b));
+
+			float x0 = 0;  
+			float y0 = 0;
+			float x4 = a;  
+			float y4 = b;
+			float x5 = c;  
+			float y5 = d;
+			float x3 = 1;  
+			float y3 = 1;
+			float x1, y1, x2, y2;
+
+			// arbitrary but reasonable 
+			// t-values for interior control points
+			float t1 = 0.3f;
+			float t2 = 0.7f;
+
+			float B0t1 = (1 - t1) * (1 - t1) * (1 - t1);
+			float B1t1 = 3.0f * t1 * (1 - t1) * (1 - t1);
+			float B2t1 = 3.0f * t1 * t1 * (1 - t1);
+			float B3t1 = t1 * t1 * t1;
+			float B0t2 = (1 - t2) * (1 - t2) * (1 - t2);
+			float B1t2 = 3.0f * t2 * (1 - t2) * (1 - t2);
+			float B2t2 = 3.0f * t2 * t2 * (1 - t2);
+			float B3t2 = t2 * t2 * t2;
+
+			float ccx = x4 - x0 * B0t1 - x3 * B3t1;
+			float ccy = y4 - y0 * B0t1 - y3 * B3t1;
+			float ffx = x5 - x0 * B0t2 - x3 * B3t2;
+			float ffy = y5 - y0 * B0t2 - y3 * B3t2;
+
+			x2 = (ccx - (ffx * B1t1) / B1t2) / (B2t1 - (B1t1 * B2t2) / B1t2);
+			y2 = (ccy - (ffy * B1t1) / B1t2) / (B2t1 - (B1t1 * B2t2) / B1t2);
+			x1 = (ccx - x2 * B2t1) / B1t1;
+			y1 = (ccy - y2 * B2t1) / B1t1;
+
+			x1 = max(0 + EPSILON, min(1 - EPSILON, x1));
+			x2 = max(0 + EPSILON, min(1 - EPSILON, x2));
+
+			res = cubicBezier(x, x1, y1, x2, y2);
+			res = max(0, min(1, y));
+
+			return res;
+		}
+	};
+};

+ 93 - 0
Source/bsf/Data/Raw/Shaders/Includes/SpriteCommon.bslinc

@@ -0,0 +1,93 @@
+mixin SpriteCommon
+{
+	#ifndef ANIMATED
+		#define ANIMATED 0
+	#endif
+
+	#ifndef TRANSPARENCY
+		#define TRANSPARENCY 0
+	#endif
+
+	#ifndef UV
+		#define UV 0
+	#endif
+
+	#if TRANSPARENCY
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			#if TRANSPARENCY == 1
+			color = { srcA, srcIA, add };
+			#else
+			color = { one, srcIA, add };
+			#endif
+			
+			#if ALPHA
+			writemask = A;
+			#else
+			writemask = RGB;
+			#endif
+		};
+	};	
+	#endif
+	
+	#if ALPHA
+	stencil
+	{
+		enabled = true;
+		readmask = 0x1;
+		writemask = 0x1;
+		
+		front = { keep, keep, inc, eq };
+		reference = 0x0;
+	};
+	#endif
+
+	depth
+	{
+		read = false;
+		write = false;
+	};
+	
+	code
+	{
+		cbuffer GUIParams
+		{
+			float4x4 gWorldTransform;
+			float gInvViewportWidth;
+			float gInvViewportHeight;
+			float gViewportYFlip;
+			float4 gTint;
+			float4 gUVSizeOffset;
+		}	
+
+		void vsmain(
+			in float3 inPos : POSITION
+			#if UV
+			, in float2 uv : TEXCOORD0
+			#endif
+			, out float4 oPosition : SV_Position
+			#if UV
+			, out float2 oUv : TEXCOORD0
+			#endif
+			)
+		{
+			float4 tfrmdPos = mul(gWorldTransform, float4(inPos.xy, 0, 1));
+
+			float tfrmdX = -1.0f + (tfrmdPos.x * gInvViewportWidth);
+			float tfrmdY = (1.0f - (tfrmdPos.y * gInvViewportHeight)) * gViewportYFlip;
+
+			oPosition = float4(tfrmdX, tfrmdY, 0, 1);
+			
+			#if UV
+			#if ANIMATED
+			oUv = uv * gUVSizeOffset.xy + gUVSizeOffset.zw;
+			#else
+			oUv = uv;
+			#endif
+			#endif
+		}
+	};
+};

+ 30 - 0
Source/bsf/Data/Raw/Shaders/Includes/SurfaceData.bslinc

@@ -0,0 +1,30 @@
+mixin SurfaceData
+{
+	code
+	{
+		struct SurfaceData
+		{
+			float4 albedo;
+			float4 worldNormal;
+			float depth;
+			float roughness;
+			float metalness;
+			float2 velocity;
+			uint mask;
+		};
+		
+		// Encodes velocity into a format suitable for storing in a 16-bit SNORM texture. 
+		// Velocity range of [-2, 2] is supported (full NDC).
+		float2 encodeVelocity16SNORM(float2 velocity)
+		{
+			return velocity * 0.5f;
+		}
+		
+		// Decodes velocity from an encoded 16-bit SNORM format. See encodeVelocity16SNORM().
+		// Velocity range of [-2, 2] is supported (full NDC).
+		float2 decodeVelocity16SNORM(float2 val)
+		{
+			return val * 2.0f;
+		}
+	};
+};

+ 567 - 0
Source/bsf/Data/Raw/Shaders/Includes/TemporalResolve.bslinc

@@ -0,0 +1,567 @@
+#include "$ENGINE$/PerCameraData.bslinc"
+#include "$ENGINE$/ColorSpace.bslinc"
+#include "$ENGINE$/SurfaceData.bslinc"
+
+mixin TemporalResolve
+{
+	mixin PerCameraData;
+	mixin ColorSpace;
+	mixin SurfaceData;
+
+	code
+	{
+		////////////////// CUSTOMIZATION PARAMETERS /////////////////////////////
+	
+		// When enabled, the system will sample a specific sample from a MS texture. UV coordinates are assumed
+		// to be in pixel space in that case. When disabled sampleIdx parameter is ignored and UV coordinates
+		// are assumed be in standard [0, 1] range.
+		#ifndef MSAA
+			#define MSAA 0
+		#endif
+		
+		// Only relevant when MSAA is enabled. When disabled color textures are assumed to be non-MSAA. When
+		// enabled all textures are assumed to be MSAA.
+		#ifndef MSAA_COLOR
+			#define MSAA_COLOR MSAA
+		#endif
+	
+		// 0 - System will use the velocity of the current pixel
+		// 1 - System will search 4 neighbor pixels in + pattern, and choose the velocity of the pixel nearest 
+		//     to the camera
+		// 2 - System will search 8 surrounding pixels and choose the velocity of the pixel nearest to the camera
+		//
+		// Searching the neighborhod instead of just using current velocity yields nicer edges for objects in 
+		// motion. See TEMPORAL_SEARCH_RADIUS in order to customize how far away to search.
+		//
+		// Only relevant if TEMPORAL_LOCAL_VELOCITY is enabled, since without it no per-object velocity
+		// information is present and everything is blended based on camera movement.
+		#ifndef TEMPORAL_SEARCH_NEAREST
+			#define TEMPORAL_SEARCH_NEAREST 1
+		#endif
+		
+		// Determine how far away to sample pixels when TEMPORAL_SEARCH_NEAREST is enabled. 
+		// 1 - Immediately adjacent pixels are searched
+		// 2 - Pixels two away are searched (looks better than 1)
+		// 3 - etc.
+		#ifndef TEMPORAL_SEARCH_RADIUS
+			#define TEMPORAL_SEARCH_RADIUS 2
+		#endif
+		
+		// 0 - The system will only account for velocity due to camera movement (not due to individual objects)
+		// 1 - The system will account both for velocity due to camera movement, as well as individual object 
+		//     movement. Requires the user to provide a per-pixel velocity buffer.
+		#ifndef TEMPORAL_LOCAL_VELOCITY
+			#define TEMPORAL_LOCAL_VELOCITY 1
+		#endif
+		
+		// If enabled the scene color value will be filtered from multiple pixels, instead of just taking the color
+		// of the center pixel. This can yield better results at the cost of more sampling.
+		#ifndef TEMPORAL_FILTER_COLOR
+			#define TEMPORAL_FILTER_COLOR 1
+		#endif
+		
+		// If enabled, the history pixel will be sampled using a catmull-rom curve. Otherwise a single history
+		// sample will be taken.
+		#ifndef TEMPORAL_BICUBIC_HISTORY
+			#define TEMPORAL_BICUBIC_HISTORY 0
+		#endif
+		
+		// When enabled, the resolve operation will be performed in YCoCg color space. This can yield better
+		// results, requires less color samples and no value clipping.
+		#ifndef TEMPORAL_YCOCG
+			#define TEMPORAL_YCOCG 0
+		#endif
+		
+		// When enabled, green color will be used instead of calculating luminosity. This will yield better
+		// performance but can result in lower quality. Ignored when TEMPORAL_YCOCG is enabled, since luminosity
+		// is already available as part of the YCoCg color space.
+		#ifndef TEMPORAL_GREEN_AS_LUMA
+			#define TEMPORAL_GREEN_AS_LUMA 0
+		#endif
+				
+		// When enabled the input samples will be tonemapped using the provided exposure value. Once the final
+		// value is resolved, it will be scaled back into original range. This ensures high frequency data from
+		// HDR content is removed, as it would cause aliasing otherwise. We scale the result back into high range
+		// so the high-quality tonemap shader can be ran on it.
+		#ifndef TEMPORAL_TONEMAP
+			#define TEMPORAL_TONEMAP 1
+		#endif
+	
+		// When enabled an extra low-pass filter is ran when sampling scene color, for better quality.
+		#ifndef TEMPORAL_LOWPASS
+			#define TEMPORAL_LOWPASS 1
+		#endif
+		
+		// When enabled, clamp/clip color neighborhood will be deduced using standard deviation of all the
+		// neighborhood samples. When disabled a min/max operation is performed instead.
+		#ifndef TEMPORAL_SMOOTH_NEIGHBORHOOD
+			#define TEMPORAL_SMOOTH_NEIGHBORHOOD 1
+		#endif
+		
+		// When enabled, neighborhood clipping will use an AABB intersection to clip the history value. When disabled
+		// just a clamp will be used instead. Not relevant when TEMPORAL_YCOCG is enabled because it always uses a clamp.
+		#ifndef TEMPORAL_CLIP_AABB
+			#define TEMPORAL_CLIP_AABB 1
+		#endif
+		
+		// Determines how is the history value blended with the current value.
+		// 0 - The system will calculate the optimal blend value automatically
+		// >0 - A fixed blend factor will be used, equal to the multiplicative inverse of the provided value.
+		//      (i.e. a value of 8 will result in blend factor of 1/8, meaning 12.5% of the history value will be used)
+		#ifndef TEMPORAL_BLEND_FACTOR
+			#define TEMPORAL_BLEND_FACTOR 0
+		#endif
+		
+		// Determines how many frames should pixels deemed as "bad" (too different from current pixel) contribute to the
+		// current frame.
+		#ifndef TEMPORAL_BAD_RETENTION
+			#define TEMPORAL_BAD_RETENTION 3
+		#endif
+		
+		// Determines how many frames should pixels deemed as "good" (similar to the current pixel) contribute to the
+		// current frame.
+		#ifndef TEMPORAL_GOOD_RETENTION
+			#define TEMPORAL_GOOD_RETENTION 10
+		#endif
+	
+		////////////////////////// HELPER MACROS /////////////////////////
+		#if MSAA
+			#define _TEX2D(n) Texture2DMS<float> n
+			#if MSAA_COLOR
+				#define _TEXCOLOR(n) Texture2DMS<float4> n
+				#define _PTEXCOLOR(n) n
+			#else
+				#define _TEXCOLOR(n) Texture2D n, SamplerState n##SampState, float4 n##TexelSize
+				#define _PTEXCOLOR(n) n, n##SampState, n##TexelSize
+			#endif
+			
+			#define _PTEX2D(n) n
+			#define _SAMPLE(n, uv) n.Load((int2)uv, sampleIdx)
+			#define _SAMPLEOFF(n, uv, offset) n.Load((int2)(uv) + offset, sampleIdx)
+			
+			#if MSAA_COLOR
+				#define _SAMPLECOL(n, uv, offset) _SAMPLEOFF(n, uv, offset)
+			#else
+				#define _SAMPLECOL(n, uv, offset) n.Sample(n##SampState, uv, offset)
+			#endif
+			
+			#define _PIXSIZE(n) int2(1, 1)
+		#else
+			#define _TEX2D(n) Texture2D n, SamplerState n##SampState, float4 n##TexelSize
+			#define _TEXCOLOR(n) _TEX2D(n)
+			#define _PTEX2D(n) n, n##SampState, n##TexelSize
+			#define _PTEXCOLOR(n) n, n##SampState, n##TexelSize
+			#define _SAMPLE(n, uv) n.Sample(n##SampState, uv)
+			#define _SAMPLEOFF(n, uv, offset) n.Sample(n##SampState, uv, offset)
+			#define _SAMPLECOL(n, uv, offset) _SAMPLEOFF(n, uv, offset)
+			#define _PIXSIZE(n) n##TexelSize.xy
+		#endif
+		
+		///////////////////////// HELPER FUNCTIONS ////////////////////////
+		float3 findNearest3x3(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
+		{
+			int r = TEMPORAL_SEARCH_RADIUS;
+			float3 dmin = float3(0, 0, 1);
+			
+			[unroll]
+			for(int y = -r; y <= r; y += r)
+			{
+				[unroll]
+				for(int x = -r; x <= r; x += r)
+				{
+					float depth = _SAMPLEOFF(sceneDepth, uv, int2(x, y)).x;
+					dmin = depth < dmin.z ? float3(x, y, depth) : dmin;	
+				}
+			}
+			
+			return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
+		}
+		
+		float3 findNearestCross(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
+		{
+			int r = TEMPORAL_SEARCH_RADIUS;
+			float3 dmin = float3(0, 0, 1);
+			
+			{
+				float depth = _SAMPLE(sceneDepth, uv).x;
+				dmin = depth < dmin.z ? float3(0, 0, depth) : dmin;	
+			}
+			
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, int2(-r, 0)).x;
+				dmin = depth < dmin.z ? float3(-r, 0, depth) : dmin;	
+			}
+			
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, int2(r, 0)).x;
+				dmin = depth < dmin.z ? float3(r, 0, depth) : dmin;	
+			}
+
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, -r)).x;
+				dmin = depth < dmin.z ? float3(0, -r, depth) : dmin;	
+			}
+
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, r)).x;
+				dmin = depth < dmin.z ? float3(0, r, depth) : dmin;	
+			}			
+			
+			return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
+		}
+		
+		float clipAABB(float3 boxMin, float3 boxMax, float3 history, float3 current)
+		{
+			// Note: Is this necessary? Will "current" always be in the box?
+			boxMin = min(current, boxMin);
+			boxMax = max(current, boxMax);
+			
+			float3 center = (boxMax + boxMin) * 0.5f;
+			float3 extents = boxMax - center;
+			
+			float3 origin = history - center; // Relative to box
+			float3 dir = current - history;
+			
+			float3 rDir = rcp(dir);
+			float3 tNeg = (extents - origin) * rDir;
+			float3 tPos = (-extents - origin) * rDir;
+			
+			return saturate(max(max(min(tNeg.x, tPos.x), min(tNeg.y, tPos.y)), min(tNeg.z, tPos.z)));
+		}
+		
+		void catmullRom2D(float2 uv, float2 size, out float2 samples[3], out float2 weights[3])
+		{
+			uv *= size;
+
+			float2 pixelCenter = floor(uv - 0.5f) + 0.5f;
+			float2 f = uv - pixelCenter;
+			float2 f2 = f * f;
+			float2 f3 = f2 * f;
+
+			float2 w0 = f2 - 0.5f * (f3 + f);
+			float2 w1 = 1.5f * f3 - 2.5f * f2 + 1.0f;
+			float2 w3 = 0.5f * (f3 - f2);
+			float2 w2 = 1.0f - w0 - w1 - w3;
+
+			weights[0] = w0;
+			weights[1] = w1 + w2;
+			weights[2] = w3;
+
+			samples[0] = pixelCenter - 1.0f;
+			samples[1] = pixelCenter + w2 / weights[1];
+			samples[2] = pixelCenter + 2.0f;
+		 
+			float2 invSize = 1.0f / size;
+			samples[0] *= invSize;
+			samples[1] *= invSize;
+			samples[2] *= invSize;
+		}
+
+		////////////////////// HELPER TONEMAP/COLOR SPACE DEFINES /////////////////////
+		// Automatically scale HDR values based on luminance, if enabled
+		#if TEMPORAL_TONEMAP
+			#if TEMPORAL_YCOCG
+				#define _TONEMAP_COLOR(v) HDRScaleY(v, exposureScale)
+			#elif TEMPORAL_GREEN_AS_LUMA
+				#define _TONEMAP_COLOR(v) HDRScaleG(v, exposureScale)
+			#else
+				#define _TONEMAP_COLOR(v) HDRScaleRGB(v, exposureScale)
+			#endif
+		#else // TEMPORAL_TONEMAP
+			#define _TONEMAP_COLOR(v) v
+		#endif // TEMPORAL_TONEMAP
+		
+		// Converts scene color to YCoCg space (if enabled) and applies tonemapping
+		float4 convertColor(
+			float4 color
+			#if TEMPORAL_TONEMAP
+			, float exposureScale
+			#endif // TEMPORAL_TONEMAP
+		)
+		{
+			#if TEMPORAL_YCOCG
+				color.rgb = _TONEMAP_COLOR(RGBToYCoCg(color.rgb));
+			#else
+				color.rgb = _TONEMAP_COLOR(color.rgb);
+			#endif
+			
+			return color;
+		}
+		
+		#if TEMPORAL_TONEMAP
+			#define _SAMPLE_COLOR(n, uv, offset) convertColor(_SAMPLECOL(n, uv, offset), exposureScale)
+		#else
+			#define _SAMPLE_COLOR(n, uv, offset) convertColor(_SAMPLECOL(n, uv, offset))
+		#endif
+		
+		///////////////////////////// MAIN /////////////////////////////////
+		[internal]
+		cbuffer TemporalInput
+		{
+			float gSampleWeights[9];
+			float gSampleWeightsLowpass[9];
+		}
+		
+		float4 temporalResolve(
+			_TEX2D(sceneDepth), 
+			_TEXCOLOR(sceneColor), 
+			_TEXCOLOR(prevColor), 
+			#if TEMPORAL_LOCAL_VELOCITY
+			_TEX2D(velocityBuffer),
+			#endif // TEMPORAL_LOCAL_VELOCITY
+			#if TEMPORAL_TONEMAP
+			float exposureScale,
+			#endif // TEMPORAL_TONEMAP
+			float2 uv,
+			float2 ndcPos, // Can be derived from UV, but we usually have it for free, so pass it directly
+			int sampleIdx)
+		{
+			///////////// DETERMINE PER-PIXEL VELOCITY & CURRENT DEPTH ///////////////////
+			float curDepth;
+			float2 velocity;
+			#if TEMPORAL_LOCAL_VELOCITY
+				#if TEMPORAL_SEARCH_NEAREST == 1
+					float3 nearest = findNearestCross(_PTEX2D(sceneDepth), uv, sampleIdx);
+					velocity = _SAMPLE(velocityBuffer, nearest.xy);
+					curDepth = nearest.z;
+				#elif TEMPORAL_SEARCH_NEAREST == 2
+					float3 nearest = findNearest3x3(_PTEX2D(sceneDepth), uv, sampleIdx);
+					velocity = _SAMPLE(velocityBuffer, nearest.xy);
+					curDepth = nearest.z;
+				#else // TEMPORAL_SEARCH_NEAREST
+					velocity = _SAMPLE(velocityBuffer, uv);
+					curDepth = _SAMPLE(sceneDepth, uv).x;
+				#endif // TEMPORAL_SEARCH_NEAREST
+			#else // TEMPORAL_LOCAL_VELOCITY
+				velocity = 0;
+				curDepth = _SAMPLE(sceneDepth, uv).x;
+			#endif // TEMPORAL_LOCAL_VELOCITY
+			
+			///////////////////// DETERMINE PREV. FRAME UV //////////////////////////////
+			float2 prevNdcPos;
+			bool hasLocalVelocity = (abs(velocity.x) + abs(velocity.y)) > 0;
+			if(hasLocalVelocity)
+			{
+				velocity = decodeVelocity16SNORM(velocity);
+				prevNdcPos = float2(ndcPos - velocity);
+			}
+			else
+			{
+				// Assumes velocity due to camera movement
+				float4 currentNDC = float4(ndcPos, curDepth, 1);
+				float4 prevClip = mul(gNDCToPrevNDC, currentNDC);
+				prevNdcPos = prevClip.xy / prevClip.w;
+			}
+			
+			#if MSAA && MSAA_COLOR
+			float2 prevUV = NDCToScreen(prevNdcPos);
+			#else
+			float2 prevUV = NDCToUV(prevNdcPos);
+			#endif
+			
+			/////////////// GET FILTERED COLOR VALUE AND NEIGHBORHOOD MIN/MAX /////////////
+			#if MSAA && !MSAA_COLOR
+			float2 uvColor = uv * sceneColorTexelSize.xy;
+			#else
+			float2 uvColor = uv;
+			#endif
+			
+			#if TEMPORAL_YCOCG
+			// YCOCG only requires a + pattern for good quality
+			float4 neighbor[5];
+			neighbor[0] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1,  0));
+			neighbor[1] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, -1));
+			neighbor[2] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0,  0));
+			neighbor[3] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1,  0));
+			neighbor[4] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0,  1));
+			
+			float4 filtered = 0;
+			
+			#if TEMPORAL_FILTER_COLOR
+			// TODO - Do a border check? If offscreen just use the center sample, or
+			// do the border check per-sample
+			
+			[unroll]
+			for(uint i = 0; i < 5; ++i)
+				filtered += neighbor[i] * gSampleWeights[i];
+			#else // TEMPORAL_FILTER_COLOR
+				filtered = neighbor[2];
+			#endif // TEMPORAL_FILTER_COLOR
+			
+			float4 filteredLow = filtered;
+			
+			float4 neighborMin = min(min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])), 
+				neighbor[4]);
+				
+			float4 neighborMax = max(max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])), 
+				neighbor[4]);
+			
+			#else // TEMPORAL_YCOCG
+			float4 neighbor[9];
+			neighbor[0] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1, -1));
+			neighbor[1] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, -1));
+			neighbor[2] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1, -1));
+			neighbor[3] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1,  0));
+			neighbor[4] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0,  0));
+			neighbor[5] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1,  0));
+			neighbor[6] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1,  1));
+			neighbor[7] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0,  1));
+			neighbor[8] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1,  1));
+			
+			float4 filtered = 0;
+			float4 filteredLow = 0;
+			
+			#if TEMPORAL_FILTER_COLOR
+			// TODO - Do a border check? If offscreen just use the center sample, or
+			// do the border check per-sample
+			
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				filtered += neighbor[i] * gSampleWeights[i];
+				
+			#if TEMPORAL_LOWPASS
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				filteredLow += neighbor[i] * gSampleWeightsLowpass[i];
+			#else // TEMPORAL_LOWPASS
+				filteredLow = filtered;
+			#endif // TEMPORAL_LOWPASS
+				
+			#else // TEMPORAL_FILTER_COLOR
+				filtered = neighbor[4];
+				filteredLow = neighbor[4];
+			#endif // TEMPORAL_FILTER_COLOR
+
+			#if TEMPORAL_SMOOTH_NEIGHBORHOOD
+			// Calculate standard deviation and determine neighborhood min/max based on it
+			float4 mean = 0;
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				mean += neighbor[i];
+			
+			mean /= 9.0f;
+			
+			float4 meanSqrd = 0;
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				meanSqrd += neighbor[i] * neighbor[i];
+			
+			meanSqrd /= 9.0f;
+			
+			float4 stdDev = sqrt(abs(meanSqrd - mean * mean));
+			float4 neighborMin = mean - stdDev;
+			float4 neighborMax = mean + stdDev;
+			
+			#else // TEMPORAL_SMOOTH_NEIGHBORHOOD
+			float4 neighborMin = min(min(
+				min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])), 
+				min(min(neighbor[4], neighbor[5]), min(neighbor[6], neighbor[7]))), 
+				neighbor[8]);
+				
+			float4 neighborMax = max(max(
+				max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])), 
+				max(max(neighbor[4], neighbor[5]), max(neighbor[6], neighbor[7]))), 
+				neighbor[8]);
+			
+			#endif // TEMPORAL_SMOOTH_NEIGHBORHOOD
+			#endif // TEMPORAL_YCOCG
+			
+			/////////////////// GET PREVIOUS FRAME COLOR ///////////////////////
+			#if TEMPORAL_BICUBIC_HISTORY && !MSAA_COLOR
+				float2 weights[3];
+				float2 samples[3];
+				catmullRom2D(prevUV.xy, prevColorTexelSize.zw, samples, weights);
+
+				float4 prevColorVal = 0.0f;
+				[unroll]
+				for(int y = 0; y < 3; y++)
+				{
+					[unroll]
+					for(int x = 0; x < 3; x++)
+					{
+						float2 sampleUV = float2(samples[x].x, samples[y].y);
+						float sampleWeight = weights[x].x * weights[y].y;
+					
+						prevColorVal += _SAMPLE_COLOR(prevColor, sampleUV, int2(0, 0)) * sampleWeight;
+					}
+				}
+			#else
+				float4 prevColorVal = _SAMPLE_COLOR(prevColor, prevUV, int2(0, 0));
+			#endif
+
+			///////////////////// CLAMP TO NEIGHBORHOOD ////////////////////////
+			// Clamping to neighborhood ensures we don't blend with values that are too
+			// different, which can happen when history data becomes invalid.
+			#if TEMPORAL_YCOCG
+				prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
+			#else // TEMPORAL_YCOCG
+				// Uses low-pass to reduce flickering
+				#if TEMPORAL_CLIP_AABB
+					float clipT = clipAABB(neighborMin.rgb, neighborMax.rgb, prevColorVal.rgb, filteredLow.rgb);
+					prevColorVal = prevColorVal + clipT * (filteredLow - prevColorVal);
+				#else // TEMPORAL_CLIP_AABB
+					prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
+				#endif // TEMPORAL_CLIP_AABB
+			#endif // TEMPORAL_YCOCG
+			
+			//////////////// BLEND BETWEEN CURRENT AND HISTORY //////////////////
+			// Find out how much impact should the previous frame's color have
+			#if TEMPORAL_BLEND_FACTOR // Fixed blend factor
+				float blendAmount = 1.0f / TEMPORAL_BLEND_FACTOR;
+				float4 output = lerp(prevColorVal, filtered, blendAmount);
+			#else // TEMPORAL_BLEND_FACTOR
+				#if TEMPORAL_YCOCG
+					float lumaCurrent = filtered.r;
+					float lumaHistory = prevColorVal.r;
+				#else // TEMPORAL_YCOCG
+					#if TEMPORAL_GREEN_AS_LUMA
+						float lumaCurrent = filtered.g;
+						float lumaHistory = prevColorVal.g;
+					#else // TEMPORAL_GREEN_AS_LUMA
+						float lumaCurrent = LuminanceRGB(filtered);
+						float lumaHistory = LuminanceRGB(prevColorVal);
+					#endif // TEMPORAL_GREEN_AS_LUMA
+				#endif // TEMPORAL_YCOCG
+			
+				// Based on T. Lottes: https://www.youtube.com/watch?v=WzpLWzGvFK4&t=18m
+				float blendWeight = 1.0f - abs(lumaCurrent - lumaHistory) / max(max(lumaCurrent, lumaHistory), 0.001f);
+				
+				float weightBad = 1.0f - 1.0f / TEMPORAL_BAD_RETENTION;
+				float weightGood = 1.0f - 1.0f / TEMPORAL_GOOD_RETENTION;
+				
+				float blendAmount = lerp(weightBad, weightGood, blendWeight * blendWeight);
+				float4 output = lerp(filtered, prevColorVal, blendAmount);
+			#endif // TEMPORAL_BLEND_FACTOR
+			
+			//////// UNDO TONEMAP & MOVE BACK TO RGB SPACE //////////////////////
+			#if TEMPORAL_TONEMAP
+				#if TEMPORAL_YCOCG
+					output.rgb = HDRScaleYInv(output.rgb, exposureScale);
+				#elif TEMPORAL_GREEN_AS_LUMA
+					output.rgb = HDRScaleGInv(output.rgb, exposureScale);
+				#else
+					output.rgb = HDRScaleRGBInv(output.rgb, exposureScale);
+				#endif
+			#endif // TEMPORAL_TONEMAP
+			
+			#if TEMPORAL_YCOCG
+				output.rgb = YCoCgToRGB(output.rgb);
+			#endif // TEMPORAL_YCOCG			
+			
+			// Note: Potential improvements:
+			//  - Add a sharpen step
+			//  - Properly handle borders when sampling neighbors
+			//  - Better blend amount calculation? (Needs experimentation)
+			
+			return output;
+		}
+		
+		#undef _TEX2D
+		#undef _PTEX2D
+		#undef _SAMPLE
+		#undef _PIXSIZE
+		#undef _TONEMAP_COLOR
+		#undef _TONEMAP_COLOR_INV
+		#undef _RESOLVE_COLOR
+	};
+};

+ 215 - 0
Source/bsf/Data/Raw/Shaders/Includes/VertexCommon.bslinc

@@ -0,0 +1,215 @@
+mixin VertexCommon
+{
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_Position;
+			float2 uv0 : TEXCOORD0;
+			
+			#if LIGHTING_DATA
+				float3 worldPosition : TEXCOORD1;
+				float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
+				float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
+			#endif
+			
+			#if CLIP_POS
+				float4 clipPos : TEXCOORD2;
+			#endif
+						
+			#if PREV_CLIP_POS
+				float4 prevClipPos : TEXCOORD3;
+			#endif
+		};
+
+		struct VertexInput
+		{
+			#if POSITION_2D
+				float2 position : POSITION;
+			#else
+				float3 position : POSITION;
+			#endif
+			
+			float2 uv0 : TEXCOORD0;
+			
+			#if LIGHTING_DATA
+				float3 normal : NORMAL; // Note: Half-precision could be used
+				float4 tangent : TANGENT; // Note: Half-precision could be used
+			#endif
+			
+			#if SKINNED
+				uint4 blendIndices : BLENDINDICES;
+				float4 blendWeights : BLENDWEIGHT;
+			#endif
+			
+			#if MORPH
+				float3 deltaPosition : POSITION1;
+				float4 deltaNormal : NORMAL1;
+			#endif				
+		};
+		
+		// Vertex input containing only position data
+		struct VertexInput_PO
+		{
+			#if POSITION_2D
+				float2 position : POSITION;
+			#else
+				float3 position : POSITION;
+			#endif
+			
+			#if SKINNED
+				uint4 blendIndices : BLENDINDICES;
+				float4 blendWeights : BLENDWEIGHT;
+			#endif			
+			
+			#if MORPH
+				float3 deltaPosition : POSITION1;
+			#endif	
+		};			
+		
+		struct VertexIntermediate
+		{
+			#if SKINNED
+				float3x4 blendMatrix;
+				
+				#if PREV_CLIP_POS
+					float3x4 prevBlendMatrix;
+				#endif
+			#endif
+		
+			#if LIGHTING_DATA
+				float3 worldNormal; // Note: Half-precision could be used
+				float4 worldTangent; // Note: Half-precision could be used
+			#endif
+		};
+		
+		#if SKINNED
+		Buffer<float4> boneMatrices;
+		Buffer<float4> prevBoneMatrices;
+		
+		float3x4 getBoneMatrix(uint idx)
+		{
+			float4 row0 = boneMatrices[idx * 3 + 0];
+			float4 row1 = boneMatrices[idx * 3 + 1];
+			float4 row2 = boneMatrices[idx * 3 + 2];
+			
+			return float3x4(row0, row1, row2);
+		}
+		
+		float3x4 getPrevBoneMatrix(uint idx)
+		{
+			float4 row0 = prevBoneMatrices[idx * 3 + 0];
+			float4 row1 = prevBoneMatrices[idx * 3 + 1];
+			float4 row2 = prevBoneMatrices[idx * 3 + 2];
+			
+			return float3x4(row0, row1, row2);
+		}
+		
+		float3x4 getBlendMatrix(float4 blendWeights, uint4 blendIndices)
+		{
+			float3x4 result = blendWeights.x * getBoneMatrix(blendIndices.x);
+			result += blendWeights.y * getBoneMatrix(blendIndices.y);
+			result += blendWeights.z * getBoneMatrix(blendIndices.z);
+			result += blendWeights.w * getBoneMatrix(blendIndices.w);
+			
+			return result;
+		}
+		
+		float3x4 getPrevBlendMatrix(float4 blendWeights, uint4 blendIndices)
+		{
+			float3x4 result = blendWeights.x * getPrevBoneMatrix(blendIndices.x);
+			result += blendWeights.y * getPrevBoneMatrix(blendIndices.y);
+			result += blendWeights.z * getPrevBoneMatrix(blendIndices.z);
+			result += blendWeights.w * getPrevBoneMatrix(blendIndices.w);
+			
+			return result;
+		}
+		#endif
+		
+		#if LIGHTING_DATA
+		float3x3 getTangentToLocal(VertexInput input, out float tangentSign
+			#if SKINNED
+			, float3x4 blendMatrix
+			#endif
+			)
+		{
+			float3 normal = input.normal * 2.0f - 1.0f;
+			float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
+			
+			#if MORPH
+				float3 deltaNormal = (input.deltaNormal.xyz * 2.0f - 1.0f) * 2.0f;
+				normal = normalize(normal + deltaNormal * input.deltaNormal.w);
+				tangent = normalize(tangent - dot(tangent, normal) * normal);
+			#endif
+			
+			#if SKINNED
+				normal = mul(blendMatrix, float4(normal, 0.0f)).xyz;
+				tangent = mul(blendMatrix, float4(tangent, 0.0f)).xyz;
+			#endif
+			
+			tangentSign = input.tangent.w < 0.5f ? -1.0f : 1.0f;
+			float3 bitangent = cross(normal, tangent) * tangentSign;
+			tangentSign *= gWorldDeterminantSign;
+			
+			// Note: Maybe it's better to store everything in row vector format?
+			float3x3 result = float3x3(tangent, bitangent, normal);
+			result = transpose(result);
+											
+			return result;
+		}
+		
+		float3 calcWorldNormal(VStoFS input, float3 surfaceNormal)
+		{
+			float3 tangentToWorldX = input.tangentToWorldX.xyz;
+			float3 tangentToWorldZ = input.tangentToWorldZ;
+			float3 tangentToWorldY = cross(tangentToWorldZ, tangentToWorldX) * input.tangentToWorldX.w;
+			
+			float3x3 tangentToWorld = float3x3(tangentToWorldX, tangentToWorldY, tangentToWorldZ);
+			
+			// Multiplication order flipped because we stored basis vectors as rows
+			return normalize(mul(surfaceNormal, tangentToWorld));
+		}
+		#endif
+		
+		VertexIntermediate getVertexIntermediate(VertexInput input)
+		{
+			VertexIntermediate result;
+			
+			float tangentSign;
+			#if SKINNED
+				result.blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
+				float3x3 tangentToLocal = getTangentToLocal(input, tangentSign, result.blendMatrix);
+				
+				#if PREV_CLIP_POS
+					result.prevBlendMatrix = getPrevBlendMatrix(input.blendWeights, input.blendIndices);
+				#endif
+			#else
+				float3x3 tangentToLocal = getTangentToLocal(input, tangentSign);
+			#endif
+			
+			#if LIGHTING_DATA
+				float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
+				
+				// Note: Consider transposing these externally, for easier reads
+				result.worldNormal = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
+				result.worldTangent = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
+			#endif
+			
+			return result;
+		}
+				
+		void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result)
+		{
+			result.uv0 = input.uv0;
+			
+			#if LIGHTING_DATA
+				result.tangentToWorldZ = intermediate.worldNormal;
+				result.tangentToWorldX = intermediate.worldTangent;
+			#endif
+			
+			#if CLIP_POS
+				result.clipPos = result.position;
+			#endif
+		}
+	};
+};

+ 67 - 0
Source/bsf/Data/Raw/Shaders/Includes/VertexInput.bslinc

@@ -0,0 +1,67 @@
+#define LIGHTING_DATA 1
+#include "$ENGINE$\VertexCommon.bslinc"
+
+mixin VertexInput
+{
+	mixin VertexCommon;
+
+	#ifndef NO_ANIMATION
+	variations
+	{
+		SKINNED = { false, true };
+		MORPH = { false, true };
+	};
+	#endif
+
+	code
+	{
+		float4 getVertexWorldPosition(VertexInput input, VertexIntermediate intermediate)
+		{
+			#if MORPH
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			#if SKINNED
+				position = float4(mul(intermediate.blendMatrix, position), 1.0f);
+			#endif
+		
+			return mul(gMatWorld, position);
+		}
+		
+		float4 getVertexWorldPosition(VertexInput_PO input)
+		{
+			#if MORPH
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			#if SKINNED
+				float3x4 blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices);
+				position = float4(mul(blendMatrix, position), 1.0f);
+			#endif
+		
+			return mul(gMatWorld, position);
+		}
+		
+		// Note: This can be made optional if velocity buffer isn't required
+		float4 getPrevVertexWorldPosition(VertexInput input, VertexIntermediate intermediate)
+		{
+			#if MORPH
+				float4 position = float4(input.position + input.deltaPosition, 1.0f);
+			#else
+				float4 position = float4(input.position, 1.0f);
+			#endif			
+		
+			#if SKINNED
+				#if PREV_CLIP_POS
+					position = float4(mul(intermediate.prevBlendMatrix, position), 1.0f);
+				#endif
+			#endif
+		
+			return mul(gMatPrevWorld, position);
+		}
+	};
+};

+ 66 - 0
Source/bsf/Data/Raw/Shaders/Includes/VolumeRenderBase.bslinc

@@ -0,0 +1,66 @@
+mixin VolumeRenderBase
+{
+	depth
+	{
+		read = false;
+		write = false;
+	};
+
+	code
+	{
+		struct VStoGS
+		{
+			float4 position : SV_POSITION;
+			float2 uv0 : TEXCOORD0;
+			uint layerIdx : TEXCOORD1;
+		};
+		
+		struct GStoFS
+		{
+			float4 position : SV_POSITION;
+			float2 uv0 : TEXCOORD0;
+			uint layerIdx : SV_RenderTargetArrayIndex;
+		};			
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+			uint layerIdx : SV_InstanceID;
+		};
+		
+		VStoGS vsmain(VertexInput input)
+		{
+			VStoGS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+			output.layerIdx = input.layerIdx;
+			
+			return output;
+		}
+	
+		[maxvertexcount(3)]
+		void gsmain(triangle VStoGS input[3], inout TriangleStream<GStoFS> outStream)
+		{
+			GStoFS vert0;
+			vert0.position = input[0].position;
+			vert0.uv0 = input[0].uv0;
+			vert0.layerIdx = input[0].layerIdx;
+
+			GStoFS vert1;
+			vert1.position = input[1].position;
+			vert1.uv0 = input[1].uv0;
+			vert1.layerIdx = input[1].layerIdx;
+
+			GStoFS vert2;
+			vert2.position = input[2].position;
+			vert2.uv0 = input[2].uv0;
+			vert2.layerIdx = input[2].layerIdx;
+
+			outStream.Append(vert0);
+			outStream.Append(vert1);
+			outStream.Append(vert2);
+		}
+	};
+};

+ 42 - 0
Source/bsf/Data/Raw/Shaders/IrradianceAccumulateCubeSH.bsl

@@ -0,0 +1,42 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+
+shader IrradianceAccumulateCubeSH
+{
+	mixin PPBase;
+	mixin ReflectionCubemapCommon;
+
+	code
+	{
+		#define PI 3.1415926
+	
+		SamplerState gInputSamp;
+		TextureCube gInputTex;
+	
+		[internal]
+		cbuffer Params
+		{
+			uint gCubeFace;
+			uint gCubeMip;
+			float2 gHalfPixel;
+		}			
+				
+		float4 fsmain(VStoFS input) : SV_Target0
+		{		
+			float2 offset[4];
+			offset[0] = float2(gHalfPixel.x, gHalfPixel.y);
+			offset[1] = float2(-gHalfPixel.x, gHalfPixel.y);
+			offset[2] = float2(gHalfPixel.x, -gHalfPixel.y);
+			offset[3] = float2(-gHalfPixel.x, -gHalfPixel.y);
+			
+			float4 sum = gInputTex.SampleLevel(gInputSamp, float3(1, 0, 0), gCubeMip);
+			sum += gInputTex.SampleLevel(gInputSamp, float3(-1, 0, 0), gCubeMip);
+			sum += gInputTex.SampleLevel(gInputSamp, float3(0, 1, 0), gCubeMip);
+			sum += gInputTex.SampleLevel(gInputSamp, float3(0, -1, 0), gCubeMip);
+			sum += gInputTex.SampleLevel(gInputSamp, float3(0, 0, 1), gCubeMip);
+			sum += gInputTex.SampleLevel(gInputSamp, float3(0, 0, -1), gCubeMip);
+			
+			return float4(4 * PI * sum.rgb / max(sum.a, 0.0001f), 0.0f);
+		}
+	};
+};

+ 48 - 0
Source/bsf/Data/Raw/Shaders/IrradianceAccumulateSH.bsl

@@ -0,0 +1,48 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+
+shader IrradianceAccumulateSH
+{
+	mixin PPBase;
+	mixin ReflectionCubemapCommon;
+
+	code
+	{
+		#define PI 3.1415926
+	
+		SamplerState gInputSamp;
+		TextureCube gInputTex;
+	
+		[internal]
+		cbuffer Params
+		{
+			uint gCubeFace;
+			uint gCubeMip;
+			float2 gHalfPixel;
+		}			
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{		
+			float2 offset[4];
+			offset[0] = float2(gHalfPixel.x, gHalfPixel.y);
+			offset[1] = float2(-gHalfPixel.x, gHalfPixel.y);
+			offset[2] = float2(gHalfPixel.x, -gHalfPixel.y);
+			offset[3] = float2(-gHalfPixel.x, -gHalfPixel.y);
+			
+			float4 sum = 0;
+			[unroll]
+			for(int i = 0; i < 4; ++i)
+			{
+				float2 uv = saturate(input.uv0 + offset[i]) * 2.0f - 1.0f;
+				
+				float3 dir = getDirFromCubeFace(gCubeFace, uv);
+				dir = normalize(dir);
+				
+				float4 value = gInputTex.SampleLevel(gInputSamp, dir, gCubeMip);
+				sum += value;
+			}
+			
+			return sum;
+		}
+	};
+};

+ 116 - 0
Source/bsf/Data/Raw/Shaders/IrradianceComputeSH.bsl

@@ -0,0 +1,116 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#include "$ENGINE$\SHCommon.bslinc"
+
+shader IrradianceComputeSH
+{
+	mixin ReflectionCubemapCommon;
+	mixin SHCommon;
+
+	featureset = HighEnd;
+	
+	variations
+	{
+		SH_ORDER = { 3, 5 };
+	};	
+	
+	code
+	{
+		struct SHCoeffsAndWeight
+		{
+			SHVectorRGB coeffs;
+			float weight;
+		};
+	
+		SamplerState gInputSamp;
+		TextureCube gInputTex;
+	
+		RWStructuredBuffer<SHCoeffsAndWeight> gOutput;
+		
+		[internal]
+		cbuffer Params
+		{
+			uint gCubeFace;
+			uint gFaceSize;
+			uint2 gDispatchSize;
+		}			
+		
+		groupshared SHCoeffsAndWeight sCoeffs[TILE_WIDTH * TILE_HEIGHT];
+		
+		[numthreads(TILE_WIDTH, TILE_HEIGHT, 1)]
+		void csmain(
+			uint groupIdx : SV_GroupIndex,
+			uint3 groupId : SV_GroupID,
+			uint3 dispatchThreadId : SV_DispatchThreadID)
+		{
+			SHCoeffsAndWeight data;
+			data.weight = 0;
+			
+			SHZero(data.coeffs.R);
+			SHZero(data.coeffs.G);
+			SHZero(data.coeffs.B);
+			
+			float invFaceSize = 1.0f / gFaceSize;
+		
+			uint2 pixelCoords = dispatchThreadId.xy * PIXELS_PER_THREAD;
+			uint2 pixelCoordsEnd = pixelCoords + uint2(PIXELS_PER_THREAD, PIXELS_PER_THREAD);
+			for(uint y = pixelCoords.y; y < pixelCoordsEnd.y; y++)
+			{
+				for(uint x = pixelCoords.x; x < pixelCoordsEnd.x; x++)
+				{
+					// Ignore pixels out of valid range
+					if (x >= gFaceSize || y >= gFaceSize)
+						break;
+						
+					// Map from [0, size-1] to [-1.0 + invSize, 1.0 - invSize].
+					// (+0.5 in order to sample center of texel)
+					float u = 2.0f * (x + 0.5f) * invFaceSize - 1.0f;
+					float v = 2.0f * (y + 0.5f) * invFaceSize - 1.0f;
+					
+					float3 dir = getDirFromCubeFace(gCubeFace, float2(u, v));
+					dir = normalize(dir);
+					
+					// Need to calculate solid angle (weight) of the texel, as cube face corners have
+					// much smaller solid angle, meaning many of them occupy the same area when projected
+					// on a sphere. Without weighing that area would look too bright.
+					float weight = texelSolidAngle(u, v, invFaceSize);
+					
+					SHVector shBasis = SHBasis(dir);
+					float3 radiance = gInputTex.SampleLevel(gInputSamp, dir, 0).rgb;
+					
+					SHMultiplyAdd(data.coeffs.R, shBasis, radiance.r * weight);
+					SHMultiplyAdd(data.coeffs.G, shBasis, radiance.g * weight);
+					SHMultiplyAdd(data.coeffs.B, shBasis, radiance.b * weight);
+					
+					data.weight += weight;
+				}
+			}
+			
+			sCoeffs[groupIdx] = data;
+			
+			GroupMemoryBarrierWithGroupSync();
+			
+			int numThreads = TILE_WIDTH * TILE_HEIGHT;
+			[unroll]
+			for(int tc = numThreads / 2; tc > 0; tc >>= 1)
+			{
+				if(groupIdx < tc)
+				{
+					SHAdd(sCoeffs[groupIdx].coeffs.R, sCoeffs[groupIdx + tc].coeffs.R);
+					SHAdd(sCoeffs[groupIdx].coeffs.G, sCoeffs[groupIdx + tc].coeffs.G);
+					SHAdd(sCoeffs[groupIdx].coeffs.B, sCoeffs[groupIdx + tc].coeffs.B);
+
+					sCoeffs[groupIdx].weight += sCoeffs[groupIdx + tc].weight;
+				}
+			
+				GroupMemoryBarrierWithGroupSync();
+			}
+			
+			if(groupIdx == 0)
+			{
+				uint faceOffset = gDispatchSize.x * gDispatchSize.y * gCubeFace;
+				uint outputIdx = faceOffset + groupId.y * gDispatchSize.x + groupId.x;
+				gOutput[outputIdx] = sCoeffs[0];
+			}
+		}
+	};
+};

+ 61 - 0
Source/bsf/Data/Raw/Shaders/IrradianceComputeSHFrag.bsl

@@ -0,0 +1,61 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#define SH_ORDER 3
+#include "$ENGINE$\SHCommon.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+
+shader IrradianceComputeSHFrag
+{
+	mixin PPBase;
+	mixin ReflectionCubemapCommon;
+	mixin SHCommon;
+
+	code
+	{
+		SamplerState gInputSamp;
+		TextureCube gInputTex;
+	
+		[internal]
+		cbuffer Params
+		{
+			uint gCubeFace;
+			uint gFaceSize;
+			uint gCoeffEntryIdx;
+			uint gCoeffComponentIdx;
+		}
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			// Move to [-1,1] range
+			float2 uv = input.uv0 * 2.0f - 1.0f;
+			
+			float3 dir = getDirFromCubeFace(gCubeFace, float2(uv.x, uv.y));
+			dir = normalize(dir);
+			
+			// Need to calculate solid angle (weight) of the texel, as cube face corners have
+			// much smaller solid angle, meaning many of them occupy the same area when projected
+			// on a sphere. Without weighing that area would look too bright.
+			float invFaceSize = 1.0f / gFaceSize;
+			float weight = texelSolidAngle(uv.x, uv.y, invFaceSize);
+			
+			SHVector b = SHBasis(dir);
+
+			// Copy to array of float4’s because of a driver bug on macOS that causes
+			// non-const indexing of an array of floats to return incorrect values
+			float4 v[3];
+			v[0] = float4(b.v[0], b.v[1], b.v[2], b.v[3]);
+			v[1] = float4(b.v[4], b.v[5], b.v[6], b.v[7]);
+			v[2] = float4(b.v[8], 0.0f, 0.0f, 0.0f);
+
+			float3 radiance = gInputTex.SampleLevel(gInputSamp, dir, 0).rgb;
+			float shCoeff = v[gCoeffEntryIdx][gCoeffComponentIdx];			
+		
+			float4 output;
+			output.r = shCoeff * radiance.r * weight;
+			output.g = shCoeff * radiance.g * weight;
+			output.b = shCoeff * radiance.b * weight;
+			output.a = weight;
+
+			return output;
+		}
+	};
+};

+ 325 - 0
Source/bsf/Data/Raw/Shaders/IrradianceEvaluate.bsl

@@ -0,0 +1,325 @@
+#if MSAA
+#define MSAA_COUNT 2
+#else
+#define MSAA_COUNT 1
+#endif
+
+#include "$ENGINE$\PPBase.bslinc"
+#define SH_ORDER 3
+#include "$ENGINE$\SHCommon.bslinc"
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader IrradianceEvaluate
+{
+	mixin PPBase;
+	mixin SHCommon;
+	mixin GBufferInput;
+	mixin PerCameraData;
+	
+	variations
+	{
+		MSAA = { true, false };
+		MSAA_RESOLVE_0TH = { true, false };
+		SKY_ONLY = { true, false };
+	};
+	
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			color = { one, one, add };
+		};
+	};	
+	
+	code
+	{
+		#if MSAA_COUNT > 1
+			Texture2DMS<uint> gInputTex;
+		#else
+			Texture2D<uint> gInputTex;
+		#endif
+		
+		struct Tetrahedron
+		{
+			uint4 indices;
+			uint2 offsets[4];
+			float3x4 transform;
+		};
+		
+		struct TetrahedronFace
+		{
+			float4 corners[3];
+			float4 normals[3];
+			uint isQuadratic;
+			float padding[3];
+		};		
+		
+		Texture2D gSHCoeffs;
+		Buffer<uint4> gTetrahedra;
+		Buffer<float4> gTetFaces;
+		
+		TextureCube gSkyIrradianceTex;
+		Texture2D gAmbientOcclusionTex;
+		SamplerState gLinearSamp;
+
+		cbuffer Params
+		{
+			float gSkyBrightness;
+			uint gNumTetrahedra;
+		}
+		
+		Tetrahedron getTetrahedron(uint idx)
+		{
+			Tetrahedron output;
+			
+			uint4 data[6];
+			
+			[unroll]
+			for(int i = 0; i < 6; i++)
+				data[i] = gTetrahedra.Load(idx * 6 + i);
+			
+			output.indices = data[0];
+			output.offsets[0] = data[1].xy;
+			output.offsets[1] = data[1].zw;
+			output.offsets[2] = data[2].xy;
+			output.offsets[3] = data[2].zw;
+			output.transform = float3x4(asfloat(data[3]), asfloat(data[4]), asfloat(data[5]));
+			
+			return output;
+		}
+		
+		TetrahedronFace getTetrahedronFace(uint idx)
+		{
+			TetrahedronFace output;
+			
+			float4 data[7];
+			
+			[unroll]
+			for(int i = 0; i < 7; i++)
+				data[i] = gTetFaces.Load(idx * 7 + i);
+			
+			[unroll]
+			for(int i = 0; i < 3; i++)
+			{
+				output.corners[i] = data[i];
+				output.normals[i] = data[3 + i];
+				output.padding[i] = 0;
+			}
+			
+			output.isQuadratic = asuint(data[6].x);
+			
+			return output;		
+		}
+		
+		float3 getSkyIndirectDiffuse(float3 dir)
+		{
+			return gSkyIrradianceTex.SampleLevel(gLinearSamp, dir, 0).rgb * gSkyBrightness;
+		}
+		
+		float evaluateLambert(SHVector coeffs)
+		{
+			// Multiply irradiance SH coefficients by cosine lobe (Lambert diffuse) and evaluate resulting SH
+			// See: http://cseweb.ucsd.edu/~ravir/papers/invlamb/josa.pdf for derivation of the
+			// cosine lobe factors
+			float output = 0.0f;
+			
+			// Band 0 (factor 1.0)
+			output += coeffs.v[0];
+			
+			// Band 1 (factor 2/3)
+			float f = (2.0f/3.0f);
+			for(int i = 1; i < 4; i++)
+				output += coeffs.v[i] * f;
+			
+			// Band 2 (factor 1/4)
+			f = (1.0f/4.0f);
+			for(int i = 4; i < 9; i++)
+				output += coeffs.v[i] * f;
+
+			return output;
+		}	
+
+		float solveQuadratic(float A, float B, float C)
+		{
+			const float EPSILON = 0.00001f;
+		
+			if (abs(A) > EPSILON)
+			{
+				float p = B / (2 * A);
+				float q = C / A;
+				float D = p * p - q;
+
+				return sqrt(D) - p;
+			}
+			else
+			{
+				if(abs(B) > EPSILON)
+					return -C / B;
+			
+				return 0.0f;
+			}
+		}
+		
+		float solveCubic(float A, float B, float C)
+		{
+			const float EPSILON = 0.00001f;
+			const float THIRD = 1.0f / 3.0f;
+		
+			float sqA = A * A;
+			float p = THIRD * (-THIRD * sqA + B);
+			float q = (0.5f) * ((2.0f / 27.0f) * A * sqA - THIRD * A * B + C);
+
+			float cbp = p * p * p;
+			float D = q * q + cbp;
+			
+			float t;
+			if(D < 0.0f)
+			{
+				float phi = THIRD * acos(-q / sqrt(-cbp));
+				t = (2 * sqrt(-p)) * cos(phi);
+			}
+			else
+			{
+				float sqrtD = sqrt(D);
+				float u = pow(sqrtD + abs(q), 1.0f / 3.0f);
+
+				
+				if (q > 0.0f)
+					t = -u + p / u;
+				else
+					t = u - p / u;
+			}
+			
+			return t - THIRD * A;
+		}
+		
+		float3 calcTriBarycentric(float3 p, float3 a, float3 b, float3 c)
+		{
+			float3 v0 = b - a;
+			float3 v1 = c - a;
+			float3 v2 = p - a;
+			
+			float d00 = dot(v0, v0);
+			float d01 = dot(v0, v1);
+			float d11 = dot(v1, v1);
+			float d20 = dot(v2, v0);
+			float d21 = dot(v2, v1);
+			
+			float denom = d00 * d11 - d01 * d01;
+			float3 coords;
+			coords.x = (d11 * d20 - d01 * d21) / denom;
+			coords.y = (d00 * d21 - d01 * d20) / denom;
+			coords.z = 1.0f - coords.x - coords.y;
+			
+			return coords;
+		}
+		
+		float4 fsmain(VStoFS input
+			#if MSAA_COUNT > 1 && !MSAA_RESOLVE_0TH
+			, uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			uint2 pixelPos = trunc(input.uv0);
+		
+			SurfaceData surfaceData;
+			#if MSAA_COUNT > 1
+				#if MSAA_RESOLVE_0TH
+					surfaceData = getGBufferData(pixelPos, 0);
+				#else
+					surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#endif
+			#else
+				surfaceData = getGBufferData(pixelPos);
+			#endif		
+			
+			float3 irradiance = 0;
+			#if SKY_ONLY
+				irradiance = gSkyIrradianceTex.SampleLevel(gLinearSamp, surfaceData.worldNormal, 0).rgb * gSkyBrightness;
+			#else
+				uint volumeIdx;
+				#if MSAA_COUNT > 1
+					#if MSAA_RESOLVE_0TH
+						volumeIdx = gInputTex.Load(uint3(pixelPos, 0), 0).x;
+					#else
+						volumeIdx = gInputTex.Load(uint3(pixelPos, 0), sampleIdx).x;
+					#endif
+				#else
+					volumeIdx = gInputTex.Load(uint3(pixelPos, 0)).x;
+				#endif
+				
+				if(volumeIdx == 0xFFFF) // Using 16-bit texture, so need to compare like this
+					irradiance = gSkyIrradianceTex.SampleLevel(gLinearSamp, surfaceData.worldNormal, 0).rgb * gSkyBrightness;
+				else
+				{
+					Tetrahedron volume = getTetrahedron(volumeIdx);
+					
+					float3 P = NDCToWorld(input.screenPos, surfaceData.depth);
+					
+					float4 coords;
+					[branch]
+					if(volumeIdx >= gNumTetrahedra)
+					{
+						uint faceIdx = volumeIdx - gNumTetrahedra;
+						TetrahedronFace face = getTetrahedronFace(faceIdx);
+					
+						float3 factors = mul(volume.transform, float4(P, 1.0f));
+						float A = factors.x;
+						float B = factors.y;
+						float C = factors.z;
+
+						float t;
+						if(face.isQuadratic > 0)
+							t = solveQuadratic(A, B, C);
+						else
+							t = solveCubic(A, B, C);
+							
+						float3 triA = face.corners[0].xyz + t * face.normals[0].xyz;
+						float3 triB = face.corners[1].xyz + t * face.normals[1].xyz;
+						float3 triC = face.corners[2].xyz + t * face.normals[2].xyz;
+						
+						float3 bary = calcTriBarycentric(P, triA, triB, triC);
+						
+						coords.x = bary.z;
+						coords.yz = bary.xy;
+						coords.w = 0.0f;
+					}
+					else
+					{
+						float3 factors = mul(volume.transform, float4(P, 1.0f));			
+						coords = float4(factors, 1.0f - factors.x - factors.y - factors.z);
+					}
+
+					SHVectorRGB shCoeffs;
+					SHZero(shCoeffs);
+					
+					for(uint i = 0; i < 4; ++i)
+					{
+						if(coords[i] > 0.0f)
+						{
+							SHVectorRGB coeff = SHLoad(gSHCoeffs, volume.offsets[i]);
+							SHMultiplyAdd(shCoeffs, coeff, coords[i]);
+						}
+					}
+					
+					SHVector shBasis = SHBasis(surfaceData.worldNormal);
+					SHMultiply(shCoeffs.R, shBasis);
+					SHMultiply(shCoeffs.G, shBasis);
+					SHMultiply(shCoeffs.B, shBasis);
+					
+					irradiance.r = evaluateLambert(shCoeffs.R);
+					irradiance.g = evaluateLambert(shCoeffs.G);
+					irradiance.b = evaluateLambert(shCoeffs.B);
+				}
+			#endif // SKY_ONLY
+			
+			float2 uv = NDCToUV(input.screenPos);
+			float ao = gAmbientOcclusionTex.Sample(gLinearSamp, uv);
+			
+			float3 diffuseColor = lerp(surfaceData.albedo.rgb, float3(0.0f, 0.0f, 0.0f), surfaceData.metalness);
+			return float4(irradiance * diffuseColor * ao, 1.0f);
+		}	
+	};
+};

+ 73 - 0
Source/bsf/Data/Raw/Shaders/IrradianceProjectSH.bsl

@@ -0,0 +1,73 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#define SH_ORDER 5
+#include "$ENGINE$\SHCommon.bslinc"
+
+shader IrradianceProjectSH
+{
+	mixin PPBase;
+	mixin ReflectionCubemapCommon;
+	mixin SHCommon;
+
+	code
+	{
+		[internal]
+		cbuffer Params
+		{
+			int gCubeFace;
+		}	
+	
+		Texture2D gSHCoeffs;
+
+		float evaluateLambert(SHVector coeffs)
+		{
+			// Multiply irradiance SH coefficients by cosine lobe (Lambert diffuse) and evaluate resulting SH
+			// See: http://cseweb.ucsd.edu/~ravir/papers/invlamb/josa.pdf for derivation of the
+			// cosine lobe factors
+			float output = 0.0f;
+			
+			// Band 0 (factor 1.0)
+			output += coeffs.v[0];
+			
+			// Band 1 (factor 2/3)
+			float f = (2.0f/3.0f);
+			for(int i = 1; i < 4; i++)
+				output += coeffs.v[i] * f;
+			
+			// Band 2 (factor 1/4)
+			f = (1.0f/4.0f);
+			for(int i = 4; i < 9; i++)
+				output += coeffs.v[i] * f;
+						
+			// Band 3 (factor 0)
+			
+			// Band 4 (factor -1/24)
+			f = (-1.0f/24.0f);
+			for(int i = 16; i < 25; i++)
+				output += coeffs.v[i] * f;
+			
+			return output;
+		}
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float2 scaledUV = input.uv0 * 2.0f - 1.0f;
+			float3 dir = getDirFromCubeFace(gCubeFace, scaledUV);
+			dir = normalize(dir);
+			
+			SHVector shBasis = SHBasis(dir);
+							
+			SHVectorRGB coeffs = SHLoad(gSHCoeffs, int2(0, 0));
+			SHMultiply(coeffs.R, shBasis);
+			SHMultiply(coeffs.G, shBasis);
+			SHMultiply(coeffs.B, shBasis);
+			
+			float3 output = 0;
+			output.r = evaluateLambert(coeffs.R);
+			output.g = evaluateLambert(coeffs.G);
+			output.b = evaluateLambert(coeffs.B);
+			
+			return float4(output.rgb, 1.0f);
+		}	
+	};
+};

+ 76 - 0
Source/bsf/Data/Raw/Shaders/IrradianceReduceSH.bsl

@@ -0,0 +1,76 @@
+#include "$ENGINE$\ReflectionCubemapCommon.bslinc"
+#include "$ENGINE$\SHCommon.bslinc"
+
+shader IrradianceReduceSH
+{
+	mixin ReflectionCubemapCommon;
+	mixin SHCommon;
+
+	featureset = HighEnd;
+	
+	variations
+	{
+		SH_ORDER = { 3, 5 };
+	};	
+	
+	code
+	{
+		#define PI 3.1415926
+	
+		struct SHCoeffsAndWeight
+		{
+			SHVectorRGB coeffs;
+			float weight;
+		};
+
+		StructuredBuffer<SHCoeffsAndWeight> gInput;
+		RWTexture2D<float4> gOutput;
+		
+		[internal]
+		cbuffer Params
+		{
+			uint2 gOutputIdx;
+			uint gNumEntries;
+		}			
+		
+		[numthreads(1, 1, 1)]
+		void csmain(
+			uint groupIdx : SV_GroupIndex,
+			uint groupId : SV_GroupID,
+			uint3 dispatchThreadId : SV_DispatchThreadID)
+		{
+			SHVectorRGB coeffs;
+			float weight = 0;
+			
+			SHZero(coeffs.R);
+			SHZero(coeffs.G);
+			SHZero(coeffs.B);
+			
+			// Note: There shouldn't be many entries, so we add them all in one thread. Otherwise we should do parallel reduction.
+			for(uint i = 0; i < gNumEntries; i++)
+			{
+				SHCoeffsAndWeight current = gInput[i];
+			
+				SHAdd(coeffs.R, current.coeffs.R);
+				SHAdd(coeffs.G, current.coeffs.G);
+				SHAdd(coeffs.B, current.coeffs.B);
+
+				weight += current.weight;
+			}
+			
+			// Normalize
+			float normFactor = (4 * PI) / weight;
+			SHMultiply(coeffs.R, normFactor);
+			SHMultiply(coeffs.G, normFactor);
+			SHMultiply(coeffs.B, normFactor);
+			
+			uint2 writeIdx = gOutputIdx;
+			[unroll]
+			for(int i = 0; i < SH_NUM_COEFFS; ++i)
+			{			
+				gOutput[writeIdx] = float4(coeffs.R.v[i], coeffs.G.v[i], coeffs.B.v[i], 0.0f);
+				writeIdx.x += 1;
+			}
+		}
+	};
+};

+ 161 - 0
Source/bsf/Data/Raw/Shaders/LightGridLLCreation.bsl

@@ -0,0 +1,161 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+#define USE_LIGHT_GRID_INDICES 1
+#include "$ENGINE$\DirectLighting.bslinc"
+#include "$ENGINE$\ImageBasedLighting.bslinc"
+#include "$ENGINE$\LightGridCommon.bslinc"
+
+shader LightGridLLCreation
+{
+	mixin PerCameraData;
+	mixin LightData;
+	mixin LightGridCommon;
+	mixin ImageBasedLighting;
+
+	featureset = HighEnd;
+	
+	code
+	{
+		StructuredBuffer<LightData> gLights;
+		StructuredBuffer<ReflProbeData> gReflectionProbes;
+	
+		RWStructuredBuffer<uint> gLightsCounter;
+		RWStructuredBuffer<uint> gLightsLLHeads;
+		RWBuffer<uint4> gLightsLL;
+		
+		RWStructuredBuffer<uint> gProbesCounter;
+		RWStructuredBuffer<uint> gProbesLLHeads;
+		RWBuffer<uint2> gProbesLL;
+			
+		// Generates a an axis aligned bounding box in NDC and transforms it to view space.
+		// Note: This will overlap other cells, so it might be better to use frustum planes
+		// instead of AABB, although frustum testing procedure could result in more false positive
+		void calcCellAABB(uint3 cellIdx, out float3 center, out float3 extent)
+		{
+			// Note:: AABB calculation in tiled deferred image based lighting shader uses less instructions than this,
+			// see if it can be applied here.
+		
+			// Convert grid XY coordinates to clip coordinates
+			float2 a = 2.0f / gGridSize.xy;
+		
+			float3 ndcMin;
+			float3 ndcMax;
+		
+			ndcMin.xy = cellIdx.xy * a - float2(1.0f, 1.0f);
+			ndcMax.xy = (cellIdx.xy + 1) * a - float2(1.0f, 1.0f);
+		
+			// Flip Y depending on render API, depending if Y in NDC is facing up or down
+			// (We negate the value because we want NDC with Y flipped, so origin is top left)
+			float flipY = -sign(gMatProj[1].y);
+			ndcMin.y *= flipY;
+			ndcMax.y *= flipY;
+		
+			// Because we're viewing along negative Z, farther end is the minimum
+			float viewZMin = calcViewZFromCellZ(cellIdx.z + 1);
+			float viewZMax = calcViewZFromCellZ(cellIdx.z);
+		
+			ndcMin.z = convertToNDCZ(viewZMax);
+			ndcMax.z = convertToNDCZ(viewZMin);
+		
+			float4 corner[8];
+			// Near
+			corner[0] = mul(gMatInvProj, float4(ndcMin.x, ndcMin.y, ndcMin.z, 1.0f));
+			corner[1] = mul(gMatInvProj, float4(ndcMax.x, ndcMin.y, ndcMin.z, 1.0f));
+			corner[2] = mul(gMatInvProj, float4(ndcMax.x, ndcMax.y, ndcMin.z, 1.0f));
+			corner[3] = mul(gMatInvProj, float4(ndcMin.x, ndcMax.y, ndcMin.z, 1.0f));
+		
+			// Far
+			corner[4] = mul(gMatInvProj, float4(ndcMin.x, ndcMin.y, ndcMax.z, 1.0f));
+			corner[5] = mul(gMatInvProj, float4(ndcMax.x, ndcMin.y, ndcMax.z, 1.0f));
+			corner[6] = mul(gMatInvProj, float4(ndcMax.x, ndcMax.y, ndcMax.z, 1.0f));
+			corner[7] = mul(gMatInvProj, float4(ndcMin.x, ndcMax.y, ndcMax.z, 1.0f));
+		
+			[unroll]
+			for(uint i = 0; i < 8; ++i)
+				corner[i].xy /= corner[i].w;
+		
+			float3 viewMin = float3(corner[0].xy, viewZMin);
+			float3 viewMax = float3(corner[0].xy, viewZMax);
+			
+			[unroll]
+			for(uint i = 1; i < 8; ++i)
+			{
+				viewMin.xy = min(viewMin.xy, corner[i].xy);
+				viewMax.xy = max(viewMax.xy, corner[i].xy);
+			}
+			
+			extent = (viewMax - viewMin) * 0.5f;
+			center = viewMin + extent;
+		}
+	
+		[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
+		void csmain(
+			uint3 groupId : SV_GroupID,
+			uint3 groupThreadId : SV_GroupThreadID,
+			uint3 dispatchThreadId : SV_DispatchThreadID)
+		{
+			// Ignore pixels out of valid range
+			if (any(dispatchThreadId.xy >= gGridSize.xy))
+				return;
+				
+			uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+			uint cellIdx = (dispatchThreadId.z * gGridSize.y + dispatchThreadId.y) * gGridSize.x + dispatchThreadId.x;
+			
+			float3 cellCenter;
+			float3 cellExtent;
+			calcCellAABB(dispatchThreadId, cellCenter, cellExtent);
+			
+			for(uint type = 1; type < 3; ++type)
+			{
+				uint lightsStart = gLightStrides[type - 1];
+				uint lightsEnd = lightsStart + gLightCounts[type];
+				for(uint i = lightsStart; i < lightsEnd; ++i)
+				{
+					float4 lightPosition = mul(gMatView, float4(gLights[i].position, 1.0f));
+					float lightRadius = gLights[i].boundRadius;
+					
+					// Calculate distance from box to light
+					float3 distances = max(abs(lightPosition - cellCenter) - cellExtent, 0);
+					float distSqrd = dot(distances, distances);
+					
+					if(distSqrd <= (lightRadius * lightRadius))
+					{
+						uint nextLink;
+						InterlockedAdd(gLightsCounter[0], 1U, nextLink);
+						
+						if(nextLink < maxNumLinks)
+						{
+							uint prevLink;
+							InterlockedExchange(gLightsLLHeads[cellIdx], nextLink, prevLink);
+							
+							gLightsLL[nextLink] = uint4(i, type, prevLink, 0);
+						}
+					}
+				}
+			}
+			
+			for(uint i = 0; i < gNumReflProbes; ++i)
+			{
+				float4 probePosition = mul(gMatView, float4(gReflectionProbes[i].position, 1.0f));
+				float probeRadius = gReflectionProbes[i].radius;
+				
+				// Calculate distance from box to light
+				float3 distances = max(abs(probePosition - cellCenter) - cellExtent, 0);
+				float distSqrd = dot(distances, distances);
+				
+				if(distSqrd <= (probeRadius * probeRadius))
+				{
+					uint nextLink;
+					InterlockedAdd(gProbesCounter[0], 1U, nextLink);
+					
+					if(nextLink < maxNumLinks)
+					{
+						uint prevLink;
+						InterlockedExchange(gProbesLLHeads[cellIdx], nextLink, prevLink);
+						
+						gProbesLL[nextLink] = uint2(i, prevLink);
+					}
+				}
+			}
+		}
+	};
+};

+ 106 - 0
Source/bsf/Data/Raw/Shaders/LightGridLLReduction.bsl

@@ -0,0 +1,106 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\LightGridCommon.bslinc"
+
+shader LightGridLLReduction
+{
+	mixin PerCameraData;
+	mixin LightGridCommon; 
+
+	featureset = HighEnd;
+	
+	code
+	{
+		StructuredBuffer<uint> gLightsLLHeads;
+		Buffer<uint4> gLightsLL;
+					
+		StructuredBuffer<uint> gProbesLLHeads;
+		Buffer<uint2> gProbesLL;
+		
+		RWStructuredBuffer<uint> gGridDataCounter;
+		
+		RWBuffer<uint4> gGridLightOffsetAndSize;
+		RWBuffer<uint> gGridLightIndices;
+
+		RWBuffer<uint2> gGridProbeOffsetAndSize;
+		RWBuffer<uint> gGridProbeIndices;
+		
+		[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, THREADGROUP_SIZE)]
+		void csmain(
+			uint3 groupId : SV_GroupID,
+			uint3 groupThreadId : SV_GroupThreadID,
+			uint3 dispatchThreadId : SV_DispatchThreadID)
+		{
+			// Ignore pixels out of valid range
+			if (any(dispatchThreadId.xy >= gGridSize.xy))
+				return;
+				
+			uint maxNumLinks = gNumCells * gMaxNumLightsPerCell;	
+			uint cellIdx = (dispatchThreadId.z * gGridSize.y + dispatchThreadId.y) * gGridSize.x + dispatchThreadId.x;
+			
+			// Reduce lights
+			//// First count total number of lights affecting the tile
+			uint currentIdx = gLightsLLHeads[cellIdx];
+			uint numRadialLights = 0;
+			uint numSpotLights = 0;
+			while(currentIdx != 0xFFFFFFFF)
+			{
+				uint4 entry = gLightsLL[currentIdx];
+			
+				if(entry.y == 1) // Radial
+					numRadialLights++;
+				else // Spot
+					numSpotLights++;
+
+				currentIdx = entry.z;
+			}
+			
+			//// Allocate enough room and remember the offset to indices
+			uint numLights = numRadialLights + numSpotLights;
+			uint indicesStart;
+			InterlockedAdd(gGridDataCounter[0], numLights, indicesStart);
+			gGridLightOffsetAndSize[cellIdx] = uint4(indicesStart, numRadialLights, numSpotLights, 0);
+			
+			//// Actually write light indices (reverse order, so that radial lights come first, as is the convention)
+			currentIdx = gLightsLLHeads[cellIdx];
+			uint lightIdx = 0;
+			while(currentIdx != 0xFFFFFFFF)
+			{
+				uint4 entry = gLightsLL[currentIdx];
+			
+				gGridLightIndices[indicesStart + numLights - 1 - lightIdx] = entry.x;
+				
+				currentIdx = entry.z;
+				lightIdx++;
+			}
+			
+			// Reduce probes
+			//// First count total number of probes affecting the tile
+			currentIdx = gProbesLLHeads[cellIdx];
+			uint numProbes = 0;
+			while(currentIdx != 0xFFFFFFFF)
+			{
+				uint2 entry = gProbesLL[currentIdx];
+			
+				numProbes++;
+				currentIdx = entry.y;
+			}
+			
+			//// Allocate enough room and remember the offset to indices
+			InterlockedAdd(gGridDataCounter[1], numProbes, indicesStart);
+			gGridProbeOffsetAndSize[cellIdx] = uint2(indicesStart, numProbes);
+			
+			//// Actually write probe indices (reverse order, in order to restore original order since LL was formed in reverse)
+			currentIdx = gProbesLLHeads[cellIdx];
+			uint probeIdx = 0;
+			while(currentIdx != 0xFFFFFFFF)
+			{
+				uint2 entry = gProbesLL[currentIdx];
+			
+				gGridProbeIndices[indicesStart + numProbes - 1 - probeIdx] = entry.x;
+				
+				currentIdx = entry.y;
+				probeIdx++;
+			}
+		}
+	};
+};

+ 49 - 0
Source/bsf/Data/Raw/Shaders/MSAACoverage.bsl

@@ -0,0 +1,49 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader MSAACoverage
+{
+	mixin PPBase;
+	mixin GBufferInput;
+	mixin PerCameraData;
+
+	variations
+	{
+		MSAA_COUNT = { 2, 4, 8 };
+	};	
+	
+	code
+	{	
+		float fsmain(VStoFS input) : SV_Target0
+		{
+			SurfaceData surfaceData[MSAA_COUNT];
+
+			[unroll]
+			for(uint i = 0; i < MSAA_COUNT; ++i)
+				surfaceData[i] = getGBufferData((int2)input.uv0, i);
+
+			float3 albedo = surfaceData[0].albedo.xyz;
+			float3 normal = surfaceData[0].worldNormal.xyz;
+			float depth = surfaceData[0].depth;
+
+			[unroll]
+			for(int i = 1; i < MSAA_COUNT; i++)
+			{
+				float3 otherAlbedo = surfaceData[i].albedo.xyz;
+				float3 otherNormal = surfaceData[i].worldNormal.xyz;
+				float otherDepth = surfaceData[i].depth;
+
+				[branch]
+				if((abs(depth - otherDepth) > 0.01f) || 
+				   (dot(normal, otherNormal) < 0.99f) || 
+				   (abs(dot(albedo - otherAlbedo, float3(1, 1, 1))) > 0.01f))
+				{
+					return 1.0f;
+				}
+			}
+			
+			return 0.0f;			
+		}	
+	};
+};

+ 32 - 0
Source/bsf/Data/Raw/Shaders/MSAACoverageStencil.bsl

@@ -0,0 +1,32 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader MSAACoverageStencil
+{
+	mixin PPBase;
+
+	stencil
+	{
+		enabled = true;
+		reference = 0x80;
+		front = { replace, replace, replace, always };
+		writemask = 0x80;
+	};		
+	
+	code
+	{	
+		Texture2D gMSAACoverage;
+	
+		float fsmain(VStoFS input) : SV_Target0
+		{
+			int2 pixelPos = (int2)input.uv0.xy;
+			float coverage = gMSAACoverage.Load(int3(pixelPos, 0));
+			
+			// Note: Consider checking 2x2 pixel block and only discard if none require per-sample
+			// evaluation. This should speed up HiStencil.
+			if(coverage < 0.5f)
+				discard;
+		
+			return 0.0f;			
+		}	
+	};
+};

+ 72 - 0
Source/bsf/Data/Raw/Shaders/PPBloomClip.bsl

@@ -0,0 +1,72 @@
+#include "$ENGINE$\ColorSpace.bslinc"
+
+shader PPBloomClip
+{
+	mixin ColorSpace;
+
+	variations
+	{
+		AUTO_EXPOSURE = { true, false };
+	};
+	
+	depth
+	{
+		read = false;
+		write = false;
+	};
+
+	code
+	{
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			float2 uv0 : TEXCOORD0;
+			float exposureScale : TEXCOORD1;
+		};
+
+		struct VertexInput
+		{
+			float2 screenPos : POSITION;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		Texture2D gEyeAdaptationTex;
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			VStoFS output;
+		
+			output.position = float4(input.screenPos, 0, 1);
+			output.uv0 = input.uv0;
+			output.exposureScale = gEyeAdaptationTex.Load(int3(0, 0, 0)).r;
+
+			return output;
+		}			
+
+		SamplerState gInputSamp;
+		Texture2D gInputTex;
+				
+		cbuffer Input
+		{
+			float gThreshold;
+			float gManualExposureScale;
+		}				
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float3 sceneColor = gInputTex.Sample(gInputSamp, input.uv0).rgb;		
+			float luminance = LuminanceRGB(sceneColor);
+			
+			#if AUTO_EXPOSURE
+				luminance = luminance * input.exposureScale;
+			#else
+				luminance = luminance * gManualExposureScale;
+			#endif
+			
+			float clippedLuminance = luminance - gThreshold;
+			float bloomMask = saturate(clippedLuminance / 2.0f);
+			
+			return float4(sceneColor * bloomMask, 0.0f);
+		}	
+	};
+};

+ 199 - 0
Source/bsf/Data/Raw/Shaders/PPBokehDOF.bsl

@@ -0,0 +1,199 @@
+#include "$ENGINE$\DepthOfFieldCommon.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader PPBokehDOF
+{
+	mixin DepthOfFieldCommon;
+	mixin PerCameraData;
+
+	variations
+	{
+		DEPTH_OCCLUSION = { true, false };
+	};
+	
+	depth
+	{
+		write = false;
+		read = false;
+	};
+	
+	blend
+	{
+		target	
+		{
+			enabled = true;
+			color = { one, one, add };
+			alpha = { one, one, add };
+		};
+	};
+
+	code
+	{
+		cbuffer Params
+		{
+			uint2 gTileCount;
+			float2 gInvInputSize;
+			float2 gInvOutputSize;
+			float gAdaptiveThresholdColor;
+			float gAdaptiveThresholdCOC;
+			float2 gBokehSize;
+			uint gLayerPixelOffset;
+			float gInvDepthRange;
+		};
+		
+		Texture2D gInputTex;
+		SamplerState gInputSampler
+		{
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+			AddressW = CLAMP;
+		};
+	
+		struct VStoFS
+		{
+			float4 position : SV_POSITION;
+			noperspective float2 uv0 : TEXCOORD0;
+			nointerpolation float4 color : TEXCOORD1;
+			
+			#if DEPTH_OCCLUSION
+			float2 screenUV : TEXCOORD2;
+			nointerpolation float depth : TEXCOORD3;
+			#endif
+		};
+
+		struct VertexInput
+		{
+			uint vertexId : SV_VertexID;
+			uint instanceId : SV_InstanceID;
+			float2 uv0 : TEXCOORD0;
+		};
+		
+		VStoFS vsmain(VertexInput input)
+		{
+			uint vid = input.vertexId;
+			uint iid = input.instanceId;
+		
+			// We group quads in tiles for perfomance reasons (less instances)
+			// Additionally, we group the quads within the tiles into 2x2 blocks for adaptive rendering
+			// (Generally a tile is 8 quads, meaning 2 blocks per tile)
+			uint quadIdxInTile = vid / 4;
+			uint quadIdxInBlock = quadIdxInTile % 4;
+			
+			// Find the top-left position of the current block, in pixels
+			uint blocksPerTile = QUADS_PER_TILE / 4;
+			
+			// TODO - Since the way we're sampling below, should this be the block center?
+			// - Then again maybe not, because the offset we apply to blockPos below seems similar?
+			uint blocksInRow = gTileCount.x / (blocksPerTile * 2);
+			float2 blockPos = 
+				float2(iid % blocksInRow, iid / blocksInRow)
+				* uint2(blocksPerTile * 2, 2) // Each tile is 2 pixels (quads) high, with N blocks with 2 pixels (quads) wide
+				+ uint2((quadIdxInTile/4) * 2, 0); // Each block has N quads laid out in a row, and each block is 2 quads wide
+			
+			// Color in .rgb, linear depth in .a
+			float4 samples[4];
+			samples[0] = gInputTex.SampleLevel(gInputSampler, (blockPos + float2(-0.5f, -0.5f)) * gInvInputSize, 0);
+			samples[1] = gInputTex.SampleLevel(gInputSampler, (blockPos + float2( 0.5f, -0.5f)) * gInvInputSize, 0);
+			samples[2] = gInputTex.SampleLevel(gInputSampler, (blockPos + float2(-0.5f,  0.5f)) * gInvInputSize, 0);
+			samples[3] = gInputTex.SampleLevel(gInputSampler, (blockPos + float2( 0.5f,  0.5f)) * gInvInputSize, 0);
+
+			float4 minSamples = min(min(samples[0], samples[1]), min(samples[2], samples[3]));
+			float4 maxSamples = max(max(samples[0], samples[1]), max(samples[2], samples[3]));
+			
+			bool needSeparateQuads = false;
+			
+			// Are the colors too different?
+			float3 colorDiff = maxSamples.rgb - minSamples.rgb;
+			if(dot(colorDiff, 1) > gAdaptiveThresholdColor)
+				needSeparateQuads = true;
+			
+			// Do the samples fall on different layers?
+			int minLayer = minSamples.a < gFocalPlaneDistance ? 0 :
+				(minSamples.a > (gFocalPlaneDistance + gFocalLength) ? 2 : 1);
+				
+			int maxLayer = maxSamples.a < gFocalPlaneDistance ? 0 :
+				(maxSamples.a > (gFocalPlaneDistance + gFocalLength) ? 2 : 1);
+
+			if(minLayer != maxLayer)
+				needSeparateQuads = true;
+
+			// Don't skip small quads
+			float avgDepth = (minSamples.a + maxSamples.a) * 0.5f;
+			float avgCOC = circleOfConfusionPhysical(avgDepth);
+			if(avgCOC < gAdaptiveThresholdCOC)
+				needSeparateQuads = true;
+			
+			float4 colorAndDepth;
+			if(needSeparateQuads)
+			{
+				colorAndDepth = samples[quadIdxInBlock];
+				blockPos += float2(quadIdxInBlock % 2, quadIdxInBlock / 2) - 0.5f;
+			}
+			else
+				colorAndDepth = (samples[0] + samples[1] + samples[2] + samples[3]) * 0.25f;
+
+			float sceneDepth = colorAndDepth.a;
+			float coc = circleOfConfusionPhysical(sceneDepth);
+
+			// 2 pixel minimum size
+			float2 cocPixelSize = max(coc * gBokehSize.xy, 2.0f);
+
+			float4 color = float4(colorAndDepth.rgb, 1);
+			float2 layer = computeLayerContributions(sceneDepth);
+
+			color *= (sceneDepth < gFocalPlaneDistance) ? layer.r : layer.g;
+
+			if(!needSeparateQuads)
+			{
+				// Make other three quads zero-sized
+				if(quadIdxInBlock != 0)
+					cocPixelSize = 0;
+			}
+			else
+				color *= 0.25f;
+
+			// Offset so we render both near and far fields in the same texture but with an offset
+			float vertOffset = sceneDepth < gFocalPlaneDistance ? gLayerPixelOffset : 0;
+			
+			// Determine location of the vertex within the current quad
+			uint vertexIdxInQuad = vid % 4;				
+			float2 localPos = float2(vertexIdxInQuad % 2, vertexIdxInQuad / 2);
+			
+			float2 screenPos = blockPos + (localPos - 0.5f) * cocPixelSize;
+			float2 uvPos = (screenPos + float2(0, vertOffset)) * gInvOutputSize;			
+			float2 ndcPos = UVToNDC(uvPos);
+			
+			VStoFS output;			
+			output.position = float4(ndcPos, 0, 1);
+			output.uv0 = input.uv0;
+			output.color = color;
+			
+			#if DEPTH_OCCLUSION
+			output.screenUV = screenPos * gInvInputSize;
+			output.depth = sceneDepth;
+			#endif
+
+			return output;
+		}			
+
+		Texture2D gBokehTex;
+		SamplerState gBokehSampler;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float4 output = gBokehTex.Sample(gBokehSampler, input.uv0).r * input.color;
+			
+			#if DEPTH_OCCLUSION
+				float spriteDepth = input.depth;
+				float sceneDepth = gInputTex.SampleLevel(gInputSampler, input.screenUV, 0).a;
+				
+				float diff = spriteDepth - sceneDepth;
+				float fade = 1.0f - saturate(diff * gInvDepthRange);
+			
+				output *= fade;
+			#endif
+			
+			return output;
+		}	
+	};
+};

+ 96 - 0
Source/bsf/Data/Raw/Shaders/PPBokehDOFCombine.bsl

@@ -0,0 +1,96 @@
+#include "$ENGINE$\DepthOfFieldCommon.bslinc"
+#if MSAA_MODE != 0
+	#define MSAA_COUNT 2
+#endif
+#include "$ENGINE$\DepthInput.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader PPBokehDOF
+{
+	mixin DepthOfFieldCommon;
+	mixin PPBase;
+	mixin DepthInput;
+	mixin PerCameraData;
+
+	variations
+	{
+		// 0 - None
+		// 1 - Resolve single sample only
+		// 2 - Resolve all samples
+		MSAA_MODE = { 0, 1, 2 };
+	};
+	
+	code
+	{
+		cbuffer Params
+		{
+			float2 gLayerUVScaleOffset;
+			float2 gFocusedImageSize;
+		};
+		
+		Texture2D gUnfocusedTex;
+		SamplerState gUnfocusedSampler
+		{
+			AddressU = CLAMP;
+			AddressV = CLAMP;
+			AddressW = CLAMP;
+		};
+		
+		#if MSAA_MODE == 0
+		Texture2D gFocusedTex;
+		SamplerState gFocusedSampler;
+		#else
+		Texture2DMS<float4> gFocusedTex;
+		#endif
+			
+		float4 fsmain(VStoFS input
+			#if MSAA_MODE == 2
+			,uint sampleIdx : SV_SampleIndex
+			#endif
+			) : SV_Target0
+		{
+			float2 uv = input.uv0;
+			float2 screenPos = uv * gFocusedImageSize;
+			
+			#if MSAA_MODE == 0
+				float deviceZ = gDepthBufferTex.SampleLevel(gDepthBufferSamp, uv, 0).r;
+				float3 focusedColor = gFocusedTex.Sample(gFocusedSampler, uv).rgb;
+			#elif MSAA_MODE == 1
+				float deviceZ = gDepthBufferTex.Load(screenPos, 0).r;
+				float3 focusedColor = gFocusedTex.Load(screenPos, 0).rgb;
+			#else
+				float deviceZ = gDepthBufferTex.Load(screenPos, sampleIdx).r;
+				float3 focusedColor = gFocusedTex.Load(screenPos, sampleIdx).rgb;
+			#endif
+			
+			float depth = -convertFromDeviceZ(deviceZ);
+			
+			float2 halfUV = uv;
+			halfUV.y = halfUV.y * gLayerUVScaleOffset.x;
+			
+			float4 blurredFocusedAndFar = gUnfocusedTex.Sample(gUnfocusedSampler, halfUV);
+			float4 blurredNear = gUnfocusedTex.Sample(gUnfocusedSampler, halfUV + float2(0, gLayerUVScaleOffset.y));
+			
+			float2 layer = computeLayerContributions(depth);
+			
+			float farMask = layer.g;
+			float focusedMask = 1.0f - saturate(circleOfConfusionPhysical(depth));
+			float nearMask = layer.r;
+
+			float4 combined = 0;
+
+			// Blend half-res far layer
+			combined.rgb = lerp(focusedColor, blurredFocusedAndFar.rgb / max(blurredFocusedAndFar.a, 0.001f), farMask);
+
+			// Blend full-res in-focus area
+			combined = lerp(combined, float4(focusedColor, 1), focusedMask);
+
+			// Blend half-res near layer
+			combined = lerp(combined, float4(blurredNear.rgb / max(blurredNear.a, 0.001f), 0), nearMask);
+
+			float weight = combined.a;
+			return float4(lerp(combined.rgb, focusedColor.rgb, weight), 1.0f);
+		}	
+	};
+};

+ 83 - 0
Source/bsf/Data/Raw/Shaders/PPBokehDOFPrepare.bsl

@@ -0,0 +1,83 @@
+#include "$ENGINE$\DepthOfFieldCommon.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\DepthInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader PPBokehDOFPrepare
+{
+	mixin DepthOfFieldCommon;
+	mixin PPBase;
+	mixin DepthInput;
+	mixin PerCameraData;
+	
+	variations
+	{
+		MSAA_COUNT = { 1, 2 };
+	};
+	
+	code
+	{
+		cbuffer Params
+		{
+			float2 gInvInputSize;
+		};
+		
+		#if MSAA_COUNT > 1
+		Texture2DMS<float4> gInputTex;
+		#else
+		Texture2D gInputTex;
+		SamplerState gInputSampler 
+		{
+			Filter = MIN_MAG_MIP_POINT;
+		};
+		#endif
+	
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float2 uvScale;
+			#if MSAA_COUNT > 1
+				uvScale = float2(1.0f, 1.0f);
+			#else
+				uvScale = gInvInputSize;
+			#endif
+		
+			float2 uvs[4];
+			uvs[0] = input.uv0 + uvScale * float2(-0.5f, -0.5f);
+			uvs[1] = input.uv0 + uvScale * float2( 0.5f, -0.5f);
+			uvs[2] = input.uv0 + uvScale * float2(-0.5f,  0.5f);
+			uvs[3] = input.uv0 + uvScale * float2( 0.5f,  0.5f);
+
+			float4 samples[4];
+			float2 layers[4];
+
+			[unroll]
+			for(uint i = 0; i < 4; ++i)
+			{
+				#if MSAA_COUNT > 1
+				samples[i].rgb = gInputTex.Load(uvs[i], 0).rgb;
+				samples[i].a = -convertFromDeviceZ(gDepthBufferTex.Load(uvs[i], 0).r);
+				#else
+				samples[i].rgb = gInputTex.Sample(gInputSampler, uvs[i]).rgb;
+				samples[i].a = -convertFromDeviceZ(gDepthBufferTex.SampleLevel(gDepthBufferSamp, uvs[i], 0).r);
+				#endif
+				
+				layers[i] = computeLayerContributions(samples[i].a);
+			}
+
+			float2 layerSum = layers[0] + layers[1] + layers[2] + layers[3];
+			bool isFrontLayer = layerSum.x > layerSum.y;
+
+			float4 mask = isFrontLayer 
+				? float4(layers[0].x, layers[1].x, layers[2].x, layers[3].x) 
+				: float4(layers[0].y, layers[1].y, layers[2].y, layers[3].y);
+
+			float maskSum = dot(mask, float4(1.0f, 1.0f, 1.0f, 1.0f));
+			
+			[flatten]
+			if(maskSum > 0.001f)
+				return (samples[0] * mask.x + samples[1] * mask.y + samples[2] * mask.z + samples[3] * mask.w) / maskSum;
+
+			return samples[0];
+		}	
+	};
+};

+ 45 - 0
Source/bsf/Data/Raw/Shaders/PPBuildHiZ.bsl

@@ -0,0 +1,45 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPBuildHiZ
+{
+	mixin PPBase;
+	
+	variations
+	{
+		NO_TEXTURE_VIEWS = { true, false };
+	};
+	
+	code
+	{	
+		SamplerState gDepthSamp;
+		Texture2D gDepthTex;
+		
+		// Older OpenGL versions don't support views, meaning we need to specify mip level explicitly
+#if NO_TEXTURE_VIEWS
+		[internal]
+		cbuffer Input
+		{
+			float2 gHalfPixelOffset;
+			int gMipLevel;
+		}
+#endif
+		
+		float fsmain(VStoFS input) : SV_Target0
+		{
+#if NO_TEXTURE_VIEWS 	
+			float4 depth;
+			
+			float2 uv = input.uv0 - gHalfPixelOffset;
+			
+			depth.x = gDepthTex.SampleLevel(gDepthSamp, uv, gMipLevel, int2(0, 0));
+			depth.y = gDepthTex.SampleLevel(gDepthSamp, uv, gMipLevel, int2(1, 0));
+			depth.z = gDepthTex.SampleLevel(gDepthSamp, uv, gMipLevel, int2(1, 1));
+			depth.w = gDepthTex.SampleLevel(gDepthSamp, uv, gMipLevel, int2(0, 1));
+#else
+			float4 depth = gDepthTex.Gather(gDepthSamp, input.uv0);
+#endif
+			
+			return min(min(depth.x, depth.y), min(depth.z, depth.w));
+		}	
+	};
+};

+ 77 - 0
Source/bsf/Data/Raw/Shaders/PPChromaticAberration.bsl

@@ -0,0 +1,77 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPChromaticAberration
+{
+	mixin PPBase;
+
+	variations
+	{
+		SIMPLE = { false, true };
+	};
+	
+	code
+	{
+		[internal]
+		cbuffer Params
+		{
+			float2 gInputSize;
+			float gShiftAmount;
+		}	
+	
+		SamplerState gInputSamp
+		{
+			Filter = MIN_MAG_MIP_POINT;
+		};
+		Texture2D gInputTex;
+		
+		SamplerState gFringeSamp;
+		Texture2D gFringeTex;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+            float2 ndc = 2.0f * input.uv0 - 1.0f;
+            float2 end = input.uv0 - ndc * dot(ndc, ndc) * gShiftAmount;
+		
+		    #if SIMPLE
+            {
+                float2 delta = (end - input.uv0) / 3.0f;
+
+                float4 filterA = float4(gFringeTex.Sample(gFringeSamp, float2(0.5f / 3.0f, 0.0f)).rgb, 1.0f);
+                float4 filterB = float4(gFringeTex.Sample(gFringeSamp, float2(1.5f / 3.0f, 0.0f)).rgb, 1.0f);
+                float4 filterC = float4(gFringeTex.Sample(gFringeSamp, float2(2.5f / 3.0f, 0.0f)).rgb, 1.0f);
+
+                float4 texelA = gInputTex.Sample(gInputSamp, input.uv0);
+                float4 texelB = gInputTex.Sample(gInputSamp, input.uv0 + delta);
+                float4 texelC = gInputTex.Sample(gInputSamp, input.uv0 + delta * 2.0f);
+
+                float4 sum = texelA * filterA + texelB * filterB + texelC * filterC;
+                float4 filterSum = filterA + filterB + filterC;
+                return sum / filterSum;
+            }
+            #else
+            {
+                float2 diff = end - input.uv0;
+                int numSamples = clamp((int)length(gInputSize * diff / 2.0f), 3, MAX_SAMPLES);
+				
+                float2 delta = diff / numSamples;
+                float2 pos = input.uv0;
+                float4 sum = 0.0f;
+				float4 filterSum = 0.0f;
+
+                for (int i = 0; i < numSamples; i++)
+                {
+                    float t = (i + 0.5f) / numSamples;
+                    float4 s = gInputTex.Sample(gInputSamp, pos);
+                    float4 filter = float4(gFringeTex.Sample(gFringeSamp, float2(t, 0.0f)).rgb, 1.0f);
+
+                    sum += s * filter;
+                    filterSum += filter;
+                    pos += delta;
+                }
+
+                return sum / filterSum;
+            }
+            #endif
+		}
+	};
+};

+ 161 - 0
Source/bsf/Data/Raw/Shaders/PPCreateTonemapLUT.bsl

@@ -0,0 +1,161 @@
+#include "$ENGINE$\PPTonemapCommon.bslinc"
+#include "$ENGINE$\PPWhiteBalance.bslinc"
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPCreateTonemapLUT
+{
+	mixin PPTonemapCommon;
+	mixin PPWhiteBalance;
+	
+	#if VOLUME_LUT
+	featureset = HighEnd;
+	#else
+	mixin PPBase;
+	#endif
+		
+	variations
+	{
+		VOLUME_LUT = { true, false };
+	};
+	
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			// [0]: x - shoulder strength, y - linear strength, z - linear angle, w - toe strength
+			// [1]: x - toe numerator, y - toe denominator, z - linear white point, w - unused
+			float4 gTonemapParams[2];
+			
+			float gGammaAdjustment;
+			// 0 - sRGB, 1 - Rec.709, 2 - 2.2 gamma
+			uint gGammaCorrectionType;
+			
+			float3 gSaturation;
+			float3 gContrast;
+			float3 gGain;
+			float3 gOffset;
+		};
+	
+		/**
+		 * Filmic curve used for tonemapping.
+		 *
+		 * @param 	linearColor		Linear color.
+		 * @return					Transformed color.
+		 */			
+		float3 FilmicCurve(float3 color)
+		{
+			// Formula from John Hable's Uncharted 2 presentation
+			float3 a = color * (gTonemapParams[0].x * color + gTonemapParams[0].y * gTonemapParams[0].z);
+			float b = gTonemapParams[0].w * gTonemapParams[1].x;
+			float3 c = color * (gTonemapParams[0].x * color + gTonemapParams[0].y);
+			float d = gTonemapParams[0].w * gTonemapParams[1].y;
+			
+			return (a + b)/(c + d) - gTonemapParams[1].x / gTonemapParams[1].y;
+		}
+		
+		/**
+		 * Applies filmic curve tonemapping to the provided color.
+		 *
+		 * @param 	linearColor		Linear color in ACEScg color space.
+		 * @return					Tonemapped color in ACEScg color space.
+		 */		
+		float3 FilmicTonemapping(float3 color)
+		{
+			return FilmicCurve(color) / FilmicCurve(gTonemapParams[1].z);
+		}
+		
+		/**
+		 * Applies color grading to the provided color.
+		 *
+		 * @param 	linearColor		Linear color in ACEScg color space.
+		 * @return					Graded color in ACEScg color space.
+		 */				
+		float3 ColorGrading(float3 color)
+		{
+			const float3 RGBToY = float3(0.2722287168f, 0.6740817658f, 0.0536895174f);
+		
+			float luminance = dot(color, RGBToY);
+			
+			color = max(0, lerp(luminance.xxx, color, gSaturation));
+			color = pow(color * (1.0f / 0.18f), gContrast) * 0.18f;
+			color = color * gGain + gOffset;
+
+			return color;
+		}
+		
+		/** Generates tonemap information to the specified color (in log encoded space). */
+		float3 tonemapColor(float3 logColor)
+		{
+			// Constants
+			const float3x3 sRGBToACES2065Matrix = mul(XYZToACES2065Matrix, mul(D65ToD60Matrix, sRGBToXYZMatrix));
+			const float3x3 sRGBToACEScgMatrix = mul(XYZToACEScgMatrix, mul(D65ToD60Matrix, sRGBToXYZMatrix));
+			const float3x3 ACEScgTosRGBMatrix = mul(XYZTosRGBMatrix, mul(D60ToD65Matrix, ACEScgToXYZMatrix));		
+		
+			float3 linearColor = LogToLinearColor(logColor);
+			
+			linearColor = WhiteBalance(linearColor);
+			linearColor = mul(sRGBToACEScgMatrix, linearColor);
+			linearColor = ColorGrading(linearColor);
+			
+			// Note: Improve this so it's closer to the ACES curve?
+			linearColor = FilmicTonemapping(linearColor);
+			// TODO - Does the white point provided in filmic curve conflict with the white balancing?
+			
+			linearColor = mul(ACEScgTosRGBMatrix, linearColor);
+			
+			// Transform to gamma space
+			float3 gammaColor = pow(linearColor, gGammaAdjustment); // User adjustment, usually 1.0f
+				
+			if(gGammaCorrectionType == 0)
+				gammaColor = LinearToGammasRGB(gammaColor);
+			else if(gGammaCorrectionType == 1)
+				gammaColor = LinearToGammaRec709(gammaColor);
+			else
+				gammaColor = pow(gammaColor, 1.0f/2.2f);
+				
+			return gammaColor;
+		}
+		
+		#if VOLUME_LUT
+		RWTexture3D<unorm float4> gOutputTex;
+		
+		[numthreads(8, 8, 1)]
+		void csmain(
+			uint3 dispatchThreadId : SV_DispatchThreadID,
+			uint threadIndex : SV_GroupIndex)
+		{
+			float3 logColor = float3(dispatchThreadId.xyz / (float)(LUT_SIZE - 1));
+			float3 gammaColor = tonemapColor(logColor);
+							
+			// TODO - Divide by 1.05f here and then re-apply it when decoding from the texture?
+			gOutputTex[dispatchThreadId] = float4(gammaColor, 1.0f);	
+		}
+		#else
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float3 logColor;
+			
+			float2 uv = input.uv0;
+			// Make sure uv maps to [0,1], as it's currently specified for pixel centers
+			// (This way we get non-extrapolated color values for 0 and 1)
+			uv -= float2(0.5f / (LUT_SIZE * LUT_SIZE), 0.5f / LUT_SIZE);
+			uv *= LUT_SIZE / (LUT_SIZE - 1);
+			
+			// Red goes from 0 to 1, in each slice along X (LUT_SIZE number of slices)
+			logColor.r = frac(uv.x * LUT_SIZE);
+			
+			// Blue value is constant within each slice, and increases by 1/LUT_SIZE with each slice along X
+			logColor.b = uv.x - logColor.r / LUT_SIZE;
+			
+			// Green increases linearly with y
+			logColor.g = uv.y;
+			
+			float3 gammaColor = tonemapColor(logColor);
+							
+			// TODO - Divide by 1.05f here and then re-apply it when decoding from the texture?
+			return float4(gammaColor, 1.0f);	
+		}	
+		#endif
+	};
+};

+ 73 - 0
Source/bsf/Data/Raw/Shaders/PPDownsample.bsl

@@ -0,0 +1,73 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPDownsample
+{
+	mixin PPBase;
+
+	variations
+	{
+		QUALITY = { 0, 1 };
+		MSAA = { true, false };
+	};
+	
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			float2 gOffsets[4];
+		}		
+
+		#if MSAA
+			Texture2DMS<float4> gInputTex;
+			
+			// position is expected to be at the center of 2x2 pixel tile, in pixels
+			float4 bilinearFilter(float2 position)
+			{
+				float4 sampleSum;
+
+				sampleSum = gInputTex.Load(trunc(position + float2(-0.5f, -0.5f)), 0);
+				sampleSum += gInputTex.Load(trunc(position + float2(0.5f, -0.5f)), 0);
+				sampleSum += gInputTex.Load(trunc(position + float2(-0.5f, 0.5f)), 0);
+				sampleSum += gInputTex.Load(trunc(position + float2(0.5f, 0.5f)), 0);
+				
+				return sampleSum * 0.25f;
+			}
+		#else
+			SamplerState gInputSamp
+			{
+				AddressU = CLAMP;
+				AddressV = CLAMP;
+				AddressW = CLAMP;
+			};
+			Texture2D gInputTex;
+			
+			// position is expected to be at the center of 2x2 pixel tile, in UV
+			float4 bilinearFilter(float2 position)
+			{
+				return gInputTex.Sample(gInputSamp, position);
+			}			
+		#endif
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			// input.uv0 is in the center of 2x2 block of pixels. If MSAA is enabled the value is
+			// in pixels, otherwise normal UV range.
+			
+			#if QUALITY == 0 
+				// Single bilinearly filtered sample (2x2 block average)
+				return bilinearFilter(input.uv0);
+			#else // QUALITY == 1
+				// Four bilinearly filtered samples (4x4 block average)
+				float4 samples[4];
+
+				[unroll]
+				for(uint i = 0; i < 4; i++)
+					samples[i] = bilinearFilter(input.uv0 + gOffsets[i]);
+
+				return (samples[0] + samples[1] + samples[2] + samples[3]) * 0.25f;
+				
+			#endif // QUALITY
+		}	
+	};
+};

+ 38 - 0
Source/bsf/Data/Raw/Shaders/PPEncodeDepth.bsl

@@ -0,0 +1,38 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+shader PPEncodeDepth
+{
+	mixin PPBase;
+	mixin PerCameraData;
+
+	blend
+	{
+		target
+		{
+			writemask = A;
+		};
+	};
+
+	code
+	{
+		[internal]
+		cbuffer Params
+		{
+			float gNear;
+			float gFar;
+		}	
+	
+		SamplerState gInputSamp;
+		Texture2D gInputTex;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float deviceZ = gInputTex.Sample(gInputSamp, input.uv0);
+			float viewZ = convertFromDeviceZ(deviceZ);
+		
+			float alpha = 1.0f - saturate((viewZ - gNear) / (gFar - gNear));
+			return float4(0.0f, 0.0f, 0.0f, alpha);
+		}	
+	};
+};

+ 108 - 0
Source/bsf/Data/Raw/Shaders/PPEyeAdaptHistogram.bsl

@@ -0,0 +1,108 @@
+#define NUM_BUCKETS (THREADGROUP_SIZE_X * THREADGROUP_SIZE_Y)
+
+shader PPEyeAdaptHistogram
+{
+	featureset = HighEnd;
+
+	code
+	{	
+		[internal]
+		cbuffer Input
+		{
+			// xy - offset, zw - size
+			uint4 gPixelOffsetAndSize;
+		
+			// x - histogram scale, y - histogram offset
+			float2 gHistogramParams;
+			uint2 gThreadGroupCount;
+		}
+	
+		Texture2D gSceneColorTex;
+		RWTexture2D<float4> gOutputTex;
+		
+		// Keep elements in this order as it ensures coalesced memory operations for non-random ops
+		groupshared float sharedData[NUM_BUCKETS][THREADGROUP_SIZE_X][THREADGROUP_SIZE_Y];
+		
+		float calcHistogramPos(float luminance)
+		{
+			return saturate(log2(luminance) * gHistogramParams.x + gHistogramParams.y);
+		}			
+		
+		[numthreads(THREADGROUP_SIZE_X, THREADGROUP_SIZE_Y, 1)]
+		void csmain(
+			uint3 groupId : SV_GroupID,
+			uint3 groupThreadId : SV_GroupThreadID,
+			uint3 dispatchThreadId : SV_DispatchThreadID,
+			uint threadIndex : SV_GroupIndex)
+		{
+			// Clear everything
+			for(uint i = 0; i < NUM_BUCKETS; i++)
+				sharedData[i][groupThreadId.x][groupThreadId.y] = 0.0f;
+				
+			GroupMemoryBarrierWithGroupSync();
+			
+			// Sort all pixel luminance for the current thread into histogram buckets
+			uint2 tileSize = uint2(LOOP_COUNT_X, LOOP_COUNT_Y);
+			uint2 maxExtent = gPixelOffsetAndSize.xy + gPixelOffsetAndSize.zw;
+			
+			uint2 tileStart = dispatchThreadId.xy * tileSize + gPixelOffsetAndSize.xy;
+			for(uint y = 0; y < LOOP_COUNT_Y; y++)
+			{
+				uint2 texelPos = tileStart + uint2(0, y);
+				if(texelPos.y > maxExtent.y)
+					break;
+			
+				for(uint x = 0; x < LOOP_COUNT_X; x++)
+				{
+					if(texelPos.x > maxExtent.x)
+						break;
+				
+					float4 hdrColor = gSceneColorTex.Load(int3(texelPos, 0));
+					float luminance = dot(hdrColor.rgb, float3(0.299f, 0.587f, 0.114f)); // TODO - Perhaps just use max() of all values?
+					
+					float histogramPos = calcHistogramPos(luminance);
+					float bucket = histogramPos * (NUM_BUCKETS - 1) * 0.9999f;
+				
+					uint bucketAIdx = (uint)bucket;
+					uint bucketBIdx = bucketAIdx + 1;
+					
+					float weightB = frac(bucket);
+					float weightA = 1.0f - weightB;
+					
+					if(bucketAIdx != 0)
+						sharedData[bucketAIdx][groupThreadId.x][groupThreadId.y] += weightA;
+				
+					sharedData[bucketBIdx][groupThreadId.x][groupThreadId.y] += weightB;
+				
+					texelPos.x++;
+				}
+			}
+			
+			GroupMemoryBarrierWithGroupSync();
+			
+			// Accumulate bucketed values from all threads in the group
+			if(threadIndex < (NUM_BUCKETS / 4))
+			{
+				float4 sum = 0.0f;
+				for(uint y = 0; y < THREADGROUP_SIZE_Y; y++)
+				{
+					for(uint x = 0; x < THREADGROUP_SIZE_X; x++)
+					{
+						sum += float4(
+							sharedData[threadIndex * 4 + 0][x][y],
+							sharedData[threadIndex * 4 + 1][x][y],
+							sharedData[threadIndex * 4 + 2][x][y],
+							sharedData[threadIndex * 4 + 3][x][y]
+						);
+					}
+				}
+				
+				// Normalize and output histogram for the group (single line per group)
+				float groupArea = THREADGROUP_SIZE_X * LOOP_COUNT_X * THREADGROUP_SIZE_Y * LOOP_COUNT_Y;
+
+				gOutputTex[uint2(threadIndex, groupId.x + groupId.y * gThreadGroupCount.x)] = sum / groupArea;					
+				
+			}
+		}	
+	};
+};

+ 39 - 0
Source/bsf/Data/Raw/Shaders/PPEyeAdaptHistogramReduce.bsl

@@ -0,0 +1,39 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPEyeAdaptHistogramReduce
+{
+	mixin PPBase;
+
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			uint gThreadGroupCount;
+		}		
+	
+		Texture2D gHistogramTex;
+		Texture2D gEyeAdaptationTex;
+
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			int2 iUV = trunc(input.uv0);
+			float4 outputValue = 0.0f;
+
+			// Output texture only has two rows, store histogram on the first
+			if(input.uv0.y < 1.0f)
+			{
+				// TODO - Potentially optimize using bilinear filtering
+				for(uint i = 0; i < gThreadGroupCount; i++)
+					outputValue += gHistogramTex.Load(int3(iUV.x, i, 0));
+
+				return outputValue / gThreadGroupCount;
+			}
+			else
+			{
+				// Store eye adaptation from last frame in the second row of the texture
+				return gEyeAdaptationTex.Load(int3(0, 0, 0)).x;
+			}
+		}	
+	};
+};

+ 133 - 0
Source/bsf/Data/Raw/Shaders/PPEyeAdaptation.bsl

@@ -0,0 +1,133 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PPEyeAdaptationCommon.bslinc"
+
+shader PPEyeAdaptation
+{
+	mixin PPBase;
+	mixin PPEyeAdaptationParams;
+
+	code
+	{
+		#define NUM_BUCKETS (THREADGROUP_SIZE_X * THREADGROUP_SIZE_Y)
+	
+		Texture2D gHistogramTex;
+		
+		/** 
+		 * Returns luminance of the histogram bucket.
+		 *
+		 * @param pos	Position of the histogram bucket in range [0, 1].
+		 * @return		Luminance of the bucket.
+		 */
+		float calcHistogramLuminance(float pos)
+		{
+			return exp2((pos - gEyeAdaptationParams[0].y) / gEyeAdaptationParams[0].x);
+		}	
+		
+		/**
+		 * Returns value of the histogram bucket.
+		 *
+		 * @param	histogram	Texture containing the histogram buckets in the first row.
+		 * @param	bucketIdx	Index of the bucket. Caller must ensure it is in valid range.
+		 * @return				Value of the needed histogram bucket.
+		 */
+		float getHistogramValue(Texture2D histogram, uint bucketIdx)
+		{
+			uint texelIdx = bucketIdx / 4;
+			
+			float4 packedValue = histogram.Load(int3(texelIdx, 0, 0));
+			float4 mask = float4(
+				(bucketIdx % 4) == 0,
+				(bucketIdx % 4) == 1,
+				(bucketIdx % 4) == 2,
+				(bucketIdx % 4) == 3);
+
+			return dot(packedValue, mask);	
+		}
+
+		/** 
+		 * Calculates the sum of all values in the histogram.
+		 *
+		 * @param	histogram	Texture containing the histogram buckets in the first row.
+		 * @return				Sum of all the values in the histogram.
+		 */
+		float calcHistogramSum(Texture2D histogram)
+		{
+			float sum = 0;
+
+			for(uint i = 0; i < NUM_BUCKETS; i++)
+				sum += getHistogramValue(histogram, i);
+
+			return sum;
+		}	
+
+		/**
+		 * Calculates the average luminance in the histogram, while ignoring the outlier values that may skew the result.
+		 *
+		 * @param	histogram	Texture containing the histogram buckets in the first row.
+		 * @param	low			Sum below which to ignore values (removing lower end outliers), in range [0, histogramSum].
+		 * @param	high		Sum above which to ignore values (removing higher end outliers), in range [0, histogramSum]. 
+		 *                      Must be higher than @low.
+		 * @return				Average luminance in the histogram.
+		 */
+		float calcHistogramAverageLuminance(Texture2D histogram, float low, float high)
+		{
+			float2 sumAndWeight = float2(0.0f, 0.0f);
+
+			for(uint i = 0; i < NUM_BUCKETS; i++)
+			{
+				float value = getHistogramValue(histogram, i);
+
+				// Ignore any values below the @low parameter, and then shift the valid range
+				// by the amount we ignored. Eventually the low end of the range reaches zero
+				// and values are no longer ignored.
+				float offset = min(value, low);
+				value = value - offset;
+				low -= offset;
+				high -= offset;
+
+				// Ignore any values above the @high parameter, and then shift the valid range.
+				value = min(value, high);
+				high -= value;
+
+				float histogramPos = i / (float)NUM_BUCKETS;
+				float luminance = calcHistogramLuminance(histogramPos);
+				
+				sumAndWeight += float2(luminance, 1) * value;
+			}
+			
+			return sumAndWeight.x / max(0.0001f, sumAndWeight.y);
+		}
+		
+		/**
+		 * Calculates the eye adaptation from the luminance in the provided histogram. Eye adaptation value will be 
+		 * used for automatically scaling expsure based on scene brightness.
+		 *
+		 * @param	histogram	Texture containing the histogram buckets in the first row.
+		 * @return				Ideal eye adaptation value for the provided luminance.
+		 */
+		float calcEyeAdaptation(Texture2D histogram)
+		{
+			float sum = calcHistogramSum(histogram);
+			float lowRange = gEyeAdaptationParams[0].z * sum;
+			float highRange = gEyeAdaptationParams[0].w * sum;
+			
+			float avgLuminance = calcHistogramAverageLuminance(histogram, lowRange, highRange);
+			avgLuminance = clamp(avgLuminance, gEyeAdaptationParams[1].x, gEyeAdaptationParams[1].y);
+
+			return avgLuminance;
+		}
+				
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float exposureScale = gEyeAdaptationParams[2].x;
+
+			float targetAdaptation = calcEyeAdaptation(gHistogramTex);
+			float oldExposure = gHistogramTex.Load(int3(0, 1, 0)).x;
+			float oldAdaptation = exposureScale / oldExposure; // Assuming same exposure scale as last frame
+			float frameDelta = gEyeAdaptationParams[2].y;
+			
+			float smoothAdaptation = smoothEyeAdaptation(oldAdaptation, targetAdaptation, frameDelta);
+			return exposureScale / smoothAdaptation; // Returns exposure
+		}	
+	};
+};

+ 80 - 0
Source/bsf/Data/Raw/Shaders/PPEyeAdaptationBasic.bsl

@@ -0,0 +1,80 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PPEyeAdaptationCommon.bslinc"
+
+shader PPEyeAdaptationBasic
+{
+	mixin PPBase;
+	mixin PPEyeAdaptationParams;
+
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			int2 gInputTexSize;
+		}		
+	
+		Texture2D gCurFrameTex;
+		Texture2D gPrevFrameTex;
+		
+		/** 
+		 * Returns the value in range [0,1] on the triangle function with slope @p slope. 
+		 * @p t must be in range [0,1].
+		 */
+		float triangleFunc(float t, float slope)
+		{
+			return max(1.0f - 2.0f * slope * abs(t - 0.5f), 0.0f);
+		}		
+		
+		/** 
+		 * Calculates the average value of all pixels in the input texture. The pixels are
+		 * weighted using a triangle filter using @p slope as the line slope. @p slope of 0
+		 * means all pixels will be uniformly weighted.
+		 */
+		float calcWeightedAverageAlpha(Texture2D input, int2 inputSize, float slope)
+		{
+			float2 invSize = 1.0f / inputSize;
+
+			float sum = 0.0f;
+			float weightSum = 0.0f;
+			for (uint i = 0; i < inputSize.x; ++i)
+			{
+				float weightX = triangleFunc(i * invSize.x, slope);  
+				for (uint j = 0; j < inputSize.y; ++j)
+				{
+					float weightY = triangleFunc(j * invSize.y, slope);
+					float weight = max(weightX * weightY, 0.05f);
+	
+					float value = input.Load(int3(i, j, 0)).a;
+					
+					sum += value * weight;
+					weightSum += weight;
+				}
+			}
+
+			return sum / weightSum;
+		}		
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float slope = 0.0f; // TODO - Allow custom slope?
+			float avgLuminance = calcWeightedAverageAlpha(gCurFrameTex, gInputTexSize, slope);
+		
+			// Scale back into normal range (was log2 encoded and scale into [0, 1] range)
+			avgLuminance = exp2((avgLuminance - gEyeAdaptationParams[0].y) / gEyeAdaptationParams[0].x);
+			avgLuminance /= 0.16f;
+		
+			// Clamp to valid range
+			avgLuminance = clamp(avgLuminance, gEyeAdaptationParams[1].x, gEyeAdaptationParams[1].y);
+			
+			float exposureScale = gEyeAdaptationParams[2].x;
+			float oldExposure = gPrevFrameTex.Load(int3(0, 0, 0)).x;
+			float oldLuminance = exposureScale / oldExposure; // Assuming same exposure scale as last frame
+			
+			float frameDelta = gEyeAdaptationParams[2].y;
+			float smoothAdaptation = smoothEyeAdaptation(oldLuminance, avgLuminance, frameDelta);
+			
+			return exposureScale / smoothAdaptation; // Returns exposure
+		}	
+	};
+};

+ 30 - 0
Source/bsf/Data/Raw/Shaders/PPEyeAdaptationBasicSetup.bsl

@@ -0,0 +1,30 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PPEyeAdaptationCommon.bslinc"
+
+shader PPEyeAdaptationBasicSetup
+{
+	mixin PPBase;
+	mixin PPEyeAdaptationParams;
+
+	code
+	{
+		Texture2D gInputTex;
+		
+		[alias(gInputTex)]
+		SamplerState gInputSamp;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float4 value = gInputTex.Sample(gInputSamp, input.uv0);
+			float luminance = dot(value.xyz, float3(0.2126, 0.7152, 0.0722));
+			
+			float maxIntensity = gEyeAdaptationParams[2].z;
+			luminance = max(maxIntensity, luminance);
+			
+			// Store intensity as log, and scale to [0, 1] range
+			value.w = gEyeAdaptationParams[0].x * log2(luminance) + gEyeAdaptationParams[0].y;
+			
+			return value;
+		}	
+	};
+};

+ 934 - 0
Source/bsf/Data/Raw/Shaders/PPFXAA.bsl

@@ -0,0 +1,934 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPFXAA
+{
+	mixin PPBase;
+
+	code
+	{
+		//----------------------------------------------------------------------------------
+		// File:        FXAA\media/FXAA.hlsl
+		// SDK Version: v1.2 
+		// Email:       [email protected]
+		// Site:        http://developer.nvidia.com/
+		//
+		// Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+		//
+		// Redistribution and use in source and binary forms, with or without
+		// modification, are permitted provided that the following conditions
+		// are met:
+		//  * Redistributions of source code must retain the above copyright
+		//    notice, this list of conditions and the following disclaimer.
+		//  * Redistributions in binary form must reproduce the above copyright
+		//    notice, this list of conditions and the following disclaimer in the
+		//    documentation and/or other materials provided with the distribution.
+		//  * Neither the name of NVIDIA CORPORATION nor the names of its
+		//    contributors may be used to endorse or promote products derived
+		//    from this software without specific prior written permission.
+		//
+		// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+		// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+		// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+		// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+		// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+		// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+		// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+		// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+		// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+		// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+		// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+		//
+		//----------------------------------------------------------------------------------
+		/*============================================================================
+
+
+							NVIDIA FXAA 3.11 by TIMOTHY LOTTES
+
+		------------------------------------------------------------------------------
+								   INTEGRATION CHECKLIST
+		------------------------------------------------------------------------------
+		(1.)
+		In the shader source, setup defines for the desired configuration.
+		When providing multiple shaders (for different presets),
+		simply setup the defines differently in multiple files.
+		Example,
+
+		  #define FXAA_PC 1
+		  #define FXAA_HLSL_5 1
+		  #define FXAA_QUALITY__PRESET 12
+
+		Or,
+
+		  #define FXAA_360 1
+		  
+		Or,
+
+		  #define FXAA_PS3 1
+		  
+		Etc.
+
+		(2.)
+		Then include this file,
+
+		  #include "Fxaa3_11.h"
+
+		(3.)
+		Then call the FXAA pixel shader from within your desired shader.
+		Look at the FXAA Quality FxaaPixelShader() for docs on inputs.
+		As for FXAA 3.11 all inputs for all shaders are the same 
+		to enable easy porting between platforms.
+
+		  return FxaaPixelShader(...);
+
+		(4.)
+		Insure pass prior to FXAA outputs RGBL (see next section).
+		Or use,
+
+		  #define FXAA_GREEN_AS_LUMA 1
+
+		(5.)
+		Setup engine to provide the following constants
+		which are used in the FxaaPixelShader() inputs,
+
+		  FxaaFloat2 fxaaQualityRcpFrame,
+		  FxaaFloat fxaaQualitySubpix,
+		  FxaaFloat fxaaQualityEdgeThreshold,
+		  FxaaFloat fxaaQualityEdgeThresholdMin,
+
+		Look at the FXAA Quality FxaaPixelShader() for docs on inputs.
+
+		(6.)
+		Have FXAA vertex shader run as a full screen triangle,
+		and output "pos" and "fxaaConsolePosPos" 
+		such that inputs in the pixel shader provide,
+
+		  // {xy} = center of pixel
+		  FxaaFloat2 pos,
+
+		  // {xy__} = upper left of pixel
+		  // {__zw} = lower right of pixel
+		  FxaaFloat4 fxaaConsolePosPos,
+
+		(7.)
+		Insure the texture sampler(s) used by FXAA are set to bilinear filtering.
+
+
+		------------------------------------------------------------------------------
+							INTEGRATION - RGBL AND COLORSPACE
+		------------------------------------------------------------------------------
+		FXAA3 requires RGBL as input unless the following is set, 
+
+		  #define FXAA_GREEN_AS_LUMA 1
+
+		In which case the engine uses green in place of luma,
+		and requires RGB input is in a non-linear colorspace.
+
+		RGB should be LDR (low dynamic range).
+		Specifically do FXAA after tonemapping.
+
+		RGB data as returned by a texture fetch can be non-linear,
+		or linear when FXAA_GREEN_AS_LUMA is not set.
+		Note an "sRGB format" texture counts as linear,
+		because the result of a texture fetch is linear data.
+		Regular "RGBA8" textures in the sRGB colorspace are non-linear.
+
+		If FXAA_GREEN_AS_LUMA is not set,
+		luma must be stored in the alpha channel prior to running FXAA.
+		This luma should be in a perceptual space (could be gamma 2.0).
+		Example pass before FXAA where output is gamma 2.0 encoded,
+
+		  color.rgb = ToneMap(color.rgb); // linear color output
+		  color.rgb = sqrt(color.rgb);    // gamma 2.0 color output
+		  return color;
+
+		To use FXAA,
+
+		  color.rgb = ToneMap(color.rgb);  // linear color output
+		  color.rgb = sqrt(color.rgb);     // gamma 2.0 color output
+		  color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma
+		  return color;
+
+		Another example where output is linear encoded,
+		say for instance writing to an sRGB formated render target,
+		where the render target does the conversion back to sRGB after blending,
+
+		  color.rgb = ToneMap(color.rgb); // linear color output
+		  return color;
+
+		To use FXAA,
+
+		  color.rgb = ToneMap(color.rgb); // linear color output
+		  color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma
+		  return color;
+
+		Getting luma correct is required for the algorithm to work correctly.
+
+
+		------------------------------------------------------------------------------
+								  BEING LINEARLY CORRECT?
+		------------------------------------------------------------------------------
+		Applying FXAA to a framebuffer with linear RGB color will look worse.
+		This is very counter intuitive, but happends to be true in this case.
+		The reason is because dithering artifacts will be more visiable 
+		in a linear colorspace.
+
+
+		------------------------------------------------------------------------------
+									 COMPLEX INTEGRATION
+		------------------------------------------------------------------------------
+		Q. What if the engine is blending into RGB before wanting to run FXAA?
+
+		A. In the last opaque pass prior to FXAA,
+		   have the pass write out luma into alpha.
+		   Then blend into RGB only.
+		   FXAA should be able to run ok
+		   assuming the blending pass did not any add aliasing.
+		   This should be the common case for particles and common blending passes.
+
+		A. Or use FXAA_GREEN_AS_LUMA.
+
+		============================================================================*/
+
+		/*============================================================================
+
+									 INTEGRATION KNOBS
+
+		============================================================================*/
+		#ifndef FXAA_GREEN_AS_LUMA
+			//
+			// For those using non-linear color,
+			// and either not able to get luma in alpha, or not wanting to,
+			// this enables FXAA to run using green as a proxy for luma.
+			// So with this enabled, no need to pack luma in alpha.
+			//
+			// This will turn off AA on anything which lacks some amount of green.
+			// Pure red and blue or combination of only R and B, will get no AA.
+			//
+			// Might want to lower the settings for both,
+			//    fxaaConsoleEdgeThresholdMin
+			//    fxaaQualityEdgeThresholdMin
+			// In order to insure AA does not get turned off on colors 
+			// which contain a minor amount of green.
+			//
+			// 1 = On.
+			// 0 = Off.
+			//
+			#define FXAA_GREEN_AS_LUMA 1
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#ifndef FXAA_DISCARD
+			//
+			// Only valid for PC OpenGL currently.
+			// Probably will not work when FXAA_GREEN_AS_LUMA = 1.
+			//
+			// 1 = Use discard on pixels which don't need AA.
+			//     For APIs which enable concurrent TEX+ROP from same surface.
+			// 0 = Return unchanged color on pixels which don't need AA.
+			//
+			#define FXAA_DISCARD 0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#ifndef FXAA_GATHER4_ALPHA
+			#define FXAA_GATHER4_ALPHA 1
+		#endif
+
+		/*============================================================================
+								FXAA QUALITY - TUNING KNOBS
+		------------------------------------------------------------------------------
+		NOTE the other tuning knobs are now in the shader function inputs!
+		============================================================================*/
+		#ifndef FXAA_QUALITY__PRESET
+			//
+			// Choose the quality preset.
+			// This needs to be compiled into the shader as it effects code.
+			// Best option to include multiple presets is to 
+			// in each shader define the preset, then include this file.
+			// 
+			// OPTIONS
+			// -----------------------------------------------------------------------
+			// 10 to 15 - default medium dither (10=fastest, 15=highest quality)
+			// 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality)
+			// 39       - no dither, very expensive 
+			//
+			// NOTES
+			// -----------------------------------------------------------------------
+			// 12 = slightly faster then FXAA 3.9 and higher edge quality (default)
+			// 13 = about same speed as FXAA 3.9 and better than 12
+			// 23 = closest to FXAA 3.9 visually and performance wise
+			//  _ = the lowest digit is directly related to performance
+			// _  = the highest digit is directly related to style
+			// 
+			#define FXAA_QUALITY__PRESET 12
+		#endif
+
+
+		/*============================================================================
+
+								   FXAA QUALITY - PRESETS
+
+		============================================================================*/
+
+		/*============================================================================
+							 FXAA QUALITY - MEDIUM DITHER PRESETS
+		============================================================================*/
+		#if (FXAA_QUALITY__PRESET == 10)
+			#define FXAA_QUALITY__PS 3
+			#define FXAA_QUALITY__P0 1.5
+			#define FXAA_QUALITY__P1 3.0
+			#define FXAA_QUALITY__P2 12.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 11)
+			#define FXAA_QUALITY__PS 4
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 3.0
+			#define FXAA_QUALITY__P3 12.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 12)
+			#define FXAA_QUALITY__PS 5
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 4.0
+			#define FXAA_QUALITY__P4 12.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 13)
+			#define FXAA_QUALITY__PS 6
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 4.0
+			#define FXAA_QUALITY__P5 12.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 14)
+			#define FXAA_QUALITY__PS 7
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 4.0
+			#define FXAA_QUALITY__P6 12.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 15)
+			#define FXAA_QUALITY__PS 8
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 4.0
+			#define FXAA_QUALITY__P7 12.0
+		#endif
+
+		/*============================================================================
+							 FXAA QUALITY - LOW DITHER PRESETS
+		============================================================================*/
+		#if (FXAA_QUALITY__PRESET == 20)
+			#define FXAA_QUALITY__PS 3
+			#define FXAA_QUALITY__P0 1.5
+			#define FXAA_QUALITY__P1 2.0
+			#define FXAA_QUALITY__P2 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 21)
+			#define FXAA_QUALITY__PS 4
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 22)
+			#define FXAA_QUALITY__PS 5
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 23)
+			#define FXAA_QUALITY__PS 6
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 24)
+			#define FXAA_QUALITY__PS 7
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 3.0
+			#define FXAA_QUALITY__P6 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 25)
+			#define FXAA_QUALITY__PS 8
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 4.0
+			#define FXAA_QUALITY__P7 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 26)
+			#define FXAA_QUALITY__PS 9
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 2.0
+			#define FXAA_QUALITY__P7 4.0
+			#define FXAA_QUALITY__P8 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 27)
+			#define FXAA_QUALITY__PS 10
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 2.0
+			#define FXAA_QUALITY__P7 2.0
+			#define FXAA_QUALITY__P8 4.0
+			#define FXAA_QUALITY__P9 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 28)
+			#define FXAA_QUALITY__PS 11
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 2.0
+			#define FXAA_QUALITY__P7 2.0
+			#define FXAA_QUALITY__P8 2.0
+			#define FXAA_QUALITY__P9 4.0
+			#define FXAA_QUALITY__P10 8.0
+		#endif
+		/*--------------------------------------------------------------------------*/
+		#if (FXAA_QUALITY__PRESET == 29)
+			#define FXAA_QUALITY__PS 12
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.5
+			#define FXAA_QUALITY__P2 2.0
+			#define FXAA_QUALITY__P3 2.0
+			#define FXAA_QUALITY__P4 2.0
+			#define FXAA_QUALITY__P5 2.0
+			#define FXAA_QUALITY__P6 2.0
+			#define FXAA_QUALITY__P7 2.0
+			#define FXAA_QUALITY__P8 2.0
+			#define FXAA_QUALITY__P9 2.0
+			#define FXAA_QUALITY__P10 4.0
+			#define FXAA_QUALITY__P11 8.0
+		#endif
+
+		/*============================================================================
+							 FXAA QUALITY - EXTREME QUALITY
+		============================================================================*/
+		#if (FXAA_QUALITY__PRESET == 39)
+			#define FXAA_QUALITY__PS 12
+			#define FXAA_QUALITY__P0 1.0
+			#define FXAA_QUALITY__P1 1.0
+			#define FXAA_QUALITY__P2 1.0
+			#define FXAA_QUALITY__P3 1.0
+			#define FXAA_QUALITY__P4 1.0
+			#define FXAA_QUALITY__P5 1.5
+			#define FXAA_QUALITY__P6 2.0
+			#define FXAA_QUALITY__P7 2.0
+			#define FXAA_QUALITY__P8 2.0
+			#define FXAA_QUALITY__P9 2.0
+			#define FXAA_QUALITY__P10 4.0
+			#define FXAA_QUALITY__P11 8.0
+		#endif
+		
+		SamplerState gInputSamp;
+		Texture2D gInputTex;
+
+		/*============================================================================
+
+										API PORTING
+
+		============================================================================*/
+		#define FxaaBool bool
+		#define FxaaDiscard clip(-1)
+		#define FxaaFloat float
+		#define FxaaFloat2 float2
+		#define FxaaFloat3 float3
+		#define FxaaFloat4 float4
+		#define FxaaHalf half
+		#define FxaaHalf2 half2
+		#define FxaaHalf3 half3
+		#define FxaaHalf4 half4
+		#define FxaaSat(x) saturate(x)
+
+		#define FxaaInt2 int2
+		#define FxaaTexTop(p) gInputTex.SampleLevel(gInputSamp, p, 0.0)
+		#define FxaaTexOff(p, o, r) gInputTex.SampleLevel(gInputSamp, p, 0.0, o)
+		#define FxaaTexAlpha4(p) gInputTex.GatherAlpha(gInputSamp, p)
+		#define FxaaTexOffAlpha4(p, o) gInputTex.GatherAlpha(gInputSamp, p, o)
+		#define FxaaTexGreen4(p) gInputTex.GatherGreen(gInputSamp, p)
+		#define FxaaTexOffGreen4(p, o) gInputTex.GatherGreen(gInputSamp, p, o)
+
+
+		/*============================================================================
+						   GREEN AS LUMA OPTION SUPPORT FUNCTION
+		============================================================================*/
+		#if (FXAA_GREEN_AS_LUMA == 0)
+			FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; }
+		#else
+			FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; }
+		#endif    
+
+		/*============================================================================
+
+									 FXAA3 QUALITY - PC
+
+		============================================================================*/
+		FxaaFloat4 FxaaPixelShader(
+			//
+			// Use noperspective interpolation here (turn off perspective interpolation).
+			// {xy} = center of pixel
+			FxaaFloat2 pos,
+			//
+			// Only used on FXAA Quality.
+			// This must be from a constant/uniform.
+			// {x_} = 1.0/screenWidthInPixels
+			// {_y} = 1.0/screenHeightInPixels
+			FxaaFloat2 fxaaQualityRcpFrame,
+			//
+			// Only used on FXAA Quality.
+			// This used to be the FXAA_QUALITY__SUBPIX define.
+			// It is here now to allow easier tuning.
+			// Choose the amount of sub-pixel aliasing removal.
+			// This can effect sharpness.
+			//   1.00 - upper limit (softer)
+			//   0.75 - default amount of filtering
+			//   0.50 - lower limit (sharper, less sub-pixel aliasing removal)
+			//   0.25 - almost off
+			//   0.00 - completely off
+			FxaaFloat fxaaQualitySubpix,
+			//
+			// Only used on FXAA Quality.
+			// This used to be the FXAA_QUALITY__EDGE_THRESHOLD define.
+			// It is here now to allow easier tuning.
+			// The minimum amount of local contrast required to apply algorithm.
+			//   0.333 - too little (faster)
+			//   0.250 - low quality
+			//   0.166 - default
+			//   0.125 - high quality 
+			//   0.063 - overkill (slower)
+			FxaaFloat fxaaQualityEdgeThreshold,
+			//
+			// Only used on FXAA Quality.
+			// This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define.
+			// It is here now to allow easier tuning.
+			// Trims the algorithm from processing darks.
+			//   0.0833 - upper limit (default, the start of visible unfiltered edges)
+			//   0.0625 - high quality (faster)
+			//   0.0312 - visible limit (slower)
+			// Special notes when using FXAA_GREEN_AS_LUMA,
+			//   Likely want to set this to zero.
+			//   As colors that are mostly not-green
+			//   will appear very dark in the green channel!
+			//   Tune by looking at mostly non-green content,
+			//   then start at zero and increase until aliasing is a problem.
+			FxaaFloat fxaaQualityEdgeThresholdMin
+		) {
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat2 posM;
+			posM.x = pos.x;
+			posM.y = pos.y;
+			#if (FXAA_GATHER4_ALPHA == 1)
+				#if (FXAA_DISCARD == 0)
+					FxaaFloat4 rgbyM = FxaaTexTop(posM);
+					#if (FXAA_GREEN_AS_LUMA == 0)
+						#define lumaM rgbyM.w
+					#else
+						#define lumaM rgbyM.y
+					#endif
+				#endif
+				#if (FXAA_GREEN_AS_LUMA == 0)
+					FxaaFloat4 luma4A = FxaaTexAlpha4(posM);
+					FxaaFloat4 luma4B = FxaaTexOffAlpha4(posM, FxaaInt2(-1, -1));
+				#else
+					FxaaFloat4 luma4A = FxaaTexGreen4(posM);
+					FxaaFloat4 luma4B = FxaaTexOffGreen4(posM, FxaaInt2(-1, -1));
+				#endif
+				#if (FXAA_DISCARD == 1)
+					#define lumaM luma4A.w
+				#endif
+				#define lumaE luma4A.z
+				#define lumaS luma4A.x
+				#define lumaSE luma4A.y
+				#define lumaNW luma4B.w
+				#define lumaN luma4B.z
+				#define lumaW luma4B.x
+			#else
+				FxaaFloat4 rgbyM = FxaaTexTop(posM);
+				#if (FXAA_GREEN_AS_LUMA == 0)
+					#define lumaM rgbyM.w
+				#else
+					#define lumaM rgbyM.y
+				#endif
+				FxaaFloat lumaS = FxaaLuma(FxaaTexOff(posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaE = FxaaLuma(FxaaTexOff(posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaN = FxaaLuma(FxaaTexOff(posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaW = FxaaLuma(FxaaTexOff(posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy));
+			#endif
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat maxSM = max(lumaS, lumaM);
+			FxaaFloat minSM = min(lumaS, lumaM);
+			FxaaFloat maxESM = max(lumaE, maxSM);
+			FxaaFloat minESM = min(lumaE, minSM);
+			FxaaFloat maxWN = max(lumaN, lumaW);
+			FxaaFloat minWN = min(lumaN, lumaW);
+			FxaaFloat rangeMax = max(maxWN, maxESM);
+			FxaaFloat rangeMin = min(minWN, minESM);
+			FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;
+			FxaaFloat range = rangeMax - rangeMin;
+			FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);
+			FxaaBool earlyExit = range < rangeMaxClamped;
+		/*--------------------------------------------------------------------------*/
+			if(earlyExit)
+				#if (FXAA_DISCARD == 1)
+					FxaaDiscard;
+				#else
+					return rgbyM;
+				#endif
+		/*--------------------------------------------------------------------------*/
+			#if (FXAA_GATHER4_ALPHA == 0)
+				FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
+			#else
+				FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy));
+				FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
+			#endif
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat lumaNS = lumaN + lumaS;
+			FxaaFloat lumaWE = lumaW + lumaE;
+			FxaaFloat subpixRcpRange = 1.0/range;
+			FxaaFloat subpixNSWE = lumaNS + lumaWE;
+			FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS;
+			FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat lumaNESE = lumaNE + lumaSE;
+			FxaaFloat lumaNWNE = lumaNW + lumaNE;
+			FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE;
+			FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat lumaNWSW = lumaNW + lumaSW;
+			FxaaFloat lumaSWSE = lumaSW + lumaSE;
+			FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);
+			FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);
+			FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;
+			FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE;
+			FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4;
+			FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE;
+			FxaaFloat lengthSign = fxaaQualityRcpFrame.x;
+			FxaaBool horzSpan = edgeHorz >= edgeVert;
+			FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;
+		/*--------------------------------------------------------------------------*/
+			if(!horzSpan) lumaN = lumaW;
+			if(!horzSpan) lumaS = lumaE;
+			if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;
+			FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat gradientN = lumaN - lumaM;
+			FxaaFloat gradientS = lumaS - lumaM;
+			FxaaFloat lumaNN = lumaN + lumaM;
+			FxaaFloat lumaSS = lumaS + lumaM;
+			FxaaBool pairN = abs(gradientN) >= abs(gradientS);
+			FxaaFloat gradient = max(abs(gradientN), abs(gradientS));
+			if(pairN) lengthSign = -lengthSign;
+			FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat2 posB;
+			posB.x = posM.x;
+			posB.y = posM.y;
+			FxaaFloat2 offNP;
+			offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;
+			offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;
+			if(!horzSpan) posB.x += lengthSign * 0.5;
+			if( horzSpan) posB.y += lengthSign * 0.5;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat2 posN;
+			posN.x = posB.x - offNP.x * FXAA_QUALITY__P0;
+			posN.y = posB.y - offNP.y * FXAA_QUALITY__P0;
+			FxaaFloat2 posP;
+			posP.x = posB.x + offNP.x * FXAA_QUALITY__P0;
+			posP.y = posB.y + offNP.y * FXAA_QUALITY__P0;
+			FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0;
+			FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(posN));
+			FxaaFloat subpixE = subpixC * subpixC;
+			FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(posP));
+		/*--------------------------------------------------------------------------*/
+			if(!pairN) lumaNN = lumaSS;
+			FxaaFloat gradientScaled = gradient * 1.0/4.0;
+			FxaaFloat lumaMM = lumaM - lumaNN * 0.5;
+			FxaaFloat subpixF = subpixD * subpixE;
+			FxaaBool lumaMLTZero = lumaMM < 0.0;
+		/*--------------------------------------------------------------------------*/
+			lumaEndN -= lumaNN * 0.5;
+			lumaEndP -= lumaNN * 0.5;
+			FxaaBool doneN = abs(lumaEndN) >= gradientScaled;
+			FxaaBool doneP = abs(lumaEndP) >= gradientScaled;
+			if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1;
+			if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1;
+			FxaaBool doneNP = (!doneN) || (!doneP);
+			if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1;
+			if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1;
+		/*--------------------------------------------------------------------------*/
+			if(doneNP) {
+				if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+				if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+				if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+				if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+				doneN = abs(lumaEndN) >= gradientScaled;
+				doneP = abs(lumaEndP) >= gradientScaled;
+				if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2;
+				if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2;
+				doneNP = (!doneN) || (!doneP);
+				if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2;
+				if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2;
+		/*--------------------------------------------------------------------------*/
+				#if (FXAA_QUALITY__PS > 3)
+				if(doneNP) {
+					if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+					if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+					if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+					if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+					doneN = abs(lumaEndN) >= gradientScaled;
+					doneP = abs(lumaEndP) >= gradientScaled;
+					if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3;
+					if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3;
+					doneNP = (!doneN) || (!doneP);
+					if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3;
+					if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3;
+		/*--------------------------------------------------------------------------*/
+					#if (FXAA_QUALITY__PS > 4)
+					if(doneNP) {
+						if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+						if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+						if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+						if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+						doneN = abs(lumaEndN) >= gradientScaled;
+						doneP = abs(lumaEndP) >= gradientScaled;
+						if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4;
+						if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4;
+						doneNP = (!doneN) || (!doneP);
+						if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4;
+						if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4;
+		/*--------------------------------------------------------------------------*/
+						#if (FXAA_QUALITY__PS > 5)
+						if(doneNP) {
+							if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+							if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+							if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+							if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+							doneN = abs(lumaEndN) >= gradientScaled;
+							doneP = abs(lumaEndP) >= gradientScaled;
+							if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5;
+							if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5;
+							doneNP = (!doneN) || (!doneP);
+							if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5;
+							if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5;
+		/*--------------------------------------------------------------------------*/
+							#if (FXAA_QUALITY__PS > 6)
+							if(doneNP) {
+								if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+								if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+								if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+								if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+								doneN = abs(lumaEndN) >= gradientScaled;
+								doneP = abs(lumaEndP) >= gradientScaled;
+								if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6;
+								if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6;
+								doneNP = (!doneN) || (!doneP);
+								if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6;
+								if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6;
+		/*--------------------------------------------------------------------------*/
+								#if (FXAA_QUALITY__PS > 7)
+								if(doneNP) {
+									if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+									if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+									if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+									if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+									doneN = abs(lumaEndN) >= gradientScaled;
+									doneP = abs(lumaEndP) >= gradientScaled;
+									if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7;
+									if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7;
+									doneNP = (!doneN) || (!doneP);
+									if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7;
+									if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7;
+		/*--------------------------------------------------------------------------*/
+			#if (FXAA_QUALITY__PS > 8)
+			if(doneNP) {
+				if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+				if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+				if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+				if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+				doneN = abs(lumaEndN) >= gradientScaled;
+				doneP = abs(lumaEndP) >= gradientScaled;
+				if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8;
+				if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8;
+				doneNP = (!doneN) || (!doneP);
+				if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8;
+				if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8;
+		/*--------------------------------------------------------------------------*/
+				#if (FXAA_QUALITY__PS > 9)
+				if(doneNP) {
+					if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+					if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+					if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+					if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+					doneN = abs(lumaEndN) >= gradientScaled;
+					doneP = abs(lumaEndP) >= gradientScaled;
+					if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9;
+					if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9;
+					doneNP = (!doneN) || (!doneP);
+					if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9;
+					if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9;
+		/*--------------------------------------------------------------------------*/
+					#if (FXAA_QUALITY__PS > 10)
+					if(doneNP) {
+						if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+						if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+						if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+						if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+						doneN = abs(lumaEndN) >= gradientScaled;
+						doneP = abs(lumaEndP) >= gradientScaled;
+						if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10;
+						if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10;
+						doneNP = (!doneN) || (!doneP);
+						if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10;
+						if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10;
+		/*--------------------------------------------------------------------------*/
+						#if (FXAA_QUALITY__PS > 11)
+						if(doneNP) {
+							if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+							if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+							if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+							if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+							doneN = abs(lumaEndN) >= gradientScaled;
+							doneP = abs(lumaEndP) >= gradientScaled;
+							if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11;
+							if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11;
+							doneNP = (!doneN) || (!doneP);
+							if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11;
+							if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11;
+		/*--------------------------------------------------------------------------*/
+							#if (FXAA_QUALITY__PS > 12)
+							if(doneNP) {
+								if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(posN.xy));
+								if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(posP.xy));
+								if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+								if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+								doneN = abs(lumaEndN) >= gradientScaled;
+								doneP = abs(lumaEndP) >= gradientScaled;
+								if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12;
+								if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12;
+								doneNP = (!doneN) || (!doneP);
+								if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12;
+								if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12;
+		/*--------------------------------------------------------------------------*/
+							}
+							#endif
+		/*--------------------------------------------------------------------------*/
+						}
+						#endif
+		/*--------------------------------------------------------------------------*/
+					}
+					#endif
+		/*--------------------------------------------------------------------------*/
+				}
+				#endif
+		/*--------------------------------------------------------------------------*/
+			}
+			#endif
+		/*--------------------------------------------------------------------------*/
+								}
+								#endif
+		/*--------------------------------------------------------------------------*/
+							}
+							#endif
+		/*--------------------------------------------------------------------------*/
+						}
+						#endif
+		/*--------------------------------------------------------------------------*/
+					}
+					#endif
+		/*--------------------------------------------------------------------------*/
+				}
+				#endif
+		/*--------------------------------------------------------------------------*/
+			}
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat dstN = posM.x - posN.x;
+			FxaaFloat dstP = posP.x - posM.x;
+			if(!horzSpan) dstN = posM.y - posN.y;
+			if(!horzSpan) dstP = posP.y - posM.y;
+		/*--------------------------------------------------------------------------*/
+			FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;
+			FxaaFloat spanLength = (dstP + dstN);
+			FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;
+			FxaaFloat spanLengthRcp = 1.0/spanLength;
+		/*--------------------------------------------------------------------------*/
+			FxaaBool directionN = dstN < dstP;
+			FxaaFloat dst = min(dstN, dstP);
+			FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP;
+			FxaaFloat subpixG = subpixF * subpixF;
+			FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5;
+			FxaaFloat subpixH = subpixG * fxaaQualitySubpix;
+		/*--------------------------------------------------------------------------*/
+			FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0;
+			FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
+			if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;
+			if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;
+			#if (FXAA_DISCARD == 1)
+				return FxaaTexTop(posM);
+			#else
+				return FxaaFloat4(FxaaTexTop(posM).xyz, lumaM);
+			#endif
+		}
+		/*==========================================================================*/
+
+		[internal]
+		cbuffer Input
+		{
+			float2 gInvTexSize;
+		}		
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			return FxaaPixelShader(input.uv0, gInvTexSize, 0.75f, 0.166f, 0.0833f);
+		}	
+	};
+};

+ 32 - 0
Source/bsf/Data/Raw/Shaders/PPFilmGrain.bsl

@@ -0,0 +1,32 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+shader PPFilmGrain
+{
+	mixin PPBase;
+
+	code
+	{
+		[internal]
+		cbuffer Params
+		{
+			float gIntensity;
+			float gTime;
+		}	
+	
+		SamplerState gInputSamp
+		{
+			Filter = MIN_MAG_MIP_POINT;
+		};
+		Texture2D gInputTex;
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float2 uv = input.uv0;
+            float x = (uv.x + 4.0f) * (uv.y + 4.0f) * gTime;
+			float4 grain = (fmod((fmod(x, 13.0f) + 1.0f) * (fmod(x, 123.0f) + 1.0f), 0.01f) - 0.005f) * gIntensity;
+			
+			float4 color = gInputTex.Sample(gInputSamp, uv);
+			return color + grain;
+		}
+	};
+};

+ 49 - 0
Source/bsf/Data/Raw/Shaders/PPGaussianBlur.bsl

@@ -0,0 +1,49 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\PPGaussianBlurCommon.bslinc"
+
+shader PPGaussianBlur
+{
+	mixin PPBase;
+	mixin PPGaussianBlurCommon;
+	
+	variations
+	{
+		ADDITIVE = { false, true };
+	};
+
+	code
+	{
+		Texture2D gInputTex;
+		
+		[alias(gInputTex)]
+		SamplerState gInputSamp
+		{
+			AddressU = BORDER;
+			AddressV = BORDER;
+			BorderColor = 0;
+		};
+		
+		#if ADDITIVE
+		Texture2D gAdditiveTex;
+		
+		[alias(gAdditiveTex)]
+		SamplerState gAdditiveSamp
+		{
+			AddressU = BORDER;
+			AddressV = BORDER;
+			BorderColor = 0;
+		};
+		#endif
+		
+		float4 fsmain(VStoFS input) : SV_Target0
+		{
+			float4 output = gaussianBlur(gInputTex, gInputSamp, input.uv0);
+		
+			#if ADDITIVE
+				output += gAdditiveTex.SampleLevel(gAdditiveSamp, input.uv0, 0);
+			#endif
+			
+			return output;
+		}	
+	};
+};

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov