Prechádzať zdrojové kódy

Skybox rendering & cubemap import fixes

BearishSun 9 rokov pred
rodič
commit
e32b4ad844

+ 4 - 0
Data/Raw/Engine/DataList.json

@@ -496,6 +496,10 @@
             "Path": "Resolve.bsl",
             "Path": "Resolve.bsl",
             "UUID": "9d5f5101-2d7e-432c-b8ad-1998de9ca5c7"
             "UUID": "9d5f5101-2d7e-432c-b8ad-1998de9ca5c7"
         },
         },
+        {
+            "Path": "Skybox.bsl",
+            "UUID": "b1c191fa-0c24-4987-8d3b-72b17511621d"
+        },		
         {
         {
             "Path": "SpriteImageAlpha.bsl",
             "Path": "SpriteImageAlpha.bsl",
             "UUID": "4c00537f-9d3e-4cb7-8a30-b4ee9f278006"
             "UUID": "4c00537f-9d3e-4cb7-8a30-b4ee9f278006"

+ 91 - 0
Data/Raw/Engine/Shaders/Skybox.bsl

@@ -0,0 +1,91 @@
+#include "$ENGINE$\PerCameraData.bslinc"
+
+Parameters =
+{
+	SamplerCUBE 	gSkySamp : alias("gSkyTex");
+	TextureCUBE  	gSkyTex;
+};
+
+Technique : inherits("PerCameraData") =
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		Cull = CW;
+		CompareFunc = LTE;
+		DepthWrite = false;
+		
+		Vertex =
+		{
+			void main(
+				in float3 inPos : POSITION,
+				out float4 oPosition : SV_Position,
+				out float3 oDir : TEXCOORD0)
+			{
+				float4 pos = mul(gMatViewProj, float4(inPos.xyz + gViewOrigin, 1));
+			
+				// Set Z = W so that final depth is 1.0f and it renders behind everything else
+				oPosition = pos.xyww;
+				oDir = inPos;
+			}
+		};
+		
+		Fragment =
+		{
+			TextureCube gSkyTex : register(t0);
+			SamplerState gSkySamp : register(s0);
+		
+			float4 main(
+				in float4 inPos : SV_Position, 
+				in float3 dir : TEXCOORD0) : SV_Target
+			{
+				return gSkyTex.Sample(gSkySamp, dir);
+			}
+		};	
+	};
+};
+
+Technique : inherits("PerCameraData") =
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Cull = CW;
+		CompareFunc = LTE;
+		DepthWrite = false;
+		
+		Vertex = 
+		{
+			layout(location = 0) in vec3 bs_position;
+			layout(location = 0) out vec3 dir;	
+		
+			out gl_PerVertex
+			{
+				vec4 gl_Position;
+			};
+		
+			void main()
+			{
+				vec4 pos = gMatViewProj * vec4(bs_position.xyz + gViewOrigin, 1);
+			
+				// Set Z = W so that final depth is 1.0f and it renders behind everything else
+				gl_Position = pos.xyww;
+				dir = bs_position;
+			}
+		};
+	
+		Fragment =
+		{
+			layout(location = 0) in vec3 dir;		
+			layout(binding = 1) uniform samplerCube gSkyTex;
+			layout(location = 0) out vec4 fragColor;
+			
+			void main()
+			{
+				fragColor = texture(gSkyTex, dir);
+			}	
+		};
+	};
+};

+ 8 - 5
Source/BansheeCore/Include/BsCCamera.h

@@ -136,15 +136,18 @@ namespace bs
 		/** @copydoc Camera::setLayers */
 		/** @copydoc Camera::setLayers */
 		void setLayers(UINT64 layers) { mInternal->setLayers(layers); }
 		void setLayers(UINT64 layers) { mInternal->setLayers(layers); }
 
 
-		/** Returns number of samples if the camera uses multiple samples per pixel. */
+		/** @copydoc Camera::getMSAACount */
 		UINT32 getMSAACount() const { return mInternal->getMSAACount(); }
 		UINT32 getMSAACount() const { return mInternal->getMSAACount(); }
 
 
-		/**
-		* Enables or disables multi-sampled anti-aliasing. Set to zero or one to disable, or to the required number of
-		* samples to enable.
-		*/
+		/** @copydoc Camera::setMSAACount */
 		void setMSAACount(UINT32 count) { mInternal->setMSAACount(count); }
 		void setMSAACount(UINT32 count) { mInternal->setMSAACount(count); }
 
 
+		/** @copydoc Camera::setSkybox */
+		void setSkybox(const HTexture& texture) { mInternal->setSkybox(texture); }
+
+		/** @copydoc Camera::getSkybox */
+		HTexture getSkybox() const { return mInternal->getSkybox(); }
+
 		/** Returns settings that are used for controling post-process operations like tonemapping. */
 		/** Returns settings that are used for controling post-process operations like tonemapping. */
 		const SPtr<PostProcessSettings>& getPostProcessSettings() const { return mInternal->getPostProcessSettings(); }
 		const SPtr<PostProcessSettings>& getPostProcessSettings() const { return mInternal->getPostProcessSettings(); }
 
 

+ 22 - 2
Source/BansheeCore/Include/BsCamera.h

@@ -506,13 +506,33 @@ namespace bs
 		mutable AABox mBoundingBox; /**< Frustum bounding box. */
 		mutable AABox mBoundingBox; /**< Frustum bounding box. */
      };
      };
 
 
+	 /** @copydoc CameraBase */
+	 template<bool Core>
+	 class TCamera : public CameraBase
+	 {
+	 public:
+		 typedef typename TTextureType<Core>::Type TextureType;
+
+		/** 
+		 * Sets a texture that will be used for rendering areas of the camera's render target not covered by any geometry. 
+		 * If not set a clear color will be used instead.
+		 */
+		void setSkybox(const TextureType& texture) { mSkyTexture = texture; _markCoreDirty(); }
+
+		/** @see setSkybox() */
+		TextureType getSkybox() const { return mSkyTexture; }
+
+	 protected:
+		 TextureType mSkyTexture;
+	 };
+
 	/** @} */
 	/** @} */
 	/** @addtogroup Renderer-Engine-Internal
 	/** @addtogroup Renderer-Engine-Internal
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
 	/** @copydoc CameraBase */
 	/** @copydoc CameraBase */
-	class BS_CORE_EXPORT CameraCore : public CoreObjectCore, public CameraBase
+	class BS_CORE_EXPORT CameraCore : public CoreObjectCore, public TCamera<true>
 	{
 	{
 	public:
 	public:
 		~CameraCore();
 		~CameraCore();
@@ -541,7 +561,7 @@ namespace bs
 	};
 	};
 
 
 	/** @copydoc CameraBase */
 	/** @copydoc CameraBase */
-	class BS_CORE_EXPORT Camera : public IReflectable, public CoreObject, public CameraBase
+	class BS_CORE_EXPORT Camera : public IReflectable, public CoreObject, public TCamera<false>
     {
     {
     public:
     public:
 		/**	Returns the viewport used by the camera. */	
 		/**	Returns the viewport used by the camera. */	

+ 1 - 0
Source/BansheeCore/Include/BsCameraRTTI.h

@@ -42,6 +42,7 @@ namespace bs
 			BS_RTTI_MEMBER_PLAIN(mMSAA, 23)
 			BS_RTTI_MEMBER_PLAIN(mMSAA, 23)
 			/** BS_RTTI_MEMBER_PLAIN(mPPSettings, 24) */
 			/** BS_RTTI_MEMBER_PLAIN(mPPSettings, 24) */
 			BS_RTTI_MEMBER_REFLPTR(mPPSettings, 25)
 			BS_RTTI_MEMBER_REFLPTR(mPPSettings, 25)
+			BS_RTTI_MEMBER_REFL(mSkyTexture, 26)
 		BS_END_RTTI_MEMBERS
 		BS_END_RTTI_MEMBERS
 			
 			
 	public:
 	public:

+ 4 - 0
Source/BansheeCore/Include/BsCommonTypes.h

@@ -600,6 +600,10 @@ namespace bs
 	template<> struct TMaterialPtrType < false > { typedef HMaterial Type; };
 	template<> struct TMaterialPtrType < false > { typedef HMaterial Type; };
 	template<> struct TMaterialPtrType < true > { typedef SPtr<MaterialCore> Type; };
 	template<> struct TMaterialPtrType < true > { typedef SPtr<MaterialCore> Type; };
 
 
+	template<bool Core> struct TTextureType {};
+	template<> struct TTextureType < false > { typedef HTexture Type; };
+	template<> struct TTextureType < true > { typedef SPtr<TextureCore> Type; };
+
 	/** @cond SPECIALIZATIONS */
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(TextureSurface);
 	BS_ALLOW_MEMCPY_SERIALIZATION(TextureSurface);
 	/** @endcond */
 	/** @endcond */

+ 1 - 1
Source/BansheeCore/Include/BsPixelData.h

@@ -312,7 +312,7 @@ namespace bs
 		 * Returns pixel data containing a sub-volume of this object. Returned data will not have its own buffer, but will
 		 * Returns pixel data containing a sub-volume of this object. Returned data will not have its own buffer, but will
 		 * instead point to this one. It is up to the caller to ensure this object outlives any sub-volume objects.
 		 * instead point to this one. It is up to the caller to ensure this object outlives any sub-volume objects.
 		 */
 		 */
-      	PixelData getSubVolume(const PixelVolume &def) const;
+      	PixelData getSubVolume(const PixelVolume& volume) const;
         
         
 		/** 
 		/** 
 		 * Samples a color at the specified coordinates using a specific filter.
 		 * Samples a color at the specified coordinates using a specific filter.

+ 22 - 0
Source/BansheeCore/Include/BsPixelUtil.h

@@ -44,6 +44,17 @@ namespace bs
 		Kaiser
 		Kaiser
 	};
 	};
 
 
+	/** Determines on which axes to mirror an image. */
+	enum class MirrorModeBits
+	{
+		X = 1 << 0,
+		Y = 1 << 1, 
+		Z = 1 << 2
+	};
+
+	typedef Flags<MirrorModeBits> MirrorMode;
+	BS_FLAGS_OPERATORS(MirrorModeBits);
+
 	/**	Options used to control texture compression. */
 	/**	Options used to control texture compression. */
 	struct CompressionOptions
 	struct CompressionOptions
 	{
 	{
@@ -229,6 +240,17 @@ namespace bs
 		 */
 		 */
 		static void scale(const PixelData& src, PixelData& dst, Filter filter = FILTER_LINEAR);
 		static void scale(const PixelData& src, PixelData& dst, Filter filter = FILTER_LINEAR);
 
 
+		/** 
+		 * Mirrors the contents of the provided object along the X, Y and/or Z axes. */
+		static void mirror(PixelData& pixelData, MirrorMode mode);
+
+		/**
+		 * Copies the contents of the @p src buffer into the @p dst buffer. The size of the copied contents is determined
+		 * by the size of the @p dst buffer. First pixel copied from @p src is determined by offset provided in
+		 * @p offsetX, @p offsetY and @p offsetZ parameters.
+		 */
+		static void copy(const PixelData& src, PixelData& dst, UINT32 offsetX = 0, UINT32 offsetY = 0, UINT32 offsetZ = 0);
+
 		/**
 		/**
 		 * Applies gamma correction to the pixels in the provided buffer.
 		 * Applies gamma correction to the pixels in the provided buffer.
 		 *
 		 *

+ 0 - 4
Source/BansheeCore/Include/BsShader.h

@@ -59,10 +59,6 @@ namespace bs
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
-	template<bool Core> struct TTextureType {};
-	template<> struct TTextureType < false > { typedef HTexture Type; };
-	template<> struct TTextureType < true > { typedef SPtr<TextureCore> Type; };
-
 	template<bool Core> struct TSamplerStateType {};
 	template<bool Core> struct TSamplerStateType {};
 	template<> struct TSamplerStateType < false > { typedef SPtr<SamplerState> Type; };
 	template<> struct TSamplerStateType < false > { typedef SPtr<SamplerState> Type; };
 	template<> struct TSamplerStateType < true > { typedef SPtr<SamplerStateCore> Type; };
 	template<> struct TSamplerStateType < true > { typedef SPtr<SamplerStateCore> Type; };

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

@@ -778,6 +778,11 @@ namespace bs
 			dataPtr = rttiReadElem(mIsActive, dataPtr);
 			dataPtr = rttiReadElem(mIsActive, dataPtr);
 			dataPtr = rttiReadElem(mMSAA, dataPtr);
 			dataPtr = rttiReadElem(mMSAA, dataPtr);
 
 
+			SPtr<TextureCore>* skyTexture = (SPtr<TextureCore>*)dataPtr;
+			mSkyTexture = *skyTexture;
+			skyTexture->~SPtr<TextureCore>();
+			dataPtr += sizeof(SPtr<TextureCore>);
+
 			UINT32 ppSize = 0;
 			UINT32 ppSize = 0;
 			dataPtr = rttiReadElem(ppSize, dataPtr);
 			dataPtr = rttiReadElem(ppSize, dataPtr);
 
 
@@ -845,6 +850,10 @@ namespace bs
 	{
 	{
 		UINT32 dirtyFlag = getCoreDirtyFlags();
 		UINT32 dirtyFlag = getCoreDirtyFlags();
 
 
+		SPtr<TextureCore> skyTexture;
+		if (mSkyTexture.isLoaded())
+			skyTexture = mSkyTexture->getCore();
+
 		UINT32 size = 0;
 		UINT32 size = 0;
 		size += rttiGetElemSize(dirtyFlag);
 		size += rttiGetElemSize(dirtyFlag);
 		size += rttiGetElemSize(mPosition);
 		size += rttiGetElemSize(mPosition);
@@ -867,7 +876,7 @@ namespace bs
 			size += rttiGetElemSize(mCameraFlags);
 			size += rttiGetElemSize(mCameraFlags);
 			size += rttiGetElemSize(mIsActive);
 			size += rttiGetElemSize(mIsActive);
 			size += rttiGetElemSize(mMSAA);
 			size += rttiGetElemSize(mMSAA);
-
+			size += sizeof(SPtr<TextureCore>);
 			size += sizeof(UINT32);
 			size += sizeof(UINT32);
 
 
 			if(mPPSettings != nullptr)
 			if(mPPSettings != nullptr)
@@ -900,6 +909,11 @@ namespace bs
 			dataPtr = rttiWriteElem(mCameraFlags, dataPtr);
 			dataPtr = rttiWriteElem(mCameraFlags, dataPtr);
 			dataPtr = rttiWriteElem(mIsActive, dataPtr);
 			dataPtr = rttiWriteElem(mIsActive, dataPtr);
 			dataPtr = rttiWriteElem(mMSAA, dataPtr);
 			dataPtr = rttiWriteElem(mMSAA, dataPtr);
+
+			SPtr<TextureCore>* skyTexDest = new (dataPtr) SPtr<TextureCore>();
+			*skyTexDest = skyTexture;
+			dataPtr += sizeof(skyTexture);
+
 			dataPtr = rttiWriteElem(ppSize, dataPtr);
 			dataPtr = rttiWriteElem(ppSize, dataPtr);
 
 
 			if(mPPSettings != nullptr)
 			if(mPPSettings != nullptr)

+ 11 - 11
Source/BansheeCore/Source/BsPixelData.cpp

@@ -58,12 +58,12 @@ namespace bs
 		return PixelUtil::getMemorySize(mRowPitch, mSlicePitch / mRowPitch, getDepth(), getFormat());
 		return PixelUtil::getMemorySize(mRowPitch, mSlicePitch / mRowPitch, getDepth(), getFormat());
 	}
 	}
 
 
-	PixelData PixelData::getSubVolume(const PixelVolume &def) const
+	PixelData PixelData::getSubVolume(const PixelVolume& volume) const
 	{
 	{
 		if (PixelUtil::isCompressed(mFormat))
 		if (PixelUtil::isCompressed(mFormat))
 		{
 		{
-			if (def.left == getLeft() && def.top == getTop() && def.front == getFront() &&
-				def.right == getRight() && def.bottom == getBottom() && def.back == getBack())
+			if (volume.left == getLeft() && volume.top == getTop() && volume.front == getFront() &&
+				volume.right == getRight() && volume.bottom == getBottom() && volume.back == getBack())
 			{
 			{
 				// Entire buffer is being queried
 				// Entire buffer is being queried
 				return *this;
 				return *this;
@@ -72,28 +72,28 @@ namespace bs
 			BS_EXCEPT(InvalidParametersException, "Cannot return subvolume of compressed PixelBuffer");
 			BS_EXCEPT(InvalidParametersException, "Cannot return subvolume of compressed PixelBuffer");
 		}
 		}
 
 
-		if (!mExtents.contains(def))
+		if (!mExtents.contains(volume))
 		{
 		{
 			BS_EXCEPT(InvalidParametersException, "Bounds out of range");
 			BS_EXCEPT(InvalidParametersException, "Bounds out of range");
 		}
 		}
 
 
 		const size_t elemSize = PixelUtil::getNumElemBytes(mFormat);
 		const size_t elemSize = PixelUtil::getNumElemBytes(mFormat);
-		PixelData rval(def.getWidth(), def.getHeight(), def.getDepth(), mFormat);
+		PixelData rval(volume.getWidth(), volume.getHeight(), volume.getDepth(), mFormat);
 
 
-		rval.setExternalBuffer(((UINT8*)getData()) + ((def.left - getLeft())*elemSize)
-			+ ((def.top - getTop())*mRowPitch*elemSize)
-			+ ((def.front - getFront())*mSlicePitch*elemSize));
+		rval.setExternalBuffer(((UINT8*)getData()) + ((volume.left - getLeft())*elemSize)
+			+ ((volume.top - getTop())*mRowPitch*elemSize)
+			+ ((volume.front - getFront())*mSlicePitch*elemSize));
 
 
-		rval.mRowPitch = mRowPitch;
-		rval.mSlicePitch = mSlicePitch;
 		rval.mFormat = mFormat;
 		rval.mFormat = mFormat;
+		PixelUtil::getPitch(volume.getWidth(), volume.getHeight(), volume.getDepth(), mFormat, rval.mRowPitch,
+							rval.mSlicePitch);
 
 
 		return rval;
 		return rval;
 	}
 	}
 
 
 	Color PixelData::sampleColorAt(const Vector2& coords, TextureFilter filter) const
 	Color PixelData::sampleColorAt(const Vector2& coords, TextureFilter filter) const
 	{
 	{
-		Vector2 pixelCoords = coords * Vector2(mExtents.getWidth(), mExtents.getHeight());
+		Vector2 pixelCoords = coords * Vector2((float)mExtents.getWidth(), (float)mExtents.getHeight());
 
 
 		INT32 maxExtentX = std::max(0, (INT32)mExtents.getWidth() - 1);
 		INT32 maxExtentX = std::max(0, (INT32)mExtents.getWidth() - 1);
 		INT32 maxExtentY = std::max(0, (INT32)mExtents.getHeight() - 1);
 		INT32 maxExtentY = std::max(0, (INT32)mExtents.getHeight() - 1);

+ 132 - 0
Source/BansheeCore/Source/BsPixelUtil.cpp

@@ -1679,6 +1679,138 @@ namespace bs
 		}
 		}
 	}
 	}
 
 
+	void PixelUtil::copy(const PixelData& src, PixelData& dst, UINT32 offsetX, UINT32 offsetY, UINT32 offsetZ)
+	{
+		if(src.getFormat() != dst.getFormat())
+		{
+			LOGERR("Source format is different from destination format for copy(). This operation cannot be used for "
+				   "a format conversion. Aborting copy.");
+			return;
+		}
+
+		UINT32 right = offsetX + dst.getWidth();
+		UINT32 bottom = offsetY + dst.getHeight();
+		UINT32 back = offsetZ + dst.getDepth();
+
+		if(right > src.getWidth() || bottom > src.getHeight() || back > src.getDepth())
+		{
+			LOGERR("Provided offset or destination size is too large and is referencing pixels that are out of bounds"
+				   " on the source texture. Aborting copy().");
+			return;
+		}
+
+		UINT8* srcPtr = (UINT8*)src.getData() + offsetZ * src.getSlicePitch();
+		UINT8* dstPtr = (UINT8*)dst.getData();
+
+		UINT32 elemSize = getNumElemBytes(dst.getFormat());
+		UINT32 rowSize = dst.getWidth() * elemSize;
+
+		for(UINT32 z = 0; z < dst.getDepth(); z++)
+		{
+			UINT8* srcRowPtr = srcPtr + offsetY * src.getRowPitch() * elemSize;
+			UINT8* dstRowPtr = dstPtr;
+
+			for(UINT32 y = 0; y < dst.getHeight(); y++)
+			{
+				memcpy(dstRowPtr, srcRowPtr + offsetX * elemSize, rowSize);
+
+				srcRowPtr += src.getRowPitch() * elemSize;
+				dstRowPtr += dst.getRowPitch() * elemSize;
+			}
+
+			srcPtr += src.getSlicePitch() * elemSize;
+			dstPtr += dst.getSlicePitch() * elemSize;
+		}
+	}
+
+	void PixelUtil::mirror(PixelData& pixelData, MirrorMode mode)
+	{
+		UINT32 width = pixelData.getWidth();
+		UINT32 height = pixelData.getHeight();
+		UINT32 depth = pixelData.getDepth();
+
+		UINT32 elemSize = getNumElemBytes(pixelData.getFormat());
+
+		if (mode.isSet(MirrorModeBits::Z))
+		{
+			UINT32 sliceSize = width * height * elemSize;
+			UINT8* sliceTemp = bs_stack_alloc<UINT8>(sliceSize);
+
+			UINT8* dataPtr = pixelData.getData();
+			UINT32 halfDepth = depth / 2;
+			for (UINT32 z = 0; z < halfDepth; z++)
+			{
+				UINT32 srcZ = z * sliceSize;
+				UINT32 dstZ = (depth - z - 1) * sliceSize;
+
+				memcpy(sliceTemp, &dataPtr[dstZ], sliceSize);
+				memcpy(&dataPtr[srcZ], &dataPtr[srcZ], sliceSize);
+				memcpy(&dataPtr[dstZ], sliceTemp, sliceSize);
+			}
+
+			// Note: If flipping Y or X as well I could do it here without an extra set of memcpys
+
+			bs_stack_free(sliceTemp);
+		}
+
+		if(mode.isSet(MirrorModeBits::Y))
+		{
+			UINT32 rowSize = width * elemSize;
+			UINT8* rowTemp = bs_stack_alloc<UINT8>(rowSize);
+
+			UINT8* slicePtr = pixelData.getData();
+			for (UINT32 z = 0; z < depth; z++)
+			{
+				UINT32 halfHeight = height / 2;
+				for (UINT32 y = 0; y < halfHeight; y++)
+				{
+					UINT32 srcY = y * rowSize;
+					UINT32 dstY = (height - y - 1) * rowSize;
+
+					memcpy(rowTemp, &slicePtr[dstY], rowSize);
+					memcpy(&slicePtr[dstY], &slicePtr[srcY], rowSize);
+					memcpy(&slicePtr[srcY], rowTemp, rowSize);
+				}
+
+				// Note: If flipping X as well I could do it here without an extra set of memcpys
+
+				slicePtr += pixelData.getSlicePitch() * elemSize;
+			}
+
+			bs_stack_free(rowTemp);
+		}
+
+		if (mode.isSet(MirrorModeBits::X))
+		{
+			UINT8* elemTemp = bs_stack_alloc<UINT8>(elemSize);
+
+			UINT8* slicePtr = pixelData.getData();
+			for (UINT32 z = 0; z < depth; z++)
+			{
+				UINT8* rowPtr = slicePtr;
+				for (UINT32 y = 0; y < height; y++)
+				{
+					UINT32 halfWidth = width / 2;
+					for (UINT32 x = 0; x < halfWidth; x++)
+					{
+						UINT32 srcX = x * elemSize;
+						UINT32 dstX = (width - x - 1) * elemSize;
+
+						memcpy(elemTemp, &rowPtr[dstX], elemSize);
+						memcpy(&rowPtr[dstX], &rowPtr[srcX], elemSize);
+						memcpy(&rowPtr[srcX], elemTemp, elemSize);
+					}
+
+					rowPtr += pixelData.getRowPitch() * elemSize;
+				}
+
+				slicePtr += pixelData.getSlicePitch() * elemSize;
+			}
+
+			bs_stack_free(elemTemp);
+		}
+	}
+
 	void PixelUtil::applyGamma(UINT8* buffer, float gamma, UINT32 size, UINT8 bpp)
 	void PixelUtil::applyGamma(UINT8* buffer, float gamma, UINT32 size, UINT8 bpp)
 	{
 	{
 		if(gamma == 1.0f)
 		if(gamma == 1.0f)

+ 2 - 1
Source/BansheeD3D11RenderAPI/Source/BsD3D11Texture.cpp

@@ -375,7 +375,8 @@ namespace bs
 
 
 		if (format != D3D11Mappings::getPF(d3dPF))
 		if (format != D3D11Mappings::getPF(d3dPF))
 		{
 		{
-			BS_EXCEPT(RenderingAPIException, "Provided pixel format is not supported by the driver: " + toString(format));
+			LOGWRN("Provided pixel format is not supported by the driver: " + toString(format) + ". Using " +
+				toString(closestFormat) + " instead.");
 		}
 		}
 
 
 		mDXGIColorFormat = d3dPF;
 		mDXGIColorFormat = d3dPF;

+ 4 - 0
Source/BansheeEngine/Include/BsRendererUtility.h

@@ -135,10 +135,14 @@ namespace bs
 		/** Returns a stencil mesh used for spot light. Actual vertex positions need to be computed in shader. */
 		/** Returns a stencil mesh used for spot light. Actual vertex positions need to be computed in shader. */
 		SPtr<MeshCore> getSpotLightStencil() const { return mSpotLightStencilMesh; }
 		SPtr<MeshCore> getSpotLightStencil() const { return mSpotLightStencilMesh; }
 
 
+		/** Returns a mesh that can be used for rendering a skybox. */
+		SPtr<MeshCore> getSkyBoxMesh() const { return mSkyBoxMesh; }
+
 	private:
 	private:
 		SPtr<MeshCore> mFullScreenQuadMesh;
 		SPtr<MeshCore> mFullScreenQuadMesh;
 		SPtr<MeshCore> mPointLightStencilMesh;
 		SPtr<MeshCore> mPointLightStencilMesh;
 		SPtr<MeshCore> mSpotLightStencilMesh;
 		SPtr<MeshCore> mSpotLightStencilMesh;
+		SPtr<MeshCore> mSkyBoxMesh;
 		SPtr<ResolveMat> mResolveMat;
 		SPtr<ResolveMat> mResolveMat;
 		SPtr<BlitMat> mBlitMat;
 		SPtr<BlitMat> mBlitMat;
 	};
 	};

+ 20 - 0
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -107,6 +107,26 @@ namespace bs
 			mSpotLightStencilMesh = MeshCore::create(meshData);
 			mSpotLightStencilMesh = MeshCore::create(meshData);
 		}
 		}
 
 
+		{
+			SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+			vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+
+			UINT32 numVertices = 0;
+			UINT32 numIndices = 0;
+
+			ShapeMeshes3D::getNumElementsAABox(numVertices, numIndices);
+			SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(numVertices, numIndices, vertexDesc);
+
+			UINT32* indexData = meshData->getIndices32();
+			UINT8* positionData = meshData->getElementData(VES_POSITION);
+
+			AABox localBox(-Vector3::ONE * 1500.0f, Vector3::ONE * 1500.0f);
+			ShapeMeshes3D::solidAABox(localBox, positionData, nullptr, 0,
+									   vertexDesc->getVertexStride(), indexData, 0);
+
+			mSkyBoxMesh = MeshCore::create(meshData);
+		}
+
 		// TODO - When I add proper preprocessor support, merge these into a single material
 		// TODO - When I add proper preprocessor support, merge these into a single material
 		mResolveMat = bs_shared_ptr_new<ResolveMat>();
 		mResolveMat = bs_shared_ptr_new<ResolveMat>();
 		mBlitMat = bs_shared_ptr_new<BlitMat>();
 		mBlitMat = bs_shared_ptr_new<BlitMat>();

+ 9 - 6
Source/BansheeEngine/Source/BsShapeMeshes3D.cpp

@@ -447,13 +447,16 @@ namespace bs
 			Vector3(0, -1, 0)
 			Vector3(0, -1, 0)
 		};
 		};
 
 
-		outNormals += (vertexOffset * vertexStride);
-		for (UINT32 face = 0; face < 6; face++)
+		if (outNormals != nullptr)
 		{
 		{
-			outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
-			outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
-			outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
-			outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
+			outNormals += (vertexOffset * vertexStride);
+			for (UINT32 face = 0; face < 6; face++)
+			{
+				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
+				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
+				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
+				outNormals = writeVector3(outNormals, vertexStride, faceNormals[face]);
+			}
 		}
 		}
 
 
 		UINT32* indices = outIndices + indexOffset;
 		UINT32* indices = outIndices + indexOffset;

+ 11 - 13
Source/BansheeFreeImgImporter/Source/BsFreeImgImporter.cpp

@@ -412,10 +412,7 @@ namespace bs
 				faceStart.x += faceSize;
 				faceStart.x += faceSize;
 
 
 			PixelVolume volume(faceStart.x, faceStart.y, faceStart.x + faceSize, faceStart.y + faceSize);
 			PixelVolume volume(faceStart.x, faceStart.y, faceStart.x + faceSize, faceStart.y + faceSize);
-			PixelData subVolumeData = source->getSubVolume(volume);
-
-			assert(output[i]->getSize() == subVolumeData.getSize());
-			memcpy(output[i]->getData(), subVolumeData.getData(), subVolumeData.getSize());
+			PixelUtil::copy(*source, *output[i], faceStart.x, faceStart.y);
 		}
 		}
 	}
 	}
 
 
@@ -424,13 +421,13 @@ namespace bs
 	 * 
 	 * 
 	 * Vertical layout:
 	 * Vertical layout:
 	 *    +Y
 	 *    +Y
-	 * -X -Z +X
+	 * -X +Z +X
 	 *    -Y
 	 *    -Y
-	 *    +Z
+	 *    -Z
 	 * 
 	 * 
 	 * Horizontal layout:
 	 * Horizontal layout:
 	 *    +Y
 	 *    +Y
-	 * -X -Z +X +Z
+	 * -X +Z +X -Z
 	 *    -Y
 	 *    -Y
 	 * 
 	 * 
 	 * @param[in]	source		Source texture to read.
 	 * @param[in]	source		Source texture to read.
@@ -441,8 +438,8 @@ namespace bs
 	void readCubemapCross(const SPtr<PixelData>& source, std::array<SPtr<PixelData>, 6>& output, UINT32 faceSize,
 	void readCubemapCross(const SPtr<PixelData>& source, std::array<SPtr<PixelData>, 6>& output, UINT32 faceSize,
 		bool vertical)
 		bool vertical)
 	{
 	{
-		const static UINT32 vertFaceIndices[] = { 5, 3, 1, 7, 10, 4 };
-		const static UINT32 horzFaceIndices[] = { 6, 4, 1, 9, 7, 5 };
+		const static UINT32 vertFaceIndices[] = { 5, 3, 1, 7, 4, 10 };
+		const static UINT32 horzFaceIndices[] = { 6, 4, 1, 9, 5, 7 };
 
 
 		const UINT32* faceIndices = vertical ? vertFaceIndices : horzFaceIndices;
 		const UINT32* faceIndices = vertical ? vertFaceIndices : horzFaceIndices;
 		UINT32 numFacesInRow = vertical ? 3 : 4;
 		UINT32 numFacesInRow = vertical ? 3 : 4;
@@ -455,11 +452,12 @@ namespace bs
 			UINT32 faceY = (faceIndices[i] / numFacesInRow) * faceSize;
 			UINT32 faceY = (faceIndices[i] / numFacesInRow) * faceSize;
 
 
 			PixelVolume volume(faceX, faceY, faceX + faceSize, faceY + faceSize);
 			PixelVolume volume(faceX, faceY, faceX + faceSize, faceY + faceSize);
-			PixelData subVolumeData = source->getSubVolume(volume);
-
-			assert(output[i]->getSize() == subVolumeData.getSize());
-			memcpy(output[i]->getData(), subVolumeData.getData(), subVolumeData.getSize());
+			PixelUtil::copy(*source, *output[i], faceX, faceY);
 		}
 		}
+
+		// Flip -Z as it's upside down
+		if (vertical)
+			PixelUtil::mirror(*output[5], MirrorModeBits::X | MirrorModeBits::Y);
 	}
 	}
 
 
 	/** Method that maps a direction to a point on a plane in range [0, 1] using spherical mapping. */
 	/** Method that maps a direction to a point on a plane in range [0, 1] using spherical mapping. */

+ 3 - 0
Source/BansheeGLRenderAPI/Source/BsGLRenderAPI.cpp

@@ -137,6 +137,9 @@ namespace bs
 		GLVertexArrayObjectManager::startUp();
 		GLVertexArrayObjectManager::startUp();
 		glFrontFace(GL_CW);
 		glFrontFace(GL_CW);
 
 
+		// Ensure cubemaps are filtered across seams
+		glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+
 		mGLInitialised = true;
 		mGLInitialised = true;
 
 
 		RenderAPICore::initializeWithWindow(primaryWindow);
 		RenderAPICore::initializeWithWindow(primaryWindow);

+ 1 - 1
Source/BansheeVulkanRenderAPI/CMakeLists.txt

@@ -2,7 +2,7 @@
 include(CMakeSources.cmake)
 include(CMakeSources.cmake)
 
 
 # Packages
 # Packages
-if(RENDER_API_MODULE MATCHES "Vulkan")
+if(RENDER_API_MODULE MATCHES "Vulkan" OR INCLUDE_ALL_IN_WORKFLOW)
 	find_package(Vulkan)
 	find_package(Vulkan)
 	find_package(glslang)
 	find_package(glslang)
 endif()
 endif()

+ 1 - 2
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuProgram.h

@@ -27,8 +27,7 @@ namespace bs
 	};
 	};
 
 
 	/**	Abstraction of a Vulkan shader object. */
 	/**	Abstraction of a Vulkan shader object. */
-	class 
-	VulkanGpuProgramCore : public GpuProgramCore
+	class VulkanGpuProgramCore : public GpuProgramCore
 	{
 	{
 	public:
 	public:
 		virtual ~VulkanGpuProgramCore();
 		virtual ~VulkanGpuProgramCore();

+ 8 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -193,6 +193,14 @@ namespace bs
 			case VK_IMAGE_VIEW_TYPE_3D:
 			case VK_IMAGE_VIEW_TYPE_3D:
 				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
 				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
 				break;
 				break;
+			default:
+				break;
+			}
+		} 
+		else if(surface.numArraySlices > 6)
+		{
+			switch (oldViewType)
+			{
 			case VK_IMAGE_VIEW_TYPE_CUBE:
 			case VK_IMAGE_VIEW_TYPE_CUBE:
 				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
 				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
 				break;
 				break;

+ 1 - 1
Source/CMakeLists.txt

@@ -46,7 +46,7 @@ set_property(CACHE RENDERER_MODULE PROPERTY STRINGS RenderBeast)
 set(BUILD_SCOPE "Runtime" CACHE STRING "Determines which parts of Banshee to build. Pick Framework to build only the low-level C++ framework. Pick Runtime to build everything, including the framework, scripting API and the editor.")
 set(BUILD_SCOPE "Runtime" CACHE STRING "Determines which parts of Banshee to build. Pick Framework to build only the low-level C++ framework. Pick Runtime to build everything, including the framework, scripting API and the editor.")
 set_property(CACHE BUILD_SCOPE PROPERTY STRINGS "Runtime" "Framework")
 set_property(CACHE BUILD_SCOPE PROPERTY STRINGS "Runtime" "Framework")
 
 
-set(INCLUDE_ALL_IN_WORKFLOW OFF CACHE BOOL "If true, all libraries (even those not selected) will be included in the generated workflow. Only relevant for workflow generators like Visual Studio.")
+set(INCLUDE_ALL_IN_WORKFLOW OFF CACHE BOOL "If true, all libraries (even those not selected) will be included in the generated workflow (e.g. Visual Studio solution). This is useful when working on engine internals with a need for easy access to all parts of it. Only relevant for workflow generators like Visual Studio or XCode.")
 
 
 if(BUILD_SCOPE MATCHES "Runtime")
 if(BUILD_SCOPE MATCHES "Runtime")
 	set(BUILD_EDITOR ON)
 	set(BUILD_EDITOR ON)

+ 1 - 0
Source/RenderBeast/Include/BsRenderBeast.h

@@ -212,6 +212,7 @@ namespace bs
 		PointLightInMat* mPointLightInMat;
 		PointLightInMat* mPointLightInMat;
 		PointLightOutMat* mPointLightOutMat;
 		PointLightOutMat* mPointLightOutMat;
 		DirectionalLightMat* mDirLightMat;
 		DirectionalLightMat* mDirLightMat;
+		SkyboxMat* mSkyboxMat;
 
 
 		ObjectRenderer* mObjectRenderer;
 		ObjectRenderer* mObjectRenderer;
 
 

+ 17 - 0
Source/RenderBeast/Include/BsRendererCamera.h

@@ -30,6 +30,23 @@ namespace bs
 
 
 	extern PerCameraParamDef gPerCameraParamDef;
 	extern PerCameraParamDef gPerCameraParamDef;
 
 
+	/** Shader that renders a skybox using a cubemap. */
+	class SkyboxMat : public RendererMaterial<SkyboxMat>
+	{
+		RMAT_DEF("Skybox.bsl");
+
+	public:
+		SkyboxMat();
+
+		/** Binds the material for rendering and sets up any global parameters. */
+		void bind(const SPtr<GpuParamBlockBufferCore>& perCamera);
+
+		/** Updates the skybox texture used by the material. */
+		void setParams(const SPtr<TextureCore>& texture);
+	private:
+		GpuParamTextureCore mSkyTextureParam;
+	};
+
 	/** Contains information about a Camera, used by the Renderer. */
 	/** Contains information about a Camera, used by the Renderer. */
 	class RendererCamera
 	class RendererCamera
 	{
 	{

+ 16 - 2
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -43,7 +43,8 @@ namespace bs
 
 
 	RenderBeast::RenderBeast()
 	RenderBeast::RenderBeast()
 		: mDefaultMaterial(nullptr), mPointLightInMat(nullptr), mPointLightOutMat(nullptr), mDirLightMat(nullptr)
 		: mDefaultMaterial(nullptr), mPointLightInMat(nullptr), mPointLightOutMat(nullptr), mDirLightMat(nullptr)
-		, mObjectRenderer(nullptr), mOptions(bs_shared_ptr_new<RenderBeastOptions>()), mOptionsDirty(true)
+		, mSkyboxMat(nullptr), mObjectRenderer(nullptr), mOptions(bs_shared_ptr_new<RenderBeastOptions>())
+		, mOptionsDirty(true)
 	{ }
 	{ }
 
 
 	const StringID& RenderBeast::getName() const
 	const StringID& RenderBeast::getName() const
@@ -78,6 +79,7 @@ namespace bs
 		mPointLightInMat = bs_new<PointLightInMat>();
 		mPointLightInMat = bs_new<PointLightInMat>();
 		mPointLightOutMat = bs_new<PointLightOutMat>();
 		mPointLightOutMat = bs_new<PointLightOutMat>();
 		mDirLightMat = bs_new<DirectionalLightMat>();
 		mDirLightMat = bs_new<DirectionalLightMat>();
+		mSkyboxMat = bs_new<SkyboxMat>();
 
 
 		RenderTexturePool::startUp();
 		RenderTexturePool::startUp();
 		PostProcessing::startUp();
 		PostProcessing::startUp();
@@ -106,6 +108,7 @@ namespace bs
 		bs_delete(mPointLightInMat);
 		bs_delete(mPointLightInMat);
 		bs_delete(mPointLightOutMat);
 		bs_delete(mPointLightOutMat);
 		bs_delete(mDirLightMat);
 		bs_delete(mDirLightMat);
+		bs_delete(mSkyboxMat);
 
 
 		RendererUtility::shutDown();
 		RendererUtility::shutDown();
 
 
@@ -743,8 +746,19 @@ namespace bs
 			}
 			}
 		}
 		}
 
 
+		// Render skybox (if any)
+		SPtr<TextureCore> skyTexture = camera->getSkybox();
+		if (skyTexture != nullptr && skyTexture->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
+		{
+			mSkyboxMat->bind(perCameraBuffer);
+			mSkyboxMat->setParams(skyTexture);
+
+			SPtr<MeshCore> mesh = gRendererUtility().getSkyBoxMesh();
+			gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
+		}
+
 		renderTargets->bindSceneColor(false);
 		renderTargets->bindSceneColor(false);
-		
+
 		// Render transparent objects (TODO - No lighting yet)
 		// Render transparent objects (TODO - No lighting yet)
 		const Vector<RenderQueueElement>& transparentElements = rendererCam->getTransparentQueue()->getSortedElements();
 		const Vector<RenderQueueElement>& transparentElements = rendererCam->getTransparentQueue()->getSortedElements();
 		for (auto iter = transparentElements.begin(); iter != transparentElements.end(); ++iter)
 		for (auto iter = transparentElements.begin(); iter != transparentElements.end(); ++iter)

+ 28 - 1
Source/RenderBeast/Source/BsRendererCamera.cpp

@@ -6,11 +6,38 @@
 #include "BsMaterial.h"
 #include "BsMaterial.h"
 #include "BsShader.h"
 #include "BsShader.h"
 #include "BsRenderTargets.h"
 #include "BsRenderTargets.h"
+#include "BsRendererUtility.h"
+#include "BsGpuParamsSet.h"
 
 
 namespace bs
 namespace bs
 {
 {
 	PerCameraParamDef gPerCameraParamDef;
 	PerCameraParamDef gPerCameraParamDef;
 
 
+	SkyboxMat::SkyboxMat()
+	{
+		SPtr<GpuParamsCore> params = mParamsSet->getGpuParams();
+
+		params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSkyTex", mSkyTextureParam);
+	}
+
+	void SkyboxMat::_initDefines(ShaderDefines& defines)
+	{
+		// Do nothing
+	}
+
+	void SkyboxMat::bind(const SPtr<GpuParamBlockBufferCore>& perCamera)
+	{
+		mParamsSet->setParamBlockBuffer("PerCamera", perCamera, true);
+
+		gRendererUtility().setPass(mMaterial, 0);
+	}
+
+	void SkyboxMat::setParams(const SPtr<TextureCore>& texture)
+	{
+		mSkyTextureParam.set(texture);
+		gRendererUtility().setPassParams(mParamsSet);
+	}
+
 	RendererCamera::RendererCamera()
 	RendererCamera::RendererCamera()
 		:mCamera(nullptr), mUsingRenderTargets(false)
 		:mCamera(nullptr), mUsingRenderTargets(false)
 	{
 	{
@@ -241,4 +268,4 @@ namespace bs
 
 
 		gPerCameraParamDef.gClipToUVScaleOffset.set(mParamBuffer, clipToUVScaleOffset);
 		gPerCameraParamDef.gClipToUVScaleOffset.set(mParamBuffer, clipToUVScaleOffset);
 	}
 	}
-}
+}