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

Selection overlay is now properly animated

BearishSun преди 9 години
родител
ревизия
4a45674df4

+ 147 - 61
Data/Raw/Editor/Shaders/Selection.bsl

@@ -1,10 +1,11 @@
 Parameters =
 {
-	mat4x4		matWorldViewProj;
-	float4		selColor;
+	mat4x4			matWorldViewProj;
+	float4			selColor;
+	StructBuffer 	boneMatrices;
 };
 
-Technique =
+Technique : base("SelectionBase") =
 {
 	Language = "HLSL11";
 	
@@ -19,18 +20,6 @@ Technique =
 			Color = { SRCA, SRCIA, ADD };
 		};
 
-		Vertex =
-		{
-			float4x4 matWorldViewProj;
-
-			void main(
-			in float3 inPos : POSITION,
-			out float4 oPosition : SV_Position)
-			{
-				oPosition = mul(matWorldViewProj, float4(inPos.xyz, 1));
-			}
-		};
-		
 		Fragment =
 		{
 			float4 selColor;
@@ -43,46 +32,74 @@ Technique =
 	};
 };
 
-Technique =
+Technique : inherits("SelectionBase") = 
 {
-	Language = "HLSL9";
-	
-	Pass =
+	Language = "HLSL11";
+
+	Vertex =
 	{
-		Fill = WIRE;
-		DepthBias = 0.00001f;
-		
-		Target =
+		float4x4 matWorldViewProj;
+
+		void main(
+		in float3 inPos : POSITION,
+		out float4 oPosition : SV_Position)
 		{
-			Blend = true;
-			Color = { SRCA, SRCIA, ADD };
-		};
+			oPosition = mul(matWorldViewProj, float4(inPos.xyz, 1));
+		}
+	};
+};
 
-		Vertex =
+Technique : inherits("SelectionBase") = 
+{
+	Language = "HLSL11";
+	Tags = { "Animated" };
+	
+	Vertex =
+	{
+		struct VertexInput
 		{
-			float4x4 matWorldViewProj;
+			float3 position : POSITION;
+			uint4 blendIndices : BLENDINDICES;
+			float4 blendWeights : BLENDWEIGHT;
+			
+			#if USE_BLEND_SHAPES
+				float3 deltaPosition : POSITION1;
+				float4 deltaNormal : NORMAL1;
+			#endif
+		};	
+	
+		StructuredBuffer<float4> boneMatrices;
+		float4x4 matWorldViewProj;
 
-			void main(
-			in float3 inPos : POSITION,	
-			out float4 oPosition : POSITION)
-			{
-				oPosition = mul(matWorldViewProj, float4(inPos.xyz, 1));
-			}
-		};
+		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);
+		}
 		
-		Fragment =
+		float3x4 getBlendMatrix(VertexInput input)
 		{
-			float4 selColor;
-
-			float4 main() : COLOR0
-			{
-				return selColor;
-			}
-		};
+			float3x4 result = input.blendWeights.x * getBoneMatrix(input.blendIndices.x);
+			result += input.blendWeights.y * getBoneMatrix(input.blendIndices.y);
+			result += input.blendWeights.z * getBoneMatrix(input.blendIndices.z);
+			result += input.blendWeights.w * getBoneMatrix(input.blendIndices.w);
+			
+			return result;
+		}		
+		
+		void main(VertexInput input, out float4 oPosition : SV_Position)
+		{
+			float3x4 blendMatrix = getBlendMatrix(input);
+			float4 position = float4(mul(blendMatrix, float4(input.position, 1.0f)), 1.0f);
+			oPosition = mul(matWorldViewProj, position);
+		}
 	};
 };
 
-Technique =
+Technique : base("SelectionBase") =
 {
 	Language = "GLSL";
 	
@@ -96,23 +113,6 @@ Technique =
 			Blend = true;
 			Color = { SRCA, SRCIA, ADD };
 		};
-
-		Vertex =
-		{
-			uniform mat4 matWorldViewProj;
-
-			in vec3 bs_position;
-
-			out gl_PerVertex
-			{
-				vec4 gl_Position;
-			};
-			
-			void main()
-			{
-				gl_Position = matWorldViewProj * vec4(bs_position.xyz, 1);
-			}
-		};
 		
 		Fragment =
 		{
@@ -127,3 +127,89 @@ Technique =
 	};
 };
 
+Technique : inherits("SelectionBase") = 
+{
+	Language = "GLSL";
+
+	Vertex =
+	{
+		uniform mat4 matWorldViewProj;
+
+		in vec3 bs_position;
+
+		out gl_PerVertex
+		{
+			vec4 gl_Position;
+		};
+		
+		void main()
+		{
+			gl_Position = matWorldViewProj * vec4(bs_position.xyz, 1);
+		}
+	};
+};
+
+Technique : inherits("SelectionBase") = 
+{
+	Language = "GLSL";
+	Tags = { "Animated" };
+	
+	Vertex =
+	{
+		uniform mat4 matWorldViewProj;
+
+		in vec3 bs_position;
+	
+		in uvec4 bs_blendindices;
+		in vec4 bs_blendweights;
+			
+		#if USE_BLEND_SHAPES
+			in vec3 bs_position1;
+			in vec4 bs_normal1;
+		#endif
+		
+		uniform samplerBuffer boneMatrices;
+		
+		out gl_PerVertex
+		{
+			vec4 gl_Position;
+		};
+		
+		void getBoneMatrix(uint idx, out mat4x3 result)
+		{
+			mat3x4 temp;
+		
+			temp[0] = texelFetch(boneMatrices, idx * 3 + 0);
+			temp[1] = texelFetch(boneMatrices, idx * 3 + 1);
+			temp[2] = texelFetch(boneMatrices, idx * 3 + 2);
+			
+			result = transpose(temp);				
+		}
+		
+		void getBlendMatrix(out mat4x3 result)
+		{
+			mat4x3 boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.x, out boneMatrix);
+			result = bs_blendweights.x * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.y, out boneMatrix);
+			result += bs_blendweights.y * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.z, out boneMatrix);
+			result += bs_blendweights.z * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.w, out boneMatrix);
+			result += bs_blendweights.w * boneMatrix;
+		}
+		
+		void main()
+		{
+			mat3x4 blendMatrix;
+			getBlendMatrix(blendMatrix);
+			
+			vec4 position = vec4(blendMatrix * vec4(bs_position, 1.0f), 1.0f);
+			gl_Position = matWorldViewProj * position;
+		}
+	};
+};

+ 12 - 4
Source/BansheeCore/Include/BsAnimationManager.h

@@ -66,10 +66,17 @@ namespace BansheeEngine
 		void postUpdate();
 
 		/** 
-		 * Gets skeleton poses required by the renderer to display all the animations. This will block the animation thread
-		 * if it has not yet finished, and it will also advance the read buffer index, meaning this shouldn't be called more
-		 * than once per frame. The returned data can be referenced, and is guaranteed to be valid for a single core-thread
-		 * frame.
+		 * Blocks the animation thread until it has finished evaluating animation, and it advances the read buffer index, 
+		 * meaning this shouldn't be called more than once per frame. It must be called before calling getRendererData().
+		 *
+		 * @note	Core thread only.
+		 */
+		void AnimationManager::waitUntilComplete();
+
+		/** 
+		 * Gets skeleton poses required by the renderer to display all the animations. The returned data can be referenced, 
+		 * and is guaranteed to be valid for a single core-thread frame. Before invoking, caller must ensure data is
+		 * available by first calling waitUntilComplete().
 		 *
 		 * @note	Core thread only.
 		 */
@@ -109,6 +116,7 @@ namespace BansheeEngine
 		UINT32 mPoseReadBufferIdx;
 		UINT32 mPoseWriteBufferIdx;
 		std::atomic<INT32> mDataReadyCount;
+		bool mDataReady;
 	};
 
 	/** Provides easier access to AnimationManager. */

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

@@ -38,6 +38,9 @@ namespace BansheeEngine
 	static StringID RPS_Diffuse = "Diffuse";
 	static StringID RPS_ViewDir = "ViewDir";
 
+	/** Technique tags. */
+	static StringID RTag_Animated = "Animated";
+
 	/**	Set of options that can be used for controlling the renderer. */	
 	struct BS_CORE_EXPORT CoreRendererOptions
 	{

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

@@ -252,7 +252,7 @@ namespace BansheeEngine
 		TGpuParamBuffer(GpuParamObjectDesc* paramDesc, const GpuParamsType& parent);
 
 		/** @copydoc TGpuDataParam::set */
-		void set(const BufferType& texture) const;
+		void set(const BufferType& buffer) const;
 
 		/** @copydoc TGpuDataParam::get */
 		BufferType get() const;

+ 15 - 10
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -12,8 +12,8 @@ namespace BansheeEngine
 {
 	AnimationManager::AnimationManager()
 		: mNextId(1), mUpdateRate(1.0f / 60.0f), mAnimationTime(0.0f), mLastAnimationUpdateTime(0.0f)
-		, mNextAnimationUpdateTime(0.0f), mPaused(false), mWorkerRunning(false), mPoseReadBufferIdx(0)
-		, mPoseWriteBufferIdx(0), mDataReadyCount(0)
+		, mNextAnimationUpdateTime(0.0f), mPaused(false), mWorkerRunning(false), mPoseReadBufferIdx(1)
+		, mPoseWriteBufferIdx(0), mDataReadyCount(0), mDataReady(false)
 	{
 		mAnimationWorker = Task::create("Animation", std::bind(&AnimationManager::evaluateAnimation, this));
 	}
@@ -251,7 +251,7 @@ namespace BansheeEngine
 		std::atomic_thread_fence(std::memory_order_release);
 	}
 
-	const RendererAnimationData& AnimationManager::getRendererData()
+	void AnimationManager::waitUntilComplete()
 	{
 		mAnimationWorker->wait();
 
@@ -261,18 +261,23 @@ namespace BansheeEngine
 		INT32 dataReadyCount = mDataReadyCount.load(std::memory_order_relaxed);
 		assert(dataReadyCount <= CoreThread::NUM_SYNC_BUFFERS);
 
-		if (dataReadyCount <= 0)
+		mDataReady = dataReadyCount > 0;
+		if (!mDataReady)
+			return;
+
+		mDataReadyCount.fetch_add(-1, std::memory_order_relaxed);
+		mPoseReadBufferIdx = (mPoseReadBufferIdx + 1) % CoreThread::NUM_SYNC_BUFFERS;
+	}
+
+	const RendererAnimationData& AnimationManager::getRendererData()
+	{
+		if (!mDataReady)
 		{
 			static RendererAnimationData dummy;
 			return dummy;
 		}
 
-		const RendererAnimationData& output = mAnimData[mPoseReadBufferIdx];
-
-		mPoseReadBufferIdx = (mPoseReadBufferIdx + 1) % CoreThread::NUM_SYNC_BUFFERS;
-		mDataReadyCount.fetch_add(-1, std::memory_order_relaxed);
-
-		return output;
+		return mAnimData[mPoseReadBufferIdx];
 	}
 
 	UINT64 AnimationManager::registerAnimation(Animation* anim)

+ 2 - 2
Source/BansheeCore/Source/BsGpuParam.cpp

@@ -224,12 +224,12 @@ namespace BansheeEngine
 	{ }
 
 	template<bool Core>
-	void TGpuParamBuffer<Core>::set(const BufferType& texture) const
+	void TGpuParamBuffer<Core>::set(const BufferType& buffer) const
 	{
 		if (mParent == nullptr)
 			return;
 
-		mParent->setBuffer(mParamDesc->slot, texture);
+		mParent->setBuffer(mParamDesc->slot, buffer);
 
 		mParent->_markResourcesDirty();
 		mParent->_markCoreDirty();

+ 9 - 12
Source/BansheeEditor/Include/BsSelectionRenderer.h

@@ -18,13 +18,6 @@ namespace BansheeEngine
 	/**	Handles rendering of the selected SceneObject%s overlay. */
 	class BS_ED_EXPORT SelectionRenderer
 	{
-		/**	Contains data about a selected mesh. */
-		struct ObjectData
-		{
-			SPtr<MeshCoreBase> mesh;
-			Matrix4 worldTfrm;
-		};
-
 	public:
 		SelectionRenderer();
 		~SelectionRenderer();
@@ -80,16 +73,20 @@ namespace BansheeEngine
 		 * @param[in]	camera		Camera to render the selection overlay in.
 		 * @param[in]	objects		A set of objects to render with the selection overlay.
 		 */
-		void updateData(const SPtr<CameraCore>& camera, const Vector<SelectionRenderer::ObjectData>& objects);
+		void updateData(const SPtr<CameraCore>& camera, const Vector<SPtr<RenderableCore>>& objects);
 
-		Vector<SelectionRenderer::ObjectData> mObjects;
+		Vector<SPtr<RenderableCore>> mObjects;
 		SPtr<CameraCore> mCamera;
 
 		// Immutable
 		SPtr<MaterialCore> mMaterial;
-		SPtr<GpuParamsSetCore> mParams;
-		GpuParamMat4Core mMatWorldViewProj;
-		GpuParamColorCore mColor;
+		SPtr<GpuParamsSetCore> mParams[2];
+		GpuParamMat4Core mMatWorldViewProj[2];
+		GpuParamColorCore mColor[2];
+		GpuParamBufferCore mBoneMatrices;
+
+		UINT32 mDefaultTechniqueIdx;
+		UINT32 mAnimatedTechniqueIdx;
 
 		static const Color SELECTION_COLOR;
 	};

+ 69 - 22
Source/BansheeEditor/Source/BsSelectionRenderer.cpp

@@ -18,6 +18,9 @@
 #include "BsRenderable.h"
 #include "BsSceneManager.h"
 #include "BsRendererUtility.h"
+#include "BsAnimationManager.h"
+#include "BsSkeleton.h"
+#include "BsGpuBuffer.h"
 
 using namespace std::placeholders;
 
@@ -50,7 +53,7 @@ namespace BansheeEngine
 
 	void SelectionRenderer::update(const SPtr<Camera>& camera)
 	{
-		Vector<ObjectData> objects;
+		Vector<SPtr<RenderableCore>> objects;
 
 		const Vector<HSceneObject>& sceneObjects = Selection::instance().getSceneObjects();
 		const Map<Renderable*, SceneRenderableData>& renderables = SceneManager::instance().getAllRenderables();
@@ -66,13 +69,7 @@ namespace BansheeEngine
 					continue;
 
 				if (renderable.first->getMesh().isLoaded())
-				{
-					objects.push_back(ObjectData());
-
-					ObjectData& newObjData = objects.back();
-					newObjData.worldTfrm = so->getWorldTfrm();
-					newObjData.mesh = renderable.first->getMesh()->getCore();
-				}
+					objects.push_back(renderable.first->getCore());
 			}
 		}
 
@@ -97,17 +94,29 @@ namespace BansheeEngine
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
+		mDefaultTechniqueIdx = mat->getDefaultTechnique();
+		mAnimatedTechniqueIdx = mat->findTechnique(RTag_Animated);
+
+		assert(mDefaultTechniqueIdx < 2 && mAnimatedTechniqueIdx < 2);
+
 		mMaterial = mat;
-		mParams = mat->createParamsSet();
 
-		SPtr<GpuParamsCore> vertParams = mParams->getGpuParams(GPT_VERTEX_PROGRAM);
-		vertParams->getParam("matWorldViewProj", mMatWorldViewProj);
+		for(UINT32 i = 0; i < 2 ; i++)
+		{
+			mParams[i] = mat->createParamsSet(i);
 
-		SPtr<GpuParamsCore> fragParams = mParams->getGpuParams(GPT_FRAGMENT_PROGRAM);
-		fragParams->getParam("selColor", mColor);
+			SPtr<GpuParamsCore> vertParams = mParams[i]->getGpuParams(GPT_VERTEX_PROGRAM);
+			vertParams->getParam("matWorldViewProj", mMatWorldViewProj[i]);
+
+			SPtr<GpuParamsCore> fragParams = mParams[i]->getGpuParams(GPT_FRAGMENT_PROGRAM);
+			fragParams->getParam("selColor", mColor[i]);
+		}
+
+		SPtr<GpuParamsCore> vertParams = mParams[mAnimatedTechniqueIdx]->getGpuParams(GPT_VERTEX_PROGRAM);
+		vertParams->getBufferParam("boneMatrices", mBoneMatrices);
 	}
 
-	void SelectionRendererCore::updateData(const SPtr<CameraCore>& camera, const Vector<SelectionRenderer::ObjectData>& objects)
+	void SelectionRendererCore::updateData(const SPtr<CameraCore>& camera, const Vector<SPtr<RenderableCore>>& objects)
 	{
 		if (mCamera != camera)
 		{
@@ -130,22 +139,60 @@ namespace BansheeEngine
 		if (mCamera == nullptr)
 			return;
 
+		const RendererAnimationData& animData = AnimationManager::instance().getRendererData();
 		Matrix4 viewProjMat = mCamera->getProjectionMatrixRS() * mCamera->getViewMatrix();
 
-		for (auto& objData : mObjects)
+		for (auto& renderable : mObjects)
 		{
-			Matrix4 worldViewProjMat = viewProjMat * objData.worldTfrm;
+			SPtr<MeshCore> mesh = renderable->getMesh();
+			if (mesh == nullptr)
+				continue;
+
+			Matrix4 worldViewProjMat = viewProjMat * renderable->getTransform();
+			UINT32 techniqueIdx = renderable->isAnimated() ? mAnimatedTechniqueIdx : mDefaultTechniqueIdx;
+
+			mMatWorldViewProj[techniqueIdx].set(worldViewProjMat);
+			mColor[techniqueIdx].set(SELECTION_COLOR);
 
-			mMatWorldViewProj.set(worldViewProjMat);
-			mColor.set(SELECTION_COLOR);
+			if(renderable->isAnimated())
+			{
+				SPtr<Skeleton> skeleton = mesh->getSkeleton();
+				UINT32 numBones = skeleton != nullptr ? skeleton->getNumBones() : 0;
+
+				if (numBones > 0)
+				{
+					// Note: If I had access to them I could just use the buffers from the Renderer instead of creating a
+					// new one every frame
+					SPtr<GpuBufferCore> buffer = GpuBufferCore::create(numBones * 3, 0, GBT_STANDARD, BF_32X4F, GBU_DYNAMIC);
+
+					auto iterFind = animData.poseInfos.find(renderable->getAnimationId());
+					if (iterFind != animData.poseInfos.end())
+					{
+						const RendererAnimationData::PoseInfo& poseInfo = iterFind->second;
+
+						UINT8* dest = (UINT8*)buffer->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
+						for (UINT32 j = 0; j < poseInfo.numBones; j++)
+						{
+							const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
+							memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
+
+							dest += 12 * sizeof(float);
+						}
+
+						buffer->unlock();
+					}
+
+					mBoneMatrices.set(buffer);
+				}
+			}
 
-			gRendererUtility().setPass(mMaterial, 0);
-			gRendererUtility().setPassParams(mParams, 0);
+			gRendererUtility().setPass(mMaterial, 0, techniqueIdx);
+			gRendererUtility().setPassParams(mParams[techniqueIdx], 0);
 
-			UINT32 numSubmeshes = objData.mesh->getProperties().getNumSubMeshes();
+			UINT32 numSubmeshes = mesh->getProperties().getNumSubMeshes();
 
 			for (UINT32 i = 0; i < numSubmeshes; i++)
-				gRendererUtility().draw(objData.mesh, objData.mesh->getProperties().getSubMesh(i));
+				gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(i));
 		}
 	}
 }

+ 3 - 0
Source/BansheeEngine/Include/BsRenderable.h

@@ -205,6 +205,9 @@ namespace BansheeEngine
 		/** Sets the animation that will be used for animating the attached mesh. */
 		void setAnimation(const SPtr<Animation>& animation);
 
+		/** Checks is the renderable animated or static. */
+		bool isAnimated() const { return mAnimation != nullptr; }
+
 		/**	Retrieves an implementation of a renderable handler usable only from the core thread. */
 		SPtr<RenderableCore> getCore() const;
 

+ 0 - 2
Source/BansheeSL/Include/BsSLFXCompiler.h

@@ -76,8 +76,6 @@ namespace BansheeEngine
 		struct TechniqueData
 		{
 			TechniqueMetaData metaData;
-
-			PassData commonPassData;
 			Vector<PassData> passes;
 		};
 

+ 40 - 12
Source/BansheeSL/Source/BsSLFXCompiler.cpp

@@ -994,6 +994,8 @@ namespace BansheeEngine
 		if (techniqueNode == nullptr || techniqueNode->type != NT_Technique)
 			return;
 
+		PassData combinedCommonPassData;
+
 		UINT32 nextPassIdx = 0;
 		// Go in reverse because options are added in reverse order during parsing
 		for (int i = techniqueNode->options->count - 1; i >= 0; i--)
@@ -1038,28 +1040,54 @@ namespace BansheeEngine
 
 				nextPassIdx = std::max(nextPassIdx, passIdx) + 1;
 
-				passData->blendIsDefault &= !parseBlendState(passData->blendDesc, techniqueNode);
-				passData->rasterizerIsDefault &= !parseRasterizerState(passData->rasterizerDesc, techniqueNode);
-				passData->depthStencilIsDefault &= !parseDepthStencilState(passData->depthStencilDesc, techniqueNode);
-
-				passData->vertexCode = techniqueData.commonPassData.vertexCode + passData->vertexCode;
-				passData->fragmentCode = techniqueData.commonPassData.fragmentCode + passData->fragmentCode;
-				passData->geometryCode = techniqueData.commonPassData.geometryCode + passData->geometryCode;
-				passData->hullCode = techniqueData.commonPassData.hullCode + passData->hullCode;
-				passData->domainCode = techniqueData.commonPassData.domainCode + passData->domainCode;
-				passData->computeCode = techniqueData.commonPassData.computeCode + passData->computeCode;
-				passData->commonCode = techniqueData.commonPassData.commonCode + passData->commonCode;
+				passData->vertexCode = combinedCommonPassData.vertexCode + passData->vertexCode;
+				passData->fragmentCode = combinedCommonPassData.fragmentCode + passData->fragmentCode;
+				passData->geometryCode = combinedCommonPassData.geometryCode + passData->geometryCode;
+				passData->hullCode = combinedCommonPassData.hullCode + passData->hullCode;
+				passData->domainCode = combinedCommonPassData.domainCode + passData->domainCode;
+				passData->computeCode = combinedCommonPassData.computeCode + passData->computeCode;
+				passData->commonCode = combinedCommonPassData.commonCode + passData->commonCode;
 				
 				parsePass(passNode, codeBlocks, *passData);
 			}
 				break;
 			case OT_Code:
-				parseCodeBlock(option->value.nodePtr, codeBlocks, techniqueData.commonPassData);
+			{
+				PassData commonPassData;
+				parseCodeBlock(option->value.nodePtr, codeBlocks, commonPassData);
+
+				for (auto& passData : techniqueData.passes)
+				{
+					passData.vertexCode += commonPassData.vertexCode;
+					passData.fragmentCode += commonPassData.fragmentCode;
+					passData.geometryCode += commonPassData.geometryCode;
+					passData.hullCode += commonPassData.hullCode;
+					passData.domainCode += commonPassData.domainCode;
+					passData.computeCode += commonPassData.computeCode;
+					passData.commonCode += commonPassData.commonCode;
+				}
+
+				combinedCommonPassData.vertexCode += commonPassData.vertexCode;
+				combinedCommonPassData.fragmentCode += commonPassData.fragmentCode;
+				combinedCommonPassData.geometryCode += commonPassData.geometryCode;
+				combinedCommonPassData.hullCode += commonPassData.hullCode;
+				combinedCommonPassData.domainCode += commonPassData.domainCode;
+				combinedCommonPassData.computeCode += commonPassData.computeCode;
+				combinedCommonPassData.commonCode += commonPassData.commonCode;
+			}
 				break;
 			default:
 				break;
 			}
 		}
+
+		// Parse common pass states
+		for (auto& passData : techniqueData.passes)
+		{
+			passData.blendIsDefault &= !parseBlendState(passData.blendDesc, techniqueNode);
+			passData.rasterizerIsDefault &= !parseRasterizerState(passData.rasterizerDesc, techniqueNode);
+			passData.depthStencilIsDefault &= !parseDepthStencilState(passData.depthStencilDesc, techniqueNode);
+		}
 	}
 
 	void BSLFXCompiler::parseParameters(SHADER_DESC& desc, ASTFXNode* parametersNode)

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

@@ -27,9 +27,6 @@ namespace BansheeEngine
 	static StringID RPS_GBufferDepth = "GBufferDepth";
 	static StringID RPS_BoneMatrices = "BoneMatrices";
 
-	/** Technique tags. */
-	static StringID RTag_Animated = "Animated";
-
 	/**
 	 * Default renderer for Banshee. Performs frustum culling, sorting and renders objects in custom ways determine by
 	 * renderable handlers.
@@ -216,6 +213,7 @@ namespace BansheeEngine
 		Vector<RendererObject> mRenderables;
 		Vector<RenderableShaderData> mRenderableShaderData;
 		Vector<Bounds> mWorldBounds;
+		Vector<bool> mVisibility; // Transient
 
 		Vector<RendererLight> mDirectionalLights;
 		Vector<RendererLight> mPointLights;

+ 13 - 2
Source/RenderBeast/Include/BsRendererCamera.h

@@ -54,8 +54,19 @@ namespace BansheeEngine
 		 */
 		const SPtr<RenderQueue>& getTransparentQueue() const { return mTransparentQueue; }
 
-		/** Populates camera render queues by determining visible renderable objects. */
-		void determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds);
+		/**
+		 * Populates camera render queues by determining visible renderable objects. 
+		 *
+		 * @param[in]	renderables			A set of renderable objects to iterate over and determine visibility for.
+		 * @param[in]	renderableBounds	A set of world bounds for the provided renderable objects. Must be the same size
+		 *									as the @p renderables array.
+		 * @param[in]	visibility			Output parameter that will have the true bit set for any visible renderable
+		 *									object. If the bit for an object is already set to true, the method will never
+		 *									change it to false which allows the same bitfield to be provided to multiple
+		 *									renderer cameras. Must be the same size as the @p renderables array.
+		 */
+		void determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds, 
+			Vector<bool>& visibility);
 
 		/** 
 		 * Returns a structure containing information about post-processing effects. This structure will be modified and

+ 48 - 26
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -88,6 +88,7 @@ namespace BansheeEngine
 		mRenderTargets.clear();
 		mCameras.clear();
 		mRenderables.clear();
+		mVisibility.clear();
 
 		PostProcessing::shutDown();
 		RenderTexturePool::shutDown();
@@ -111,6 +112,7 @@ namespace BansheeEngine
 		mRenderables.push_back(RendererObject());
 		mRenderableShaderData.push_back(RenderableShaderData());
 		mWorldBounds.push_back(renderable->getBounds());
+		mVisibility.push_back(false);
 
 		RendererObject& rendererObject = mRenderables.back();
 		rendererObject.renderable = renderable;
@@ -281,6 +283,7 @@ namespace BansheeEngine
 		mRenderables.erase(mRenderables.end() - 1);
 		mWorldBounds.erase(mWorldBounds.end() - 1);
 		mRenderableShaderData.erase(mRenderableShaderData.end() - 1);
+		mVisibility.erase(mVisibility.end() - 1);
 	}
 
 	void RenderBeast::notifyRenderableUpdated(RenderableCore* renderable)
@@ -531,12 +534,56 @@ namespace BansheeEngine
 		mObjectRenderer->setParamFrameParams(time);
 
 		// Generate render queues per camera
+		mVisibility.assign(mVisibility.size(), false);
+
 		for (auto& entry : mCameras)
-			entry.second.determineVisible(mRenderables, mWorldBounds);
+			entry.second.determineVisible(mRenderables, mWorldBounds, mVisibility);
 
+		AnimationManager::instance().waitUntilComplete();
 		const RendererAnimationData& animData = AnimationManager::instance().getRendererData();
 		RendererFrame frameInfo(delta, animData);
 
+		// Update bone matrix buffers
+		UINT32 numRenderables = (UINT32)mRenderables.size();
+		for (UINT32 i = 0; i < numRenderables; i++)
+		{
+			if (!mVisibility[i])
+				continue;
+
+			for (auto& element : mRenderables[i].elements)
+			{
+				if (element.animationId == (UINT64)-1)
+					continue;
+
+				// Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
+				// all such elements
+				SPtr<GpuBufferCore> boneMatrices = element.boneMatrixBuffer;
+
+				auto iterFind = animData.poseInfos.find(element.animationId);
+				if (iterFind != animData.poseInfos.end())
+				{
+					const RendererAnimationData::PoseInfo& poseInfo = iterFind->second;
+
+					UINT8* dest = (UINT8*)boneMatrices->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
+					for (UINT32 j = 0; j < poseInfo.numBones; j++)
+					{
+						const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
+						memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
+
+						dest += 12 * sizeof(float);
+					}
+
+					boneMatrices->unlock();
+				}
+			}
+
+			// TODO - Also move per-object buffer updates here (will require worldViewProj matrix to be moved to a separate buffer (or a push constant))
+			// TODO - Before uploading bone matrices and per-object data, check if it has actually been changed since last frame (most objects will be static)
+			// TODO - Also move per-camera buffer updates in a separate loop
+		}
+
+		// TODO - When porting to Vulkan, start upload and issue barrier (but somehow avoid blocking too long here?)
+
 		// Render everything, target by target
 		for (auto& rtInfo : mRenderTargets)
 		{
@@ -778,32 +825,7 @@ namespace BansheeEngine
 
 		UINT32 rendererId = element.renderableId;
 		Matrix4 worldViewProjMatrix = viewProj * mRenderableShaderData[rendererId].worldTransform;
-
 		SPtr<GpuBufferCore> boneMatrices = element.boneMatrixBuffer;
-		if(element.animationId != (UINT64)-1)
-		{
-			// Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
-			// all such elements
-
-			const RendererAnimationData& animData = frameInfo.animData;
-
-			auto iterFind = animData.poseInfos.find(element.animationId);
-			if(iterFind != animData.poseInfos.end())
-			{
-				const RendererAnimationData::PoseInfo& poseInfo = iterFind->second;
-
-				UINT8* dest = (UINT8*)boneMatrices->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
-				for(UINT32 i = 0; i < poseInfo.numBones; i++)
-				{
-					const Matrix4& transform = animData.transforms[poseInfo.startIdx + i];
-					memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
-
-					dest += 12 * sizeof(float);
-				}
-
-				boneMatrices->unlock();
-			}
-		}
 
 		mObjectRenderer->setPerObjectParams(element, mRenderableShaderData[rendererId], worldViewProjMatrix, boneMatrices);
 		material->updateParamsSet(element.params, element.techniqueIdx);

+ 7 - 4
Source/RenderBeast/Source/BsRendererCamera.cpp

@@ -78,7 +78,8 @@ namespace BansheeEngine
 		}
 	}
 
-	void RendererCamera::determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds)
+	void RendererCamera::determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds, 
+		Vector<bool>& visibility)
 	{
 		bool isOverlayCamera = mCamera->getFlags().isSet(CameraFlag::Overlay);
 		if (isOverlayCamera)
@@ -88,9 +89,9 @@ namespace BansheeEngine
 		ConvexVolume worldFrustum = mCamera->getWorldFrustum();
 
 		// Update per-object param buffers and queue render elements
-		for (auto& renderableData : renderables)
+		for(UINT32 i = 0; i < (UINT32)renderables.size(); i++)
 		{
-			RenderableCore* renderable = renderableData.renderable;
+			RenderableCore* renderable = renderables[i].renderable;
 			UINT32 rendererId = renderable->getRendererId();
 
 			if ((renderable->getLayer() & cameraLayers) == 0)
@@ -107,9 +108,11 @@ namespace BansheeEngine
 
 				if (worldFrustum.intersects(boundingBox))
 				{
+					visibility[i] = true;
+
 					float distanceToCamera = (mCamera->getPosition() - boundingBox.getCenter()).length();
 
-					for (auto& renderElem : renderableData.elements)
+					for (auto& renderElem : renderables[i].elements)
 					{
 						bool isTransparent = (renderElem.material->getShader()->getFlags() & (UINT32)ShaderFlags::Transparent) != 0;