Răsfoiți Sursa

Merge branch 'master' of https://github.com/MarcoROG/BansheeEngine into MarcoROG-master

BearishSun 9 ani în urmă
părinte
comite
c170418a1c

+ 14 - 3
Data/Raw/Editor/Includes/PickingAlphaCull.bslinc

@@ -23,11 +23,14 @@ Technique : base("PickingAlphaCull") =
 
 			void main(
 				in float3 inPos : POSITION,
+				in float3 inNorm : NORMAL,
 				in float2 uv : TEXCOORD0,
 				out float4 oPosition : SV_Position,
+				out float4 oNorm : NORMAL,
 				out float2 oUv : TEXCOORD0)
 			{
 				oPosition = mul(matWorldViewProj, float4(inPos.xyz, 1));
+				oNorm = float4(inNorm, 0);
 				oUv = uv;
 			}
 		};
@@ -42,9 +45,12 @@ Technique : base("PickingAlphaCull") =
 
 			float4 main(
 				in float4 inPos : SV_Position,
-				in float2 uv : TEXCOORD0) : SV_Target
+				in float4 inNorm : NORMAL,
+				in float2 uv : TEXCOORD0,
+				out float4 oNorm : SV_Target1) : SV_Target0
 			{
 				float4 color = mainTexture.Sample(mainTexSamp, uv);
+				oNorm = (inNorm + float4(1,1,1,0)) / 2;
 				if(color.a < alphaCutoff)
 					discard;
 				
@@ -111,6 +117,8 @@ Technique : base("PickingAlphaCull") =
 			uniform mat4 matWorldViewProj;
 			in vec3 bs_position;
 			in vec2 bs_texcoord0;
+			in vec3 bs_normal;
+			out vec4 bs_outNorm
 			out vec2 texcoord0;
 
 			out gl_PerVertex
@@ -122,6 +130,7 @@ Technique : base("PickingAlphaCull") =
 			{
 				gl_Position = matWorldViewProj * vec4(bs_position.xyz, 1);
 				texcoord0 = bs_texcoord0;
+				bs_outNorm = vec4(bs_normal, 0);
 			}
 		};
 		
@@ -130,7 +139,9 @@ Technique : base("PickingAlphaCull") =
 			uniform sampler2D mainTexture;
 			uniform vec4 colorIndex;
 			uniform float alphaCutoff;
+			in vec4 bs_outNorm;
 			in vec2 texcoord0;
+			out vec4 normalsColor;
 			out vec4 fragColor;
 
 			void main()
@@ -138,9 +149,9 @@ Technique : base("PickingAlphaCull") =
 				vec4 color = texture2D(mainTexture, texcoord0);
 				if(color.a < alphaCutoff)
 					discard;
-				
+				normalsColor = (bs_outNorm + vec4(1,1,1,0)) / 2;
 				fragColor = colorIndex;
 			}
 		};
 	};
-};
+};

+ 17 - 4
Data/Raw/Editor/Includes/PickingCull.bslinc

@@ -19,9 +19,12 @@ Technique : base("PickingCull") =
 
 			void main(
 				in float3 inPos : POSITION,
-				out float4 oPosition : SV_Position)
+				in float3 inNorm : NORMAL,
+				out float4 oPosition : SV_Position,
+				out float4 oNorm : NORMAL)
 			{
 				oPosition = mul(matWorldViewProj, float4(inPos.xyz, 1));
+				oNorm = float4(inNorm, 0);
 			}
 		};
 		
@@ -29,8 +32,13 @@ Technique : base("PickingCull") =
 		{
 			float4 colorIndex;
 
-			float4 main(in float4 inPos : SV_Position) : SV_Target
+			float4 main(
+				in float4 inPos : SV_Position,
+				in float4 inNorm : NORMAL,	
+				out float4 oNorm :SV_Target1
+			) : SV_Target0
 			{
+				oNorm = (inNorm + float4(1,1,1,0)) / 2;
 				return colorIndex;
 			}
 		};
@@ -81,7 +89,8 @@ Technique : base("PickingCull") =
 		{
 			uniform mat4 matWorldViewProj;
 			in vec3 bs_position;
-
+			in vec3 normal;
+			out vec4 outNorm;
 			out gl_PerVertex
 			{
 				vec4 gl_Position;
@@ -89,19 +98,23 @@ Technique : base("PickingCull") =
 			
 			void main()
 			{
+				outNorm = vec4(normal,0);
 				gl_Position = matWorldViewProj * vec4(bs_position.xyz, 1);
 			}
 		};
 		
 		Fragment =
 		{
+			in vec4 outNorm;
 			uniform vec4 colorIndex;
+			out vec4 normalsColor;
 			out vec4 fragColor;
 
 			void main()
 			{
+				normalsColor = (outNorm + vec4(1,1,1,0)) / 2;
 				fragColor = colorIndex;
 			}
 		};
 	};
-};
+};

+ 10 - 0
Source/BansheeCore/Include/BsCamera.h

@@ -353,6 +353,16 @@ namespace BansheeEngine
 		 */
 		Vector3 screenToWorldPoint(const Vector2I& screenPoint, float depth = 0.5f) const;
 
+		/**
+		* Converts a point in screen space (pixels corresponding to render target attached to the camera) to a point in
+		* world space.
+		*
+		* @param[in]	screenPoint	Point to transform.
+		* @param[in]	deviceDepth	Device depth to place the world point at. The depth is applied to the vector going from camera
+		*							origin to the point on the near plane.
+		*/
+		Vector3 screenToWorldPointDeviceDepth(const Vector2I& screenPoint, float deviceDepth = 0.5f) const;
+
 		/**
 		 * Converts a point in screen space (pixels corresponding to render target attached to the camera) to a point
 		 * relative to camera's coordinate system (view space).

+ 17 - 0
Source/BansheeCore/Include/BsMultiRenderTexture.h

@@ -121,6 +121,23 @@ namespace BansheeEngine
 		/** Returns properties that describe the render texture. */
 		const MultiRenderTextureProperties& getProperties() const;
 
+		/**
+		* Returns a depth/stencil surface texture you may bind as an input to an GPU program.
+		*
+		* @note	Be aware that you cannot bind a render texture for reading and writing at the same time.
+		*/
+		const SPtr<TextureView> getBindableDepthStencilTexture() const { return mDepthStencilSurface; }
+
+		/**
+		* Returns a color surface texture you may bind as an input to an GPU program.
+		*
+		* @note	Be aware that you cannot bind a render texture for reading and writing at the same time.
+		*/
+		const SPtr<TextureView>& getBindableColorTexture(UINT32 idx) const { return mColorSurfaces[idx]; }
+
+		/** @copydoc	TextureManager::createMultiRenderTexture(const MULTI_RENDER_TEXTURE_DESC&) */
+		static SPtr<MultiRenderTextureCore> create(const MULTI_RENDER_TEXTURE_CORE_DESC& desc);
+
 	protected:
 		MultiRenderTextureCore(const MULTI_RENDER_TEXTURE_CORE_DESC& desc);
 

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

@@ -326,6 +326,23 @@ namespace BansheeEngine
 		 */
 		void setColors(Color* colors, UINT32 numElements);
 
+		/** 
+		 * Decodes data stored in a depth texture at the specified pixel coordinates, and outputs a floating point depth
+		 * value in range [0, 1]. 
+		 */
+		float getDepthAt(UINT32 x, UINT32 y, UINT32 z = 0) const;
+
+		/** 
+		 * Sets a depth value in range [0, 1] at the specified pixel coordinates.
+		 */
+		void setDepthAt(const float depth, UINT32 x, UINT32 y, UINT32 z = 0);
+
+		/**
+		* Converts all the internal data into an array of float. Array is mapped as such:
+		* arrayIdx = x + y * width + z * width * height.
+		*/
+		Vector<float> getDepths() const;
+
 		/**
 		 * Constructs a new object with an internal buffer capable of holding "extents" volume of pixels, where each pixel
 		 * is of the specified pixel format. Extent offsets are also stored, but are not used internally.
@@ -377,4 +394,4 @@ namespace BansheeEngine
     };
 
 	/** @} */
-}
+}

+ 7 - 1
Source/BansheeCore/Include/BsPixelUtil.h

@@ -189,6 +189,12 @@ namespace BansheeEngine
 		 * is not natively floating point a conversion is done in such a way that returned values range [0.0, 1.0].
 		 */
         static void unpackColor(float* r, float* g, float* b, float* a, PixelFormat format, const void* src); 
+
+		/** Writes a depth value to the provided memory location. Depth should be in [0,1]*/
+		static void packDepth(float depth, const PixelFormat format, void* dest);
+
+		/** Reads the depth from the provided memory location. Value ranges in [0, 1]. */
+		static float unpackDepth(PixelFormat format, void* src);
         
 		/**
 		 * Converts pixels from one format to another. Provided pixel data objects must have previously allocated buffers
@@ -227,4 +233,4 @@ namespace BansheeEngine
     };
 
 	/** @} */
-}
+}

+ 18 - 0
Source/BansheeCore/Source/BsCamera.cpp

@@ -554,6 +554,24 @@ namespace BansheeEngine
 		return ndcToWorldPoint(ndcPoint, depth);
 	}
 
+	Vector3 CameraBase::screenToWorldPointDeviceDepth(const Vector2I& screenPoint, float deviceDepth) const
+	{
+		Vector2 ndcPoint = screenToNdcPoint(screenPoint);
+		Vector4 worldPoint(ndcPoint.x, ndcPoint.y, deviceDepth, 1.0f);
+		worldPoint = getProjectionMatrixRS().inverse().multiply(worldPoint);
+		Vector3 worldPoint3D;
+
+		if (Math::abs(worldPoint.w) > 1e-7f)
+		{
+			float invW = 1.0f / worldPoint.w;
+
+			worldPoint3D.x = worldPoint.x * invW;
+			worldPoint3D.y = worldPoint.y * invW;
+			worldPoint3D.z = worldPoint.z * invW;
+		}
+		return viewToWorldPoint(worldPoint3D);
+	}
+
 	Vector3 CameraBase::screenToViewPoint(const Vector2I& screenPoint, float depth) const
 	{
 		Vector2 ndcPoint = screenToNdcPoint(screenPoint);

+ 5 - 0
Source/BansheeCore/Source/BsMultiRenderTexture.cpp

@@ -127,6 +127,11 @@ namespace BansheeEngine
 		return static_cast<const MultiRenderTextureProperties&>(getPropertiesInternal());
 	}
 
+	SPtr<MultiRenderTextureCore> MultiRenderTextureCore::create(const MULTI_RENDER_TEXTURE_CORE_DESC& desc)
+	{
+		return TextureCoreManager::instance().createMultiRenderTexture(desc);
+	}
+
 	void MultiRenderTextureCore::throwIfBuffersDontMatch() const
 	{
 		SPtr<TextureView> firstSurfaceDesc = nullptr;

+ 49 - 1
Source/BansheeCore/Source/BsPixelData.cpp

@@ -176,6 +176,54 @@ namespace BansheeEngine
 		setColorsInternal(colors, numElements);
 	}
 
+	float PixelData::getDepthAt(UINT32 x, UINT32 y, UINT32 z) const
+	{
+		UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
+		UINT32 pixelOffset = pixelSize * (z * mSlicePitch + y * mRowPitch + x);
+		return PixelUtil::unpackDepth(mFormat, (unsigned char *)getData() + pixelOffset);;
+	}
+
+	void PixelData::setDepthAt(const float depth, UINT32 x, UINT32 y, UINT32 z)
+	{
+		UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
+		UINT32 pixelOffset = pixelSize * (z * mSlicePitch + y * mRowPitch + x);
+		PixelUtil::packDepth(depth, mFormat, (unsigned char *)getData() + pixelOffset);
+	}
+
+	Vector<float> PixelData::getDepths() const
+	{
+		UINT32 depth = mExtents.getDepth();
+		UINT32 height = mExtents.getHeight();
+		UINT32 width = mExtents.getWidth();
+
+		UINT32 pixelSize = PixelUtil::getNumElemBytes(mFormat);
+		UINT8* data = getData();
+
+		Vector<float> depths(width * height * depth);
+		for (UINT32 z = 0; z < depth; z++)
+		{
+			UINT32 zArrayIdx = z * width * height;
+			UINT32 zDataIdx = z * mSlicePitch * pixelSize;
+
+			for (UINT32 y = 0; y < height; y++)
+			{
+				UINT32 yArrayIdx = y * width;
+				UINT32 yDataIdx = y * mRowPitch * pixelSize;
+
+				for (UINT32 x = 0; x < width; x++)
+				{
+					UINT32 arrayIdx = x + yArrayIdx + zArrayIdx;
+					UINT32 dataIdx = x * pixelSize + yDataIdx + zDataIdx;
+
+					UINT8* dest = data + dataIdx;
+					depths[arrayIdx] = PixelUtil::unpackDepth(mFormat, dest);
+				}
+			}
+		}
+
+		return depths;
+	}
+
 	SPtr<PixelData> PixelData::create(const PixelVolume &extents, PixelFormat pixelFormat)
 	{
 		SPtr<PixelData> pixelData = bs_shared_ptr_new<PixelData>(extents, pixelFormat);
@@ -210,4 +258,4 @@ namespace BansheeEngine
 	{
 		return PixelData::getRTTIStatic();
 	}
-}
+}

+ 48 - 5
Source/BansheeCore/Source/BsPixelUtil.cpp

@@ -740,11 +740,11 @@ namespace BansheeEngine
 	//-----------------------------------------------------------------------
 		{"PF_D32_S8X24",
 		/* Bytes per element */
-		4,
+		8,
 		/* Flags */
 		PFF_DEPTH | PFF_FLOAT,
 		/* Component type and count */
-		PCT_FLOAT32, 1,
+		PCT_FLOAT32, 2,
 		/* rbits, gbits, bbits, abits */
 		0, 0, 0, 0,
 		/* Masks and shifts */
@@ -753,11 +753,11 @@ namespace BansheeEngine
 	//-----------------------------------------------------------------------
 		{"PF_D24_S8",
 		/* Bytes per element */
-		8,
+		4,
 		/* Flags */
 		PFF_DEPTH | PFF_FLOAT,
 		/* Component type and count */
-		PCT_FLOAT32, 2,
+		PCT_FLOAT32, 1,
 		/* rbits, gbits, bbits, abits */
 		0, 0, 0, 0,
 		/* Masks and shifts */
@@ -1275,7 +1275,6 @@ namespace BansheeEngine
     void PixelUtil::unpackColor(float* r, float* g, float* b, float* a, PixelFormat format, const void* src)
     {
         const PixelFormatDescription &des = getDescriptionFor(format);
-
         if(des.flags & PFF_NATIVEENDIAN) 
 		{
             // Shortcut for integer formats unpacking
@@ -1359,6 +1358,50 @@ namespace BansheeEngine
         }
     }
 
+	void PixelUtil::packDepth(float depth, const PixelFormat format, void* dest)
+	{
+		if (!isDepth(format))
+		{
+			LOGERR("Cannot convert depth to " + getFormatName(format) + ": it is not a depth format");
+			return;
+		}
+		
+		LOGERR("Method is not implemented");	
+		//TODO implement depth packing
+
+	}
+
+	float PixelUtil::unpackDepth(PixelFormat format, void* src)
+	{
+		const PixelFormatDescription &des = getDescriptionFor(format);
+		if (!isDepth(format))
+		{
+			LOGERR("Cannot unpack from " + getFormatName(format) + ": it is not a depth format");
+			return 0;
+		}
+		
+		UINT32* color = (UINT32 *)src;
+		switch (format) 
+		{
+		case PF_D24S8:
+			return  static_cast<float>(*color & 0x00FFFFFF) / (float)16777216;
+			break;
+		case PF_D16:
+			return static_cast<float>(*color & 0xFFFF) / (float)65536;
+			break;
+		case PF_D32:
+			return static_cast<float>(*color & 0xFFFFFFFF) / (float)4294967296;
+			break;
+		case PF_D32_S8X24:
+			return static_cast<float>(*color & 0xFFFFFFFF) / (float)4294967296;
+			break;
+		default:
+			LOGERR("Cannot unpack from " + getFormatName(format));
+			return 0;
+			break;
+		}
+	}
+
     void PixelUtil::bulkPixelConversion(const PixelData &src, PixelData &dst)
     {
         assert(src.getWidth() == dst.getWidth() &&

+ 25 - 4
Source/BansheeEditor/Include/BsScenePicking.h

@@ -13,6 +13,20 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	 /** Contains the data of a scene picking action. */
+	struct SnapData
+	{
+		Vector3 normal;
+		Vector3 pickPosition;
+	};
+
+	/** Contains the results of a scene picking action. */
+	struct PickResults
+	{
+		Vector<UINT32> objects;
+		SnapData data;
+	};
+
 	class ScenePickingCore;
 
 	/**	Handles picking of scene objects with a pointer in scene view. */
@@ -40,9 +54,11 @@ namespace BansheeEngine
 		 * @param[in]	position	Pointer position relative to the camera viewport, in pixels.
 		 * @param[in]	area		Width/height of the checked area in pixels. Use (1, 1) if you want the exact position
 		 *							under the pointer.
+		 * @param[in]	ignoreRenderables		A list of objects that should not be rendered during scene picking.
+		 * @param[out]	data		Picking data regarding position and normal.
 		 * @return					Nearest SceneObject under the provided area, or an empty handle if no object is found.
 		 */
-		HSceneObject pickClosestObject(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area);
+		HSceneObject pickClosestObject(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area, Vector<HSceneObject>& ignoreRenderables, SnapData* data = nullptr);
 
 		/**
 		 * Attempts to find all scene objects under the provided position and area. This does not mean objects occluded by
@@ -52,9 +68,11 @@ namespace BansheeEngine
 		 * @param[in]	position	Pointer position relative to the camera viewport, in pixels.
 		 * @param[in]	area		Width/height of the checked area in pixels. Use (1, 1) if you want the exact position
 		 *							under the pointer.
+		 * @param[in]	ignoreRenderables		A list of objects that should not be rendered during scene picking.
+		 * @param[out]	data		Picking data regarding position and normal.
 		 * @return					A list of SceneObject%s under the provided area.
 		 */
-		Vector<HSceneObject> pickObjects(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area);
+		Vector<HSceneObject> pickObjects(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area, Vector<HSceneObject>& ignoreRenderables, SnapData* data = nullptr);
 
 	private:
 		friend class ScenePickingCore;
@@ -123,19 +141,22 @@ namespace BansheeEngine
 		 * @param[in]	viewportArea	Normalized area of the render target we're rendering in.
 		 * @param[in]	position		Position of the pointer where to pick objects, in pixels relative to viewport.
 		 * @param[in]	area			Width/height of the area to pick objects, in pixels.
+		 * @param[in]	gatherSnapData	Whether it should gather normal and picking position information.
 		 * @param[out]	asyncOp			Async operation handle that when complete will contain the results of the picking
 		 *								operation in the form of Vector<SelectedObject>.
 		 */
 		void corePickingEnd(const SPtr<RenderTargetCore>& target, const Rect2& viewportArea, const Vector2I& position,
-			const Vector2I& area, AsyncOp& asyncOp);
+			const Vector2I& area, bool gatherSnapData, AsyncOp& asyncOp);
 
 	private:
 		friend class ScenePicking;
 
+		SPtr<MultiRenderTextureCore> mPickingTexture;
+
 		static const float ALPHA_CUTOFF;
 
 		MaterialData mMaterialData[3];
 	};
 
 	/** @} */
-}
+}

+ 106 - 18
Source/BansheeEditor/Source/BsScenePicking.cpp

@@ -17,6 +17,7 @@
 #include "BsPass.h"
 #include "BsRasterizerState.h"
 #include "BsRenderTarget.h"
+#include "BsMultiRenderTexture.h"
 #include "BsPixelData.h"
 #include "BsGpuParams.h"
 #include "BsGpuParamsSet.h"
@@ -53,16 +54,23 @@ namespace BansheeEngine
 		gCoreAccessor().queueCommand(std::bind(&ScenePickingCore::destroy, mCore));
 	}
 
-	HSceneObject ScenePicking::pickClosestObject(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area)
+	HSceneObject ScenePicking::pickClosestObject(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area, Vector<HSceneObject>& ignoreRenderables, SnapData* data)
 	{
-		Vector<HSceneObject> selectedObjects = pickObjects(cam, position, area);
+		Vector<HSceneObject> selectedObjects = pickObjects(cam, position, area, ignoreRenderables, data);
 		if (selectedObjects.size() == 0)
 			return HSceneObject();
-
+			
+		if (data != nullptr)
+		{
+			Matrix3 rotation;
+			selectedObjects[0]->getWorldRotation().toRotationMatrix(rotation);
+			data->normal = rotation.inverse().transpose().transform(data->normal);
+		}
+		
 		return selectedObjects[0];
 	}
 
-	Vector<HSceneObject> ScenePicking::pickObjects(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area)
+	Vector<HSceneObject> ScenePicking::pickObjects(const SPtr<Camera>& cam, const Vector2I& position, const Vector2I& area, Vector<HSceneObject>& ignoreRenderables, SnapData* data)
 	{
 		auto comparePickElement = [&] (const ScenePicking::RenderablePickData& a, const ScenePicking::RenderablePickData& b)
 		{
@@ -96,6 +104,19 @@ namespace BansheeEngine
 			if (!mesh.isLoaded())
 				continue;
 
+			bool found = false;
+			for (int i = 0; i < ignoreRenderables.size(); i++)
+			{
+				if (ignoreRenderables[i] == so)
+				{
+					found = true;
+					break;
+				}
+			}
+			
+			if (found)
+				continue;
+
 			Bounds worldBounds = mesh->getProperties().getBounds();
 			Matrix4 worldTransform = so->getWorldTfrm();
 			worldBounds.transformAffine(worldTransform);
@@ -163,12 +184,21 @@ namespace BansheeEngine
 		GizmoManager::instance().renderForPicking(cam, [&](UINT32 inputIdx) { return encodeIndex(firstGizmoIdx + inputIdx); });
 
 		AsyncOp op = gCoreAccessor().queueReturnCommand(std::bind(&ScenePickingCore::corePickingEnd, mCore, target, 
-			cam->getViewport()->getNormArea(), position, area, _1));
+			cam->getViewport()->getNormArea(), position, area, data != nullptr, _1));
 		gCoreAccessor().submitToCoreThread(true);
 
 		assert(op.hasCompleted());
 
-		Vector<UINT32> selectedObjects = op.getReturnValue<Vector<UINT32>>();
+		PickResults returnValue = op.getReturnValue<PickResults>();
+
+		if (data != nullptr)
+		{
+			Vector3 pos = returnValue.data.pickPosition;
+			*data = returnValue.data;
+			data->pickPosition = cam->screenToWorldPointDeviceDepth(Vector2I(pos.x, pos.y), pos.z);
+		}
+
+		Vector<UINT32> selectedObjects = returnValue.objects;
 		Vector<HSceneObject> results;
 
 		for (auto& selectedObjectIdx : selectedObjects)
@@ -257,8 +287,28 @@ namespace BansheeEngine
 	{
 		RenderAPICore& rs = RenderAPICore::instance();
 
+		SPtr<RenderTextureCore> rtt = std::static_pointer_cast<RenderTextureCore>(target);
+		SPtr<TextureCore> outputTexture = rtt->getBindableColorTexture();
+		TextureProperties outputTextureProperties = outputTexture->getProperties();
+		SPtr<TextureCore> depthTexture = rtt->getBindableDepthStencilTexture();
+
+		MULTI_RENDER_TEXTURE_CORE_DESC multiTextureDescription;
+		multiTextureDescription.colorSurfaces.resize(2);
+		multiTextureDescription.colorSurfaces[0].face = 0;
+		multiTextureDescription.colorSurfaces[0].texture = outputTexture;
+		multiTextureDescription.colorSurfaces[1].face = 0;
+		SPtr<TextureCore> core = TextureCore::create(TextureType::TEX_TYPE_2D, outputTextureProperties.getWidth(),
+			outputTextureProperties.getHeight(), 0, PixelFormat::PF_R8G8B8A8, TU_RENDERTARGET, false, 1);
+		multiTextureDescription.colorSurfaces[1].texture = core;
+		RENDER_SURFACE_CORE_DESC depthStencilDescription;
+		depthStencilDescription.face = 0;
+		depthStencilDescription.texture = depthTexture;
+		multiTextureDescription.depthStencilSurface = depthStencilDescription;
+		
+		mPickingTexture = MultiRenderTextureCore::create(multiTextureDescription);
+
 		rs.beginFrame();
-		rs.setRenderTarget(target);
+		rs.setRenderTarget(mPickingTexture);
 		rs.setViewport(viewportArea);
 		rs.clearRenderTarget(FBT_COLOR | FBT_DEPTH | FBT_STENCIL, Color::White);
 		rs.setScissorRect(position.x, position.y, position.x + area.x, position.y + area.y);
@@ -309,9 +359,13 @@ namespace BansheeEngine
 	}
 
 	void ScenePickingCore::corePickingEnd(const SPtr<RenderTargetCore>& target, const Rect2& viewportArea, const Vector2I& position,
-		const Vector2I& area, AsyncOp& asyncOp)
+		const Vector2I& area, bool gatherSnapData, AsyncOp& asyncOp)
 	{
 		const RenderTargetProperties& rtProps = target->getProperties();
+		RenderAPICore& rs = RenderAPICore::instance();
+
+		rs.endFrame();
+		rs.setRenderTarget(nullptr);
 
 		if (rtProps.isWindow())
 		{
@@ -319,8 +373,9 @@ namespace BansheeEngine
 			BS_EXCEPT(NotImplementedException, "Picking is not supported on render windows as framebuffer readback methods aren't implemented");
 		}
 
-		SPtr<RenderTextureCore> rtt = std::static_pointer_cast<RenderTextureCore>(target);
-		SPtr<TextureCore> outputTexture = rtt->getBindableColorTexture();
+		SPtr<TextureCore> outputTexture = mPickingTexture->getBindableColorTexture(0)->getTexture();
+		SPtr<TextureCore> normalsTexture = mPickingTexture->getBindableColorTexture(1)->getTexture();
+		SPtr<TextureCore> depthTexture = mPickingTexture->getBindableDepthStencilTexture()->getTexture();
 
 		if (position.x < 0 || position.x >= (INT32)outputTexture->getProperties().getWidth() ||
 			position.y < 0 || position.y >= (INT32)outputTexture->getProperties().getHeight())
@@ -330,11 +385,14 @@ namespace BansheeEngine
 		}
 
 		SPtr<PixelData> outputPixelData = outputTexture->getProperties().allocateSubresourceBuffer(0);
-		AsyncOp unused;
-
-		RenderAPICore& rs = RenderAPICore::instance();
+		SPtr<PixelData> normalsPixelData;
+		SPtr<PixelData> depthPixelData;
+		if (gatherSnapData)
+		{
+			normalsPixelData = normalsTexture->getProperties().allocateSubresourceBuffer(0);
+			depthPixelData = depthTexture->getProperties().allocateSubresourceBuffer(0);
+		}
 
-		rs.endFrame();
 		outputTexture->readSubresource(0, *outputPixelData);
 
 		Map<UINT32, UINT32> selectionScores;
@@ -400,10 +458,40 @@ namespace BansheeEngine
 			return b.score < a.score;
 		});
 
-		Vector<UINT32> results;
+		Vector<UINT32> objects;
 		for (auto& selectedObject : selectedObjects)
-			results.push_back(selectedObject.index);
+			objects.push_back(selectedObject.index);
+		
+		PickResults result;
+		if (gatherSnapData)
+		{
+			depthTexture->readSubresource(0, *depthPixelData);
+			normalsTexture->readSubresource(0, *normalsPixelData);
+			float depth;
+			Color normal;
+			if (rtProps.requiresTextureFlipping())
+			{
+				depth = depthPixelData->getDepthAt(position.x, depthPixelData->getHeight() - position.y);
+				normal = normalsPixelData->getColorAt(position.x, depthPixelData->getHeight() - position.y);
+			}
+			else
+			{
+				depth = depthPixelData->getDepthAt(position.x, position.y);
+				normal = normalsPixelData->getColorAt(position.x, position.y);
+			}
 
-		asyncOp._completeOperation(results);
+			SnapData data;
+			const RenderAPIInfo& rapiInfo = rs.getAPIInfo();
+			float max = rapiInfo.getMaximumDepthInputValue();
+			float min = rapiInfo.getMinimumDepthInputValue();
+			depth = depth * Math::abs(max - min) + min;
+			data.pickPosition = Vector3(position.x, position.y, depth);
+			data.normal = Vector3((normal.r * 2) - 1, (normal.g * 2) - 1, (normal.b * 2) - 1);
+			result.data = data;
+		}
+		
+		result.objects = objects;
+		asyncOp._completeOperation(result);
+		mNormalsTexture = nullptr;
 	}
-}
+}

+ 2 - 0
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -88,6 +88,8 @@ namespace BansheeEngine
 		:SpecificImporter(), mFBXManager(nullptr)
 	{
 		mExtensions.push_back(L"fbx");
+		mExtensions.push_back(L"obj");
+		mExtensions.push_back(L"dae");
 	}
 
 	FBXImporter::~FBXImporter() 

+ 1 - 1
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -232,7 +232,7 @@
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
-    <PostBuildEvent>$(ProjectDir)..\..\Scripts\pdb2mdb.bat "$(TargetPath)" "$(ConfigurationName)"</PostBuildEvent>
+    <PostBuildEvent>"$(ProjectDir)..\..\Scripts\pdb2mdb.bat" "$(TargetPath)" "$(ConfigurationName)"</PostBuildEvent>
   </PropertyGroup>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 6 - 6
Source/MBansheeEditor/Window/MenuItems.cs

@@ -720,16 +720,16 @@ namespace BansheeEditor
         /// </summary>
         private static void FocusOnHierarchyOrScene()
         {
-            HierarchyWindow hierarchyWindow = EditorWindow.GetWindow<HierarchyWindow>();
-            if (hierarchyWindow != null)
+            SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
+            if (sceneWindow != null)
             {
-                hierarchyWindow.HasFocus = true;
+                sceneWindow.HasFocus = true;
                 return;
             }
 
-            SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
-            if (sceneWindow != null)
-                sceneWindow.HasFocus = true;
+            HierarchyWindow hierarchyWindow = EditorWindow.GetWindow<HierarchyWindow>();
+            if (hierarchyWindow != null)
+                hierarchyWindow.HasFocus = true;
         }
     }
 

+ 1 - 1
Source/MBansheeEditor/Windows/Scene/SceneCamera.cs

@@ -271,7 +271,7 @@ namespace BansheeEditor
             }
 
             SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
-            if (sceneWindow.Active && sceneWindow.HasFocus)
+            if ((sceneWindow.Active && sceneWindow.HasFocus) || sceneWindow.IsPointerHovering)
             {
                 Rect2I bounds = sceneWindow.Bounds;
 

+ 52 - 3
Source/MBansheeEditor/Windows/Scene/SceneSelection.cs

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 using System;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -10,6 +11,23 @@ namespace BansheeEditor
      *  @{
      */
 
+    /// <summary>
+    /// Contains Object containing the world position and normal of the surface under the snapping point.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    struct SnapData
+    {
+        /// <summary>
+        /// The normal to the object surface at the snapping point.
+        /// </summary>
+        public Vector3 normal;
+
+        /// <summary>
+        /// The 3D position on the surface of the object.
+        /// </summary>
+        public Vector3 position;
+    }
+
     /// <summary>
     /// Handles rendering of the selection overlay and picking of objects in the target camera's view.
     /// </summary>
@@ -37,9 +55,34 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
         /// <param name="controlHeld">Should this selection add to the existing selection, or replace it.</param>
-        internal void PickObject(Vector2I pointerPos, bool controlHeld)
+        /// <param name="ignoreSceneObjects">Optional set of objects to ignore during scene picking.</param>
+        internal void PickObject(Vector2I pointerPos, bool controlHeld, SceneObject[] ignoreSceneObjects = null)
         {
-            Internal_PickObject(mCachedPtr, ref pointerPos, controlHeld);
+            Internal_PickObject(mCachedPtr, ref pointerPos, controlHeld, ignoreSceneObjects);
+        }
+
+        /// <summary>
+        /// Attempts to select a scene object in the specified area.
+        /// </summary>
+        /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
+        /// <param name="area">The screen area in which objects will be selected.</param>
+        /// <param name="controlHeld">Should this selection add to the existing selection, or replace it.</param>
+        /// <param name="ignoreSceneObjects">Optional set of objects to ignore during scene picking.</param>
+        internal void PickObjects(Vector2I pointerPos, Vector2I area, bool controlHeld, SceneObject[] ignoreSceneObjects = null)
+        {
+            Internal_PickObjects(mCachedPtr, ref pointerPos, ref area, controlHeld, ignoreSceneObjects);
+        }
+
+        /// <summary>
+        /// Object containing the world position and normal of the surface under the provided screen point.
+        /// </summary>
+        /// <param name="pointerPos">Position of the pointer relative to the scene camera viewport.</param>
+        /// <param name="data">A struct containing the position on the object surface and the normal in that point.</param>
+        /// <param name="ignoreSceneObjects">Optional set of objects to ignore during scene picking.</param>
+        /// <returns>The object the pointer is snapping to.</returns>
+        internal SceneObject Snap(Vector2I pointerPos, out SnapData data, SceneObject[] ignoreSceneObjects = null)
+        {
+            return Internal_Snap(mCachedPtr, ref pointerPos, out data, ignoreSceneObjects);
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
@@ -49,7 +92,13 @@ namespace BansheeEditor
         private static extern void Internal_Draw(IntPtr thisPtr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_PickObject(IntPtr thisPtr, ref Vector2I pointerPos, bool controlHeld);
+        private static extern void Internal_PickObject(IntPtr thisPtr, ref Vector2I pointerPos, bool controlHeld, SceneObject[] ignoreRenderables);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_PickObjects(IntPtr thisPtr, ref Vector2I pointerPos, ref Vector2I extents, bool controlHeld, SceneObject[] ignoreRenderables);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SceneObject Internal_Snap(IntPtr thisPtr, ref Vector2I pointerPos, out SnapData data, SceneObject[] ignoreRenderables);
     }
 
     /** @} */

+ 137 - 16
Source/MBansheeEditor/Windows/Scene/SceneWindow.cs

@@ -23,6 +23,8 @@ namespace BansheeEditor
         internal const string ScaleToolBinding = "ScaleTool";
         internal const string FrameBinding = "SceneFrame";
 
+        public SceneSelection sceneSelection;
+
         private const int HeaderHeight = 20;
         private const float DefaultPlacementDepth = 5.0f;
         private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
@@ -40,7 +42,6 @@ namespace BansheeEditor
 
         private GUIRenderTexture renderTextureGUI;
         private SceneGrid sceneGrid;
-        private SceneSelection sceneSelection;
         private SceneGizmos sceneGizmos;
         private SceneHandles sceneHandles;
 
@@ -85,6 +86,12 @@ namespace BansheeEditor
         private bool dragActive;
         private SceneObject draggedSO;
         private Vector3 draggedSOOffset;
+        private GUITexture dragSelection;
+        private bool isDraggingSelection;
+        private Vector2I dragSelectionStart;
+        private Vector2I dragSelectionEnd;
+        private Vector2I mouseDownPosition;
+        private GUIPanel selectionPanel;
 
         /// <summary>
         /// Returns the scene camera.
@@ -265,6 +272,8 @@ namespace BansheeEditor
             GUIPanel mainPanel = mainLayout.AddPanel();
             rtPanel = mainPanel.AddPanel();
 
+            selectionPanel = mainPanel.AddPanel(-1);
+
             GUIPanel sceneAxesPanel = mainPanel.AddPanel(-1);
             sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
 
@@ -427,7 +436,7 @@ namespace BansheeEditor
         /// <param name="screenPos">Coordinates relative to the screen.</param>
         /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
         /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
-        private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
+        public bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
         {
             scenePos = screenPos;
             Vector2I windowPos = ScreenToWindowPos(screenPos);
@@ -478,10 +487,13 @@ namespace BansheeEditor
             // Update scene view handles and selection
             sceneGizmos.Draw();
             sceneGrid.Draw();
-
+            bool dragResult = false;
             bool handleActive = false;
+            Vector2I scenePos;
+            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
             if (Input.IsPointerButtonUp(PointerButton.Left))
             {
+                dragResult = EndDragSelection();
                 if (sceneHandles.IsActive())
                 {
                     sceneHandles.ClearSelection();
@@ -493,11 +505,12 @@ namespace BansheeEditor
                     sceneAxesGUI.ClearSelection();
                     handleActive = true;
                 }
+            } 
+            else if (Input.IsPointerButtonDown(PointerButton.Left))
+            {
+                mouseDownPosition = scenePos;
             }
 
-            Vector2I scenePos;
-            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
-
             bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
             draggedOver &= IsPointerHovering && inBounds && DragDrop.Type == DragDropType.Resource;
 
@@ -506,13 +519,12 @@ namespace BansheeEditor
                 if (DragDrop.DropInProgress)
                 {
                     dragActive = false;
-
                     if (draggedSO != null)
                     {
                         Selection.SceneObject = draggedSO;
                         EditorApplication.SetSceneDirty();
                     }
-
+                    
                     draggedSO = null;
                 }
                 else
@@ -540,7 +552,6 @@ namespace BansheeEditor
 
                                         Renderable renderable = draggedSO.AddComponent<Renderable>();
                                         renderable.Mesh = mesh;
-
                                         if (mesh != null)
                                             draggedSOOffset = mesh.Bounds.Box.Center;
                                         else
@@ -573,8 +584,35 @@ namespace BansheeEditor
 
                     if (draggedSO != null)
                     {
-                        Ray worldRay = camera.ViewportToWorldRay(scenePos);
-                        draggedSO.Position = worldRay*DefaultPlacementDepth - draggedSOOffset;
+                        if (Input.IsButtonHeld(ButtonCode.Space))
+                        {
+                            SnapData snapData;
+                            var snappedTo = sceneSelection.Snap(scenePos, out snapData, new SceneObject[] {draggedSO});
+                            if (snappedTo != null)
+                            {
+                                Quaternion q = Quaternion.FromToRotation(Vector3.YAxis, snapData.normal);
+                                draggedSO.Position = snapData.position;
+                                draggedSO.Rotation = q;
+                            }
+                            else
+                            {
+                                Ray worldRay = camera.ViewportToWorldRay(scenePos);
+                                Vector3 pos = worldRay*DefaultPlacementDepth - draggedSOOffset;
+                                float interval = EditorSettings.MoveHandleSnapAmount;
+                                if (EditorSettings.MoveHandleSnapActive)
+                                    draggedSO.Position = new Vector3(pos.x - (pos.x % interval), pos.y - (pos.y % interval),
+                                        pos.z - (pos.z % interval));
+                                else
+                                    draggedSO.Position = new Vector3(pos.x, pos.y - (pos.y % interval), pos.z);
+
+                                draggedSO.Rotation = Quaternion.LookRotation(Vector3.YAxis);
+                            }
+                        }
+                        else
+                        {
+                            Ray worldRay = camera.ViewportToWorldRay(scenePos);
+                            draggedSO.Position = worldRay * DefaultPlacementDepth - draggedSOOffset;
+                        }
                     }
                 }
 
@@ -594,11 +632,11 @@ namespace BansheeEditor
                 }
             }
 
-            if (HasContentFocus)
+            if (HasContentFocus || IsPointerHovering)
             {
                 cameraController.EnableInput(true);
 
-                if (inBounds)
+                if (inBounds && HasContentFocus)
                 {
                     if (Input.IsPointerButtonDown(PointerButton.Left))
                     {
@@ -610,21 +648,29 @@ namespace BansheeEditor
                         else
                             sceneHandles.TrySelect(scenePos);
                     }
+                    else if (Input.IsPointerButtonHeld(PointerButton.Left) && !handleActive && !dragActive &&
+                             draggedSO == null && scenePos != mouseDownPosition)
+                    {
+                        if (isDraggingSelection)
+                            UpdateDragSelection(scenePos);
+                        else
+                            StartDragSelection(scenePos);
+                    }
                     else if (Input.IsPointerButtonUp(PointerButton.Left))
                     {
-                        if (!handleActive)
+                        if (!handleActive && !dragActive && !dragResult)
                         {
                             bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
                                             Input.IsButtonHeld(ButtonCode.RightControl);
 
-                            sceneSelection.PickObject(scenePos, ctrlHeld);
+                            sceneSelection.PickObject(scenePos, ctrlHeld, new SceneObject[] {draggedSO});
                         }
                     }
                 }
             }
             else
                 cameraController.EnableInput(false);
-
+            
             SceneHandles.BeginInput();
             sceneHandles.UpdateInput(scenePos, Input.PointerDelta);
             sceneHandles.Draw();
@@ -911,6 +957,81 @@ namespace BansheeEditor
 
 		    objects = cleanList.ToArray();
 	    }
+
+        /// <summary>
+        /// Starts a drag operation that displays a selection outline allowing the user to select multiple entries at once.
+        /// </summary>
+        /// <param name="scenePos">Coordinates relative to the scene where the drag originated.</param>
+        private void StartDragSelection(Vector2I scenePos)
+        {
+            isDraggingSelection = true;
+            dragSelectionStart = scenePos;
+            dragSelectionEnd = dragSelectionStart;
+        }
+
+        /// <summary>
+        /// Updates a selection outline drag operation by expanding the outline to the new location. Elements in the outline
+        /// are selected.
+        /// </summary>
+        /// <param name="scenePos">Coordinates of the pointer relative to the scene.</param>
+        /// <returns>True if the selection outline drag is valid and was updated, false otherwise.</returns>
+        private bool UpdateDragSelection(Vector2I scenePos)
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection == null)
+            {
+                dragSelection = new GUITexture(null, true, EditorStylesInternal.SelectionArea);
+                selectionPanel.AddElement(dragSelection);
+            }
+            
+            dragSelectionEnd = scenePos;
+
+            Rect2I selectionArea = new Rect2I();
+
+            Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x), Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
+            Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x), Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
+            selectionArea.x = min.x;
+            selectionArea.y = min.y;
+            selectionArea.width = Math.Max(max.x - min.x, 1);
+            selectionArea.height = Math.Max(max.y - min.y, 1);
+
+            dragSelection.Bounds = selectionArea;
+
+            return true;
+        }
+
+        /// <summary>
+        /// Ends the selection outline drag operation. Elements in the outline are selected.
+        /// </summary>
+        /// <returns>True if the selection outline drag is valid and was ended, false otherwise.</returns>
+        private bool EndDragSelection()
+        {
+            if (!isDraggingSelection)
+                return false;
+
+            if (dragSelection != null)
+            {
+                dragSelection.Destroy();
+                dragSelection = null;
+            }
+
+            if ((dragSelectionEnd - dragSelectionStart).Length < 1)
+            {
+                isDraggingSelection = false;
+                return false;
+            }
+
+            Vector2I min = new Vector2I(Math.Min(dragSelectionStart.x, dragSelectionEnd.x),
+                    Math.Min(dragSelectionStart.y, dragSelectionEnd.y));
+            Vector2I max = new Vector2I(Math.Max(dragSelectionStart.x, dragSelectionEnd.x),
+                Math.Max(dragSelectionStart.y, dragSelectionEnd.y));
+            sceneSelection.PickObjects(min, max - min,
+                Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl));
+            isDraggingSelection = false;
+            return true;
+        }
     }
 
     /** @} */

+ 1 - 1
Source/MBansheeEngine/MBansheeEngine.csproj

@@ -215,7 +215,7 @@
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
-    <PostBuildEvent>$(ProjectDir)..\..\Scripts\pdb2mdb.bat "$(TargetPath)" "$(ConfigurationName)"</PostBuildEvent>
+    <PostBuildEvent>"$(ProjectDir)..\..\Scripts\pdb2mdb.bat" "$(TargetPath)" "$(ConfigurationName)"</PostBuildEvent>
   </PropertyGroup>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 10 - 10
Source/RenderBeast/Include/BsRendererCamera.h

@@ -68,6 +68,16 @@ namespace BansheeEngine
 		void determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds, 
 			Vector<bool>& visibility);
 
+		/**
+		* Extracts the necessary values from the projection matrix that allow you to transform device Z value into
+		* world Z value.
+		* 
+		* @param[in]	projMatrix	Projection matrix that was used to create the device Z value to transform.
+		* @return					Returns two values that can be used to transform device z to world z using this formula:
+		* 							z = (deviceZ + y) * x.
+		*/
+		Vector2 getDeviceZTransform(const Matrix4& projMatrix) const;
+
 		/** 
 		 * Returns a structure containing information about post-processing effects. This structure will be modified and
 		 * maintained by the post-processing system.
@@ -78,16 +88,6 @@ namespace BansheeEngine
 		CameraShaderData getShaderData();
 
 	private:
-		/**
-		 * Extracts the necessary values from the projection matrix that allow you to transform device Z value into
-		 * world Z value.
-		 * 			
-		 * @param[in]	projMatrix	Projection matrix that was used to create the device Z value to transform.
-		 * @return					Returns two values that can be used to transform device z to world z using this formula:
-		 * 							z = (deviceZ + y) * x.
-		 */
-		Vector2 getDeviceZTransform(const Matrix4& projMatrix);
-
 		const CameraCore* mCamera;
 		SPtr<RenderQueue> mOpaqueQueue;
 		SPtr<RenderQueue> mTransparentQueue;

+ 4 - 1
Source/SBansheeEditor/Include/BsScriptSceneSelection.h

@@ -4,6 +4,7 @@
 
 #include "BsScriptEditorPrerequisites.h"
 #include "BsScriptObject.h"
+#include "BsScenePicking.h"
 
 namespace BansheeEngine
 {
@@ -29,7 +30,9 @@ namespace BansheeEngine
 		/************************************************************************/
 		static void internal_Create(MonoObject* managedInstance, ScriptCamera* camera);
 		static void internal_Draw(ScriptSceneSelection* thisPtr);
-		static void internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive);
+		static void internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive, MonoArray* ignoreRenderables);
+		static void internal_PickObjects(ScriptSceneSelection* thisPtr, Vector2I* inputPos, Vector2I* area, bool additive, MonoArray* ignoreRenderables);
+		static MonoObject* internal_Snap(ScriptSceneSelection* thisPtr, Vector2I* inputPos, SnapData* data, MonoArray* ignoreRenderables);
 	};
 
 	/** @} */

+ 116 - 5
Source/SBansheeEditor/Source/BsScriptSceneSelection.cpp

@@ -3,8 +3,12 @@
 #include "BsScriptSceneSelection.h"
 #include "BsScriptCamera.h"
 #include "BsSelectionRenderer.h"
+#include "BsScriptSceneObject.h"
+#include "BsScriptGameObjectManager.h"
 #include "BsScenePicking.h"
 #include "BsSelection.h"
+#include "BsMonoPrerequisites.h"
+#include <BsMonoArray.h>
 
 namespace BansheeEngine
 {
@@ -24,6 +28,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_Create", &ScriptSceneSelection::internal_Create);
 		metaData.scriptClass->addInternalCall("Internal_Draw", &ScriptSceneSelection::internal_Draw);
 		metaData.scriptClass->addInternalCall("Internal_PickObject", &ScriptSceneSelection::internal_PickObject);
+		metaData.scriptClass->addInternalCall("Internal_PickObjects", &ScriptSceneSelection::internal_PickObjects);
+		metaData.scriptClass->addInternalCall("Internal_Snap", &ScriptSceneSelection::internal_Snap);
 	}
 
 	void ScriptSceneSelection::internal_Create(MonoObject* managedInstance, ScriptCamera* camera)
@@ -36,11 +42,29 @@ namespace BansheeEngine
 		thisPtr->mSelectionRenderer->update(thisPtr->mCamera);
 	}
 
-	void ScriptSceneSelection::internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive)
+	void ScriptSceneSelection::internal_PickObject(ScriptSceneSelection* thisPtr, Vector2I* inputPos, bool additive, MonoArray* ignoreRenderables)
 	{
-		// TODO - Handle multi-selection (i.e. selection rectangle when dragging)
-		HSceneObject pickedObject = ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1));
+		Vector<HSceneObject> ignoredSceneObjects;
 
+		if (ignoreRenderables != nullptr)
+		{
+			ScriptArray scriptArray(ignoreRenderables);
+
+			UINT32 arrayLen = scriptArray.size();
+			for (UINT32 i = 0; i < arrayLen; i++)
+			{
+				MonoObject* monoSO = scriptArray.get<MonoObject*>(i);
+				ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(monoSO);
+
+				if (scriptSO == nullptr)
+					continue;
+
+				HSceneObject so = static_object_cast<SceneObject>(scriptSO->getNativeHandle());
+				ignoredSceneObjects.push_back(so);
+			}
+		}
+
+		HSceneObject pickedObject = ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), ignoredSceneObjects);
 		if (pickedObject)
 		{
 			if (additive) // Append to existing selection
@@ -59,11 +83,98 @@ namespace BansheeEngine
 			else
 			{
 				Vector<HSceneObject> selectedSOs = { pickedObject };
+				Selection::instance().setSceneObjects(selectedSOs);
+			}
+		}
+		else if (!additive)
+		{
+			Selection::instance().clearSceneSelection();
+		}
+			
+	}
+
+	void ScriptSceneSelection::internal_PickObjects(ScriptSceneSelection* thisPtr, Vector2I* inputPos, Vector2I* area, bool additive, MonoArray* ignoreRenderables)
+	{
+		Vector<HSceneObject> ignoredSceneObjects;
+
+		if (ignoreRenderables != nullptr)
+		{
+			ScriptArray scriptArray(ignoreRenderables);
+
+			UINT32 arrayLen = scriptArray.size();
+			for (UINT32 i = 0; i < arrayLen; i++)
+			{
+				MonoObject* monoSO = scriptArray.get<MonoObject*>(i);
+				ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(monoSO);
+
+				if (scriptSO == nullptr)
+					continue;
+
+				HSceneObject so = static_object_cast<SceneObject>(scriptSO->getNativeHandle());
+				ignoredSceneObjects.push_back(so);
+			}
+		}
+
+		Vector<HSceneObject> pickedObjects = ScenePicking::instance().pickObjects(thisPtr->mCamera, *inputPos, *area, ignoredSceneObjects);
+
+		if (pickedObjects.size() != 0)
+		{
+			if (additive) // Append to existing selection
+			{
+				Vector<HSceneObject> selectedSOs = Selection::instance().getSceneObjects();
 
+				for (int i = 0; i < pickedObjects.size(); i++) {
+					bool found = false;
+					for (int j = 0; j < selectedSOs.size(); j++)
+					{
+						if (selectedSOs[j] == pickedObjects[i])
+						{
+							found = true;
+							break;
+						}
+					}
+					if (!found)
+						selectedSOs.push_back(pickedObjects[i]);
+				}
 				Selection::instance().setSceneObjects(selectedSOs);
 			}
+			else
+				Selection::instance().setSceneObjects(pickedObjects);
 		}
-		else
+		else if (!additive)
+		{
 			Selection::instance().clearSceneSelection();
+		}
+	}
+
+	MonoObject* ScriptSceneSelection::internal_Snap(ScriptSceneSelection* thisPtr, Vector2I* inputPos, SnapData* data, MonoArray* ignoreRenderables)
+	{
+		Vector<HSceneObject> ignoredSceneObjects;
+
+		if (ignoreRenderables != nullptr)
+		{
+			ScriptArray scriptArray(ignoreRenderables);
+
+			UINT32 arrayLen = scriptArray.size();
+			for (UINT32 i = 0; i < arrayLen; i++)
+			{
+				MonoObject* monoSO = scriptArray.get<MonoObject*>(i);
+				ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(monoSO);
+
+				if (scriptSO == nullptr)
+					continue;
+
+				HSceneObject so = static_object_cast<SceneObject>(scriptSO->getNativeHandle());
+				ignoredSceneObjects.push_back(so);
+			}
+		}
+
+		HSceneObject instance = ScenePicking::instance().pickClosestObject(thisPtr->mCamera, *inputPos, Vector2I(1, 1), ignoredSceneObjects, data);
+		MonoObject* managedInstance = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(instance)->getManagedInstance();
+		if (instance == nullptr)
+			return nullptr;
+
+		return  managedInstance;
 	}
-}
+
+}