Browse Source

More work on gizmos

Marko Pintera 11 years ago
parent
commit
cfffeaa859

+ 2 - 2
BansheeCore/Include/BsMeshData.h

@@ -35,7 +35,7 @@ namespace BansheeEngine
 		 * @brief	Adds a new value to the iterators current position and
 		 *			advances the iterator.
 		 */
-		void addValue(T& value)
+		void addValue(const T& value)
 		{
 			setValue(value);
 			moveNext();
@@ -44,7 +44,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Sets a new value at the iterators current position.
 		 */
-		void setValue(T& value)
+		void setValue(const T& value)
 		{
 			memcpy(mData, &value, sizeof(T));
 		}

+ 1 - 0
BansheeCore/Source/BsRenderer.cpp

@@ -91,6 +91,7 @@ namespace BansheeEngine
 		RenderSystem& rs = RenderSystem::instance();
 		MeshBasePtr mesh;
 
+		// TODO: Instead of this lock consider just storing all needed data in MeshProxy and not referencing Mesh at all?
 		if (!meshProxy.mesh.expired())
 			mesh = meshProxy.mesh.lock();
 		else

+ 41 - 0
BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -35,6 +35,21 @@ namespace BansheeEngine
 		 */
 		HMaterial createPickingAlpha(CullingMode cullMode) const;
 
+		/**
+		 * @brief	Creates a material used for rendering wireframe gizmos.
+		 */
+		HMaterial createWireGizmoMat() const;
+
+		/**
+		 * @brief	Creates a material used for rendering solid gizmos.
+		 */
+		HMaterial createSolidGizmoMat() const;
+
+		/**
+		 * @brief	Creates a material used for rendering icon gizmos.
+		 */
+		HMaterial createIconGizmoMat() const;
+
 		static const String ObjectFieldStyleName;
 		static const String ObjectFieldLabelStyleName;
 		static const String ObjectFieldDropBtnStyleName;
@@ -86,6 +101,21 @@ namespace BansheeEngine
 		 */
 		void initPickingAlphaShader(CullingMode cullMode);
 
+		/**
+		 * @brief	Loads and compiles a shader used for rendering wireframe gizmos.
+		 */
+		void initWireGizmoShader();
+
+		/**
+		 * @brief	Loads and compiles a shader used for rendering solid gizmos.
+		 */
+		void initSolidGizmoShader();
+
+		/**
+		 * @brief	Loads and compiles a shader used for rendering icon gizmos.
+		 */
+		void initIconGizmoShader();
+
 		RenderSystemPlugin mRenderSystemPlugin;
 		WString mActiveShaderSubFolder;
 		String mActiveRenderSystem;
@@ -94,6 +124,9 @@ namespace BansheeEngine
 		ShaderPtr mShaderSceneGrid;
 		ShaderPtr mShaderPicking[3];
 		ShaderPtr mShaderPickingAlpha[3];
+		ShaderPtr mShaderGizmoSolid;
+		ShaderPtr mShaderGizmoWire;
+		ShaderPtr mShaderGizmoIcon;
 
 		GUISkin mSkin;
 
@@ -237,5 +270,13 @@ namespace BansheeEngine
 		static const WString PickingPSFile;
 		static const WString PickingAlphaVSFile;
 		static const WString PickingAlphaPSFile;
+		static const WString ShaderWireGizmoVSFile;
+		static const WString ShaderWireGizmoPSFile;
+		static const WString ShaderSolidGizmoVSFile;
+		static const WString ShaderSolidGizmoPSFile;
+		static const WString ShaderIconGizmo0VSFile;
+		static const WString ShaderIconGizmo0PSFile;
+		static const WString ShaderIconGizmo1VSFile;
+		static const WString ShaderIconGizmo1PSFile;
 	};
 }

+ 56 - 1
BansheeEditor/Include/BsGizmoManager.h

@@ -75,14 +75,64 @@ namespace BansheeEngine
 			HSpriteTexture texture;
 		};
 
+		struct IconRenderData
+		{
+			UINT32 count;
+			HTexture texture;
+		};
+
+		struct SolidMaterialData
+		{
+			HMaterial material;
+			
+			// Core
+			MaterialProxyPtr proxy;
+			GpuParamMat4 mViewProj;
+			GpuParamMat4 mViewIT;
+		};
+
+		struct WireMaterialData
+		{
+			HMaterial material;
+
+			// Core
+			MaterialProxyPtr proxy;
+			GpuParamMat4 mViewProj;
+		};
+
+		struct IconMaterialData
+		{
+			HMaterial material;
+
+			// Core
+			MaterialProxyPtr proxy;
+			GpuParamMat4 mViewProj;
+			GpuParamTexture mTexture;
+		};
+
+		typedef Vector<IconRenderData> IconRenderDataVec;
+		typedef std::shared_ptr<IconRenderDataVec> IconRenderDataVecPtr;
+
 		void buildSolidMesh();
 		void buildWireMesh();
-		void buildIconMesh();
+		IconRenderDataVecPtr buildIconMesh();
+
+		void coreRenderSolidGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, MeshProxyPtr mesh);
+		void coreRenderWireGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, MeshProxyPtr mesh);
+		void coreRenderIconGizmos(RectI screenArea, MeshProxyPtr mesh, IconRenderDataVecPtr renderData);
+
+		void limitIconSize(UINT32& width, UINT32& height);
+		void calculateIconColors(const Color& tint, const Camera& camera, UINT32 iconHeight, bool fixedScale,
+			Color& normalColor, Color& fadedColor);
+
+		void initializeCore();
 
 		static const UINT32 VERTEX_BUFFER_GROWTH;
 		static const UINT32 INDEX_BUFFER_GROWTH;
 		static const UINT32 SPHERE_QUALITY;
 		static const float MAX_ICON_RANGE;
+		static const UINT32 OPTIMAL_ICON_SIZE;
+		static const float ICON_TEXEL_WORLD_SIZE;
 
 		typedef Set<IconData, std::function<bool(const IconData&, const IconData&)>> IconSet;
 
@@ -115,10 +165,15 @@ namespace BansheeEngine
 		TransientMeshPtr mWireMesh;
 		TransientMeshPtr mIconMesh;
 
+		// Immutable
 		VertexDataDescPtr mSolidVertexDesc;
 		VertexDataDescPtr mWireVertexDesc;
 		VertexDataDescPtr mIconVertexDesc;
 
+		SolidMaterialData mSolidMaterial;
+		WireMaterialData mWireMaterial;
+		IconMaterialData mIconMaterial;
+
 		// Transient
 		struct SortedIconData
 		{

+ 140 - 1
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -197,6 +197,14 @@ namespace BansheeEngine
 	const WString BuiltinEditorResources::PickingPSFile = L"pickingPS.gpuprog";
 	const WString BuiltinEditorResources::PickingAlphaVSFile = L"pickingAlphaVS.gpuprog";
 	const WString BuiltinEditorResources::PickingAlphaPSFile = L"pickingAlphaPS.gpuprog";
+	const WString BuiltinEditorResources::ShaderWireGizmoVSFile = L"wireGizmoVS.gpuprog";
+	const WString BuiltinEditorResources::ShaderWireGizmoPSFile = L"wireGizmoPS.gpuprog";
+	const WString BuiltinEditorResources::ShaderSolidGizmoVSFile = L"solidGizmoVS.gpuprog";
+	const WString BuiltinEditorResources::ShaderSolidGizmoPSFile = L"solidGizmoPS.gpuprog";
+	const WString BuiltinEditorResources::ShaderIconGizmo0VSFile = L"iconGizmo0VS.gpuprog";
+	const WString BuiltinEditorResources::ShaderIconGizmo0PSFile = L"iconGizmo0PS.gpuprog";
+	const WString BuiltinEditorResources::ShaderIconGizmo1VSFile = L"iconGizmo1VS.gpuprog";
+	const WString BuiltinEditorResources::ShaderIconGizmo1PSFile = L"iconGizmo1PS.gpuprog";
 
 	BuiltinEditorResources::BuiltinEditorResources(RenderSystemPlugin activeRSPlugin)
 		:mRenderSystemPlugin(activeRSPlugin)
@@ -227,6 +235,9 @@ namespace BansheeEngine
 		initPickingAlphaShader(CULL_NONE);
 		initPickingAlphaShader(CULL_CLOCKWISE);
 		initPickingAlphaShader(CULL_COUNTERCLOCKWISE);
+		initWireGizmoShader();
+		initSolidGizmoShader();
+		initIconGizmoShader();
 
 		Path fontPath = FileSystem::getWorkingDirectoryPath();
 		fontPath.append(DefaultSkinFolder);
@@ -1056,6 +1067,14 @@ namespace BansheeEngine
 			{ PickingPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ PickingAlphaVSFile,		"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ PickingAlphaPSFile,		"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderWireGizmoVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderWireGizmoPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderSolidGizmoVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderSolidGizmoPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderIconGizmo0VSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderIconGizmo0PSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderIconGizmo1VSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ ShaderIconGizmo1PSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ SceneGridVSFile,			"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ SceneGridPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ ShaderDockOverlayVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
@@ -1064,6 +1083,14 @@ namespace BansheeEngine
 			{ PickingPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ PickingAlphaVSFile,		"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ PickingAlphaPSFile,		"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderWireGizmoVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderWireGizmoPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderSolidGizmoVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderSolidGizmoPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderIconGizmo0VSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderIconGizmo0PSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderIconGizmo1VSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ ShaderIconGizmo1PSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ SceneGridVSFile,			"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ SceneGridPSFile,			"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ ShaderDockOverlayVSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
@@ -1071,7 +1098,15 @@ namespace BansheeEngine
 			{ PickingVSFile,			"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ PickingPSFile,			"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ PickingAlphaVSFile,		"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
-			{ PickingAlphaPSFile,		"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder }
+			{ PickingAlphaPSFile,		"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
+			{ ShaderWireGizmoVSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderWireGizmoPSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderSolidGizmoVSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderSolidGizmoPSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderIconGizmo0VSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderIconGizmo0PSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderIconGizmo1VSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", GLSLShaderSubFolder },
+			{ ShaderIconGizmo1PSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", GLSLShaderSubFolder },
 		};
 
 		if (FileSystem::exists(DefaultSkinFolderRaw))
@@ -1317,6 +1352,95 @@ namespace BansheeEngine
 		newPass->setRasterizerState(rasterizerState);
 	}
 
+	void BuiltinEditorResources::initWireGizmoShader()
+	{
+		HGpuProgram vsProgram = getGpuProgram(ShaderWireGizmoVSFile);
+		HGpuProgram psProgram = getGpuProgram(ShaderWireGizmoPSFile);
+
+		mShaderGizmoWire = Shader::create("GizmoWire");
+
+		mShaderGizmoWire->addParameter("matViewProj", "matViewProj", GPDT_MATRIX_4X4);
+
+		TechniquePtr newTechnique = mShaderGizmoWire->addTechnique(mActiveRenderSystem, RendererInvariant);
+		PassPtr newPass = newTechnique->addPass();
+		newPass->setVertexProgram(vsProgram);
+		newPass->setFragmentProgram(psProgram);
+	}
+
+	void BuiltinEditorResources::initSolidGizmoShader()
+	{
+		HGpuProgram vsProgram = getGpuProgram(ShaderSolidGizmoVSFile);
+		HGpuProgram psProgram = getGpuProgram(ShaderSolidGizmoPSFile);
+
+		mShaderGizmoSolid = Shader::create("GizmoSolid");
+
+		mShaderGizmoSolid->addParameter("matViewProj", "matViewProj", GPDT_MATRIX_4X4);
+		mShaderGizmoSolid->addParameter("matViewIT", "matViewIT", GPDT_MATRIX_4X4);
+
+		TechniquePtr newTechnique = mShaderGizmoSolid->addTechnique(mActiveRenderSystem, RendererInvariant);
+		PassPtr newPass = newTechnique->addPass();
+		newPass->setVertexProgram(vsProgram);
+		newPass->setFragmentProgram(psProgram);
+	}
+
+	void BuiltinEditorResources::initIconGizmoShader()
+	{
+		HGpuProgram vsProgram0 = getGpuProgram(ShaderIconGizmo0VSFile);
+		HGpuProgram psProgram0 = getGpuProgram(ShaderIconGizmo0PSFile);
+		HGpuProgram vsProgram1 = getGpuProgram(ShaderIconGizmo1VSFile);
+		HGpuProgram psProgram1 = getGpuProgram(ShaderIconGizmo1PSFile);
+
+		mShaderGizmoIcon = Shader::create("GizmoIcon");
+
+		mShaderGizmoIcon->addParameter("matViewProj", "matViewProj", GPDT_MATRIX_4X4);
+		mShaderGizmoIcon->addParameter("mainTexSamp", "mainTexSamp", GPOT_SAMPLER2D);
+		mShaderGizmoIcon->addParameter("mainTexSamp", "mainTexture", GPOT_SAMPLER2D);
+		mShaderGizmoIcon->addParameter("mainTexture", "mainTexture", GPOT_TEXTURE2D);
+
+		BLEND_STATE_DESC blendDesc;
+		blendDesc.renderTargetDesc[0].blendEnable = true;
+		blendDesc.renderTargetDesc[0].srcBlend = BF_SOURCE_ALPHA;
+		blendDesc.renderTargetDesc[0].dstBlend = BF_INV_SOURCE_ALPHA;
+		blendDesc.renderTargetDesc[0].blendOp = BO_ADD;
+		blendDesc.renderTargetDesc[0].renderTargetWriteMask = 0x7; // Don't write to alpha
+
+		HBlendState blendState = BlendState::create(blendDesc);
+
+		RASTERIZER_STATE_DESC rasterizerDesc;
+		rasterizerDesc.cullMode = CULL_NONE;
+
+		HRasterizerState rasterizerState = RasterizerState::create(rasterizerDesc);
+
+		DEPTH_STENCIL_STATE_DESC depthStencilState0Desc;
+		depthStencilState0Desc.depthWriteEnable = false;
+		depthStencilState0Desc.backStencilComparisonFunc = CMPF_LESS_EQUAL;
+
+		HDepthStencilState depthStencilState0 = DepthStencilState::create(depthStencilState0Desc);
+
+		DEPTH_STENCIL_STATE_DESC depthStencilState1Desc;
+		depthStencilState1Desc.depthWriteEnable = false;
+		depthStencilState1Desc.backStencilComparisonFunc = CMPF_GREATER;
+
+		HDepthStencilState depthStencilState1 = DepthStencilState::create(depthStencilState1Desc);
+
+		TechniquePtr newTechnique = mShaderGizmoIcon->addTechnique(mActiveRenderSystem, RendererInvariant);
+		PassPtr newPass0 = newTechnique->addPass();
+		newPass0->setVertexProgram(vsProgram0);
+		newPass0->setFragmentProgram(psProgram0);
+
+		newPass0->setBlendState(blendState);
+		newPass0->setDepthStencilState(depthStencilState0);
+		newPass0->setRasterizerState(rasterizerState);
+
+		PassPtr newPass1 = newTechnique->addPass();
+		newPass1->setVertexProgram(vsProgram1);
+		newPass1->setFragmentProgram(psProgram1);
+
+		newPass1->setBlendState(blendState);
+		newPass1->setDepthStencilState(depthStencilState1);
+		newPass1->setRasterizerState(rasterizerState);
+	}
+
 	HMaterial BuiltinEditorResources::createDockDropOverlayMaterial() const
 	{
 		return Material::create(mShaderDockOverlay);
@@ -1340,4 +1464,19 @@ namespace BansheeEngine
 
 		return Material::create(mShaderPickingAlpha[modeIdx]);
 	}
+
+	HMaterial BuiltinEditorResources::createWireGizmoMat() const
+	{
+		return Material::create(mShaderGizmoWire);
+	}
+
+	HMaterial BuiltinEditorResources::createSolidGizmoMat() const
+	{
+		return Material::create(mShaderGizmoSolid);
+	}
+
+	HMaterial BuiltinEditorResources::createIconGizmoMat() const
+	{
+		return Material::create(mShaderGizmoIcon);
+	}
 }

+ 297 - 34
BansheeEditor/Source/BsGizmoManager.cpp

@@ -7,6 +7,13 @@
 #include "BsMeshHeap.h"
 #include "BsCamera.h"
 #include "BsSpriteTexture.h"
+#include "BsCoreThread.h"
+#include "BsBuiltinEditorResources.h"
+#include "BsMaterial.h"
+#include "BsGpuParams.h"
+#include "BsRenderSystem.h"
+#include "BsRenderer.h"
+#include "BsTransientMesh.h"
 
 namespace BansheeEngine
 {
@@ -14,6 +21,8 @@ namespace BansheeEngine
 	const UINT32 GizmoManager::INDEX_BUFFER_GROWTH = 4096 * 2;
 	const UINT32 GizmoManager::SPHERE_QUALITY = 1;
 	const float GizmoManager::MAX_ICON_RANGE = 500.0f;
+	const UINT32 GizmoManager::OPTIMAL_ICON_SIZE = 64;
+	const float GizmoManager::ICON_TEXEL_WORLD_SIZE = 0.05f;
 
 	GizmoManager::GizmoManager(const HCamera& camera)
 		:mTotalRequiredSolidIndices(0), mTotalRequiredSolidVertices(0),
@@ -31,11 +40,22 @@ namespace BansheeEngine
 		mIconVertexDesc = bs_shared_ptr<VertexDataDesc>();
 		mIconVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
 		mIconVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
-		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
+		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 0);
+		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 1);
 
 		mSolidMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mSolidVertexDesc);
 		mWireMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mWireVertexDesc);
 		mIconMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mIconVertexDesc);
+
+		mSolidMaterial.material = BuiltinEditorResources::instance().createSolidGizmoMat();
+		mWireMaterial.material = BuiltinEditorResources::instance().createWireGizmoMat();
+		mIconMaterial.material = BuiltinEditorResources::instance().createIconGizmoMat();
+
+		mSolidMaterial.proxy = mSolidMaterial.material->_createProxy();
+		mWireMaterial.proxy = mWireMaterial.material->_createProxy();
+		mIconMaterial.proxy = mIconMaterial.material->_createProxy();
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::initializeCore, this));
 	}
 
 	GizmoManager::~GizmoManager()
@@ -50,6 +70,38 @@ namespace BansheeEngine
 			mIconMeshHeap->dealloc(mIconMesh);
 	}
 
+	void GizmoManager::initializeCore()
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		// TODO - Make a better interface when dealing with parameters through proxies?
+		{
+			MaterialProxyPtr proxy = mWireMaterial.proxy;
+			GpuParamsPtr vertParams = proxy->params[proxy->passes[0].vertexProgParamsIdx];
+
+			vertParams->getParam("matViewProj", mWireMaterial.mViewProj);
+		}
+
+		{
+			MaterialProxyPtr proxy = mSolidMaterial.proxy;
+			GpuParamsPtr vertParams = proxy->params[proxy->passes[0].vertexProgParamsIdx];
+
+			vertParams->getParam("matViewProj", mSolidMaterial.mViewProj);
+			vertParams->getParam("matViewIT", mSolidMaterial.mViewIT);
+		}
+
+		{
+			MaterialProxyPtr proxy = mIconMaterial.proxy;
+			GpuParamsPtr vertParams = proxy->params[proxy->passes[0].vertexProgParamsIdx];
+
+			vertParams->getParam("matViewProj", mIconMaterial.mViewProj);
+
+			GpuParamsPtr fragParams = proxy->params[proxy->passes[0].fragmentProgParamsIdx];
+
+			fragParams->getTextureParam("mainTexture", mIconMaterial.mTexture);
+		}
+	}
+
 	void GizmoManager::startGizmo(const HSceneObject& gizmoParent)
 	{
 		mActiveSO = gizmoParent;
@@ -193,7 +245,23 @@ namespace BansheeEngine
 	{
 		buildSolidMesh();
 		buildWireMesh();
-		buildIconMesh();
+		IconRenderDataVecPtr iconRenderData = buildIconMesh();
+
+		// TODO - This must be rendered while Scene view is being rendered
+		Matrix4 viewMat = mCamera->getViewMatrix();
+		Matrix4 projMat = mCamera->getProjectionMatrix();
+		ViewportPtr viewport = mCamera->getViewport();
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::coreRenderSolidGizmos, 
+			this, viewMat, projMat, mSolidMesh->_createProxy(0)));
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::coreRenderWireGizmos, 
+			this, viewMat, projMat, mWireMesh->_createProxy(0)));
+
+		RectI screenArea = mCamera->getViewport()->getArea();
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::coreRenderIconGizmos, 
+			this, screenArea, mIconMesh->_createProxy(0), iconRenderData));
 	}
 
 	void GizmoManager::clearGizmos()
@@ -369,7 +437,7 @@ namespace BansheeEngine
 		mWireMesh = mWireMeshHeap->alloc(meshData, DOT_LINE_LIST);
 	}
 
-	void GizmoManager::buildIconMesh()
+	GizmoManager::IconRenderDataVecPtr GizmoManager::buildIconMesh()
 	{
 		mSortedIconData.clear();
 		
@@ -407,8 +475,8 @@ namespace BansheeEngine
 		{
 			if (a.distance == b.distance)
 			{
-				HSpriteTexture texA = mIconData[a.iconIdx].texture;
-				HSpriteTexture texB = mIconData[b.iconIdx].texture;
+				HSpriteTexture texA = mIconData[a.iconIdx].texture->getTexture();
+				HSpriteTexture texB = mIconData[b.iconIdx].texture->getTexture();
 
 				if (texA == texB)
 					return a.iconIdx < b.iconIdx;
@@ -419,39 +487,234 @@ namespace BansheeEngine
 				return a.distance > b.distance;
 		});
 
+		MeshDataPtr meshData = bs_shared_ptr<MeshData>(actualNumIcons * 4, actualNumIcons * 6, mIconVertexDesc);
+
+		auto positionIter = meshData->getVec3DataIter(VES_POSITION);
+		auto texcoordIter = meshData->getVec2DataIter(VES_TEXCOORD);
+		auto normalColorIter = meshData->getDWORDDataIter(VES_COLOR, 0);
+		auto fadedColorIter = meshData->getDWORDDataIter(VES_COLOR, 1);
+
+		UINT32* indices = meshData->getIndices32();
+
+		float cameraScale = 1.0f;
+		if (mCamera->getProjectionType() == PT_ORTHOGRAPHIC)
+			cameraScale = mCamera->getViewport()->getHeight() / mCamera->getOrthoWindowHeight();
+		else
+		{
+			Radian vertFOV(Math::tan(mCamera->getHorzFOV() * 0.5f));
+			cameraScale = (mCamera->getViewport()->getHeight() * 0.5f) / vertFOV.valueRadians();
+		}
+
+		IconRenderDataVecPtr iconRenderData = bs_shared_ptr<IconRenderDataVec>();
+		UINT32 lastTextureIdx = 0;
+		HTexture curTexture;
+
+		// Note: This assumes the meshes will be rendered using the same camera
+		// properties as when they are created
 		for (i = 0; i < actualNumIcons; i++)
 		{
+			SortedIconData& sortedIconData = mSortedIconData[i];
+			IconData& iconData = mIconData[sortedIconData.iconIdx];
+
+			if (curTexture != iconData.texture)
+			{
+				UINT32 numIconsPerTexture = i - lastTextureIdx;
+				if (numIconsPerTexture > 0)
+				{
+					iconRenderData->push_back(IconRenderData());
+					IconRenderData& renderData = iconRenderData->back();
+					renderData.count = numIconsPerTexture;
+					renderData.texture = curTexture;
+				}
+
+				lastTextureIdx = i;
+				curTexture = iconData.texture;
+			}
+
+			UINT32 iconWidth = iconData.texture->getWidth();
+			UINT32 iconHeight = iconData.texture->getHeight();
+
+			limitIconSize(iconWidth, iconHeight);
+
+			Color normalColor, fadedColor;
+			calculateIconColors(iconData.color, *mCamera.get(), iconHeight, iconData.fixedScale, normalColor, fadedColor);
+
+			Vector3 position(sortedIconData.screenPosition.x, sortedIconData.screenPosition.y, sortedIconData.distance);
+			// TODO - Does the depth need to be corrected since it was taken from a projective camera (probably)?
+
+			float halfWidth = iconWidth * 0.5f;
+			float halfHeight = iconHeight * 0.5f;
+
+			if (!iconData.fixedScale)
+			{
+				float iconScale = 1.0f;
+				if (mCamera->getProjectionType() == PT_ORTHOGRAPHIC)
+					iconScale = cameraScale;
+				else
+					iconScale = cameraScale / sortedIconData.distance;
+
+				halfWidth *= iconScale;
+				halfHeight *= iconScale;
+			}
+
+			Vector3 positions[4];
+			positions[0] = position + Vector3(-halfWidth, -halfHeight, 0.0f);
+			positions[1] = position + Vector3(halfWidth, -halfHeight, 0.0f);
+			positions[2] = position + Vector3(-halfWidth, halfHeight, 0.0f);
+			positions[3] = position + Vector3(halfWidth, halfHeight, 0.0f);
+
+			Vector2 uvs[4];
+			uvs[0] = iconData.texture->transformUV(Vector2(0.0f, 0.0f));
+			uvs[1] = iconData.texture->transformUV(Vector2(1.0f, 0.0f));
+			uvs[2] = iconData.texture->transformUV(Vector2(0.0f, 1.0f));
+			uvs[3] = iconData.texture->transformUV(Vector2(1.0f, 1.0f));
+
+			for (UINT32 j = 0; j < 4; j++)
+			{
+				positionIter.addValue(positions[j]);
+				texcoordIter.addValue(uvs[j]);
+				normalColorIter.addValue(normalColor.getAsRGBA());
+				fadedColorIter.addValue(fadedColor.getAsRGBA());
+			}
+
+			UINT32 vertOffset = i * 4;
+
+			indices[0] = vertOffset + 0;
+			indices[1] = vertOffset + 1;
+			indices[2] = vertOffset + 2;
+			indices[3] = vertOffset + 1;
+			indices[4] = vertOffset + 3;
+			indices[5] = vertOffset + 2;
+
+			indices += 6;
+		}
 
+		if (mIconMesh != nullptr)
+			mIconMeshHeap->dealloc(mIconMesh);
+
+		mIconMesh = mIconMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
+
+		return iconRenderData;
+	}
+
+	void GizmoManager::limitIconSize(UINT32& width, UINT32& height)
+	{
+		if (width <= OPTIMAL_ICON_SIZE && height <= OPTIMAL_ICON_SIZE)
+			return;
+
+		float relWidth = OPTIMAL_ICON_SIZE / width;
+		float relHeight = OPTIMAL_ICON_SIZE / height;
+
+		float scale = std::min(relWidth, relHeight);
+
+		width = Math::roundToInt(width * scale);
+		height = Math::roundToInt(height * scale);
+	}
+
+	void GizmoManager::calculateIconColors(const Color& tint, const Camera& camera,
+		UINT32 iconHeight, bool fixedScale, Color& normalColor, Color& fadedColor)
+	{
+		normalColor = tint;
+
+		if (!fixedScale)
+		{
+			float iconToScreenRatio = iconHeight / (float)camera.getViewport()->getHeight();
+
+			if (iconToScreenRatio > 0.3f)
+			{
+				float alpha = 1.0f - Math::lerp01(iconToScreenRatio, 0.3f, 1.0f);
+				normalColor.a *= alpha;
+			}
+			else if (iconToScreenRatio < 0.1f)
+			{
+				float alpha = Math::lerp01(iconToScreenRatio, 0.0f, 0.1f);
+				normalColor.a *= alpha;
+			}
+		}
+
+		fadedColor = normalColor;
+		fadedColor.a *= 0.2f;
+	}
+
+	void GizmoManager::coreRenderSolidGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, MeshProxyPtr meshProxy)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		Matrix4 viewProjMat = projMatrix * viewMatrix;
+		Matrix4 viewIT = viewMatrix.inverse().transpose();
+
+		mSolidMaterial.mViewProj.set(viewProjMat);
+		mSolidMaterial.mViewIT.set(viewIT);
+
+		Renderer::setPass(*mSolidMaterial.proxy, 0);
+		Renderer::draw(*meshProxy);
+	}
+
+	void GizmoManager::coreRenderWireGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, MeshProxyPtr meshProxy)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		Matrix4 viewProjMat = projMatrix * viewMatrix;
+		Matrix4 viewIT = viewMatrix.inverse().transpose();
+
+		mWireMaterial.mViewProj.set(viewProjMat);
+
+		Renderer::setPass(*mWireMaterial.proxy, 0);
+		Renderer::draw(*meshProxy);
+	}
+
+	void GizmoManager::coreRenderIconGizmos(RectI screenArea, MeshProxyPtr meshProxy, IconRenderDataVecPtr renderData)
+	{
+		RenderSystem& rs = RenderSystem::instance();
+		MeshBasePtr mesh;
+
+		// TODO: Instead of this lock consider just storing all needed data in MeshProxy and not referencing Mesh at all?
+		if (!meshProxy->mesh.expired())
+			mesh = meshProxy->mesh.lock();
+		else
+			return;
+
+		std::shared_ptr<VertexData> vertexData = mesh->_getVertexData();
+
+		rs.setVertexDeclaration(vertexData->vertexDeclaration);
+		auto vertexBuffers = vertexData->getBuffers();
+
+		VertexBufferPtr vertBuffers[1] = { vertexBuffers.begin()->second };
+		rs.setVertexBuffers(0, vertBuffers, 1);
+
+		IndexBufferPtr indexBuffer = mesh->_getIndexBuffer();
+		rs.setIndexBuffer(indexBuffer);
+
+		rs.setDrawOperation(DOT_TRIANGLE_LIST);
+
+		// Set up ortho matrix
+		Matrix4 projMat; 
+
+		float left = screenArea.x + rs.getHorizontalTexelOffset();
+		float right = screenArea.x + screenArea.width + rs.getHorizontalTexelOffset();
+		float top = screenArea.y + rs.getVerticalTexelOffset();
+		float bottom = screenArea.y + screenArea.height + rs.getVerticalTexelOffset();
+		float near = rs.getMinimumDepthInputValue();
+		float far = rs.getMaximumDepthInputValue();
+
+		projMat.makeProjectionOrtho(left, right, top, bottom, near, far);
+		mIconMaterial.mViewProj.set(projMat);
+
+		for (UINT32 passIdx = 0; passIdx < 2; passIdx++)
+		{
+			Renderer::setPass(*mIconMaterial.proxy, passIdx);
+
+			UINT32 curIndexOffset = 0;
+			for (auto curRenderData : *renderData)
+			{
+				mIconMaterial.mTexture.set(curRenderData.texture);
+
+				rs.drawIndexed(curIndexOffset, curRenderData.count * 6, 0, curRenderData.count * 4);
+				curIndexOffset += curRenderData.count * 6;
+
+			}
 		}
 
-		// TODO - Group by texture
-		// TODO - Scale icons to always fit in some max size
-		// TODO - Render in screen space
-
-
-		// TODO - Set up a render method that actually renders the gizmos
-		//	      - Optionally also add a special method that renders the gizmos for picking purposes
-
-
-		// ----------------------------
-		// TODO - In SBansheeEditor:
-		//  - Add BsScriptGizmos that implements the C# Gizmos interface
-		//  - It tracks when an assembly is reloaded, and finds all valid gizmo drawing methods
-		//     - They need to have DrawGizmo attribute and accept a Component of a specific type as first param (and be static)
-		//     - Internally they call GizmoManager
-		//  - Then in update:
-		//   - Call GizmoManager and clear gizmos
-		//   - Go through every SceneObject and their components to find custom gizmos for those types
-		//   - Call their draw gizmo methods
-		//
-		// TODO - In ScenePicking:
-		//  - Call GizmoManager that renders all gizmos using picking color coding
-		//   - I might instead just add a way to retrieve render data from GizmoManager since GizmoManager doesn't have access to picking materials
-		//
-		// TODO - How do I handle C++ types like Camera and Renderable?
-		//  - Add ComponentBase.cs from which Camera and Renderable inherit
-		//  - Ensure any instances where I currently use Component I use ComponentBase instead (except for actual ManagedComponents)
-		//  - Camera and Renderable will have their own specialized implementation for scripts, but interally I will use normal Camera/Renderable components
-		//  - TODO Haven't thought this out yet
+		mesh->_notifyUsedOnGPU();
 	}
 }

+ 2 - 0
BansheeEditor/Source/BsScenePicking.cpp

@@ -252,6 +252,8 @@ namespace BansheeEngine
 				md.mParamPickingWVP.set(renderable.wvpTransform);
 				md.mParamPickingColor.set(color);
 			}
+
+			Renderer::draw(*renderable.mesh);
 		}
 		rs.endFrame();
 

+ 10 - 0
BansheeEngine/Include/BsSpriteTexture.h

@@ -30,6 +30,16 @@ namespace BansheeEngine
 		 */
 		static const HSpriteTexture& dummy();
 
+		/**
+		 * @brief	Returns width of the sprite texture in pixels.
+		 */
+		UINT32 getWidth() const;
+
+		/**
+		 * @brief	Returns height of the sprite texture in pixels.
+		 */
+		UINT32 getHeight() const;
+
 		/**
 		 * @brief	Creates a new sprite texture that references the entire area of the provided
 		 *			texture.

+ 10 - 0
BansheeEngine/Source/BsSpriteTexture.cpp

@@ -33,6 +33,16 @@ namespace BansheeEngine
 		return tex != nullptr && tex.isLoaded() && tex->getTexture() != nullptr && tex.isLoaded();
 	}
 
+	UINT32 SpriteTexture::getWidth() const
+	{
+		return Math::roundToInt(mAtlasTexture->getWidth() * mUVScale.x);
+	}
+
+	UINT32 SpriteTexture::getHeight() const
+	{
+		return Math::roundToInt(mAtlasTexture->getHeight() * mUVScale.y);
+	}
+
 	HSpriteTexture SpriteTexture::create(const HTexture& texture)
 	{
 		SpriteTexturePtr texturePtr = bs_core_ptr<SpriteTexture, PoolAlloc>

+ 11 - 0
BansheeUtility/Include/BsMath.h

@@ -333,6 +333,17 @@ namespace BansheeEngine
 		 */
 		static float fastATan1(float val);
 
+		/**
+		 * @brief	Interpolates between min and max. Returned value is in
+		 *			[0, 1] range where min = 0, max = 1 and 0.5 is the average
+		 *			of min and max.
+		 */
+		template <typename T>
+		static float lerp01(T val, T min, T max)
+		{
+			return clamp01((val - min) / std::max(max - min, 0.0001F));
+		}
+
         static const float POS_INFINITY;
         static const float NEG_INFINITY;
         static const float PI;

+ 5 - 0
BansheeUtility/Include/BsMatrix4.h

@@ -420,6 +420,11 @@ namespace BansheeEngine
 		 */
 		void makeView(const Vector3& position, const Quaternion& orientation, const Matrix4* reflectMatrix = nullptr);
 
+		/**
+		 * @brief	Creates an ortographic projection matrix.
+		 */
+		void makeProjectionOrtho(float left, float right, float top, float bottom, float near, float far);
+
 		static const Matrix4 ZERO;
 		static const Matrix4 IDENTITY;
     };

+ 28 - 0
BansheeUtility/Source/BsMatrix4.cpp

@@ -245,4 +245,32 @@ namespace BansheeEngine
 			*this = (*this) * (*reflectMatrix);
 		}
 	}
+
+	void Matrix4::makeProjectionOrtho(float left, float right, float bottom,
+		float top, float near, float far)
+	{
+		float deltaX = right - left;
+		float deltaY = top - bottom;
+		float deltaZ = far - near;
+
+		m[0][0] = 2.0F / deltaX;
+		m[0][1] = 0.0f;
+		m[0][2] = 0.0f;
+		m[0][3] = -(right + left) / deltaX;
+
+		m[1][0] = 0.0f;
+		m[1][1] = 2.0F / deltaY;
+		m[1][2] = 0.0f;
+		m[1][3] = -(top + bottom) / deltaY;
+
+		m[2][0] = 0.0f;
+		m[2][1] = 0.0f;
+		m[2][2] = -2.0F / deltaZ;
+		m[2][3] = -(far + near) / deltaZ;
+
+		m[3][0] = 0.0f;
+		m[3][1] = 0.0f;
+		m[3][2] = 0.0f;
+		m[3][3] = 1.0f;
+	}
 }

+ 13 - 9
SBansheeEditor/Source/BsScriptGizmoManager.cpp

@@ -47,14 +47,24 @@ namespace BansheeEngine
 		todo.push(rootSO);
 
 		bool isParentSelected = false;
+		UINT32 parentSelectedPopIdx = 0;
 		
 		while (!todo.empty())
 		{
+			if (isParentSelected && parentSelectedPopIdx == (UINT32)todo.size())
+			{
+				isParentSelected = false;
+			}
+
 			HSceneObject curSO = todo.top();
 			todo.pop();
 
 			bool isSelected = dummyIsSelected(curSO);
-			bool isParentSelected = false; // TODO - Currently ignoring this
+			if (isSelected && !isParentSelected)
+			{
+				isParentSelected = true;
+				parentSelectedPopIdx = (UINT32)todo.size();
+			}
 
 			const Vector<HComponent>& components = curSO->getComponents();
 			for (auto& component : components)
@@ -68,9 +78,6 @@ namespace BansheeEngine
 					{
 						UINT32 flags = iterFind->second.flags;
 
-						// TODO - Check if gizmo is to be drawn only when selected
-						// TODO - Set pickable if needed
-
 						bool drawGizmo = false;
 						if (((flags & (UINT32)DrawGizmoFlags::Selected) != 0) && isSelected)
 							drawGizmo = true;
@@ -86,11 +93,8 @@ namespace BansheeEngine
 							bool pickable = (flags & (UINT32)DrawGizmoFlags::Pickable) != 0;
 							GizmoManager::instance().setPickable(pickable);
 
-							if ((flags & (UINT32)DrawGizmoFlags::Pickable) != 0)
-							{
-								void* params[1] = { managedComponent->getManagedInstance() };
-								iterFind->second.drawGizmosMethod->invoke(nullptr, params);
-							}
+							void* params[1] = { managedComponent->getManagedInstance() };
+							iterFind->second.drawGizmosMethod->invoke(nullptr, params);
 						}
 					}
 				}

+ 5 - 0
SceneView.txt

@@ -11,6 +11,11 @@ IMMEDIATE:
    - Or just use MeshHeap and update the icon/lines every frame?
  - Test all the new DrawHelper3D methods
 
+ GIZMO TODO:
+  - IMPORTANT: Gizmo rendering happens in update() but it should happen whenever scene view is being rendered as the render target isn't set anywhere
+  - Add a method that renders gizmos for picking
+  - Figure out how to deal with builtin components like Camera and Renderable (e.g. how will they have gizmos since they're not managed components?)
+
 LATER:
  - Need a way to render text for gizmos and handles, and in scene in general