Browse Source

Draw helper can now generate text meshes

BearishSun 10 years ago
parent
commit
e032131090

+ 866 - 866
BansheeEditor/Source/BsGizmoManager.cpp

@@ -1,867 +1,867 @@
-#include "BsGizmoManager.h"
-#include "BsMesh.h"
-#include "BsAABox.h"
-#include "BsSphere.h"
-#include "BsVertexDataDesc.h"
-#include "BsShapeMeshes3D.h"
-#include "BsMeshHeap.h"
-#include "BsCCamera.h"
-#include "BsSpriteTexture.h"
-#include "BsCoreThread.h"
-#include "BsBuiltinEditorResources.h"
-#include "BsMaterial.h"
-#include "BsGpuParams.h"
-#include "BsRenderAPI.h"
-#include "BsCoreRenderer.h"
-#include "BsRendererUtility.h"
-#include "BsTransientMesh.h"
-#include "BsRendererManager.h"
-#include "BsDrawHelper.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	const UINT32 GizmoManager::VERTEX_BUFFER_GROWTH = 4096;
-	const UINT32 GizmoManager::INDEX_BUFFER_GROWTH = 4096 * 2;
-	const UINT32 GizmoManager::SPHERE_QUALITY = 1;
-	const UINT32 GizmoManager::WIRE_SPHERE_QUALITY = 10;
-	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()
-		:mPickable(false), mDrawHelper(nullptr), mPickingDrawHelper(nullptr), mCore(nullptr), mCurrentIdx(0)
-	{
-		mTransform = Matrix4::IDENTITY;
-		mDrawHelper = bs_new<DrawHelper>();
-		mPickingDrawHelper = bs_new<DrawHelper>();
-
-		mIconVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
-		mIconVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
-		mIconVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
-		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 0);
-		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 1);
-
-		mIconMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mIconVertexDesc);
-
-		HMaterial solidMaterial = BuiltinEditorResources::instance().createSolidGizmoMat();
-		HMaterial wireMaterial = BuiltinEditorResources::instance().createWireGizmoMat();
-		HMaterial iconMaterial = BuiltinEditorResources::instance().createIconGizmoMat();
-		HMaterial pickingMaterial = BuiltinEditorResources::instance().createGizmoPickingMat();
-		HMaterial alphaPickingMaterial = BuiltinEditorResources::instance().createAlphaGizmoPickingMat();
-
-		CoreInitData initData;
-
-		initData.solidMat = solidMaterial->getCore();
-		initData.wireMat = wireMaterial->getCore();
-		initData.iconMat = iconMaterial->getCore();
-		initData.pickingMat = pickingMaterial->getCore();
-		initData.alphaPickingMat = alphaPickingMaterial->getCore();
-
-		mCore.store(bs_new<GizmoManagerCore>(GizmoManagerCore::PrivatelyConstuct()), std::memory_order_release);
-
-		gCoreAccessor().queueCommand(std::bind(&GizmoManager::initializeCore, this, initData));
-	}
-
-	GizmoManager::~GizmoManager()
-	{
-		if (mIconMesh != nullptr)
-			mIconMeshHeap->dealloc(mIconMesh);
-
-		bs_delete(mDrawHelper);
-		bs_delete(mPickingDrawHelper);
-
-		gCoreAccessor().queueCommand(std::bind(&GizmoManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
-	}
-
-	void GizmoManager::initializeCore(const CoreInitData& initData)
-	{
-		mCore.load(std::memory_order_acquire)->initialize(initData);
-	}
-
-	void GizmoManager::destroyCore(GizmoManagerCore* core)
-	{
-		bs_delete(core);
-	}
-
-	void GizmoManager::startGizmo(const HSceneObject& gizmoParent)
-	{
-		mActiveSO = gizmoParent;
-	}
-
-	void GizmoManager::endGizmo()
-	{
-		mActiveSO = nullptr;
-	}
-
-	void GizmoManager::setColor(const Color& color)
-	{
-		mDrawHelper->setColor(color);
-		mColor = color;
-	}
-
-	void GizmoManager::setTransform(const Matrix4& transform)
-	{
-		mDrawHelper->setTransform(transform);
-		mTransform = transform;
-	}
-
-	void GizmoManager::drawCube(const Vector3& position, const Vector3& extents)
-	{
-		mSolidCubeData.push_back(CubeData());
-		CubeData& cubeData = mSolidCubeData.back();
-
-		cubeData.idx = mCurrentIdx++;
-		cubeData.position = position;
-		cubeData.extents = extents;
-		cubeData.color = mColor;
-		cubeData.transform = mTransform;
-		cubeData.sceneObject = mActiveSO;
-		cubeData.pickable = mPickable;
-
-		mDrawHelper->cube(position, extents);
-		mIdxToSceneObjectMap[cubeData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawSphere(const Vector3& position, float radius)
-	{
-		mSolidSphereData.push_back(SphereData());
-		SphereData& sphereData = mSolidSphereData.back();
-
-		sphereData.idx = mCurrentIdx++;
-		sphereData.position = position;
-		sphereData.radius = radius;
-		sphereData.color = mColor;
-		sphereData.transform = mTransform;
-		sphereData.sceneObject = mActiveSO;
-		sphereData.pickable = mPickable;
-
-		mDrawHelper->sphere(position, radius);
-		mIdxToSceneObjectMap[sphereData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawWireCube(const Vector3& position, const Vector3& extents)
-	{
-		mWireCubeData.push_back(CubeData());
-		CubeData& cubeData = mWireCubeData.back();
-
-		cubeData.idx = mCurrentIdx++;
-		cubeData.position = position;
-		cubeData.extents = extents;
-		cubeData.color = mColor;
-		cubeData.transform = mTransform;
-		cubeData.sceneObject = mActiveSO;
-		cubeData.pickable = mPickable;
-
-		mDrawHelper->wireCube(position, extents);
-		mIdxToSceneObjectMap[cubeData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawWireSphere(const Vector3& position, float radius)
-	{
-		mWireSphereData.push_back(SphereData());
-		SphereData& sphereData = mWireSphereData.back();
-
-		sphereData.idx = mCurrentIdx++;
-		sphereData.position = position;
-		sphereData.radius = radius;
-		sphereData.color = mColor;
-		sphereData.transform = mTransform;
-		sphereData.sceneObject = mActiveSO;
-		sphereData.pickable = mPickable;
-
-		mDrawHelper->wireSphere(position, radius);
-		mIdxToSceneObjectMap[sphereData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawLine(const Vector3& start, const Vector3& end)
-	{
-		mLineData.push_back(LineData());
-		LineData& lineData = mLineData.back();
-
-		lineData.idx = mCurrentIdx++;
-		lineData.start = start;
-		lineData.end = end;
-		lineData.color = mColor;
-		lineData.transform = mTransform;
-		lineData.sceneObject = mActiveSO;
-		lineData.pickable = mPickable;
-
-		mDrawHelper->line(start, end);
-		mIdxToSceneObjectMap[lineData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawFrustum(const Vector3& position, float aspect, Degree FOV, float near, float far)
-	{
-		mFrustumData.push_back(FrustumData());
-		FrustumData& frustumData = mFrustumData.back();
-
-		frustumData.idx = mCurrentIdx++;
-		frustumData.position = position;
-		frustumData.aspect = aspect;
-		frustumData.FOV = FOV;
-		frustumData.near = near;
-		frustumData.far = far;
-		frustumData.color = mColor;
-		frustumData.transform = mTransform;
-		frustumData.sceneObject = mActiveSO;
-		frustumData.pickable = mPickable;
-
-		mDrawHelper->frustum(position, aspect, FOV, near, far);
-		mIdxToSceneObjectMap[frustumData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::drawIcon(Vector3 position, HSpriteTexture image, bool fixedScale)
-	{
-		mIconData.push_back(IconData());
-		IconData& iconData = mIconData.back();
-
-		iconData.idx = mCurrentIdx++;
-		iconData.position = position;
-		iconData.texture = image;
-		iconData.fixedScale = fixedScale;
-		iconData.color = mColor;
-		iconData.transform = mTransform;
-		iconData.sceneObject = mActiveSO;
-		iconData.pickable = mPickable;
-
-		mIdxToSceneObjectMap[iconData.idx] = mActiveSO;
-	}
-
-	void GizmoManager::update(const CameraPtr& camera)
-	{
-		mDrawHelper->clearMeshes();
-
-		if (mIconMesh != nullptr)
-			mIconMeshHeap->dealloc(mIconMesh);
-
-		IconRenderDataVecPtr iconRenderData;
-
-		mDrawHelper->buildMeshes();
-		const Vector<DrawHelper::ShapeMeshData>& meshes = mDrawHelper->getMeshes();
-
-		SPtr<MeshCoreBase> solidMesh = nullptr;
-		SPtr<MeshCoreBase> wireMesh = nullptr;
-		for (auto& meshData : meshes)
-		{
-			if (meshData.type == DrawHelper::MeshType::Solid)
-			{
-				if (solidMesh == nullptr)
-					solidMesh = meshData.mesh->getCore();
-			}
-			else // Wire
-			{
-				if (wireMesh == nullptr)
-					wireMesh = meshData.mesh->getCore();
-			}
-		}
-
-		// Since there is no sorting used with draw helper meshes we only expect up to two of them,
-		// one for solids, one for wireframe
-		assert(meshes.size() <= 2);
-
-		mIconMesh = buildIconMesh(camera, mIconData, false, iconRenderData);
-		SPtr<MeshCoreBase> iconMesh = mIconMesh->getCore();
-
-		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
-
-		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, camera->getCore(),
-			solidMesh, wireMesh, iconMesh, iconRenderData));
-	}
-
-	void GizmoManager::renderForPicking(const CameraPtr& camera, std::function<Color(UINT32)> idxToColorCallback)
-	{
-		Vector<IconData> iconData;
-		IconRenderDataVecPtr iconRenderData;
-
-		mPickingDrawHelper->clear();
-
-		for (auto& cubeDataEntry : mSolidCubeData)
-		{
-			if (!cubeDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(cubeDataEntry.idx));
-			mPickingDrawHelper->setTransform(cubeDataEntry.transform);
-
-			mPickingDrawHelper->cube(cubeDataEntry.position, cubeDataEntry.extents);
-		}
-
-		for (auto& cubeDataEntry : mWireCubeData)
-		{
-			if (!cubeDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(cubeDataEntry.idx));
-			mPickingDrawHelper->setTransform(cubeDataEntry.transform);
-
-			mPickingDrawHelper->wireCube(cubeDataEntry.position, cubeDataEntry.extents);
-		}
-
-		for (auto& sphereDataEntry : mSolidSphereData)
-		{
-			if (!sphereDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(sphereDataEntry.idx));
-			mPickingDrawHelper->setTransform(sphereDataEntry.transform);
-
-			mPickingDrawHelper->sphere(sphereDataEntry.position, sphereDataEntry.radius);
-		}
-
-		for (auto& sphereDataEntry : mWireSphereData)
-		{
-			if (!sphereDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(sphereDataEntry.idx));
-			mPickingDrawHelper->setTransform(sphereDataEntry.transform);
-
-			mPickingDrawHelper->wireSphere(sphereDataEntry.position, sphereDataEntry.radius);
-		}
-
-		for (auto& lineDataEntry : mLineData)
-		{
-			if (!lineDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(lineDataEntry.idx));
-			mPickingDrawHelper->setTransform(lineDataEntry.transform);
-
-			mPickingDrawHelper->line(lineDataEntry.start, lineDataEntry.end);
-		}
-
-		for (auto& frustumDataEntry : mFrustumData)
-		{
-			if (!frustumDataEntry.pickable)
-				continue;
-
-			mPickingDrawHelper->setColor(idxToColorCallback(frustumDataEntry.idx));
-			mPickingDrawHelper->setTransform(frustumDataEntry.transform);
-
-			mPickingDrawHelper->frustum(frustumDataEntry.position, frustumDataEntry.aspect, frustumDataEntry.FOV, 
-				frustumDataEntry.near, frustumDataEntry.far);
-		}
-
-		for (auto& iconDataEntry : mIconData)
-		{
-			if (!iconDataEntry.pickable)
-				continue;
-
-			iconData.push_back(iconDataEntry);
-			iconData.back().color = idxToColorCallback(iconDataEntry.idx);
-		}
-
-		mPickingDrawHelper->buildMeshes();
-		const Vector<DrawHelper::ShapeMeshData>& meshes = mPickingDrawHelper->getMeshes();
-
-		TransientMeshPtr iconMesh = buildIconMesh(camera, iconData, true, iconRenderData);
-
-		// Note: This must be rendered while Scene view is being rendered
-		Matrix4 viewMat = camera->getViewMatrix();
-		Matrix4 projMat = camera->getProjectionMatrixRS();
-		ViewportPtr viewport = camera->getViewport();
-
-		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
-
-		for (auto& meshData : meshes)
-		{
-			if (meshData.type == DrawHelper::MeshType::Solid)
-			{
-				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
-					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
-			}
-			else // Wire
-			{
-				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
-					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
-			}
-		}
-
-		Rect2I screenArea = camera->getViewport()->getArea();
-
-		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderIconGizmos,
-			core, screenArea, iconMesh->getCore(), iconRenderData, true));
-
-		mPickingDrawHelper->clearMeshes();
-		mIconMeshHeap->dealloc(iconMesh);
-	}
-
-	void GizmoManager::clearGizmos()
-	{
-		mSolidCubeData.clear();
-		mWireCubeData.clear();
-		mSolidSphereData.clear();
-		mWireSphereData.clear();
-		mLineData.clear();
-		mFrustumData.clear();
-		mIconData.clear();
-		mIdxToSceneObjectMap.clear();
-
-		mDrawHelper->clear();
-
-		mCurrentIdx = 0;
-	}
-
-	void GizmoManager::clearRenderData()
-	{
-		mDrawHelper->clearMeshes();
-
-		if (mIconMesh != nullptr)
-			mIconMeshHeap->dealloc(mIconMesh);
-
-		mIconMesh = nullptr;
-
-		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
-		IconRenderDataVecPtr iconRenderData = bs_shared_ptr_new<IconRenderDataVec>();
-		
-		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, nullptr, nullptr, nullptr, nullptr, iconRenderData));
-	}
-
-	TransientMeshPtr GizmoManager::buildIconMesh(const CameraPtr& camera, const Vector<IconData>& iconData,
-		bool forPicking, GizmoManager::IconRenderDataVecPtr& iconRenderData)
-	{
-		mSortedIconData.clear();
-		
-		if (iconData.size() > mSortedIconData.size())
-			mSortedIconData.resize(iconData.size());
-
-		UINT32 i = 0;
-		for (auto& iconEntry : iconData)
-		{
-			Vector3 viewPoint = camera->worldToViewPoint(iconEntry.position);
-
-			float distance = -viewPoint.z;
-			if (distance < camera->getNearClipDistance()) // Ignore behind clip plane
-				continue;
-
-			if (distance > MAX_ICON_RANGE) // Ignore too far away
-				continue;
-
-			if (!iconEntry.texture) // Ignore missing texture
-				continue;
-
-			if (forPicking && !iconEntry.pickable)
-				continue;
-
-			SortedIconData& sortedIconData = mSortedIconData[i];
-			sortedIconData.iconIdx = i;
-			sortedIconData.distance = distance;
-			sortedIconData.screenPosition = camera->viewToScreenPoint(viewPoint);
-
-			i++;
-		}
-
-		UINT32 actualNumIcons = i;
-
-		// Sort back to front first, then by texture
-		std::sort(mSortedIconData.begin(), mSortedIconData.begin() + actualNumIcons, 
-			[&](const SortedIconData& a, const SortedIconData& b)
-		{
-			if (a.distance == b.distance)
-			{
-				HTexture texA = iconData[a.iconIdx].texture->getTexture();
-				HTexture texB = iconData[b.iconIdx].texture->getTexture();
-
-				if (texA == texB)
-					return a.iconIdx < b.iconIdx;
-
-				return texA->getInternalID() < texB->getInternalID();
-			}
-			else
-				return a.distance > b.distance;
-		});
-
-		MeshDataPtr meshData = bs_shared_ptr_new<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 (camera->getProjectionType() == PT_ORTHOGRAPHIC)
-			cameraScale = camera->getViewport()->getHeight() / camera->getOrthoWindowHeight();
-		else
-		{
-			Radian vertFOV(Math::tan(camera->getHorzFOV() * 0.5f));
-			cameraScale = (camera->getViewport()->getHeight() * 0.5f) / vertFOV.valueRadians();
-		}
-
-		iconRenderData = bs_shared_ptr_new<IconRenderDataVec>();
-		UINT32 lastTextureIdx = std::numeric_limits<UINT32>::max();
-		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];
-			const IconData& curIconData = iconData[sortedIconData.iconIdx];
-
-			HTexture atlasTexture = curIconData.texture->getTexture();
-
-			if (curTexture != atlasTexture)
-			{
-				UINT32 numIconsPerTexture = i - lastTextureIdx;
-				if (numIconsPerTexture > 0)
-				{
-					iconRenderData->push_back(IconRenderData());
-					IconRenderData& renderData = iconRenderData->back();
-					renderData.count = numIconsPerTexture;
-					renderData.texture = atlasTexture->getCore();
-				}
-
-				lastTextureIdx = i;
-				curTexture = atlasTexture;
-			}
-
-			UINT32 iconWidth = curIconData.texture->getWidth();
-			UINT32 iconHeight = curIconData.texture->getHeight();
-
-			limitIconSize(iconWidth, iconHeight);
-
-			Vector3 position((float)sortedIconData.screenPosition.x, (float)sortedIconData.screenPosition.y, -sortedIconData.distance);
-			Vector3 projPosition = camera->projectPoint(position);
-			position.z = projPosition.z;
-
-			float halfWidth = iconWidth * 0.5f;
-			float halfHeight = iconHeight * 0.5f;
-
-			if (!curIconData.fixedScale)
-			{
-				float iconScale = 1.0f;
-				if (camera->getProjectionType() == PT_ORTHOGRAPHIC)
-					iconScale = cameraScale * ICON_TEXEL_WORLD_SIZE;
-				else
-					iconScale = (cameraScale * ICON_TEXEL_WORLD_SIZE) / sortedIconData.distance;
-
-				halfWidth *= iconScale;
-				halfHeight *= iconScale;
-			}
-
-			Color normalColor, fadedColor;
-			calculateIconColors(curIconData.color, camera, (UINT32)(halfHeight * 2.0f), curIconData.fixedScale, normalColor, fadedColor);
-
-			if (forPicking)
-			{
-				normalColor = curIconData.color;
-				fadedColor = curIconData.color;
-			}
-
-			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] = curIconData.texture->transformUV(Vector2(0.0f, 0.0f));
-			uvs[1] = curIconData.texture->transformUV(Vector2(1.0f, 0.0f));
-			uvs[2] = curIconData.texture->transformUV(Vector2(1.0f, 1.0f));
-			uvs[3] = curIconData.texture->transformUV(Vector2(0.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 + 0;
-			indices[4] = vertOffset + 2;
-			indices[5] = vertOffset + 3;
-
-			indices += 6;
-		}
-
-		return mIconMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
-	}
-
-	void GizmoManager::limitIconSize(UINT32& width, UINT32& height)
-	{
-		if (width <= OPTIMAL_ICON_SIZE && height <= OPTIMAL_ICON_SIZE)
-			return;
-
-		float relWidth = OPTIMAL_ICON_SIZE / (float)width;
-		float relHeight = OPTIMAL_ICON_SIZE / (float)height;
-
-		float scale = std::min(relWidth, relHeight);
-
-		width = Math::roundToInt(width * scale);
-		height = Math::roundToInt(height * scale);
-	}
-
-	void GizmoManager::calculateIconColors(const Color& tint, const CameraPtr& 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;
-	}
-
-	HSceneObject GizmoManager::getSceneObject(UINT32 gizmoIdx)
-	{
-		auto iterFind = mIdxToSceneObjectMap.find(gizmoIdx);
-
-		if (iterFind != mIdxToSceneObjectMap.end())
-			return iterFind->second;
-
-		return HSceneObject();
-	}
-
-	const float GizmoManagerCore::PICKING_ALPHA_CUTOFF = 0.5f;
-
-	GizmoManagerCore::GizmoManagerCore(const PrivatelyConstuct& dummy)
-	{
-	}
-
-	GizmoManagerCore::~GizmoManagerCore()
-	{
-		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-		if (mCamera != nullptr)
-			activeRenderer->_unregisterRenderCallback(mCamera.get(), 20);
-	}
-
-	void GizmoManagerCore::initialize(const GizmoManager::CoreInitData& initData)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		mSolidMaterial.mat = initData.solidMat;
-		mWireMaterial.mat = initData.wireMat;
-		mIconMaterial.mat = initData.iconMat;
-		mPickingMaterial.mat = initData.pickingMat;
-		mAlphaPickingMaterial.mat = initData.alphaPickingMat;
-
-		{
-			SPtr<MaterialCore> mat = mWireMaterial.mat;
-			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
-
-			vertParams->getParam("matViewProj", mWireMaterial.mViewProj);
-		}
-
-		{
-			SPtr<MaterialCore> mat = mSolidMaterial.mat;
-			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
-			SPtr<GpuParamsCore> fragParams = mat->getPassParameters(0)->mFragParams;
-
-			vertParams->getParam("matViewProj", mSolidMaterial.mViewProj);
-			fragParams->getParam("viewDir", mSolidMaterial.mViewDir);
-		}
-
-		{
-			SPtr<MaterialCore> mat = mIconMaterial.mat;
-			SPtr<PassParametersCore> pass0Params = mat->getPassParameters(0);
-			SPtr<PassParametersCore> pass1Params = mat->getPassParameters(1);
-
-			SPtr<GpuParamsCore> vertParams0 = pass0Params->mVertParams;
-			SPtr<GpuParamsCore> vertParams1 = pass1Params->mVertParams;
-
-			vertParams0->getParam("matViewProj", mIconMaterial.mViewProj[0]);
-			vertParams1->getParam("matViewProj", mIconMaterial.mViewProj[1]);
-
-			mIconMaterial.mFragParams[0] = pass0Params->mFragParams;
-			mIconMaterial.mFragParams[1] = pass1Params->mFragParams;
-
-			mIconMaterial.mFragParams[0]->getTextureParam("mainTexture", mIconMaterial.mTexture[0]);
-			mIconMaterial.mFragParams[1]->getTextureParam("mainTexture", mIconMaterial.mTexture[1]);
-		}
-
-		{
-			SPtr<MaterialCore> mat = mPickingMaterial.mat;
-			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
-
-			vertParams->getParam("matViewProj", mPickingMaterial.mViewProj);
-		}
-
-		{
-			SPtr<MaterialCore> mat = mAlphaPickingMaterial.mat;
-			SPtr<PassParametersCore> passParams = mat->getPassParameters(0);
-			SPtr<GpuParamsCore> vertParams = passParams->mVertParams;
-
-			vertParams->getParam("matViewProj", mAlphaPickingMaterial.mViewProj);
-
-			mAlphaPickingMaterial.mFragParams = passParams->mFragParams;
-			mAlphaPickingMaterial.mFragParams->getTextureParam("mainTexture", mAlphaPickingMaterial.mTexture);
-
-			GpuParamFloatCore alphaCutoffParam;
-			mAlphaPickingMaterial.mFragParams->getParam("alphaCutoff", alphaCutoffParam);
-			alphaCutoffParam.set(PICKING_ALPHA_CUTOFF);
-		}
-	}
-
-	void GizmoManagerCore::updateData(const SPtr<CameraCore>& camera, const SPtr<MeshCoreBase>& solidMesh, const SPtr<MeshCoreBase>& wireMesh,
-		const SPtr<MeshCoreBase>& iconMesh, const GizmoManager::IconRenderDataVecPtr& iconRenderData)
-	{
-		if (mCamera != camera)
-		{
-			CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-			if (mCamera != nullptr)
-				activeRenderer->_unregisterRenderCallback(mCamera.get(), 0);
-
-			if (camera != nullptr)
-				activeRenderer->_registerRenderCallback(camera.get(), 0, std::bind(&GizmoManagerCore::render, this));
-		}
-
-		mCamera = camera;
-		mSolidMesh = solidMesh;
-		mWireMesh = wireMesh;
-		mIconMesh = iconMesh;
-		mIconRenderData = iconRenderData;
-	}
-
-	void GizmoManagerCore::render()
-	{
-		if (mCamera == nullptr)
-			return;
-
-		SPtr<RenderTargetCore> renderTarget = mCamera->getViewport()->getTarget();
-
-		float width = (float)renderTarget->getProperties().getWidth();
-		float height = (float)renderTarget->getProperties().getHeight();
-
-		Rect2 normArea = mCamera->getViewport()->getNormArea();
-
-		Rect2I screenArea;
-		screenArea.x = (int)(normArea.x * width);
-		screenArea.y = (int)(normArea.y * height);
-		screenArea.width = (int)(normArea.width * width);
-		screenArea.height = (int)(normArea.height * height);
-
-		if (mSolidMesh != nullptr)
-			renderGizmos(mCamera->getViewMatrix(), mCamera->getProjectionMatrixRS(), mCamera->getForward(), mSolidMesh, GizmoManager::GizmoMaterial::Solid);
-
-		if (mWireMesh != nullptr)
-			renderGizmos(mCamera->getViewMatrix(), mCamera->getProjectionMatrixRS(), mCamera->getForward(), mWireMesh, GizmoManager::GizmoMaterial::Wire);
-
-		if (mIconMesh != nullptr)
-			renderIconGizmos(screenArea, mIconMesh, mIconRenderData, false);
-	}
-
-	void GizmoManagerCore::renderGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, Vector3 viewDir, SPtr<MeshCoreBase> mesh, GizmoManager::GizmoMaterial material)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		Matrix4 viewProjMat = projMatrix * viewMatrix;
-
-		switch (material)
-		{
-		case GizmoManager::GizmoMaterial::Solid:
-			mSolidMaterial.mViewProj.set(viewProjMat);
-			mSolidMaterial.mViewDir.set((Vector4)viewDir);
-			gRendererUtility().setPass(mSolidMaterial.mat, 0);
-			break;
-		case GizmoManager::GizmoMaterial::Wire:
-			mWireMaterial.mViewProj.set(viewProjMat);
-			gRendererUtility().setPass(mWireMaterial.mat, 0);
-			break;
-		case GizmoManager::GizmoMaterial::Picking:
-			mPickingMaterial.mViewProj.set(viewProjMat);
-			gRendererUtility().setPass(mPickingMaterial.mat, 0);
-			break;
-		}
-		
-		gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
-	}
-
-	void GizmoManagerCore::renderIconGizmos(Rect2I screenArea, SPtr<MeshCoreBase> mesh, GizmoManager::IconRenderDataVecPtr renderData, bool usePickingMaterial)
-	{
-		RenderAPICore& rs = RenderAPICore::instance();
-		const MeshProperties& meshProps = mesh->getProperties();
-
-		std::shared_ptr<VertexData> vertexData = mesh->getVertexData();
-
-		rs.setVertexDeclaration(vertexData->vertexDeclaration);
-		auto vertexBuffers = vertexData->getBuffers();
-
-		SPtr<VertexBufferCore> vertBuffers[1] = { vertexBuffers.begin()->second };
-		rs.setVertexBuffers(0, vertBuffers, 1);
-
-		SPtr<IndexBufferCore> 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();
-
-		// Top/bottom have been swapped because we're moving from window coordinates (origin top left)
-		// to normalized device coordinates (origin bottom left)
-		// Negative near/far because Z is flipped for normalized device coordinates 
-		// (positive Z goes into screen as opposed to view space here we're looking along negative Z)
-		projMat.makeProjectionOrtho(left, right, top, bottom, -near, -far);
-
-		if (!usePickingMaterial)
-		{
-			mIconMaterial.mViewProj[0].set(projMat);
-			mIconMaterial.mViewProj[1].set(projMat);
-
-			for (UINT32 passIdx = 0; passIdx < 2; passIdx++)
-			{
-				gRendererUtility().setPass(mIconMaterial.mat, passIdx);
-
-				UINT32 curIndexOffset = mesh->getIndexOffset();
-				for (auto curRenderData : *renderData)
-				{
-					mIconMaterial.mTexture[passIdx].set(curRenderData.texture);
-					rs.setGpuParams(GPT_FRAGMENT_PROGRAM, mIconMaterial.mFragParams[passIdx]);
-
-					rs.drawIndexed(curIndexOffset, curRenderData.count * 6, mesh->getVertexOffset(), curRenderData.count * 4);
-					curIndexOffset += curRenderData.count * 6;
-				}
-			}
-		}
-		else
-		{
-			mAlphaPickingMaterial.mViewProj.set(projMat);
-
-			gRendererUtility().setPass(mAlphaPickingMaterial.mat, 0);
-
-			UINT32 curIndexOffset = 0;
-			for (auto curRenderData : *renderData)
-			{
-				mAlphaPickingMaterial.mTexture.set(curRenderData.texture);
-				rs.setGpuParams(GPT_FRAGMENT_PROGRAM, mAlphaPickingMaterial.mFragParams);
-
-				rs.drawIndexed(curIndexOffset, curRenderData.count * 6, mesh->getVertexOffset(), curRenderData.count * 4);
-				curIndexOffset += curRenderData.count * 6;
-
-			}
-		}
-
-		mesh->_notifyUsedOnGPU();
-	}
+#include "BsGizmoManager.h"
+#include "BsMesh.h"
+#include "BsAABox.h"
+#include "BsSphere.h"
+#include "BsVertexDataDesc.h"
+#include "BsShapeMeshes3D.h"
+#include "BsMeshHeap.h"
+#include "BsCCamera.h"
+#include "BsSpriteTexture.h"
+#include "BsCoreThread.h"
+#include "BsBuiltinEditorResources.h"
+#include "BsMaterial.h"
+#include "BsGpuParams.h"
+#include "BsRenderAPI.h"
+#include "BsCoreRenderer.h"
+#include "BsRendererUtility.h"
+#include "BsTransientMesh.h"
+#include "BsRendererManager.h"
+#include "BsDrawHelper.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	const UINT32 GizmoManager::VERTEX_BUFFER_GROWTH = 4096;
+	const UINT32 GizmoManager::INDEX_BUFFER_GROWTH = 4096 * 2;
+	const UINT32 GizmoManager::SPHERE_QUALITY = 1;
+	const UINT32 GizmoManager::WIRE_SPHERE_QUALITY = 10;
+	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()
+		:mPickable(false), mDrawHelper(nullptr), mPickingDrawHelper(nullptr), mCore(nullptr), mCurrentIdx(0)
+	{
+		mTransform = Matrix4::IDENTITY;
+		mDrawHelper = bs_new<DrawHelper>();
+		mPickingDrawHelper = bs_new<DrawHelper>();
+
+		mIconVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mIconVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		mIconVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
+		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 0);
+		mIconVertexDesc->addVertElem(VET_COLOR, VES_COLOR, 1);
+
+		mIconMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mIconVertexDesc);
+
+		HMaterial solidMaterial = BuiltinEditorResources::instance().createSolidGizmoMat();
+		HMaterial wireMaterial = BuiltinEditorResources::instance().createWireGizmoMat();
+		HMaterial iconMaterial = BuiltinEditorResources::instance().createIconGizmoMat();
+		HMaterial pickingMaterial = BuiltinEditorResources::instance().createGizmoPickingMat();
+		HMaterial alphaPickingMaterial = BuiltinEditorResources::instance().createAlphaGizmoPickingMat();
+
+		CoreInitData initData;
+
+		initData.solidMat = solidMaterial->getCore();
+		initData.wireMat = wireMaterial->getCore();
+		initData.iconMat = iconMaterial->getCore();
+		initData.pickingMat = pickingMaterial->getCore();
+		initData.alphaPickingMat = alphaPickingMaterial->getCore();
+
+		mCore.store(bs_new<GizmoManagerCore>(GizmoManagerCore::PrivatelyConstuct()), std::memory_order_release);
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::initializeCore, this, initData));
+	}
+
+	GizmoManager::~GizmoManager()
+	{
+		if (mIconMesh != nullptr)
+			mIconMeshHeap->dealloc(mIconMesh);
+
+		bs_delete(mDrawHelper);
+		bs_delete(mPickingDrawHelper);
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
+	}
+
+	void GizmoManager::initializeCore(const CoreInitData& initData)
+	{
+		mCore.load(std::memory_order_acquire)->initialize(initData);
+	}
+
+	void GizmoManager::destroyCore(GizmoManagerCore* core)
+	{
+		bs_delete(core);
+	}
+
+	void GizmoManager::startGizmo(const HSceneObject& gizmoParent)
+	{
+		mActiveSO = gizmoParent;
+	}
+
+	void GizmoManager::endGizmo()
+	{
+		mActiveSO = nullptr;
+	}
+
+	void GizmoManager::setColor(const Color& color)
+	{
+		mDrawHelper->setColor(color);
+		mColor = color;
+	}
+
+	void GizmoManager::setTransform(const Matrix4& transform)
+	{
+		mDrawHelper->setTransform(transform);
+		mTransform = transform;
+	}
+
+	void GizmoManager::drawCube(const Vector3& position, const Vector3& extents)
+	{
+		mSolidCubeData.push_back(CubeData());
+		CubeData& cubeData = mSolidCubeData.back();
+
+		cubeData.idx = mCurrentIdx++;
+		cubeData.position = position;
+		cubeData.extents = extents;
+		cubeData.color = mColor;
+		cubeData.transform = mTransform;
+		cubeData.sceneObject = mActiveSO;
+		cubeData.pickable = mPickable;
+
+		mDrawHelper->cube(position, extents);
+		mIdxToSceneObjectMap[cubeData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawSphere(const Vector3& position, float radius)
+	{
+		mSolidSphereData.push_back(SphereData());
+		SphereData& sphereData = mSolidSphereData.back();
+
+		sphereData.idx = mCurrentIdx++;
+		sphereData.position = position;
+		sphereData.radius = radius;
+		sphereData.color = mColor;
+		sphereData.transform = mTransform;
+		sphereData.sceneObject = mActiveSO;
+		sphereData.pickable = mPickable;
+
+		mDrawHelper->sphere(position, radius);
+		mIdxToSceneObjectMap[sphereData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawWireCube(const Vector3& position, const Vector3& extents)
+	{
+		mWireCubeData.push_back(CubeData());
+		CubeData& cubeData = mWireCubeData.back();
+
+		cubeData.idx = mCurrentIdx++;
+		cubeData.position = position;
+		cubeData.extents = extents;
+		cubeData.color = mColor;
+		cubeData.transform = mTransform;
+		cubeData.sceneObject = mActiveSO;
+		cubeData.pickable = mPickable;
+
+		mDrawHelper->wireCube(position, extents);
+		mIdxToSceneObjectMap[cubeData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawWireSphere(const Vector3& position, float radius)
+	{
+		mWireSphereData.push_back(SphereData());
+		SphereData& sphereData = mWireSphereData.back();
+
+		sphereData.idx = mCurrentIdx++;
+		sphereData.position = position;
+		sphereData.radius = radius;
+		sphereData.color = mColor;
+		sphereData.transform = mTransform;
+		sphereData.sceneObject = mActiveSO;
+		sphereData.pickable = mPickable;
+
+		mDrawHelper->wireSphere(position, radius);
+		mIdxToSceneObjectMap[sphereData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawLine(const Vector3& start, const Vector3& end)
+	{
+		mLineData.push_back(LineData());
+		LineData& lineData = mLineData.back();
+
+		lineData.idx = mCurrentIdx++;
+		lineData.start = start;
+		lineData.end = end;
+		lineData.color = mColor;
+		lineData.transform = mTransform;
+		lineData.sceneObject = mActiveSO;
+		lineData.pickable = mPickable;
+
+		mDrawHelper->line(start, end);
+		mIdxToSceneObjectMap[lineData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawFrustum(const Vector3& position, float aspect, Degree FOV, float near, float far)
+	{
+		mFrustumData.push_back(FrustumData());
+		FrustumData& frustumData = mFrustumData.back();
+
+		frustumData.idx = mCurrentIdx++;
+		frustumData.position = position;
+		frustumData.aspect = aspect;
+		frustumData.FOV = FOV;
+		frustumData.near = near;
+		frustumData.far = far;
+		frustumData.color = mColor;
+		frustumData.transform = mTransform;
+		frustumData.sceneObject = mActiveSO;
+		frustumData.pickable = mPickable;
+
+		mDrawHelper->frustum(position, aspect, FOV, near, far);
+		mIdxToSceneObjectMap[frustumData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::drawIcon(Vector3 position, HSpriteTexture image, bool fixedScale)
+	{
+		mIconData.push_back(IconData());
+		IconData& iconData = mIconData.back();
+
+		iconData.idx = mCurrentIdx++;
+		iconData.position = position;
+		iconData.texture = image;
+		iconData.fixedScale = fixedScale;
+		iconData.color = mColor;
+		iconData.transform = mTransform;
+		iconData.sceneObject = mActiveSO;
+		iconData.pickable = mPickable;
+
+		mIdxToSceneObjectMap[iconData.idx] = mActiveSO;
+	}
+
+	void GizmoManager::update(const CameraPtr& camera)
+	{
+		mDrawHelper->clearMeshes();
+
+		if (mIconMesh != nullptr)
+			mIconMeshHeap->dealloc(mIconMesh);
+
+		IconRenderDataVecPtr iconRenderData;
+
+		mDrawHelper->buildMeshes();
+		const Vector<DrawHelper::ShapeMeshData>& meshes = mDrawHelper->getMeshes();
+
+		SPtr<MeshCoreBase> solidMesh = nullptr;
+		SPtr<MeshCoreBase> wireMesh = nullptr;
+		for (auto& meshData : meshes)
+		{
+			if (meshData.type == DrawHelper::MeshType::Solid)
+			{
+				if (solidMesh == nullptr)
+					solidMesh = meshData.mesh->getCore();
+			}
+			else // Wire
+			{
+				if (wireMesh == nullptr)
+					wireMesh = meshData.mesh->getCore();
+			}
+		}
+
+		// Since there is no sorting used with draw helper meshes we only expect up to two of them,
+		// one for solids, one for wireframe
+		assert(meshes.size() <= 2);
+
+		mIconMesh = buildIconMesh(camera, mIconData, false, iconRenderData);
+		SPtr<MeshCoreBase> iconMesh = mIconMesh->getCore();
+
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, camera->getCore(),
+			solidMesh, wireMesh, iconMesh, iconRenderData));
+	}
+
+	void GizmoManager::renderForPicking(const CameraPtr& camera, std::function<Color(UINT32)> idxToColorCallback)
+	{
+		Vector<IconData> iconData;
+		IconRenderDataVecPtr iconRenderData;
+
+		mPickingDrawHelper->clear();
+
+		for (auto& cubeDataEntry : mSolidCubeData)
+		{
+			if (!cubeDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(cubeDataEntry.idx));
+			mPickingDrawHelper->setTransform(cubeDataEntry.transform);
+
+			mPickingDrawHelper->cube(cubeDataEntry.position, cubeDataEntry.extents);
+		}
+
+		for (auto& cubeDataEntry : mWireCubeData)
+		{
+			if (!cubeDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(cubeDataEntry.idx));
+			mPickingDrawHelper->setTransform(cubeDataEntry.transform);
+
+			mPickingDrawHelper->wireCube(cubeDataEntry.position, cubeDataEntry.extents);
+		}
+
+		for (auto& sphereDataEntry : mSolidSphereData)
+		{
+			if (!sphereDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(sphereDataEntry.idx));
+			mPickingDrawHelper->setTransform(sphereDataEntry.transform);
+
+			mPickingDrawHelper->sphere(sphereDataEntry.position, sphereDataEntry.radius);
+		}
+
+		for (auto& sphereDataEntry : mWireSphereData)
+		{
+			if (!sphereDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(sphereDataEntry.idx));
+			mPickingDrawHelper->setTransform(sphereDataEntry.transform);
+
+			mPickingDrawHelper->wireSphere(sphereDataEntry.position, sphereDataEntry.radius);
+		}
+
+		for (auto& lineDataEntry : mLineData)
+		{
+			if (!lineDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(lineDataEntry.idx));
+			mPickingDrawHelper->setTransform(lineDataEntry.transform);
+
+			mPickingDrawHelper->line(lineDataEntry.start, lineDataEntry.end);
+		}
+
+		for (auto& frustumDataEntry : mFrustumData)
+		{
+			if (!frustumDataEntry.pickable)
+				continue;
+
+			mPickingDrawHelper->setColor(idxToColorCallback(frustumDataEntry.idx));
+			mPickingDrawHelper->setTransform(frustumDataEntry.transform);
+
+			mPickingDrawHelper->frustum(frustumDataEntry.position, frustumDataEntry.aspect, frustumDataEntry.FOV, 
+				frustumDataEntry.near, frustumDataEntry.far);
+		}
+
+		for (auto& iconDataEntry : mIconData)
+		{
+			if (!iconDataEntry.pickable)
+				continue;
+
+			iconData.push_back(iconDataEntry);
+			iconData.back().color = idxToColorCallback(iconDataEntry.idx);
+		}
+
+		mPickingDrawHelper->buildMeshes();
+		const Vector<DrawHelper::ShapeMeshData>& meshes = mPickingDrawHelper->getMeshes();
+
+		TransientMeshPtr iconMesh = buildIconMesh(camera, iconData, true, iconRenderData);
+
+		// Note: This must be rendered while Scene view is being rendered
+		Matrix4 viewMat = camera->getViewMatrix();
+		Matrix4 projMat = camera->getProjectionMatrixRS();
+		ViewportPtr viewport = camera->getViewport();
+
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
+
+		for (auto& meshData : meshes)
+		{
+			if (meshData.type == DrawHelper::MeshType::Solid)
+			{
+				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
+					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
+			}
+			else // Wire
+			{
+				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
+					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
+			}
+		}
+
+		Rect2I screenArea = camera->getViewport()->getArea();
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderIconGizmos,
+			core, screenArea, iconMesh->getCore(), iconRenderData, true));
+
+		mPickingDrawHelper->clearMeshes();
+		mIconMeshHeap->dealloc(iconMesh);
+	}
+
+	void GizmoManager::clearGizmos()
+	{
+		mSolidCubeData.clear();
+		mWireCubeData.clear();
+		mSolidSphereData.clear();
+		mWireSphereData.clear();
+		mLineData.clear();
+		mFrustumData.clear();
+		mIconData.clear();
+		mIdxToSceneObjectMap.clear();
+
+		mDrawHelper->clear();
+
+		mCurrentIdx = 0;
+	}
+
+	void GizmoManager::clearRenderData()
+	{
+		mDrawHelper->clearMeshes();
+
+		if (mIconMesh != nullptr)
+			mIconMeshHeap->dealloc(mIconMesh);
+
+		mIconMesh = nullptr;
+
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
+		IconRenderDataVecPtr iconRenderData = bs_shared_ptr_new<IconRenderDataVec>();
+		
+		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, nullptr, nullptr, nullptr, nullptr, iconRenderData));
+	}
+
+	TransientMeshPtr GizmoManager::buildIconMesh(const CameraPtr& camera, const Vector<IconData>& iconData,
+		bool forPicking, GizmoManager::IconRenderDataVecPtr& iconRenderData)
+	{
+		mSortedIconData.clear();
+		
+		if (iconData.size() > mSortedIconData.size())
+			mSortedIconData.resize(iconData.size());
+
+		UINT32 i = 0;
+		for (auto& iconEntry : iconData)
+		{
+			Vector3 viewPoint = camera->worldToViewPoint(iconEntry.position);
+
+			float distance = -viewPoint.z;
+			if (distance < camera->getNearClipDistance()) // Ignore behind clip plane
+				continue;
+
+			if (distance > MAX_ICON_RANGE) // Ignore too far away
+				continue;
+
+			if (!iconEntry.texture.isLoaded()) // Ignore missing texture
+				continue;
+
+			if (forPicking && !iconEntry.pickable)
+				continue;
+
+			SortedIconData& sortedIconData = mSortedIconData[i];
+			sortedIconData.iconIdx = i;
+			sortedIconData.distance = distance;
+			sortedIconData.screenPosition = camera->viewToScreenPoint(viewPoint);
+
+			i++;
+		}
+
+		UINT32 actualNumIcons = i;
+
+		// Sort back to front first, then by texture
+		std::sort(mSortedIconData.begin(), mSortedIconData.begin() + actualNumIcons, 
+			[&](const SortedIconData& a, const SortedIconData& b)
+		{
+			if (a.distance == b.distance)
+			{
+				HTexture texA = iconData[a.iconIdx].texture->getTexture();
+				HTexture texB = iconData[b.iconIdx].texture->getTexture();
+
+				if (texA == texB)
+					return a.iconIdx < b.iconIdx;
+
+				return texA->getInternalID() < texB->getInternalID();
+			}
+			else
+				return a.distance > b.distance;
+		});
+
+		MeshDataPtr meshData = bs_shared_ptr_new<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 (camera->getProjectionType() == PT_ORTHOGRAPHIC)
+			cameraScale = camera->getViewport()->getHeight() / camera->getOrthoWindowHeight();
+		else
+		{
+			Radian vertFOV(Math::tan(camera->getHorzFOV() * 0.5f));
+			cameraScale = (camera->getViewport()->getHeight() * 0.5f) / vertFOV.valueRadians();
+		}
+
+		iconRenderData = bs_shared_ptr_new<IconRenderDataVec>();
+		UINT32 lastTextureIdx = std::numeric_limits<UINT32>::max();
+		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];
+			const IconData& curIconData = iconData[sortedIconData.iconIdx];
+
+			HTexture atlasTexture = curIconData.texture->getTexture();
+
+			if (curTexture != atlasTexture)
+			{
+				UINT32 numIconsPerTexture = i - lastTextureIdx;
+				if (numIconsPerTexture > 0)
+				{
+					iconRenderData->push_back(IconRenderData());
+					IconRenderData& renderData = iconRenderData->back();
+					renderData.count = numIconsPerTexture;
+					renderData.texture = atlasTexture->getCore();
+				}
+
+				lastTextureIdx = i;
+				curTexture = atlasTexture;
+			}
+
+			UINT32 iconWidth = curIconData.texture->getWidth();
+			UINT32 iconHeight = curIconData.texture->getHeight();
+
+			limitIconSize(iconWidth, iconHeight);
+
+			Vector3 position((float)sortedIconData.screenPosition.x, (float)sortedIconData.screenPosition.y, -sortedIconData.distance);
+			Vector3 projPosition = camera->projectPoint(position);
+			position.z = projPosition.z;
+
+			float halfWidth = iconWidth * 0.5f;
+			float halfHeight = iconHeight * 0.5f;
+
+			if (!curIconData.fixedScale)
+			{
+				float iconScale = 1.0f;
+				if (camera->getProjectionType() == PT_ORTHOGRAPHIC)
+					iconScale = cameraScale * ICON_TEXEL_WORLD_SIZE;
+				else
+					iconScale = (cameraScale * ICON_TEXEL_WORLD_SIZE) / sortedIconData.distance;
+
+				halfWidth *= iconScale;
+				halfHeight *= iconScale;
+			}
+
+			Color normalColor, fadedColor;
+			calculateIconColors(curIconData.color, camera, (UINT32)(halfHeight * 2.0f), curIconData.fixedScale, normalColor, fadedColor);
+
+			if (forPicking)
+			{
+				normalColor = curIconData.color;
+				fadedColor = curIconData.color;
+			}
+
+			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] = curIconData.texture->transformUV(Vector2(0.0f, 0.0f));
+			uvs[1] = curIconData.texture->transformUV(Vector2(1.0f, 0.0f));
+			uvs[2] = curIconData.texture->transformUV(Vector2(1.0f, 1.0f));
+			uvs[3] = curIconData.texture->transformUV(Vector2(0.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 + 0;
+			indices[4] = vertOffset + 2;
+			indices[5] = vertOffset + 3;
+
+			indices += 6;
+		}
+
+		return mIconMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
+	}
+
+	void GizmoManager::limitIconSize(UINT32& width, UINT32& height)
+	{
+		if (width <= OPTIMAL_ICON_SIZE && height <= OPTIMAL_ICON_SIZE)
+			return;
+
+		float relWidth = OPTIMAL_ICON_SIZE / (float)width;
+		float relHeight = OPTIMAL_ICON_SIZE / (float)height;
+
+		float scale = std::min(relWidth, relHeight);
+
+		width = Math::roundToInt(width * scale);
+		height = Math::roundToInt(height * scale);
+	}
+
+	void GizmoManager::calculateIconColors(const Color& tint, const CameraPtr& 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;
+	}
+
+	HSceneObject GizmoManager::getSceneObject(UINT32 gizmoIdx)
+	{
+		auto iterFind = mIdxToSceneObjectMap.find(gizmoIdx);
+
+		if (iterFind != mIdxToSceneObjectMap.end())
+			return iterFind->second;
+
+		return HSceneObject();
+	}
+
+	const float GizmoManagerCore::PICKING_ALPHA_CUTOFF = 0.5f;
+
+	GizmoManagerCore::GizmoManagerCore(const PrivatelyConstuct& dummy)
+	{
+	}
+
+	GizmoManagerCore::~GizmoManagerCore()
+	{
+		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+		if (mCamera != nullptr)
+			activeRenderer->_unregisterRenderCallback(mCamera.get(), 20);
+	}
+
+	void GizmoManagerCore::initialize(const GizmoManager::CoreInitData& initData)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		mSolidMaterial.mat = initData.solidMat;
+		mWireMaterial.mat = initData.wireMat;
+		mIconMaterial.mat = initData.iconMat;
+		mPickingMaterial.mat = initData.pickingMat;
+		mAlphaPickingMaterial.mat = initData.alphaPickingMat;
+
+		{
+			SPtr<MaterialCore> mat = mWireMaterial.mat;
+			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
+
+			vertParams->getParam("matViewProj", mWireMaterial.mViewProj);
+		}
+
+		{
+			SPtr<MaterialCore> mat = mSolidMaterial.mat;
+			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
+			SPtr<GpuParamsCore> fragParams = mat->getPassParameters(0)->mFragParams;
+
+			vertParams->getParam("matViewProj", mSolidMaterial.mViewProj);
+			fragParams->getParam("viewDir", mSolidMaterial.mViewDir);
+		}
+
+		{
+			SPtr<MaterialCore> mat = mIconMaterial.mat;
+			SPtr<PassParametersCore> pass0Params = mat->getPassParameters(0);
+			SPtr<PassParametersCore> pass1Params = mat->getPassParameters(1);
+
+			SPtr<GpuParamsCore> vertParams0 = pass0Params->mVertParams;
+			SPtr<GpuParamsCore> vertParams1 = pass1Params->mVertParams;
+
+			vertParams0->getParam("matViewProj", mIconMaterial.mViewProj[0]);
+			vertParams1->getParam("matViewProj", mIconMaterial.mViewProj[1]);
+
+			mIconMaterial.mFragParams[0] = pass0Params->mFragParams;
+			mIconMaterial.mFragParams[1] = pass1Params->mFragParams;
+
+			mIconMaterial.mFragParams[0]->getTextureParam("mainTexture", mIconMaterial.mTexture[0]);
+			mIconMaterial.mFragParams[1]->getTextureParam("mainTexture", mIconMaterial.mTexture[1]);
+		}
+
+		{
+			SPtr<MaterialCore> mat = mPickingMaterial.mat;
+			SPtr<GpuParamsCore> vertParams = mat->getPassParameters(0)->mVertParams;
+
+			vertParams->getParam("matViewProj", mPickingMaterial.mViewProj);
+		}
+
+		{
+			SPtr<MaterialCore> mat = mAlphaPickingMaterial.mat;
+			SPtr<PassParametersCore> passParams = mat->getPassParameters(0);
+			SPtr<GpuParamsCore> vertParams = passParams->mVertParams;
+
+			vertParams->getParam("matViewProj", mAlphaPickingMaterial.mViewProj);
+
+			mAlphaPickingMaterial.mFragParams = passParams->mFragParams;
+			mAlphaPickingMaterial.mFragParams->getTextureParam("mainTexture", mAlphaPickingMaterial.mTexture);
+
+			GpuParamFloatCore alphaCutoffParam;
+			mAlphaPickingMaterial.mFragParams->getParam("alphaCutoff", alphaCutoffParam);
+			alphaCutoffParam.set(PICKING_ALPHA_CUTOFF);
+		}
+	}
+
+	void GizmoManagerCore::updateData(const SPtr<CameraCore>& camera, const SPtr<MeshCoreBase>& solidMesh, const SPtr<MeshCoreBase>& wireMesh,
+		const SPtr<MeshCoreBase>& iconMesh, const GizmoManager::IconRenderDataVecPtr& iconRenderData)
+	{
+		if (mCamera != camera)
+		{
+			CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+			if (mCamera != nullptr)
+				activeRenderer->_unregisterRenderCallback(mCamera.get(), 0);
+
+			if (camera != nullptr)
+				activeRenderer->_registerRenderCallback(camera.get(), 0, std::bind(&GizmoManagerCore::render, this));
+		}
+
+		mCamera = camera;
+		mSolidMesh = solidMesh;
+		mWireMesh = wireMesh;
+		mIconMesh = iconMesh;
+		mIconRenderData = iconRenderData;
+	}
+
+	void GizmoManagerCore::render()
+	{
+		if (mCamera == nullptr)
+			return;
+
+		SPtr<RenderTargetCore> renderTarget = mCamera->getViewport()->getTarget();
+
+		float width = (float)renderTarget->getProperties().getWidth();
+		float height = (float)renderTarget->getProperties().getHeight();
+
+		Rect2 normArea = mCamera->getViewport()->getNormArea();
+
+		Rect2I screenArea;
+		screenArea.x = (int)(normArea.x * width);
+		screenArea.y = (int)(normArea.y * height);
+		screenArea.width = (int)(normArea.width * width);
+		screenArea.height = (int)(normArea.height * height);
+
+		if (mSolidMesh != nullptr)
+			renderGizmos(mCamera->getViewMatrix(), mCamera->getProjectionMatrixRS(), mCamera->getForward(), mSolidMesh, GizmoManager::GizmoMaterial::Solid);
+
+		if (mWireMesh != nullptr)
+			renderGizmos(mCamera->getViewMatrix(), mCamera->getProjectionMatrixRS(), mCamera->getForward(), mWireMesh, GizmoManager::GizmoMaterial::Wire);
+
+		if (mIconMesh != nullptr)
+			renderIconGizmos(screenArea, mIconMesh, mIconRenderData, false);
+	}
+
+	void GizmoManagerCore::renderGizmos(Matrix4 viewMatrix, Matrix4 projMatrix, Vector3 viewDir, SPtr<MeshCoreBase> mesh, GizmoManager::GizmoMaterial material)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		Matrix4 viewProjMat = projMatrix * viewMatrix;
+
+		switch (material)
+		{
+		case GizmoManager::GizmoMaterial::Solid:
+			mSolidMaterial.mViewProj.set(viewProjMat);
+			mSolidMaterial.mViewDir.set((Vector4)viewDir);
+			gRendererUtility().setPass(mSolidMaterial.mat, 0);
+			break;
+		case GizmoManager::GizmoMaterial::Wire:
+			mWireMaterial.mViewProj.set(viewProjMat);
+			gRendererUtility().setPass(mWireMaterial.mat, 0);
+			break;
+		case GizmoManager::GizmoMaterial::Picking:
+			mPickingMaterial.mViewProj.set(viewProjMat);
+			gRendererUtility().setPass(mPickingMaterial.mat, 0);
+			break;
+		}
+		
+		gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
+	}
+
+	void GizmoManagerCore::renderIconGizmos(Rect2I screenArea, SPtr<MeshCoreBase> mesh, GizmoManager::IconRenderDataVecPtr renderData, bool usePickingMaterial)
+	{
+		RenderAPICore& rs = RenderAPICore::instance();
+		const MeshProperties& meshProps = mesh->getProperties();
+
+		std::shared_ptr<VertexData> vertexData = mesh->getVertexData();
+
+		rs.setVertexDeclaration(vertexData->vertexDeclaration);
+		auto vertexBuffers = vertexData->getBuffers();
+
+		SPtr<VertexBufferCore> vertBuffers[1] = { vertexBuffers.begin()->second };
+		rs.setVertexBuffers(0, vertBuffers, 1);
+
+		SPtr<IndexBufferCore> 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();
+
+		// Top/bottom have been swapped because we're moving from window coordinates (origin top left)
+		// to normalized device coordinates (origin bottom left)
+		// Negative near/far because Z is flipped for normalized device coordinates 
+		// (positive Z goes into screen as opposed to view space here we're looking along negative Z)
+		projMat.makeProjectionOrtho(left, right, top, bottom, -near, -far);
+
+		if (!usePickingMaterial)
+		{
+			mIconMaterial.mViewProj[0].set(projMat);
+			mIconMaterial.mViewProj[1].set(projMat);
+
+			for (UINT32 passIdx = 0; passIdx < 2; passIdx++)
+			{
+				gRendererUtility().setPass(mIconMaterial.mat, passIdx);
+
+				UINT32 curIndexOffset = mesh->getIndexOffset();
+				for (auto curRenderData : *renderData)
+				{
+					mIconMaterial.mTexture[passIdx].set(curRenderData.texture);
+					rs.setGpuParams(GPT_FRAGMENT_PROGRAM, mIconMaterial.mFragParams[passIdx]);
+
+					rs.drawIndexed(curIndexOffset, curRenderData.count * 6, mesh->getVertexOffset(), curRenderData.count * 4);
+					curIndexOffset += curRenderData.count * 6;
+				}
+			}
+		}
+		else
+		{
+			mAlphaPickingMaterial.mViewProj.set(projMat);
+
+			gRendererUtility().setPass(mAlphaPickingMaterial.mat, 0);
+
+			UINT32 curIndexOffset = 0;
+			for (auto curRenderData : *renderData)
+			{
+				mAlphaPickingMaterial.mTexture.set(curRenderData.texture);
+				rs.setGpuParams(GPT_FRAGMENT_PROGRAM, mAlphaPickingMaterial.mFragParams);
+
+				rs.drawIndexed(curIndexOffset, curRenderData.count * 6, mesh->getVertexOffset(), curRenderData.count * 4);
+				curIndexOffset += curRenderData.count * 6;
+
+			}
+		}
+
+		mesh->_notifyUsedOnGPU();
+	}
 }

+ 265 - 242
BansheeEngine/Include/BsDrawHelper.h

@@ -1,243 +1,266 @@
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "BsMatrix4.h"
-#include "BsVector3.h"
-#include "BsColor.h"
-#include "BsRect3.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Helper class for immediate drawing of common geometric shapes.
-	 */
-	class BS_EXPORT DrawHelper
-	{
-	public:
-		/**
-		 * @brief	Controls in what order will elements be rendered,
-		 *			depending on some reference point
-		 */
-		enum class SortType
-		{
-			BackToFront,
-			FrontToBack,
-			None
-		};
-
-		/**
-		 * @brief	Type of meshes that are output by DrawHelper.
-		 */
-		enum class MeshType
-		{
-			Solid, Wire
-		};
-
-		/**
-		 * @brief	Container for mesh of a specific type output by
-		 *			the DrawHelper.
-		 */
-		struct ShapeMeshData
-		{
-			TransientMeshPtr mesh;
-			MeshType type;
-		};
-
-		DrawHelper();
-		~DrawHelper();
-
-		/**
-		 * @brief	Sets a color that will be used for any shapes recorded after this call.
-		 */
-		void setColor(const Color& color);
-
-		/**
-		 * @brief	Sets a transform matrix that will be used for any shapes recorded after this call.
-		 */
-		void setTransform(const Matrix4& transform);
-
-		/**
-		 * @brief	Records a solid cuboid with the specified properties in the internal draw queue.
-		 */
-		void cube(const Vector3& position, const Vector3& extents);
-
-		/**
-		 * @brief	Records a solid sphere with the specified properties in the internal draw queue.
-		 */
-		void sphere(const Vector3& position, float radius, UINT32 quality = 1);
-
-		/**
-		 * @brief	Records a wireframe cube with the specified properties in the internal draw queue.
-		 */
-		void wireCube(const Vector3& position, const Vector3& extents);
-
-		/**
-		 * @brief	Records a wireframe sphere with the specified properties in the internal draw queue.
-		 */
-		void wireSphere(const Vector3& position, float radius, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a line with the specified properties in the internal draw queue.
-		 */
-		void line(const Vector3& start, const Vector3& end);
-
-		/**
-		 * @brief	Records a wireframe frustum with the specified properties in the internal draw queue.
-		 */
-		void frustum(const Vector3& position, float aspect, Degree FOV, float near, float far);
-
-		/**
-		 * @brief	Records a solid cone with the specified properties in the internal draw queue.
-		 */
-		void cone(const Vector3& base, const Vector3& normal, float height, float radius, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a solid disc with the specified properties in the internal draw queue.
-		 */
-		void disc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a wireframe disc with the specified properties in the internal draw queue.
-		 */
-		void wireDisc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a solid arc with the specified properties in the internal draw queue.
-		 */
-		void arc(const Vector3& position, const Vector3& normal, float radius, Degree startAngle, Degree amountAngle, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a wireframe arc with the specified properties in the internal draw queue.
-		 */
-		void wireArc(const Vector3& position, const Vector3& normal, float radius, Degree startAngle, Degree amountAngle, UINT32 quality = 10);
-
-		/**
-		 * @brief	Records a solid rectangle with the specified properties in the internal draw queue.
-		 */
-		void rectangle(const Rect3& area);
-
-		/**
-		 * @brief	Clears all recorded shapes.
-		 */
-		void clear();
-
-		/**
-		 * @brief	Generates a set of meshes from all the recorded solid and wireframe shapes.
-		 *			The meshes can be accessed via "getMeshes" and released via "clearMeshes". 
-		 *			Any previously active meshes will be cleared when this method is called.
-		 *
-		 * @param	sorting		Determines how (and if) should elements be sorted
-		 *						based on their distance from the reference point.
-		 * @param	reference	Reference point to use for determining distance when
-		 *						sorting.
-		 *
-		 * @note	You must call releaseSolidMesh when done.
-		 */
-		void buildMeshes(SortType sorting = SortType::None, const Vector3& reference = Vector3::ZERO);
-
-		/**
-		 * @brief	Returns a set of meshes you have previously built using "buildMeshes".
-		 */
-		const Vector<ShapeMeshData>& getMeshes() const { return mMeshes; }
-
-		/**
-		 * @brief	Deallocates all active meshes.
-		 */
-		void clearMeshes();
-
-	private:
-		struct CommonData
-		{
-			Color color;
-			Matrix4 transform;
-			Vector3 center;
-		};
-
-		struct CubeData : public CommonData
-		{
-			Vector3 position;
-			Vector3 extents;
-		};
-
-		struct SphereData : public CommonData
-		{
-			Vector3 position;
-			float radius;
-			UINT32 quality;
-		};
-
-		struct LineData : public CommonData
-		{
-			Vector3 start;
-			Vector3 end;
-		};
-
-		struct Rect3Data : public CommonData
-		{
-			Rect3 area;
-		};
-
-		struct FrustumData : public CommonData
-		{
-			Vector3 position;
-			float aspect;
-			Degree FOV;
-			float near;
-			float far;
-		};
-
-		struct ConeData : public CommonData
-		{
-			Vector3 base;
-			Vector3 normal;
-			float height;
-			float radius;
-			UINT32 quality;
-		};
-
-		struct DiscData : public CommonData
-		{
-			Vector3 position;
-			Vector3 normal;
-			float radius;
-			UINT32 quality;
-		};
-
-		struct ArcData : public CommonData
-		{
-			Vector3 position;
-			Vector3 normal;
-			float radius;
-			Degree startAngle;
-			Degree amountAngle;
-			UINT32 quality;
-		};
-
-		static const UINT32 VERTEX_BUFFER_GROWTH;
-		static const UINT32 INDEX_BUFFER_GROWTH;
-
-		Color mColor;
-		Matrix4 mTransform;
-
-		Vector<CubeData> mSolidCubeData;
-		Vector<CubeData> mWireCubeData;
-		Vector<SphereData> mSolidSphereData;
-		Vector<SphereData> mWireSphereData;
-		Vector<LineData> mLineData;
-		Vector<Rect3Data> mRect3Data;
-		Vector<FrustumData> mFrustumData;
-		Vector<ConeData> mConeData;
-		Vector<DiscData> mDiscData;
-		Vector<DiscData> mWireDiscData;
-		Vector<ArcData> mArcData;
-		Vector<ArcData> mWireArcData;
-
-		Vector<ShapeMeshData> mMeshes;
-
-		MeshHeapPtr mSolidMeshHeap;
-		MeshHeapPtr mWireMeshHeap;
-
-		VertexDataDescPtr mSolidVertexDesc;
-		VertexDataDescPtr mWireVertexDesc;
-	};
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsMatrix4.h"
+#include "BsVector3.h"
+#include "BsColor.h"
+#include "BsRect3.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Helper class for immediate drawing of common geometric shapes.
+	 */
+	class BS_EXPORT DrawHelper
+	{
+	public:
+		/**
+		 * @brief	Controls in what order will elements be rendered,
+		 *			depending on some reference point
+		 */
+		enum class SortType
+		{
+			BackToFront,
+			FrontToBack,
+			None
+		};
+
+		/**
+		 * @brief	Type of meshes that are output by DrawHelper.
+		 */
+		enum class MeshType
+		{
+			Solid, Wire, Text
+		};
+
+		/**
+		 * @brief	Container for mesh of a specific type output by
+		 *			the DrawHelper.
+		 */
+		struct ShapeMeshData
+		{
+			TransientMeshPtr mesh;
+			MeshType type;
+		};
+
+		DrawHelper();
+		~DrawHelper();
+
+		/**
+		 * @brief	Sets a color that will be used for any shapes recorded after this call.
+		 */
+		void setColor(const Color& color);
+
+		/**
+		 * @brief	Sets a transform matrix that will be used for any shapes recorded after this call.
+		 */
+		void setTransform(const Matrix4& transform);
+
+		/**
+		 * @brief	Records a solid cuboid with the specified properties in the internal draw queue.
+		 */
+		void cube(const Vector3& position, const Vector3& extents);
+
+		/**
+		 * @brief	Records a solid sphere with the specified properties in the internal draw queue.
+		 */
+		void sphere(const Vector3& position, float radius, UINT32 quality = 1);
+
+		/**
+		 * @brief	Records a wireframe cube with the specified properties in the internal draw queue.
+		 */
+		void wireCube(const Vector3& position, const Vector3& extents);
+
+		/**
+		 * @brief	Records a wireframe sphere with the specified properties in the internal draw queue.
+		 */
+		void wireSphere(const Vector3& position, float radius, UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a line with the specified properties in the internal draw queue.
+		 */
+		void line(const Vector3& start, const Vector3& end);
+
+		/**
+		 * @brief	Records a wireframe frustum with the specified properties in the internal draw queue.
+		 */
+		void frustum(const Vector3& position, float aspect, Degree FOV, float near, float far);
+
+		/**
+		 * @brief	Records a solid cone with the specified properties in the internal draw queue.
+		 */
+		void cone(const Vector3& base, const Vector3& normal, float height, float radius, UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a solid disc with the specified properties in the internal draw queue.
+		 */
+		void disc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a wireframe disc with the specified properties in the internal draw queue.
+		 */
+		void wireDisc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a solid arc with the specified properties in the internal draw queue.
+		 */
+		void arc(const Vector3& position, const Vector3& normal, float radius, Degree startAngle, Degree amountAngle, 
+			UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a wireframe arc with the specified properties in the internal draw queue.
+		 */
+		void wireArc(const Vector3& position, const Vector3& normal, float radius, Degree startAngle, Degree amountAngle, 
+			UINT32 quality = 10);
+
+		/**
+		 * @brief	Records a solid rectangle with the specified properties in the internal draw queue.
+		 */
+		void rectangle(const Rect3& area);
+
+		/**
+		 * Records a mesh representing 2D text with the specified properties in the internal draw queue. 
+		 *
+		 * @param[in]	position		Position to render the text at. Text will be centered around this point.
+		 * @param[in]	text			Text to draw.
+		 * @param[in]	font			Font to use for rendering the text's characters.
+		 * @param[in]	size			Size of the text, in points.
+		 */
+		void text(const Vector3& position, const WString& text, const HFont& font, UINT32 size = 10);
+
+		/**
+		 * @brief	Clears all recorded shapes.
+		 */
+		void clear();
+
+		/**
+		 * @brief	Generates a set of meshes from all the recorded solid and wireframe shapes.
+		 *			The meshes can be accessed via getMeshes() and released via clearMeshes(). 
+		 *			Any previously active meshes will be cleared when this method is called.
+		 *
+		 * @param	sorting		Determines how (and if) should elements be sorted
+		 *						based on their distance from the reference point.
+		 * @param	reference	Reference point to use for determining distance when
+		 *						sorting.
+		 *
+		 * @note	You must call releaseSolidMesh() when done.
+		 */
+		void buildMeshes(SortType sorting = SortType::None, const Vector3& reference = Vector3::ZERO);
+
+		/**
+		 * @brief	Returns a set of meshes you have previously built using buildMeshes().
+		 */
+		const Vector<ShapeMeshData>& getMeshes() const { return mMeshes; }
+
+		/**
+		 * @brief	Deallocates all active meshes.
+		 */
+		void clearMeshes();
+
+	private:
+		struct CommonData
+		{
+			Color color;
+			Matrix4 transform;
+			Vector3 center;
+		};
+
+		struct CubeData : public CommonData
+		{
+			Vector3 position;
+			Vector3 extents;
+		};
+
+		struct SphereData : public CommonData
+		{
+			Vector3 position;
+			float radius;
+			UINT32 quality;
+		};
+
+		struct LineData : public CommonData
+		{
+			Vector3 start;
+			Vector3 end;
+		};
+
+		struct Rect3Data : public CommonData
+		{
+			Rect3 area;
+		};
+
+		struct FrustumData : public CommonData
+		{
+			Vector3 position;
+			float aspect;
+			Degree FOV;
+			float near;
+			float far;
+		};
+
+		struct ConeData : public CommonData
+		{
+			Vector3 base;
+			Vector3 normal;
+			float height;
+			float radius;
+			UINT32 quality;
+		};
+
+		struct DiscData : public CommonData
+		{
+			Vector3 position;
+			Vector3 normal;
+			float radius;
+			UINT32 quality;
+		};
+
+		struct ArcData : public CommonData
+		{
+			Vector3 position;
+			Vector3 normal;
+			float radius;
+			Degree startAngle;
+			Degree amountAngle;
+			UINT32 quality;
+		};
+
+		struct Text2DData : public CommonData
+		{
+			Vector3 position;
+			WString text;
+			HFont font;
+			UINT32 size;
+		};
+
+		static const UINT32 VERTEX_BUFFER_GROWTH;
+		static const UINT32 INDEX_BUFFER_GROWTH;
+
+		Color mColor;
+		Matrix4 mTransform;
+
+		Vector<CubeData> mSolidCubeData;
+		Vector<CubeData> mWireCubeData;
+		Vector<SphereData> mSolidSphereData;
+		Vector<SphereData> mWireSphereData;
+		Vector<LineData> mLineData;
+		Vector<Rect3Data> mRect3Data;
+		Vector<FrustumData> mFrustumData;
+		Vector<ConeData> mConeData;
+		Vector<DiscData> mDiscData;
+		Vector<DiscData> mWireDiscData;
+		Vector<ArcData> mArcData;
+		Vector<ArcData> mWireArcData;
+		Vector<Text2DData> mText2DData;
+
+		Vector<ShapeMeshData> mMeshes;
+
+		MeshHeapPtr mSolidMeshHeap;
+		MeshHeapPtr mWireMeshHeap;
+		MeshHeapPtr mTextMeshHeap;
+
+		VertexDataDescPtr mSolidVertexDesc;
+		VertexDataDescPtr mWireVertexDesc;
+		VertexDataDescPtr mTextVertexDesc;
+	};
 }

+ 1 - 1
BansheeEngine/Include/BsTextSprite.h

@@ -63,7 +63,7 @@ namespace BansheeEngine
 		 * @param	desc	Describes the geometry and material of the sprite.
 		 * @param	groupId	Group identifier that forces different materials to be used for
 		 *					different groups (e.g. you don't want the sprites to
-		 *					share the same material if they use different world transform matrices)
+		 *					share the same group if they use different world transform matrices)
 		 */
 		void update(const TEXT_SPRITE_DESC& desc, UINT64 groupId);
 

+ 903 - 737
BansheeEngine/Source/BsDrawHelper.cpp

@@ -1,738 +1,904 @@
-#include "BsDrawHelper.h"
-#include "BsMesh.h"
-#include "BsAABox.h"
-#include "BsSphere.h"
-#include "BsVertexDataDesc.h"
-#include "BsMeshHeap.h"
-#include "BsShapeMeshes3D.h"
-
-namespace BansheeEngine
-{
-	const UINT32 DrawHelper::VERTEX_BUFFER_GROWTH = 4096;
-	const UINT32 DrawHelper::INDEX_BUFFER_GROWTH = 4096 * 2;
-
-	DrawHelper::DrawHelper()
-	{
-		mTransform = Matrix4::IDENTITY;
-
-		mSolidVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
-		mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
-		mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
-		mSolidVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
-
-		mWireVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
-		mWireVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
-		mWireVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
-
-		mSolidMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mSolidVertexDesc);
-		mWireMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mWireVertexDesc);
-	}
-
-	DrawHelper::~DrawHelper()
-	{
-		clearMeshes();
-	}
-
-	void DrawHelper::setColor(const Color& color)
-	{
-		mColor = color;
-	}
-
-	void DrawHelper::setTransform(const Matrix4& transform)
-	{
-		mTransform = transform;
-	}
-
-	void DrawHelper::cube(const Vector3& position, const Vector3& extents)
-	{
-		mSolidCubeData.push_back(CubeData());
-		CubeData& cubeData = mSolidCubeData.back();
-
-		cubeData.position = position;
-		cubeData.extents = extents;
-		cubeData.color = mColor;
-		cubeData.transform = mTransform;
-		cubeData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::sphere(const Vector3& position, float radius, UINT32 quality)
-	{
-		mSolidSphereData.push_back(SphereData());
-		SphereData& sphereData = mSolidSphereData.back();
-
-		sphereData.position = position;
-		sphereData.radius = radius;
-		sphereData.quality = quality;
-		sphereData.color = mColor;
-		sphereData.transform = mTransform;
-		sphereData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::wireCube(const Vector3& position, const Vector3& extents)
-	{
-		mWireCubeData.push_back(CubeData());
-		CubeData& cubeData = mWireCubeData.back();
-
-		cubeData.position = position;
-		cubeData.extents = extents;
-		cubeData.color = mColor;
-		cubeData.transform = mTransform;
-		cubeData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::wireSphere(const Vector3& position, float radius, UINT32 quality)
-	{
-		mWireSphereData.push_back(SphereData());
-		SphereData& sphereData = mWireSphereData.back();
-
-		sphereData.position = position;
-		sphereData.radius = radius;
-		sphereData.quality = quality;
-		sphereData.color = mColor;
-		sphereData.transform = mTransform;
-		sphereData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::line(const Vector3& start, const Vector3& end)
-	{
-		mLineData.push_back(LineData());
-		LineData& lineData = mLineData.back();
-
-		lineData.start = start;
-		lineData.end = end;
-		lineData.color = mColor;
-		lineData.transform = mTransform;
-		lineData.center = mTransform.multiplyAffine((start + end) * 0.5f);
-	}
-
-	void DrawHelper::frustum(const Vector3& position, float aspect, Degree FOV, float near, float far)
-	{
-		mFrustumData.push_back(FrustumData());
-		FrustumData& frustumData = mFrustumData.back();
-
-		frustumData.position = position;
-		frustumData.aspect = aspect;
-		frustumData.FOV = FOV;
-		frustumData.near = near;
-		frustumData.far = far;
-		frustumData.color = mColor;
-		frustumData.transform = mTransform;
-		frustumData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::cone(const Vector3& base, const Vector3& normal, float height, float radius, UINT32 quality)
-	{
-		mConeData.push_back(ConeData());
-		ConeData& coneData = mConeData.back();
-
-		coneData.base = base;
-		coneData.normal = normal;
-		coneData.height = height;
-		coneData.radius = radius;
-		coneData.quality = quality;
-		coneData.color = mColor;
-		coneData.transform = mTransform;
-		coneData.center = mTransform.multiplyAffine(base + normal * height * 0.5f);
-	}
-
-	void DrawHelper::disc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
-	{
-		mDiscData.push_back(DiscData());
-		DiscData& discData = mDiscData.back();
-
-		discData.position = position;
-		discData.normal = normal;
-		discData.radius = radius;
-		discData.quality = quality;
-		discData.color = mColor;
-		discData.transform = mTransform;
-		discData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::wireDisc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
-	{
-		mWireDiscData.push_back(DiscData());
-		DiscData& discData = mWireDiscData.back();
-
-		discData.position = position;
-		discData.normal = normal;
-		discData.radius = radius;
-		discData.quality = quality;
-		discData.color = mColor;
-		discData.transform = mTransform;
-		discData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::arc(const Vector3& position, const Vector3& normal, float radius, 
-		Degree startAngle, Degree amountAngle, UINT32 quality)
-	{
-		mArcData.push_back(ArcData());
-		ArcData& arcData = mArcData.back();
-
-		arcData.position = position;
-		arcData.normal = normal;
-		arcData.radius = radius;
-		arcData.startAngle = startAngle;
-		arcData.amountAngle = amountAngle;
-		arcData.quality = quality;
-		arcData.color = mColor;
-		arcData.transform = mTransform;
-		arcData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::wireArc(const Vector3& position, const Vector3& normal, float radius, 
-		Degree startAngle, Degree amountAngle, UINT32 quality)
-	{
-		mWireArcData.push_back(ArcData());
-		ArcData& arcData = mWireArcData.back();
-
-		arcData.position = position;
-		arcData.normal = normal;
-		arcData.radius = radius;
-		arcData.startAngle = startAngle;
-		arcData.amountAngle = amountAngle;
-		arcData.quality = quality;
-		arcData.color = mColor;
-		arcData.transform = mTransform;
-		arcData.center = mTransform.multiplyAffine(position);
-	}
-
-	void DrawHelper::rectangle(const Rect3& area)
-	{
-		mRect3Data.push_back(Rect3Data());
-		Rect3Data& rectData = mRect3Data.back();
-
-		rectData.area = area;
-		rectData.color = mColor;
-		rectData.transform = mTransform;
-		rectData.center = mTransform.multiplyAffine(area.getCenter());
-	}
-
-	void DrawHelper::clear()
-	{
-		mSolidCubeData.clear();
-		mWireCubeData.clear();
-		mSolidSphereData.clear();
-		mWireSphereData.clear();
-		mLineData.clear();
-		mRect3Data.clear();
-		mFrustumData.clear();
-		mFrustumData.clear();
-		mDiscData.clear();
-		mWireDiscData.clear();
-		mArcData.clear();
-		mWireArcData.clear();
-		mConeData.clear();
-	}
-
-	void DrawHelper::buildMeshes(SortType sorting, const Vector3& reference)
-	{
-		clearMeshes();
-
-		enum class ShapeType
-		{
-			Cube, Sphere, WireCube, WireSphere, Line, Frustum, 
-			Cone, Disc, WireDisc, Arc, WireArc, Rectangle
-		};
-
-		struct RawData
-		{
-			ShapeType shapeType;
-			MeshType meshType;
-			UINT32 idx;
-			float distance;
-			UINT32 numVertices;
-			UINT32 numIndices;
-		};
-
-		/************************************************************************/
-		/* 			Sort everything according to specified sorting rule         */
-		/************************************************************************/
-
-		UINT32 totalNumShapes = (UINT32)(mSolidCubeData.size() + mSolidSphereData.size() + 
-			mWireCubeData.size() + mWireSphereData.size() + mLineData.size() + mFrustumData.size() + mConeData.size() +
-			mDiscData.size() + mWireDiscData.size() + mArcData.size() + mWireArcData.size() + mRect3Data.size());
-
-		UINT32 idx = 0;
-		Vector<RawData> allShapes(totalNumShapes);
-
-		UINT32 localIdx = 0;
-		for (auto& shapeData : mSolidCubeData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Cube;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsAABox(rawData.numVertices, rawData.numIndices);
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mSolidSphereData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Sphere;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsSphere(shapeData.quality, 
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mConeData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Cone;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsCone(shapeData.quality, 
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mDiscData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Disc;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsDisc(shapeData.quality,
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mArcData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Arc;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsArc(shapeData.quality, 
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mRect3Data)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Solid;
-			rawData.shapeType = ShapeType::Rectangle;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsQuad(rawData.numVertices, rawData.numIndices);
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mWireCubeData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::WireCube;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsWireAABox(rawData.numVertices, rawData.numIndices);
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mWireSphereData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::WireSphere;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsWireSphere(shapeData.quality,
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mLineData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::Line;
-			rawData.distance = shapeData.center.distance(reference);
-			rawData.numVertices = 2;
-			rawData.numIndices = 2;
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mFrustumData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::Frustum;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsFrustum(rawData.numVertices, rawData.numIndices);
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mWireDiscData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::WireDisc;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsWireDisc(shapeData.quality, 
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		localIdx = 0;
-		for (auto& shapeData : mWireArcData)
-		{
-			RawData& rawData = allShapes[idx];
-
-			rawData.idx = localIdx++;
-			rawData.meshType = MeshType::Wire;
-			rawData.shapeType = ShapeType::WireArc;
-			rawData.distance = shapeData.center.distance(reference);
-
-			ShapeMeshes3D::getNumElementsWireArc(shapeData.quality,
-				rawData.numVertices, rawData.numIndices);
-
-			idx++;
-		}
-
-		if (sorting == SortType::FrontToBack)
-		{
-			std::sort(begin(allShapes), end(allShapes),
-				[&](const RawData& x, const RawData& y)
-			{
-				return x.distance < y.distance;
-			});
-		}
-		else if (sorting == SortType::BackToFront)
-		{
-			std::sort(begin(allShapes), end(allShapes),
-				[&](const RawData& x, const RawData& y)
-			{
-				return y.distance < x.distance;
-			});
-		}
-
-		/************************************************************************/
-		/* 							Create batches                      		*/
-		/************************************************************************/
-		struct Batch
-		{
-			MeshType type;
-			UINT32 startIdx;
-			UINT32 endIdx;
-			UINT32 numVertices;
-			UINT32 numIndices;
-		};
-
-		Vector<Batch> batches;
-		if (totalNumShapes > 0)
-		{
-			batches.push_back(Batch());
-
-			{
-				Batch& currentBatch = batches.back();
-				currentBatch.startIdx = 0;
-				currentBatch.type = allShapes[0].meshType;
-				currentBatch.numVertices = allShapes[0].numVertices;
-				currentBatch.numIndices = allShapes[0].numIndices;
-			}
-
-			for (UINT32 i = 1; i < totalNumShapes; i++)
-			{
-				Batch& currentBatch = batches.back();
-
-				if (allShapes[i].meshType != currentBatch.type)
-				{
-					currentBatch.endIdx = i - 1;
-
-					batches.push_back(Batch());
-
-					Batch& newBatch = batches.back();
-					newBatch.startIdx = i;
-					newBatch.type = allShapes[i].meshType;
-					newBatch.numVertices = allShapes[i].numVertices;
-					newBatch.numIndices = allShapes[i].numIndices;
-				}
-				else
-				{
-					currentBatch.endIdx = i;
-					currentBatch.numVertices += allShapes[i].numVertices;
-					currentBatch.numIndices += allShapes[i].numIndices;
-				}
-			}
-
-			{
-				Batch& currentBatch = batches.back();
-				currentBatch.endIdx = totalNumShapes - 1;
-			}
-		}
-
-		/************************************************************************/
-		/* 					Generate geometry for each batch                    */
-		/************************************************************************/
-		for (auto& batch : batches)
-		{
-			if (batch.type == MeshType::Solid)
-			{
-				MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(batch.numVertices, batch.numIndices, mSolidVertexDesc);
-
-				UINT32 curVertexOffset = 0;
-				UINT32 curIndexOffet = 0;
-
-				auto positionIter = meshData->getVec3DataIter(VES_POSITION);
-				auto normalIter = meshData->getVec3DataIter(VES_NORMAL);
-				auto colorIter = meshData->getDWORDDataIter(VES_COLOR);
-
-				for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
-				{
-					RawData& shapeData = allShapes[i];
-
-					Matrix4* transform = nullptr;
-					RGBA color = 0;
-
-					switch (shapeData.shapeType)
-					{
-					case ShapeType::Cube:
-					{
-						CubeData& cubeData = mSolidCubeData[shapeData.idx];
-						AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
-						ShapeMeshes3D::solidAABox(box, meshData, curVertexOffset, curIndexOffet);
-
-						transform = &cubeData.transform;
-						color = cubeData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Sphere:
-					{
-						SphereData& sphereData = mSolidSphereData[shapeData.idx];
-						Sphere sphere(sphereData.position, sphereData.radius);
-						ShapeMeshes3D::solidSphere(sphere, meshData, curVertexOffset, curIndexOffet, sphereData.quality);
-
-						transform = &sphereData.transform;
-						color = sphereData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Cone:
-					{
-						ConeData& coneData = mConeData[shapeData.idx];
-						ShapeMeshes3D::solidCone(coneData.base, coneData.normal, coneData.height, coneData.radius,
-							meshData, curVertexOffset, curIndexOffet, coneData.quality);
-
-						transform = &coneData.transform;
-						color = coneData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Disc:
-					{
-						DiscData& discData = mDiscData[shapeData.idx];
-						ShapeMeshes3D::solidDisc(discData.position, discData.radius, discData.normal,
-							meshData, curVertexOffset, curIndexOffet, discData.quality);
-
-						transform = &discData.transform;
-						color = discData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Arc:
-					{
-						ArcData& arcData = mArcData[shapeData.idx];
-						ShapeMeshes3D::solidArc(arcData.position, arcData.radius, arcData.normal,
-							arcData.startAngle, arcData.amountAngle, meshData, curVertexOffset, curIndexOffet, arcData.quality);
-
-						transform = &arcData.transform;
-						color = arcData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Rectangle:
-					{
-						Rect3Data rectData = mRect3Data[shapeData.idx];
-						ShapeMeshes3D::solidQuad(rectData.area, meshData, curVertexOffset, curIndexOffet);
-
-						transform = &rectData.transform;
-						color = rectData.color.getAsRGBA();
-					}
-						break;
-					}
-
-					Matrix4 transformIT = transform->inverseAffine().transpose();
-					for (UINT32 i = 0; i < shapeData.numVertices; i++)
-					{
-						Vector3 worldPos = transform->multiplyAffine(positionIter.getValue());
-						Vector3 worldNormal = transformIT.multiplyAffine(normalIter.getValue());
-
-						positionIter.addValue(worldPos);
-						normalIter.addValue(worldNormal);
-						colorIter.addValue(color);
-					}
-
-					curVertexOffset += shapeData.numVertices;
-					curIndexOffet += shapeData.numIndices;
-				}
-
-				mMeshes.push_back(ShapeMeshData());
-				ShapeMeshData& newMesh = mMeshes.back();
-				newMesh.mesh = mSolidMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
-				newMesh.type = MeshType::Solid;
-			}
-			else // Wire
-			{
-				MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(batch.numVertices,
-					batch.numIndices, mWireVertexDesc);
-
-				UINT32 curVertexOffset = 0;
-				UINT32 curIndexOffet = 0;
-
-				auto positionIter = meshData->getVec3DataIter(VES_POSITION);
-				auto colorIter = meshData->getDWORDDataIter(VES_COLOR);
-
-				for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
-				{
-					RawData& shapeData = allShapes[i];
-
-					Matrix4* transform = nullptr;
-					RGBA color = 0;
-
-					switch (shapeData.shapeType)
-					{
-					case ShapeType::WireCube:
-					{
-						CubeData& cubeData = mWireCubeData[shapeData.idx];
-
-						AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
-						ShapeMeshes3D::wireAABox(box, meshData, curVertexOffset, curIndexOffet);
-
-						transform = &cubeData.transform;
-						color = cubeData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::WireSphere:
-					{
-						SphereData& sphereData = mWireSphereData[shapeData.idx];
-
-						Sphere sphere(sphereData.position, sphereData.radius);
-						ShapeMeshes3D::wireSphere(sphere, meshData, curVertexOffset, curIndexOffet, sphereData.quality);
-
-						transform = &sphereData.transform;
-						color = sphereData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Line:
-					{
-						LineData& lineData = mLineData[shapeData.idx];
-
-						ShapeMeshes3D::pixelLine(lineData.start, lineData.end, meshData, curVertexOffset, curIndexOffet);
-
-						transform = &lineData.transform;
-						color = lineData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::Frustum:
-					{
-						FrustumData& frustumData = mFrustumData[shapeData.idx];
-
-						ShapeMeshes3D::wireFrustum(frustumData.position, frustumData.aspect, frustumData.FOV, frustumData.near,
-							frustumData.far, meshData, curVertexOffset, curIndexOffet);
-
-						transform = &frustumData.transform;
-						color = frustumData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::WireDisc:
-					{
-						DiscData& discData = mWireDiscData[shapeData.idx];
-
-						ShapeMeshes3D::wireDisc(discData.position, discData.radius, discData.normal,
-							meshData, curVertexOffset, curIndexOffet, discData.quality);
-
-						transform = &discData.transform;
-						color = discData.color.getAsRGBA();
-					}
-						break;
-					case ShapeType::WireArc:
-					{
-						ArcData& arcData = mWireArcData[shapeData.idx];
-
-						ShapeMeshes3D::wireArc(arcData.position, arcData.radius, arcData.normal,
-							arcData.startAngle, arcData.amountAngle, meshData, curVertexOffset, curIndexOffet, arcData.quality);
-
-						transform = &arcData.transform;
-						color = arcData.color.getAsRGBA();
-					}
-						break;
-					}
-
-					for (UINT32 i = 0; i < shapeData.numVertices; i++)
-					{
-						Vector3 worldPos = transform->multiplyAffine(positionIter.getValue());
-
-						positionIter.addValue(worldPos);
-						colorIter.addValue(color);
-					}
-
-					curVertexOffset += shapeData.numVertices;
-					curIndexOffet += shapeData.numIndices;
-				}
-
-				mMeshes.push_back(ShapeMeshData());
-				ShapeMeshData& newMesh = mMeshes.back();
-				newMesh.mesh = mWireMeshHeap->alloc(meshData, DOT_LINE_LIST);
-				newMesh.type = MeshType::Wire;
-			}
-		}
-	}
-
-	void DrawHelper::clearMeshes()
-	{
-		for (auto meshData : mMeshes)
-		{
-			if (meshData.type == MeshType::Solid)
-				mSolidMeshHeap->dealloc(meshData.mesh);
-			else
-				mWireMeshHeap->dealloc(meshData.mesh);
-		}
-
-		mMeshes.clear();
-	}
+#include "BsDrawHelper.h"
+#include "BsMesh.h"
+#include "BsAABox.h"
+#include "BsSphere.h"
+#include "BsVertexDataDesc.h"
+#include "BsMeshHeap.h"
+#include "BsShapeMeshes3D.h"
+#include "BsTextData.h"
+#include "BsVector2.h"
+
+namespace BansheeEngine
+{
+	const UINT32 DrawHelper::VERTEX_BUFFER_GROWTH = 4096;
+	const UINT32 DrawHelper::INDEX_BUFFER_GROWTH = 4096 * 2;
+
+	DrawHelper::DrawHelper()
+	{
+		mTransform = Matrix4::IDENTITY;
+
+		mSolidVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
+		mSolidVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
+
+		mWireVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mWireVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		mWireVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
+
+		mTextVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mTextVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		mTextVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
+		mTextVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
+
+		mSolidMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mSolidVertexDesc);
+		mWireMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mWireVertexDesc);
+		mTextMeshHeap = MeshHeap::create(VERTEX_BUFFER_GROWTH, INDEX_BUFFER_GROWTH, mTextVertexDesc);
+	}
+
+	DrawHelper::~DrawHelper()
+	{
+		clearMeshes();
+	}
+
+	void DrawHelper::setColor(const Color& color)
+	{
+		mColor = color;
+	}
+
+	void DrawHelper::setTransform(const Matrix4& transform)
+	{
+		mTransform = transform;
+	}
+
+	void DrawHelper::cube(const Vector3& position, const Vector3& extents)
+	{
+		mSolidCubeData.push_back(CubeData());
+		CubeData& cubeData = mSolidCubeData.back();
+
+		cubeData.position = position;
+		cubeData.extents = extents;
+		cubeData.color = mColor;
+		cubeData.transform = mTransform;
+		cubeData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::sphere(const Vector3& position, float radius, UINT32 quality)
+	{
+		mSolidSphereData.push_back(SphereData());
+		SphereData& sphereData = mSolidSphereData.back();
+
+		sphereData.position = position;
+		sphereData.radius = radius;
+		sphereData.quality = quality;
+		sphereData.color = mColor;
+		sphereData.transform = mTransform;
+		sphereData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::wireCube(const Vector3& position, const Vector3& extents)
+	{
+		mWireCubeData.push_back(CubeData());
+		CubeData& cubeData = mWireCubeData.back();
+
+		cubeData.position = position;
+		cubeData.extents = extents;
+		cubeData.color = mColor;
+		cubeData.transform = mTransform;
+		cubeData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::wireSphere(const Vector3& position, float radius, UINT32 quality)
+	{
+		mWireSphereData.push_back(SphereData());
+		SphereData& sphereData = mWireSphereData.back();
+
+		sphereData.position = position;
+		sphereData.radius = radius;
+		sphereData.quality = quality;
+		sphereData.color = mColor;
+		sphereData.transform = mTransform;
+		sphereData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::line(const Vector3& start, const Vector3& end)
+	{
+		mLineData.push_back(LineData());
+		LineData& lineData = mLineData.back();
+
+		lineData.start = start;
+		lineData.end = end;
+		lineData.color = mColor;
+		lineData.transform = mTransform;
+		lineData.center = mTransform.multiplyAffine((start + end) * 0.5f);
+	}
+
+	void DrawHelper::frustum(const Vector3& position, float aspect, Degree FOV, float near, float far)
+	{
+		mFrustumData.push_back(FrustumData());
+		FrustumData& frustumData = mFrustumData.back();
+
+		frustumData.position = position;
+		frustumData.aspect = aspect;
+		frustumData.FOV = FOV;
+		frustumData.near = near;
+		frustumData.far = far;
+		frustumData.color = mColor;
+		frustumData.transform = mTransform;
+		frustumData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::cone(const Vector3& base, const Vector3& normal, float height, float radius, UINT32 quality)
+	{
+		mConeData.push_back(ConeData());
+		ConeData& coneData = mConeData.back();
+
+		coneData.base = base;
+		coneData.normal = normal;
+		coneData.height = height;
+		coneData.radius = radius;
+		coneData.quality = quality;
+		coneData.color = mColor;
+		coneData.transform = mTransform;
+		coneData.center = mTransform.multiplyAffine(base + normal * height * 0.5f);
+	}
+
+	void DrawHelper::disc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
+	{
+		mDiscData.push_back(DiscData());
+		DiscData& discData = mDiscData.back();
+
+		discData.position = position;
+		discData.normal = normal;
+		discData.radius = radius;
+		discData.quality = quality;
+		discData.color = mColor;
+		discData.transform = mTransform;
+		discData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::wireDisc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
+	{
+		mWireDiscData.push_back(DiscData());
+		DiscData& discData = mWireDiscData.back();
+
+		discData.position = position;
+		discData.normal = normal;
+		discData.radius = radius;
+		discData.quality = quality;
+		discData.color = mColor;
+		discData.transform = mTransform;
+		discData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::arc(const Vector3& position, const Vector3& normal, float radius, 
+		Degree startAngle, Degree amountAngle, UINT32 quality)
+	{
+		mArcData.push_back(ArcData());
+		ArcData& arcData = mArcData.back();
+
+		arcData.position = position;
+		arcData.normal = normal;
+		arcData.radius = radius;
+		arcData.startAngle = startAngle;
+		arcData.amountAngle = amountAngle;
+		arcData.quality = quality;
+		arcData.color = mColor;
+		arcData.transform = mTransform;
+		arcData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::wireArc(const Vector3& position, const Vector3& normal, float radius, 
+		Degree startAngle, Degree amountAngle, UINT32 quality)
+	{
+		mWireArcData.push_back(ArcData());
+		ArcData& arcData = mWireArcData.back();
+
+		arcData.position = position;
+		arcData.normal = normal;
+		arcData.radius = radius;
+		arcData.startAngle = startAngle;
+		arcData.amountAngle = amountAngle;
+		arcData.quality = quality;
+		arcData.color = mColor;
+		arcData.transform = mTransform;
+		arcData.center = mTransform.multiplyAffine(position);
+	}
+
+	void DrawHelper::rectangle(const Rect3& area)
+	{
+		mRect3Data.push_back(Rect3Data());
+		Rect3Data& rectData = mRect3Data.back();
+
+		rectData.area = area;
+		rectData.color = mColor;
+		rectData.transform = mTransform;
+		rectData.center = mTransform.multiplyAffine(area.getCenter());
+	}
+
+	void DrawHelper::text(const Vector3& position, const WString& text, const HFont& font, UINT32 size)
+	{
+		if (!font.isLoaded() || text.empty())
+			return;
+
+		mText2DData.push_back(Text2DData());
+		Text2DData& textData = mText2DData.back();
+
+		textData.position = position;
+		textData.color = mColor;
+		textData.transform = mTransform;
+		textData.center = mTransform.multiplyAffine(position);
+		textData.text = text;
+		textData.font = font;
+		textData.size = size;
+	}
+
+	void DrawHelper::clear()
+	{
+		mSolidCubeData.clear();
+		mWireCubeData.clear();
+		mSolidSphereData.clear();
+		mWireSphereData.clear();
+		mLineData.clear();
+		mRect3Data.clear();
+		mFrustumData.clear();
+		mFrustumData.clear();
+		mDiscData.clear();
+		mWireDiscData.clear();
+		mArcData.clear();
+		mWireArcData.clear();
+		mConeData.clear();
+		mText2DData.clear();
+	}
+
+	void DrawHelper::buildMeshes(SortType sorting, const Vector3& reference)
+	{
+		clearMeshes();
+
+		enum class ShapeType
+		{
+			Cube, Sphere, WireCube, WireSphere, Line, Frustum, 
+			Cone, Disc, WireDisc, Arc, WireArc, Rectangle, Text
+		};
+
+		struct RawData
+		{
+			ShapeType shapeType;
+			MeshType meshType;
+			UINT32 idx;
+			UINT32 textIdx;
+			float distance;
+			UINT32 numVertices;
+			UINT32 numIndices;
+		};
+
+		/************************************************************************/
+		/* 			Sort everything according to specified sorting rule         */
+		/************************************************************************/
+
+		UINT32 totalNumShapes = (UINT32)(mSolidCubeData.size() + mSolidSphereData.size() + 
+			mWireCubeData.size() + mWireSphereData.size() + mLineData.size() + mFrustumData.size() + mConeData.size() +
+			mDiscData.size() + mWireDiscData.size() + mArcData.size() + mWireArcData.size() + mRect3Data.size() + mText2DData.size());
+
+		UINT32 idx = 0;
+		Vector<RawData> allShapes(totalNumShapes);
+
+		UINT32 localIdx = 0;
+		for (auto& shapeData : mSolidCubeData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Cube;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsAABox(rawData.numVertices, rawData.numIndices);
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mSolidSphereData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Sphere;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsSphere(shapeData.quality, 
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mConeData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Cone;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsCone(shapeData.quality, 
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mDiscData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Disc;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsDisc(shapeData.quality,
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mArcData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Arc;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsArc(shapeData.quality, 
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mRect3Data)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Solid;
+			rawData.shapeType = ShapeType::Rectangle;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsQuad(rawData.numVertices, rawData.numIndices);
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mWireCubeData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::WireCube;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsWireAABox(rawData.numVertices, rawData.numIndices);
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mWireSphereData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::WireSphere;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsWireSphere(shapeData.quality,
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mLineData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::Line;
+			rawData.distance = shapeData.center.distance(reference);
+			rawData.numVertices = 2;
+			rawData.numIndices = 2;
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mFrustumData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::Frustum;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsFrustum(rawData.numVertices, rawData.numIndices);
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mWireDiscData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::WireDisc;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsWireDisc(shapeData.quality, 
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		localIdx = 0;
+		for (auto& shapeData : mWireArcData)
+		{
+			RawData& rawData = allShapes[idx];
+
+			rawData.idx = localIdx++;
+			rawData.textIdx = 0;
+			rawData.meshType = MeshType::Wire;
+			rawData.shapeType = ShapeType::WireArc;
+			rawData.distance = shapeData.center.distance(reference);
+
+			ShapeMeshes3D::getNumElementsWireArc(shapeData.quality,
+				rawData.numVertices, rawData.numIndices);
+
+			idx++;
+		}
+
+		struct TextRenderData
+		{
+			UINT32 page;
+			SPtr<TextData<>> textData;
+		};
+
+		UnorderedMap<UINT32, TextRenderData> textRenderData;
+		UINT32 textIdx = 0;
+
+		localIdx = 0;
+		for (auto& shapeData : mText2DData)
+		{
+			SPtr<TextData<>> textData = bs_shared_ptr_new<TextData<>>(shapeData.text, shapeData.font, shapeData.size);
+
+			UINT32 numPages = textData->getNumPages();
+			for (UINT32 j = 0; j < numPages; j++)
+			{
+				UINT32 numQuads = textData->getNumQuadsForPage(j);
+
+				RawData& rawData = allShapes[idx];
+
+				rawData.idx = localIdx;
+				rawData.textIdx = textIdx;
+				rawData.meshType = MeshType::Text;
+				rawData.shapeType = ShapeType::Text;
+				rawData.distance = shapeData.center.distance(reference);
+				rawData.numVertices = numQuads * 4; 
+				rawData.numIndices = numQuads * 6;
+
+				TextRenderData& renderData = textRenderData[textIdx];
+				renderData.page = j;
+				renderData.textData = textData;
+
+				textIdx++;
+				idx++;
+			}
+
+			localIdx++;
+		}
+
+		if (sorting == SortType::FrontToBack)
+		{
+			std::sort(begin(allShapes), end(allShapes),
+				[&](const RawData& x, const RawData& y)
+			{
+				return x.distance < y.distance;
+			});
+		}
+		else if (sorting == SortType::BackToFront)
+		{
+			std::sort(begin(allShapes), end(allShapes),
+				[&](const RawData& x, const RawData& y)
+			{
+				return y.distance < x.distance;
+			});
+		}
+
+		/************************************************************************/
+		/* 							Create batches                      		*/
+		/************************************************************************/
+		struct Batch
+		{
+			MeshType type;
+			HTexture texture;
+			UINT32 startIdx;
+			UINT32 endIdx;
+			UINT32 numVertices;
+			UINT32 numIndices;
+		};
+
+		Vector<Batch> batches;
+		if (totalNumShapes > 0)
+		{
+			batches.push_back(Batch());
+
+			{
+				Batch& currentBatch = batches.back();
+				currentBatch.startIdx = 0;
+				currentBatch.type = allShapes[0].meshType;
+				currentBatch.numVertices = allShapes[0].numVertices;
+				currentBatch.numIndices = allShapes[0].numIndices;
+
+				if (allShapes[0].meshType == MeshType::Text)
+				{
+					TextRenderData& renderData = textRenderData[allShapes[0].textIdx];
+					currentBatch.texture = renderData.textData->getTextureForPage(renderData.page);
+				}
+			}
+
+			for (UINT32 i = 1; i < totalNumShapes; i++)
+			{
+				Batch& currentBatch = batches.back();
+
+				HTexture texture;
+				bool startNewBatch = false;
+				if(allShapes[i].meshType != currentBatch.type)
+				{
+					startNewBatch = true;
+				}
+				else
+				{
+					if(allShapes[i].meshType == MeshType::Text)
+					{
+						TextRenderData& renderData = textRenderData[allShapes[i].textIdx];
+						texture = renderData.textData->getTextureForPage(renderData.page);
+
+						if (texture != currentBatch.texture)
+							startNewBatch = true;
+					}
+				}
+
+				if (startNewBatch)
+				{
+					currentBatch.endIdx = i - 1;
+
+					batches.push_back(Batch());
+
+					Batch& newBatch = batches.back();
+					newBatch.startIdx = i;
+					newBatch.type = allShapes[i].meshType;
+					newBatch.numVertices = allShapes[i].numVertices;
+					newBatch.numIndices = allShapes[i].numIndices;
+					newBatch.texture = texture;
+				}
+				else
+				{
+					currentBatch.endIdx = i;
+					currentBatch.numVertices += allShapes[i].numVertices;
+					currentBatch.numIndices += allShapes[i].numIndices;
+				}
+			}
+
+			{
+				Batch& currentBatch = batches.back();
+				currentBatch.endIdx = totalNumShapes - 1;
+			}
+		}
+
+		/************************************************************************/
+		/* 					Generate geometry for each batch                    */
+		/************************************************************************/
+		for (auto& batch : batches)
+		{
+			if (batch.type == MeshType::Solid)
+			{
+				MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(batch.numVertices, batch.numIndices, mSolidVertexDesc);
+
+				UINT32 curVertexOffset = 0;
+				UINT32 curIndexOffet = 0;
+
+				auto positionIter = meshData->getVec3DataIter(VES_POSITION);
+				auto normalIter = meshData->getVec3DataIter(VES_NORMAL);
+				auto colorIter = meshData->getDWORDDataIter(VES_COLOR);
+
+				for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
+				{
+					RawData& shapeData = allShapes[i];
+
+					Matrix4* transform = nullptr;
+					RGBA color = 0;
+
+					switch (shapeData.shapeType)
+					{
+					case ShapeType::Cube:
+					{
+						CubeData& cubeData = mSolidCubeData[shapeData.idx];
+						AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
+						ShapeMeshes3D::solidAABox(box, meshData, curVertexOffset, curIndexOffet);
+
+						transform = &cubeData.transform;
+						color = cubeData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Sphere:
+					{
+						SphereData& sphereData = mSolidSphereData[shapeData.idx];
+						Sphere sphere(sphereData.position, sphereData.radius);
+						ShapeMeshes3D::solidSphere(sphere, meshData, curVertexOffset, curIndexOffet, sphereData.quality);
+
+						transform = &sphereData.transform;
+						color = sphereData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Cone:
+					{
+						ConeData& coneData = mConeData[shapeData.idx];
+						ShapeMeshes3D::solidCone(coneData.base, coneData.normal, coneData.height, coneData.radius,
+							meshData, curVertexOffset, curIndexOffet, coneData.quality);
+
+						transform = &coneData.transform;
+						color = coneData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Disc:
+					{
+						DiscData& discData = mDiscData[shapeData.idx];
+						ShapeMeshes3D::solidDisc(discData.position, discData.radius, discData.normal,
+							meshData, curVertexOffset, curIndexOffet, discData.quality);
+
+						transform = &discData.transform;
+						color = discData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Arc:
+					{
+						ArcData& arcData = mArcData[shapeData.idx];
+						ShapeMeshes3D::solidArc(arcData.position, arcData.radius, arcData.normal,
+							arcData.startAngle, arcData.amountAngle, meshData, curVertexOffset, curIndexOffet, arcData.quality);
+
+						transform = &arcData.transform;
+						color = arcData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Rectangle:
+					{
+						Rect3Data rectData = mRect3Data[shapeData.idx];
+						ShapeMeshes3D::solidQuad(rectData.area, meshData, curVertexOffset, curIndexOffet);
+
+						transform = &rectData.transform;
+						color = rectData.color.getAsRGBA();
+					}
+						break;
+					}
+
+					Matrix4 transformIT = transform->inverseAffine().transpose();
+					for (UINT32 i = 0; i < shapeData.numVertices; i++)
+					{
+						Vector3 worldPos = transform->multiplyAffine(positionIter.getValue());
+						Vector3 worldNormal = transformIT.multiplyAffine(normalIter.getValue());
+
+						positionIter.addValue(worldPos);
+						normalIter.addValue(worldNormal);
+						colorIter.addValue(color);
+					}
+
+					curVertexOffset += shapeData.numVertices;
+					curIndexOffet += shapeData.numIndices;
+				}
+
+				mMeshes.push_back(ShapeMeshData());
+				ShapeMeshData& newMesh = mMeshes.back();
+				newMesh.mesh = mSolidMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
+				newMesh.type = MeshType::Solid;
+			}
+			else if(batch.type == MeshType::Wire)
+			{
+				MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(batch.numVertices,
+					batch.numIndices, mWireVertexDesc);
+
+				UINT32 curVertexOffset = 0;
+				UINT32 curIndexOffet = 0;
+
+				auto positionIter = meshData->getVec3DataIter(VES_POSITION);
+				auto colorIter = meshData->getDWORDDataIter(VES_COLOR);
+
+				for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
+				{
+					RawData& shapeData = allShapes[i];
+
+					Matrix4* transform = nullptr;
+					RGBA color = 0;
+
+					switch (shapeData.shapeType)
+					{
+					case ShapeType::WireCube:
+					{
+						CubeData& cubeData = mWireCubeData[shapeData.idx];
+
+						AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
+						ShapeMeshes3D::wireAABox(box, meshData, curVertexOffset, curIndexOffet);
+
+						transform = &cubeData.transform;
+						color = cubeData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::WireSphere:
+					{
+						SphereData& sphereData = mWireSphereData[shapeData.idx];
+
+						Sphere sphere(sphereData.position, sphereData.radius);
+						ShapeMeshes3D::wireSphere(sphere, meshData, curVertexOffset, curIndexOffet, sphereData.quality);
+
+						transform = &sphereData.transform;
+						color = sphereData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Line:
+					{
+						LineData& lineData = mLineData[shapeData.idx];
+
+						ShapeMeshes3D::pixelLine(lineData.start, lineData.end, meshData, curVertexOffset, curIndexOffet);
+
+						transform = &lineData.transform;
+						color = lineData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::Frustum:
+					{
+						FrustumData& frustumData = mFrustumData[shapeData.idx];
+
+						ShapeMeshes3D::wireFrustum(frustumData.position, frustumData.aspect, frustumData.FOV, frustumData.near,
+							frustumData.far, meshData, curVertexOffset, curIndexOffet);
+
+						transform = &frustumData.transform;
+						color = frustumData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::WireDisc:
+					{
+						DiscData& discData = mWireDiscData[shapeData.idx];
+
+						ShapeMeshes3D::wireDisc(discData.position, discData.radius, discData.normal,
+							meshData, curVertexOffset, curIndexOffet, discData.quality);
+
+						transform = &discData.transform;
+						color = discData.color.getAsRGBA();
+					}
+						break;
+					case ShapeType::WireArc:
+					{
+						ArcData& arcData = mWireArcData[shapeData.idx];
+
+						ShapeMeshes3D::wireArc(arcData.position, arcData.radius, arcData.normal,
+							arcData.startAngle, arcData.amountAngle, meshData, curVertexOffset, curIndexOffet, arcData.quality);
+
+						transform = &arcData.transform;
+						color = arcData.color.getAsRGBA();
+					}
+						break;
+					}
+
+					for (UINT32 i = 0; i < shapeData.numVertices; i++)
+					{
+						Vector3 worldPos = transform->multiplyAffine(positionIter.getValue());
+
+						positionIter.addValue(worldPos);
+						colorIter.addValue(color);
+					}
+
+					curVertexOffset += shapeData.numVertices;
+					curIndexOffet += shapeData.numIndices;
+				}
+
+				mMeshes.push_back(ShapeMeshData());
+				ShapeMeshData& newMesh = mMeshes.back();
+				newMesh.mesh = mWireMeshHeap->alloc(meshData, DOT_LINE_LIST);
+				newMesh.type = MeshType::Wire;
+			}
+			else // Text
+			{
+				MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(batch.numVertices,
+					batch.numIndices, mTextVertexDesc);
+
+				UINT32 curVertexOffset = 0;
+				UINT32 curIndexOffet = 0;
+
+				auto positionIter = meshData->getVec3DataIter(VES_POSITION);
+				auto uvIter = meshData->getVec2DataIter(VES_TEXCOORD);
+				auto colorIter = meshData->getDWORDDataIter(VES_COLOR);
+
+				for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
+				{
+					RawData& shapeData = allShapes[i];
+					Text2DData& text2DData = mText2DData[shapeData.idx];
+
+					TextRenderData& renderData = textRenderData[shapeData.textIdx];
+					UINT32 numQuads = renderData.textData->getNumQuadsForPage(renderData.page);
+
+					UINT32* indices = meshData->getIndices32();
+
+					// Note: Need temporary buffers because TextLine doesn't support arbitrary vertex stride. Eventually
+					// that should be supported (should be almost trivial to implement)
+					Vector2* tempVertices = bs_stack_alloc<Vector2>(shapeData.numVertices);
+					Vector2* tempUVs = bs_stack_alloc<Vector2>(shapeData.numVertices);
+
+					UINT32 numLines = renderData.textData->getNumLines();
+					UINT32 quadOffset = 0;
+					for (UINT32 j = 0; j < numLines; j++)
+					{
+						const TextDataBase::TextLine& line = renderData.textData->getLine(j);
+						UINT32 writtenQuads = line.fillBuffer(renderData.page, tempVertices, tempUVs, indices, quadOffset, numQuads);
+
+						quadOffset += writtenQuads;
+					}
+
+					for (UINT32 j = 0; j < shapeData.numVertices; j++)
+					{
+						Vector3 localPos(tempVertices[j].x, tempVertices[j].y, 0.0f);
+						Vector3 worldPos = text2DData.transform.multiplyAffine(localPos);
+
+						positionIter.addValue(worldPos);
+						uvIter.addValue(tempUVs[j]);
+						colorIter.addValue(text2DData.color.getAsRGBA());
+					}
+
+					bs_stack_free(tempVertices);
+					bs_stack_free(tempUVs);
+
+					curVertexOffset += shapeData.numVertices;
+					curIndexOffet += shapeData.numIndices;
+				}
+
+				mMeshes.push_back(ShapeMeshData());
+				ShapeMeshData& newMesh = mMeshes.back();
+				newMesh.mesh = mTextMeshHeap->alloc(meshData, DOT_TRIANGLE_LIST);
+				newMesh.type = MeshType::Text;
+			}
+		}
+	}
+
+	void DrawHelper::clearMeshes()
+	{
+		for (auto meshData : mMeshes)
+		{
+			if (meshData.type == MeshType::Solid)
+				mSolidMeshHeap->dealloc(meshData.mesh);
+			else if (meshData.type == MeshType::Wire)
+				mWireMeshHeap->dealloc(meshData.mesh);
+			else // Text
+				mTextMeshHeap->dealloc(meshData.mesh);
+		}
+
+		mMeshes.clear();
+	}
 }

+ 3 - 3
BansheeEngine/Source/BsTextSprite.cpp

@@ -121,10 +121,10 @@ namespace BansheeEngine
 
 			Vector2I position = offset + alignmentOffsets[i];
 			UINT32 numVertices = writtenQuads * 4;
-			for(UINT32 i = 0; i < numVertices; i++)
+			for(UINT32 j = 0; j < numVertices; j++)
 			{
-				vertices[quadOffset * 4 + i].x += (float)position.x;
-				vertices[quadOffset * 4 + i].y += (float)position.y;
+				vertices[quadOffset * 4 + j].x += (float)position.x;
+				vertices[quadOffset * 4 + j].y += (float)position.y;
 			}
 
 			quadOffset += writtenQuads;

+ 0 - 1
MBansheeEditor/Scene/SceneAxesHandle.cs

@@ -153,7 +153,6 @@ namespace BansheeEditor
 
             HandleDrawing.DrawCube(Vector3.Zero, new Vector3(0.2f, 0.2f, 0.2f), handleSize);
 
-            // TODO - Hide the axis pointing straight to/from the camera
             // TODO - Add a text notifying the user whether ortho/proj is active
         }