Prechádzať zdrojové kódy

Fixed lag between material updates when rendering GUI
WIP implementations of rotate and scale handles
Various fixes to disc sliders and torus intersection code as well as equation solvers
Made arc/disc rendering double sided
Fixed to C# GUIToggle so ToggleOn and ToggleOff methods work

Marko Pintera 11 rokov pred
rodič
commit
f33b14ebe1
40 zmenil súbory, kde vykonal 866 pridanie a 383 odobranie
  1. 3 0
      BansheeCore/Include/BsMaterial.h
  2. 0 3
      BansheeCore/Source/BsCoreApplication.cpp
  3. 6 6
      BansheeD3D9RenderSystem/BansheeD3D9RenderSystem.vcxproj
  4. 12 6
      BansheeEditor/Include/BsEditorSettings.h
  5. 2 1
      BansheeEditor/Include/BsHandleSlider.h
  6. 4 4
      BansheeEditor/Include/BsHandleSliderDisc.h
  7. 3 0
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  8. 1 0
      BansheeEditor/Source/BsEditorApplication.cpp
  9. 3 3
      BansheeEditor/Source/BsEditorSettings.cpp
  10. 30 38
      BansheeEditor/Source/BsHandleSliderDisc.cpp
  11. 4 4
      BansheeEngine/Include/BsShapeMeshes3D.h
  12. 25 7
      BansheeEngine/Source/BsShapeMeshes3D.cpp
  13. 1 1
      BansheeGLRenderSystem/Source/BsGLRenderAPI.cpp
  14. 4 0
      BansheeRenderer/Source/BsBansheeRenderer.cpp
  15. 121 142
      BansheeUtility/Include/BsMath.h
  16. 2 1
      BansheeUtility/Include/BsTorus.h
  17. 0 3
      BansheeUtility/Source/BsAABox.cpp
  18. 16 14
      BansheeUtility/Source/BsTorus.cpp
  19. 8 8
      MBansheeEditor/DbgGizmo.cs
  20. 3 3
      MBansheeEditor/DebugCameraHandle.cs
  21. 9 13
      MBansheeEditor/EditorApplication.cs
  22. 43 24
      MBansheeEditor/EditorSettings.cs
  23. 14 12
      MBansheeEditor/Scene/DefaultHandleManager.cs
  24. 15 2
      MBansheeEditor/Scene/HandleSliderDisc.cs
  25. 8 15
      MBansheeEditor/Scene/Handles.cs
  26. 132 3
      MBansheeEditor/Scene/RotateHandle.cs
  27. 92 3
      MBansheeEditor/Scene/ScaleHandle.cs
  28. 5 5
      MBansheeEditor/Scene/SceneCamera.cs
  29. 159 6
      MBansheeEditor/Scene/SceneWindow.cs
  30. 4 4
      MBansheeEngine/GUI/GUIToggle.cs
  31. 8 0
      MBansheeEngine/Math/Matrix4.cs
  32. 31 0
      MBansheeEngine/Math/Vector3.cs
  33. 5 0
      MBansheeEngine/Math/Vector4.cs
  34. 12 12
      MBansheeEngine/SceneObject.cs
  35. 7 4
      SBansheeEditor/Include/BsScriptEditorSettings.h
  36. 1 0
      SBansheeEditor/Include/BsScriptHandleSliderDisc.h
  37. 45 24
      SBansheeEditor/Source/BsScriptEditorSettings.cpp
  38. 7 1
      SBansheeEditor/Source/BsScriptHandleSliderDisc.cpp
  39. 8 4
      SBansheeEngine/Source/BsScriptGUIToggle.cpp
  40. 13 7
      TODO.txt

+ 3 - 0
BansheeCore/Include/BsMaterial.h

@@ -139,6 +139,9 @@ namespace BansheeEngine
 		Map<String, String> mValidParams; // Also maps Shader param name -> gpu variable name
 	};
 
+	/**
+	 * @copydoc	MaterialBase
+	 */
 	template<bool Core>
 	class BS_CORE_EXPORT TMaterial : public MaterialBase
 	{

+ 0 - 3
BansheeCore/Source/BsCoreApplication.cpp

@@ -195,9 +195,6 @@ namespace BansheeEngine
 			// Send out resource events in case any were loaded/destroyed/modified
 			ResourceListenerManager::instance().update();
 
-			// Sync all dirty sim thread CoreObject data to core thread
-			CoreObjectManager::instance().syncToCore(gCoreAccessor());
-
 			PROFILE_CALL(RendererManager::instance().getActive()->renderAll(), "Render");
 
 			// Core and sim thread run in lockstep. This will result in a larger input latency than if I was 

+ 6 - 6
BansheeD3D9RenderSystem/BansheeD3D9RenderSystem.vcxproj

@@ -122,7 +122,7 @@
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <InlineFunctionExpansion>Default</InlineFunctionExpansion>
@@ -138,7 +138,7 @@
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <InlineFunctionExpansion>Default</InlineFunctionExpansion>
@@ -156,7 +156,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
@@ -178,7 +178,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
@@ -200,7 +200,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
@@ -222,7 +222,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(DXSDK_DIR)Include;.\Include;..\BansheeCore\Include;..\BansheeUtility\Include;..\Dependencies\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>BS_RSD3D9_EXPORTS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <BufferSecurityCheck>false</BufferSecurityCheck>

+ 12 - 6
BansheeEditor/Include/BsEditorSettings.h

@@ -12,11 +12,9 @@ namespace BansheeEngine
 
 		bool getMoveHandleSnapActive() const { return mMoveSnapActive; }
 		bool getRotateHandleSnapActive() const { return mRotateSnapActive; }
-		bool getScaleHandleSnapActive() const { return mScaleSnapActive; }
 
 		float getMoveHandleSnap() const { return mMoveSnap; }
 		Degree getRotationHandleSnap() const { return mRotationSnap; }
-		float getScaleHandleSnap() const { return mScaleSnap; }
 
 		UINT32 getGridSize() const { return mGridSize; }
 		float getGridSpacing() const { return mGridAxisSpacing; }
@@ -25,13 +23,15 @@ namespace BansheeEngine
 
 		float getHandleSize() const { return mHandleSize; }
 
+		UINT32 getActiveSceneTool() const { return mActiveSceneTool; }
+		UINT32 getActiveCoordinateMode() const { return mActiveCoordinateMode; }
+		UINT32 getActivePivotMode() const { return mActivePivotMode; }
+
 		void setMoveHandleSnapActive(bool snapActive) { mMoveSnapActive = snapActive; markAsDirty(); }
 		void setRotateHandleSnapActive(bool snapActive) { mRotateSnapActive = snapActive; markAsDirty(); }
-		void setScaleHandleSnapActive(bool snapActive) { mScaleSnapActive = snapActive; markAsDirty(); }
 
 		void setMoveHandleSnap(float value) { mMoveSnap = value; markAsDirty(); }
 		void setRotationHandleSnap(Degree value) { mRotationSnap = value; markAsDirty(); }
-		void setScaleHandleSnap(float value) { mScaleSnap = value; markAsDirty(); }
 
 		void setGridSize(UINT32 value) { mGridSize = value; markAsDirty(); }
 		void setGridSpacing(float value) { mGridAxisSpacing = value; markAsDirty(); }
@@ -40,6 +40,10 @@ namespace BansheeEngine
 
 		void setHandleSize(float value) { mHandleSize = value; markAsDirty(); }
 
+		void setActiveSceneTool(UINT32 value) { mActiveSceneTool = value; markAsDirty(); }
+		void setActiveCoordinateMode(UINT32 value) { mActiveCoordinateMode = value; markAsDirty(); }
+		void setActivePivotMode(UINT32 value) { mActivePivotMode = value; markAsDirty(); }
+
 		UINT32 getHash() const { return mHash; }
 
 	private:
@@ -47,17 +51,19 @@ namespace BansheeEngine
 
 		bool mMoveSnapActive;
 		bool mRotateSnapActive;
-		bool mScaleSnapActive;
 
 		float mMoveSnap;
 		Degree mRotationSnap;
-		float mScaleSnap;
 
 		UINT32 mGridSize;
 		float mGridAxisSpacing;
 		UINT32 mGridMajorAxisSpacing;
 		UINT32 mGridAxisMarkerSpacing;
 
+		UINT32 mActiveSceneTool;
+		UINT32 mActiveCoordinateMode;
+		UINT32 mActivePivotMode;
+
 		float mHandleSize;
 
 		mutable UINT32 mHash;

+ 2 - 1
BansheeEditor/Include/BsHandleSlider.h

@@ -47,7 +47,8 @@ namespace BansheeEngine
 
 		virtual void activate(const CameraHandlerPtr& camera, const Vector2I& pointerPos) { }
 		virtual void reset() { }
-		virtual void updateCachedTransform() const;
+
+		void updateCachedTransform() const;
 
 		float calcDelta(const CameraHandlerPtr& camera, const Vector3& position, const Vector3& direction,
 			const Vector2I& pointerStart, const Vector2I& pointerEnd);

+ 4 - 4
BansheeEditor/Include/BsHandleSliderDisc.h

@@ -15,12 +15,12 @@ namespace BansheeEngine
 		bool intersects(const Ray& ray, float& t) const;
 		void handleInput(const CameraHandlerPtr& camera, const Vector2I& inputDelta);
 
-		float getDelta() const { return mDelta; }
+		Radian getDelta() const { return mDelta; }
+		Radian getStartAngle() const { return mStartAngle; }
 
 	protected:
 		void activate(const CameraHandlerPtr& camera, const Vector2I& pointerPos);
 		void reset() { mDelta = 0.0f; }
-		virtual void updateCachedTransform() const;
 
 		Vector3 calculateClosestPointOnArc(const Ray& inputRay, const Vector3& center, const Vector3& up,
 			float radius, Degree startAngle, Degree angleAmount);
@@ -31,11 +31,11 @@ namespace BansheeEngine
 
 		Vector3 mNormal;
 		float mRadius;
-		Matrix4 mTorusRotation;
 
 		Vector3 mDirection;
 		Vector3 mStartPosition;
-		float mDelta;
+		Degree mStartAngle;
+		Degree mDelta;
 
 		Torus mCollider;
 	};

+ 3 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -280,6 +280,9 @@ namespace BansheeEngine
 		buttonStyle.normal.texture = getGUITexture(ButtonNormalTex);
 		buttonStyle.hover.texture = getGUITexture(ButtonHoverTex);
 		buttonStyle.active.texture = getGUITexture(ButtonActiveTex);
+		buttonStyle.normalOn.texture = getGUITexture(ButtonActiveTex);
+		buttonStyle.hoverOn.texture = getGUITexture(ButtonActiveTex);
+		buttonStyle.activeOn.texture = getGUITexture(ButtonActiveTex);
 		buttonStyle.border.left = 6;
 		buttonStyle.border.right = 6;
 		buttonStyle.border.top = 6;

+ 1 - 0
BansheeEditor/Source/BsEditorApplication.cpp

@@ -41,6 +41,7 @@
 #include "BsCoreRenderer.h"
 #include "BsEditorSettings.h"
 #include "BsMesh.h"
+#include "BsMath.h"
 
 namespace BansheeEngine
 {

+ 3 - 3
BansheeEditor/Source/BsEditorSettings.cpp

@@ -3,8 +3,8 @@
 namespace BansheeEngine
 {
 	EditorSettings::EditorSettings()
-		:mMoveSnapActive(false), mRotateSnapActive(false), mScaleSnapActive(false), mMoveSnap(0.1f),
-		mRotationSnap(20.0f), mScaleSnap(0.1f), mGridSize(256), mGridAxisSpacing(1.0f), mGridMajorAxisSpacing(10),
-		mGridAxisMarkerSpacing(25), mHandleSize(0.15f), mHash(0)
+		:mMoveSnapActive(false), mRotateSnapActive(false), mMoveSnap(0.1f), mRotationSnap(20.0f), 
+		mGridSize(256), mGridAxisSpacing(1.0f), mGridMajorAxisSpacing(10), mGridAxisMarkerSpacing(25), 
+		mHandleSize(0.15f), mHash(0), mActiveSceneTool(0), mActiveCoordinateMode(0), mActivePivotMode(0)
 	{ }
 }

+ 30 - 38
BansheeEditor/Source/BsHandleSliderDisc.cpp

@@ -6,19 +6,17 @@
 #include "BsQuaternion.h"
 #include "BsCamera.h"
 
+// DEBUG ONLY
+#include "BsDebug.h"
+
 namespace BansheeEngine
 {
-	const float HandleSliderDisc::TORUS_RADIUS = 0.5f;
+	const float HandleSliderDisc::TORUS_RADIUS = 0.1f;
 
 	HandleSliderDisc::HandleSliderDisc(const Vector3& normal, float radius, bool fixedScale)
-		:HandleSlider(fixedScale), mRadius(radius), mDelta(0.0f)
+		:HandleSlider(fixedScale), mRadius(radius), mNormal(normal), mDelta(0.0f)
 	{
-		Vector3 x, z;
-		mNormal.orthogonalComplement(x, z);
-
-		mTorusRotation = (Matrix4)Matrix3(x, mNormal, z); // Our Torus class doesn't allow us to specify a normal so we embed it here
-
-		mCollider = Torus(radius, TORUS_RADIUS);
+		mCollider = Torus(normal, radius, TORUS_RADIUS);
 
 		HandleSliderManager& sliderManager = HandleManager::instance().getSliderManager();
 		sliderManager._registerSlider(this);
@@ -30,18 +28,6 @@ namespace BansheeEngine
 		sliderManager._unregisterSlider(this);
 	}
 
-	void HandleSliderDisc::updateCachedTransform() const
-	{
-		if (mFixedScale)
-			mTransform.setTRS(mPosition, mRotation, mScale * mDistanceScale);
-		else
-			mTransform.setTRS(mPosition, mRotation, mScale);
-
-		mTransform = mTransform * mTorusRotation;
-		mTransformInv = mTransform.inverseAffine();
-		mTransformDirty = false;
-	}
-
 	bool HandleSliderDisc::intersects(const Ray& ray, float& t) const
 	{
 		Ray localRay = ray;
@@ -79,8 +65,8 @@ namespace BansheeEngine
 		auto intersectResult = plane.intersects(inputRay);
 
 		float t = 0.0f;
-		if (intersectResult.second)
-			pointOnPlane = inputRay.getPoint(intersectResult.first);
+		if (intersectResult.first)
+			pointOnPlane = inputRay.getPoint(intersectResult.second);
 		else
 			pointOnPlane = Vector3::ZERO;
 
@@ -97,23 +83,33 @@ namespace BansheeEngine
 		Radian angle = Math::atan2(-closestPoint2D.y, -closestPoint2D.x) + Math::PI;
 
 		float angleRad = angle.valueRadians();
+		float angleAmountRad = Math::clamp(angleAmount.valueRadians(), 0.0f, Math::PI * 2);
+
 		float startAngleRad = startAngle.wrap().valueRadians();
-		float endAngleRad = (startAngle + angleAmount).wrap().valueRadians();
+		float endAngleRad = startAngleRad + angleAmountRad;
 
-		float clampedAngle;
-		if (startAngleRad <= endAngleRad)
+		float clampedAngle = angleRad;
+		if (endAngleRad <= Math::PI * 2)
+		{
 			clampedAngle = Math::clamp(angleRad, startAngleRad, endAngleRad);
+		}
 		else
 		{
-			if ((angleRad < startAngleRad) && (angleRad > endAngleRad))
+			if (angleRad >= startAngleRad)
+				clampedAngle = Math::clamp(angleRad, startAngleRad, Math::PI * 2);
+			else
 			{
-				if ((startAngleRad - angleRad) > (angleRad - endAngleRad))
-					clampedAngle = endAngleRad;
+				endAngleRad -= Math::PI * 2;
+				if (angleRad > endAngleRad)
+				{
+					if ((startAngleRad - angleRad) > (angleRad - endAngleRad))
+						clampedAngle = endAngleRad;
+					else
+						clampedAngle = startAngleRad;
+				}
 				else
-					clampedAngle = startAngleRad;
+					clampedAngle = angleRad;
 			}
-			else
-				clampedAngle = angleRad;
 		}
 
 		Vector3 clampedAnglePoint;
@@ -144,12 +140,8 @@ namespace BansheeEngine
 		Ray localRay = camera->screenPointToRay(pointerPos);
 		localRay.transformAffine(getTransformInv());
 
-		Quaternion camLocalRotation = camera->getRotation() * getRotation().inverse();
-
-		Vector3 startDir = camLocalRotation.zAxis().cross(mNormal);
-		Degree startAngle = pointOnCircleToAngle(mNormal, startDir);
-
-		mStartPosition = calculateClosestPointOnArc(localRay, Vector3::ZERO, mNormal, mRadius, startAngle, Degree(180.0f));
+		mStartPosition = calculateClosestPointOnArc(localRay, Vector3::ZERO, mNormal, mRadius, Degree(0.0f), Degree(360.0f));
+		mStartAngle = pointOnCircleToAngle(mNormal, mStartPosition);
 		mStartPosition = getTransform().multiplyAffine(mStartPosition);
 
 		mDirection = mNormal.cross(mStartPosition - getPosition());
@@ -161,6 +153,6 @@ namespace BansheeEngine
 		assert(getState() == State::Active);
 
 		mCurrentPointerPos += inputDelta;
-		mDelta = calcDelta(camera, mStartPosition, mDirection, mStartPointerPos, mCurrentPointerPos);
+		mDelta = calcDelta(camera, mStartPosition, mDirection, mStartPointerPos, mCurrentPointerPos) * Math::RAD2DEG;
 	}
 }

+ 4 - 4
BansheeEngine/Include/BsShapeMeshes3D.h

@@ -132,8 +132,8 @@ namespace BansheeEngine
 		 * 			  Vector3 VES_POSITION
 		 *			  Vector3 VES_NORMAL
 		 * 			  32bit index buffer
-		 * 			  Enough space for ((quality + 1) * 5 + 1) vertices 
-		 *			  Enough space for (((quality + 1) * 5 - 1) * 3) indices
+		 * 			  Enough space for ((quality + 1) * 5 + 1) * 2 vertices 
+		 *			  Enough space for (((quality + 1) * 5 - 1) * 6) indices
 		 *
 		 *			Primitives are output in the form of a triangle list.
 		 */
@@ -179,8 +179,8 @@ namespace BansheeEngine
 		 * 			  Vector3 VES_POSITION
 		 *			  Vector3 VES_NORMAL
 		 * 			  32bit index buffer
-		 * 			  Enough space for ((quality + 1) * 5 + 1) vertices 
-		 *			  Enough space for (((quality + 1) * 5 - 1) * 3) indices
+		 * 			  Enough space for ((quality + 1) * 5 + 1) * 2 vertices 
+		 *			  Enough space for (((quality + 1) * 5 - 1) * 6) indices
 		 *
 		 *			Primitives are output in the form of a triangle list.
 		 */

+ 25 - 7
BansheeEngine/Source/BsShapeMeshes3D.cpp

@@ -14,6 +14,9 @@
 #include "BsBuiltinResources.h"
 #include "BsVertexDataDesc.h"
 
+// DEBUG ONLY
+#include "BsDebug.h"
+
 namespace BansheeEngine
 {
 	const UINT32 ShapeMeshes3D::NUM_VERTICES_AA_LINE = 8;
@@ -266,8 +269,8 @@ namespace BansheeEngine
 
 	void ShapeMeshes3D::getNumElementsArc(UINT32 quality, UINT32& numVertices, UINT32& numIndices)
 	{
-		numVertices = (quality + 1) * 5 + 1;
-		numIndices = ((quality + 1) * 5 - 1) * 3;
+		numVertices = ((quality + 1) * 5 + 1) * 2;
+		numIndices = ((quality + 1) * 5 - 1) * 6;
 	}
 
 	void ShapeMeshes3D::getNumElementsWireArc(UINT32 quality, UINT32& numVertices, UINT32& numIndices)
@@ -505,18 +508,35 @@ namespace BansheeEngine
 
 		UINT32 totalNumVertices = numArcVertices + 1;
 		outNormals += vertexOffset * vertexStride;
+		outVertices += vertexOffset * vertexStride;
+
+		UINT8* otherSideVertices = outVertices + (totalNumVertices * vertexStride);
+		UINT8* otherSideNormals = outNormals + (totalNumVertices * vertexStride);
 		for (UINT32 i = 0; i < totalNumVertices; i++)
 		{
+			otherSideVertices = writeVector3(otherSideVertices, vertexStride, *(Vector3*)outVertices);
+			outVertices += vertexStride;
+
 			outNormals = writeVector3(outNormals, vertexStride, normal);
+			otherSideNormals = writeVector3(otherSideNormals, vertexStride, -normal);
 		}
 
 		outIndices += indexOffset;
 		UINT32 numTriangles = numArcVertices - 1;
+
+		// If angle is negative the order of vertices is reversed so we need to reverse the indexes too
+		UINT32 frontSideOffset = vertexOffset + (amountAngle.valueDegrees() < 0.0f ? totalNumVertices : 0);
+		UINT32 backSideOffset = vertexOffset + (amountAngle.valueDegrees() >= 0.0f ? totalNumVertices : 0);
+
 		for (UINT32 i = 0; i < numTriangles; i++)
 		{
-			outIndices[i * 3 + 0] = vertexOffset + 0;
-			outIndices[i * 3 + 1] = vertexOffset + i;
-			outIndices[i * 3 + 2] = vertexOffset + i + 1;
+			outIndices[i * 6 + 0] = frontSideOffset + 0;
+			outIndices[i * 6 + 1] = frontSideOffset + i;
+			outIndices[i * 6 + 2] = frontSideOffset + i + 1;
+
+			outIndices[i * 6 + 3] = backSideOffset + 0;
+			outIndices[i * 6 + 4] = backSideOffset + i + 1;
+			outIndices[i * 6 + 5] = backSideOffset + i;
 		}
 	}
 
@@ -947,8 +967,6 @@ namespace BansheeEngine
 	{
 		assert(numVertices >= 2);
 
-		startAngle += Degree(90.0f); // Offset so arc starts the same as trig functions
-
 		Vector3 normalizedUp = Vector3::normalize(up);
 		Vector3 right;
 

+ 1 - 1
BansheeGLRenderSystem/Source/BsGLRenderAPI.cpp

@@ -301,7 +301,7 @@ namespace BansheeEngine
 				if (uniformBufferData == nullptr && paramBlockBuffer->getSize() > 0)
 				{
 					uniformBufferData = (UINT8*)bs_alloc<ScratchAlloc>(paramBlockBuffer->getSize());
-					paramBlockBuffer->readFromGPU(uniformBufferData);
+					paramBlockBuffer->readFromGPU(uniformBufferData); // TODO - Don't read from GPU!? Just read the cached version
 				}
 
 				continue;

+ 4 - 0
BansheeRenderer/Source/BsBansheeRenderer.cpp

@@ -28,6 +28,7 @@
 #include "BsTime.h"
 #include "BsRenderableElement.h"
 #include "BsFrameAlloc.h"
+#include "BsCoreObjectManager.h"
 
 using namespace std::placeholders;
 
@@ -195,6 +196,9 @@ namespace BansheeEngine
 			gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::addToRenderQueue, this, camera->getCore(), renderQueue));
 		}
 
+		// Sync all dirty sim thread CoreObject data to core thread
+		CoreObjectManager::instance().syncToCore(gCoreAccessor());
+
 		gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::renderAllCore, this, gTime().getTime()));
 	}
 

+ 121 - 142
BansheeUtility/Include/BsMath.h

@@ -361,18 +361,14 @@ namespace BansheeEngine
 		template <typename T>
 		static UINT32 solveLinear(T A, T B, T* roots)
 		{
-			if (!approxEquals(B, (T)0))
+			if (!approxEquals(A, (T)0))
 			{
-				roots[0] = -A / B;
-				return 1;
-			}
-			else if (approxEquals(A, (T)0))
-			{
-				roots[0] = 0.0f;
+				roots[0] = -B / A;
 				return 1;
 			}
 
-			return 0;
+			roots[0] = 0.0f;
+			return 1;
 		}
 
 		/**
@@ -387,32 +383,34 @@ namespace BansheeEngine
 		template <typename T>
 		static UINT32 solveQuadratic(T A, T B, T C, T* roots)
 		{
-			if (!approxEquals(C, (T)0))
+			if (!approxEquals(A, (T)0))
 			{
-				T discr = B * B - 4 * A * C;
-				if (discr > std::numeric_limits<T>::epsilon())
-				{
-					float temp = ((T)0.5) / C;
-					discr = std::sqrt(discr);
+				T p = B / (2 * A);
+				T q = C / A;
+				T D = p * p - q;
 
-					roots[0] = temp * (-B - discr);
-					roots[1] = temp * (-B + discr);
+				if (!approxEquals(D, (T)0))
+				{
+					if (D < (T)0)
+						return 0;
+					
+					T sqrtD = sqrt(D);
+					roots[0] = sqrtD - p;
+					roots[1] = -sqrtD - p;
 
 					return 2;
 				}
-				else if (discr < -std::numeric_limits<T>::epsilon())
-				{
-					return 0;
-				}
 				else
 				{
-					roots[0] = ((T)-0.5) * (B / C);
+					roots[0] = -p;
+					roots[1] = -p;
+
 					return 1;
 				}
 			}
 			else
 			{
-				return solveLinear(A, B, roots);
+				return solveLinear(B, C, roots);
 			}
 		}
 
@@ -430,70 +428,67 @@ namespace BansheeEngine
 		{
 			static const T THIRD = (1 / (T)3);
 
-			if (!approxEquals(D, (T)0))
-			{
-				T invD = 1 / D;
-				T k0 = A * invD;
-				T k1 = B * invD;
-				T k2 = C * invD;
-
-				T offset = THIRD * k2;
-				T a = k1 - k2 * offset;
-				T b = k0 + k2 * (2 * k2 * k2 - 9 * k1) * (1 / (T)27);
-				T halfB = ((T)0.5) * b;
-
-				T discr = halfB * halfB + a * a * a * (1 / (T)27);
-				if (discr > std::numeric_limits<T>::epsilon())
-				{
-					discr = std::sqrt(discr);
-					T temp = -halfB + discr;
-					if (temp >= (T)0)
-						roots[0] = pow(temp, THIRD);
-					else
-						roots[0] = -pow(-temp, THIRD);
+			T invA = 1 / A;
+			A = B * invA;
+			B = C * invA;
+			C = D * invA;
 
-					temp = -halfB - discr;
-					if (temp >= 0)
-						roots[0] += pow(temp, THIRD);
-					else
-						roots[0] -= -pow(-temp, THIRD);
+			T sqA = A * A;
+			T p = THIRD * (-THIRD * sqA + B);
+			T q = ((T)0.5) * ((2 / (T)27) * A * sqA - THIRD * A * B + C);
 
-					roots[0] -= offset;
-					return 1;
-				}
-				else if (discr < -std::numeric_limits<T>::epsilon())
+			T cbp = p * p * p;
+			D = q * q + cbp;
+
+			UINT32 numRoots = 0;
+			if (!approxEquals(D, (T)0))
+			{
+				if (D < 0.0)
 				{
-					T sqrtThree = std::sqrt((T)3);
-					T dist = sqrt(-THIRD * a);
-					T angle = THIRD * atan2(std::sqrt(-discr), -halfB).valueRadians();
-					T angleCos = cos(angle);
-					T angleSin = sin(angle);
+					T phi = THIRD * ::acos(-q / sqrt(-cbp));
+					T t = 2 * sqrt(-p);
 
-					roots[0] = 2 * dist * angleCos - offset;
-					roots[1] = -dist * (angleCos + sqrtThree * angleSin) - offset;
-					roots[2] = -dist * (angleCos - sqrtThree * angleSin) - offset;
+					roots[0] = t * cos(phi);
+					roots[1] = -t * cos(phi + PI * THIRD);
+					roots[2] = -t * cos(phi - PI * THIRD);
 
-					return 3;
+					numRoots = 3;
 				}
 				else
 				{
-					T temp;
-					if (halfB >= (T)0)
-						temp = -pow(halfB, THIRD);
-					else
-						temp = pow(-halfB, THIRD);
+					T sqrtD = sqrt(D);
+					T u = cbrt(sqrtD + fabs(q));
 
-					roots[0] = 2 * temp - offset;
-					roots[1] = -temp - offset;
-					roots[2] = roots[1];
+					if (q > (T)0)
+						roots[0] = -u + p / u;
+					else
+						roots[0] = u - p / u;
 
-					return 3;
+					numRoots = 1;
 				}
 			}
 			else
 			{
-				return solveQuadratic(A, B, C, roots);
+				if (!approxEquals(q, (T)0))
+				{
+					T u = cbrt(-q);
+					roots[0] = 2 * u;
+					roots[1] = -u;
+
+					numRoots = 2;
+				}
+				else
+				{
+					roots[0] = 0.0f;
+					numRoots = 1;
+				}
 			}
+
+			T sub = THIRD * A;
+			for (UINT32 i = 0; i < numRoots; i++)
+				roots[i] -= sub;
+
+			return numRoots;
 		}
 
 		/**
@@ -508,84 +503,68 @@ namespace BansheeEngine
 		template <typename T>
 		static UINT32 solveQuartic(T A, T B, T C, T D, T E, T* roots)
 		{
-			if (!approxEquals(E, (T)0))
+			T invA = 1 / A;
+			A = B * invA;
+			B = C * invA;
+			C = D * invA;
+			D = E * invA;
+
+			T sqA = A*A;
+			T p = -(3 / (T)8) * sqA + B;
+			T q = (1 / (T)8) * sqA * A - (T)0.5 * A * B + C;
+			T r = -(3 / (T)256) * sqA * sqA + (1 / (T)16) * sqA * B - (1 / (T)4) * A * C + D;
+
+			UINT32 numRoots = 0;
+			if (!approxEquals(r, (T)0))
 			{
-				T invE = 1 / E;
-				T k0 = A * invE;
-				T k1 = B * invE;
-				T k2 = C * invE;
-				T k3 = D * invE;
-
-				T r0 = k0 * (4 * k2 - k3 * k3) - k1 * k1;
-				T r1 = k3 * k1 - 4 * k0;
-				T r2 = -k2;
-				solveCubic(r0, r1, r2, (T)1, roots);
-				T y = roots[0];
-
-				UINT32 numRoots = 0;
-				T discr = ((T)0.25) * k3 * k3 - k2 + y;
-				if (discr > std::numeric_limits<T>::epsilon())
-				{
-					T r = sqrt(discr);
-					T t1 = ((T)0.75) * k3 * k3 - r * r - 2*k2;
-					T t2 = (k3 * k2 - 2 * k1 - ((T)0.25) * k3 * k3 * k3) / r;
-
-					T tPlus = t1 + t2;
-					if (tPlus >= ((T)0))
-					{
-						T d = std::sqrt(tPlus);
-						roots[0] = ((T)-0.25) * k3 + ((T)0.5) * (r + d);
-						roots[1] = ((T)-0.25) * k3 + ((T)0.5) * (r - d);
-
-						numRoots += 2;
-					}
-
-					T tMinus = t1 - t2;
-					if (tMinus >= ((T)0))
-					{
-						T e = std::sqrt(tMinus);
-						roots[numRoots++] = ((T)-0.25) * k3 + ((T)0.5) * (e - r);
-						roots[numRoots++] = ((T)-0.25) * k3 - ((T)0.5) * (e + r);
-					}
-				}
-				else if (discr < -std::numeric_limits<T>::epsilon())
-				{
-					numRoots = 0;
-				}
+				T cubicA = 1;
+				T cubicB = -(T)0.5 * p ;
+				T cubicC = -r;
+				T cubicD = (T)0.5 * r * p - (1 / (T)8) * q * q;
+
+				solveCubic(cubicA, cubicB, cubicC, cubicD, roots);
+				T z = roots[0];
+
+				T u = z * z - r;
+				T v = 2 * z - p;
+
+				if (approxEquals(u, T(0)))
+					u = 0;
+				else if (u > 0)
+					u = sqrt(u);
 				else
-				{
-					T t2 = y * y - 4 * k0;
-					if (t2 >= ((T)0))
-					{
-						t2 = 2 * std::sqrt(t2);
-						T t1 = ((T)0.75) * k3 * k3 - 2 * k2;
-
-						T tPlus = t1 + t2;
-						if (tPlus >= ((T)0))
-						{
-							T d = std::sqrt(tPlus);
-							roots[0] = ((T)-0.25) * k3 + ((T)0.5) * d;
-							roots[1] = ((T)-0.25) * k3 + ((T)0.5) * d;
-
-							numRoots += 2;
-						}
-
-						T tMinus = t1 - t2;
-						if (tMinus >= ((T)0))
-						{
-							T e = std::sqrt(tMinus);
-							roots[numRoots++] = ((T)-0.25) * k3 + ((T)0.5) * e;
-							roots[numRoots++] = ((T)-0.25) * k3 - ((T)0.5) * e;
-						}
-					}
-				}
+					return 0;
+
+				if (approxEquals(v, T(0)))
+					v = 0;
+				else if (v > 0)
+					v = sqrt(v);
+				else
+					return 0;
 
-				return numRoots;
+				T quadraticA = 1;
+				T quadraticB = q < 0 ? -v : v;
+				T quadraticC = z - u;
+
+				numRoots = solveQuadratic(quadraticA, quadraticB, quadraticC, roots);
+
+				quadraticA = 1;
+				quadraticB = q < 0 ? v : -v;
+				quadraticC = z + u;
+
+				numRoots += solveQuadratic(quadraticA, quadraticB, quadraticC, roots + numRoots);
 			}
 			else
 			{
-				return solveCubic(A, B, C, D, roots);
+				numRoots = solveCubic(q, p, (T)0, (T)1, roots);
+				roots[numRoots++] = 0;
 			}
+
+			T sub = (1/(T)4) * A;
+			for (UINT32 i = 0; i < numRoots; i++)
+				roots[i] -= sub;
+
+			return numRoots;
 		}
 
         static const float POS_INFINITY;

+ 2 - 1
BansheeUtility/Include/BsTorus.h

@@ -14,13 +14,14 @@ namespace BansheeEngine
 	{
 	public:
 		Torus();
-		Torus(float outerRadius, float innerRadius);
+		Torus(const Vector3& normal, float outerRadius, float innerRadius);
 
 		/**
 		 * @brief	Ray/torus intersection, returns boolean result and distance to nearest intersection point.
 		 */
 		std::pair<bool, float> intersects(const Ray& ray) const;
 
+		Vector3 normal;
 		float outerRadius;
 		float innerRadius;
 	};

+ 0 - 3
BansheeUtility/Source/BsAABox.cpp

@@ -36,9 +36,6 @@ namespace BansheeEngine
 
 	void AABox::setExtents(const Vector3& min, const Vector3& max)
 	{
-        assert( (min.x <= max.x && min.y <= max.y && min.z <= max.z) &&
-            "The minimum corner of the box must be less than or equal to maximum corner" );
-
 		mMinimum = min;
 		mMaximum = max;
 	}

+ 16 - 14
BansheeUtility/Source/BsTorus.cpp

@@ -1,6 +1,7 @@
 #include "BsTorus.h"
 #include "BsRay.h"
 #include "BsMath.h"
+#include "BsDebug.h"
 
 namespace BansheeEngine
 {
@@ -8,8 +9,8 @@ namespace BansheeEngine
 		:outerRadius(0.0f), innerRadius(0.0f)
 	{ }
 
-	Torus::Torus(float outerRadius, float innerRadius)
-		:outerRadius(outerRadius), innerRadius(innerRadius)
+	Torus::Torus(const Vector3& normal, float outerRadius, float innerRadius)
+		:normal(normal), outerRadius(outerRadius), innerRadius(innerRadius)
 	{ }
 
 	std::pair<bool, float> Torus::intersects(const Ray& ray) const
@@ -17,18 +18,19 @@ namespace BansheeEngine
 		const Vector3& org = ray.getOrigin();
 		const Vector3& dir = ray.getDirection();
 
-		float a = org.dot(dir);
-		float b = org.dot(org);
+		float u = normal.dot(org);
+		float v = normal.dot(dir);
 
-		float outerSqrd = outerRadius*outerRadius;
-		float innerSqrd = innerRadius*innerRadius;
-		float K = a - innerSqrd - outerSqrd;
+		float a = dir.dot(dir) - v * v;
+		float b = 2 * (org.dot(dir) - u * v);
+		float c = org.dot(org) - u * u;
+		float d = org.dot(org) + outerRadius*outerRadius - innerRadius*innerRadius;
 
-		float E = 1.0f;
-		float D = 4 * b;
-		float C = 2 * (2 * b*b + K + 2 * outerSqrd*dir.z*dir.z);
-		float B = 4 * (K*b + 2 * outerSqrd*org.z*dir.z);
-		float A = K*K + 4 * outerSqrd*(org.z*org.z - innerSqrd);
+		float A = 1.0f;
+		float B = 4 * org.dot(dir);
+		float C = 2 * d + 0.25f * B * B - 4 * outerRadius * outerRadius * a;
+		float D = B * d - 4 * outerRadius * outerRadius * b;
+		float E = d * d - 4 * outerRadius * outerRadius * c;
 
 		float roots[4];
 		UINT32 numRoots = Math::solveQuartic(A, B, C, D, E, roots);
@@ -41,7 +43,7 @@ namespace BansheeEngine
 			{
 				float t = roots[i];
 				float x = org.x + t*dir.x;
-				float y = org.x + t*dir.y;
+				float y = org.y + t*dir.y;
 				float l = outerRadius*(Math::PI / 2 - Math::atan2(y, x).valueRadians());
 
 				if (l >= 0 && t < nearestT)
@@ -51,6 +53,6 @@ namespace BansheeEngine
 			return std::make_pair(true, nearestT);
 		}
 
-		return std::make_pair(false, 0.0f);
+		return std::make_pair(false, 0.0f); 
 	}
 }

+ 8 - 8
MBansheeEditor/DbgGizmo.cs

@@ -20,15 +20,15 @@ namespace BansheeEditor
                 iconTexture = new SpriteTexture(iconTex);
             }
 
-            Gizmos.DrawCube(target.sceneObject.position, new Vector3(1, 1, 1));
-            Gizmos.DrawSphere(target.sceneObject.position + 2 * Vector3.xAxis, 1.0f);
-            Gizmos.DrawWireCube(target.sceneObject.position + 4 * Vector3.xAxis, new Vector3(1, 1, 1));
-            Gizmos.DrawWireSphere(target.sceneObject.position + 6 * Vector3.xAxis, 1.0f);
-            Gizmos.DrawLine(target.sceneObject.position + 7.5f * Vector3.xAxis,
-                target.sceneObject.position + 8.5f * Vector3.xAxis);
-            Gizmos.DrawFrustum(target.sceneObject.position + 10 * Vector3.xAxis, 1920.0f / 1080.0f, 90, 1.0f, 1000.0f);
+            Gizmos.DrawCube(target.sceneObject.Position, new Vector3(1, 1, 1));
+            Gizmos.DrawSphere(target.sceneObject.Position + 2 * Vector3.xAxis, 1.0f);
+            Gizmos.DrawWireCube(target.sceneObject.Position + 4 * Vector3.xAxis, new Vector3(1, 1, 1));
+            Gizmos.DrawWireSphere(target.sceneObject.Position + 6 * Vector3.xAxis, 1.0f);
+            Gizmos.DrawLine(target.sceneObject.Position + 7.5f * Vector3.xAxis,
+                target.sceneObject.Position + 8.5f * Vector3.xAxis);
+            Gizmos.DrawFrustum(target.sceneObject.Position + 10 * Vector3.xAxis, 1920.0f / 1080.0f, 90, 1.0f, 1000.0f);
 
-            Gizmos.DrawIcon(target.sceneObject.position + new Vector3(0, 10, 0), iconTexture, false);
+            Gizmos.DrawIcon(target.sceneObject.Position + new Vector3(0, 10, 0), iconTexture, false);
         }
     }
 }

+ 3 - 3
MBansheeEditor/DebugCameraHandle.cs

@@ -18,7 +18,7 @@ namespace BansheeEditor
 
         protected override void PreInput()
         {
-            xAxis.Position = target.sceneObject.position;
+            xAxis.Position = target.sceneObject.Position;
         }
 
         protected override void PostInput()
@@ -28,14 +28,14 @@ namespace BansheeEditor
 
         protected override void Draw()
         {
-            Vector3 end = target.sceneObject.position + Vector3.xAxis * 5;
+            Vector3 end = target.sceneObject.Position + Vector3.xAxis * 5;
 
             if (xAxis.State == HandleSlider.StateType.Active)
                 HandleDrawing.SetColor(Color.white);
             else
                 HandleDrawing.SetColor(Color.green);
 
-            HandleDrawing.DrawLine(target.sceneObject.position, end);
+            HandleDrawing.DrawLine(target.sceneObject.Position, end);
         }
     }
 }

+ 9 - 13
MBansheeEditor/EditorApplication.cs

@@ -12,7 +12,7 @@ namespace BansheeEditor
         Scale
     }
 
-    public enum HandlePositionMode
+    public enum HandlePivotMode
     {
         Center,
         Pivot
@@ -26,26 +26,22 @@ namespace BansheeEditor
 
     public class EditorApplication
     {
-        private static SceneViewTool activeSceneTool = SceneViewTool.Move; // TODO - Actually retrieve this from somewhere
-        private static HandlePositionMode handlePositionMode = HandlePositionMode.Pivot; // TODO - Actually retrieve this from somewhere
-        private static HandleCoordinateMode handleCoordinateMode = HandleCoordinateMode.World; // TODO - Actually retrieve this from somewhere
-
         public static SceneViewTool ActiveSceneTool
         {
-            get { return activeSceneTool; }
-            set { activeSceneTool = value; } // TODO - Will likely need to update active GUI button when this changes
+            get { return EditorSettings.ActiveSceneTool; }
+            set { EditorSettings.ActiveSceneTool = value; }
         }
 
-        public static HandlePositionMode HandlePositionMode
+        public static HandleCoordinateMode ActiveCoordinateMode
         {
-            get { return handlePositionMode; }
-            set { handlePositionMode = value; } // TODO - Will likely need to update active GUI button when this changes
+            get { return EditorSettings.ActiveCoordinateMode; }
+            set { EditorSettings.ActiveCoordinateMode = value; }
         }
 
-        public static HandleCoordinateMode HandleCoordinateMode
+        public static HandlePivotMode ActivePivotMode
         {
-            get { return handleCoordinateMode; }
-            set { handleCoordinateMode = value; } // TODO - Will likely need to update active GUI button when this changes
+            get { return EditorSettings.ActivePivotMode; }
+            set { EditorSettings.ActivePivotMode = value; }
         }
 
         public static Camera SceneViewCamera

+ 43 - 24
MBansheeEditor/EditorSettings.cs

@@ -8,7 +8,7 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
-    public static class EditorSettings
+    internal static class EditorSettings
     {
         public static bool MoveHandleSnapActive
         {
@@ -22,28 +22,16 @@ namespace BansheeEditor
             set { Internal_SetRotateHandleSnapActive(value); }
         }
 
-        public static bool ScaleHandleSnapActive
-        {
-            get { return Internal_GetScaleHandleSnapActive(); }
-            set { Internal_SetScaleHandleSnapActive(value); }
-        }
-
         public static float MoveHandleSnapAmount
         {
             get { return Internal_GetMoveHandleSnapAmount(); }
             set { Internal_SetMoveHandleSnapAmount(value); }
         }
 
-        public static float RotateHandleSnapAmount
+        public static Degree RotateHandleSnapAmount
         {
             get { return Internal_GetRotateHandleSnapAmount(); }
-            set { Internal_SetRotateHandleSnapAmount(value); }
-        }
-
-        public static float ScaleHandleSnapAmount
-        {
-            get { return Internal_GetScaleHandleSnapAmount(); }
-            set { Internal_SetScaleHandleSnapAmount(value); }
+            set { Internal_SetRotateHandleSnapAmount(value.GetDegrees()); }
         }
 
         public static float DefaultHandleSize
@@ -52,6 +40,29 @@ namespace BansheeEditor
             set { Internal_SetDefaultHandleSize(value); }
         }
 
+        public static SceneViewTool ActiveSceneTool
+        {
+            get { return (SceneViewTool)Internal_GetActiveSceneTool(); }
+            set { Internal_SetActiveSceneTool((int)value); }
+        }
+
+        public static HandleCoordinateMode ActiveCoordinateMode
+        {
+            get { return (HandleCoordinateMode)Internal_GetActiveCoordinateMode(); }
+            set { Internal_SetActiveCoordinateMode((int)value); }
+        }
+
+        public static HandlePivotMode ActivePivotMode
+        {
+            get { return (HandlePivotMode)Internal_GetActivePivotMode(); }
+            set { Internal_SetActivePivotMode((int)value); }
+        }
+
+        public static int Hash
+        {
+            get { return Internal_GetHash(); }
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_GetMoveHandleSnapActive();
         [MethodImpl(MethodImplOptions.InternalCall)]
@@ -62,11 +73,6 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetRotateHandleSnapActive(bool value);
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetScaleHandleSnapActive();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetScaleHandleSnapActive(bool value);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern float Internal_GetMoveHandleSnapAmount();
         [MethodImpl(MethodImplOptions.InternalCall)]
@@ -78,13 +84,26 @@ namespace BansheeEditor
         private static extern void Internal_SetRotateHandleSnapAmount(float value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetScaleHandleSnapAmount();
+        private static extern float Internal_GetDefaultHandleSize();
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetScaleHandleSnapAmount(float value);
+        private static extern void Internal_SetDefaultHandleSize(float value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern float Internal_GetDefaultHandleSize();
+        private static extern int Internal_GetActiveSceneTool();
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDefaultHandleSize(float value);
+        private static extern void Internal_SetActiveSceneTool(int value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetActiveCoordinateMode();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetActiveCoordinateMode(int value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetActivePivotMode();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetActivePivotMode(int value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetHash();
     }
 }

+ 14 - 12
MBansheeEditor/Scene/DefaultHandleManager.cs

@@ -10,11 +10,15 @@ namespace BansheeEditor
             public HandledObject(SceneObject so)
             {
                 this.so = so;
-                initialPosition = so.position;
+                initialPosition = so.Position;
+                initialRotation = so.Rotation;
+                initialScale = so.LocalScale;
             }
 
             public SceneObject so;
             public Vector3 initialPosition;
+            public Quaternion initialRotation;
+            public Vector3 initialScale;
         }
 
         private SceneViewTool activeHandleType = SceneViewTool.View;
@@ -65,14 +69,14 @@ namespace BansheeEditor
             if (activeHandle != null)
             {
                 Quaternion rotation;
-                if (EditorApplication.HandleCoordinateMode == HandleCoordinateMode.World)
+                if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
                     rotation = Quaternion.identity;
                 else
-                    rotation = selectedSceneObjects[0].rotation; // We don't average rotation in case of multi-selection
+                    rotation = selectedSceneObjects[0].Rotation; // We don't average rotation in case of multi-selection
 
                 Vector3 position;
-                if (EditorApplication.HandlePositionMode == HandlePositionMode.Pivot)
-                    position = selectedSceneObjects[0].position; // Just take pivot from the first one, no averaging
+                if (EditorApplication.ActivePivotMode == HandlePivotMode.Pivot)
+                    position = selectedSceneObjects[0].Position; // Just take pivot from the first one, no averaging
                 else
                 {
                     List<SceneObject> flatenedHierarchy = new List<SceneObject>();
@@ -125,7 +129,7 @@ namespace BansheeEditor
                             MoveHandle moveHandle = (MoveHandle) activeHandle;
 
                             foreach (var selectedObj in activeSelection)
-                                selectedObj.so.position = selectedObj.initialPosition + moveHandle.Delta;
+                                selectedObj.so.Position = selectedObj.initialPosition + moveHandle.Delta;
                         }
 
                             break;
@@ -133,18 +137,16 @@ namespace BansheeEditor
                         {
                             RotateHandle rotateHandle = (RotateHandle) activeHandle;
 
-                            // TODO - Add delta rotation
-                            //foreach (var so in selectedSceneObjects)
-                            //    so.rotation += rotateHandle.Delta;
+                            foreach (var selectedObj in activeSelection)
+                                selectedObj.so.Rotation = selectedObj.initialRotation * rotateHandle.Delta;
                         }
                             break;
                         case SceneViewTool.Scale:
                         {
                             ScaleHandle scaleHandle = (ScaleHandle) activeHandle;
 
-                            // TODO - Add delta scale
-                            //foreach (var so in selectedSceneObjects)
-                            //    so.localScale += scaleHandle.Delta;
+                            foreach (var selectedObj in activeSelection)
+                                selectedObj.so.LocalScale = selectedObj.initialScale + scaleHandle.Delta;
                         }
                             break;
                     }

+ 15 - 2
MBansheeEditor/Scene/HandleSliderDisc.cs

@@ -6,13 +6,13 @@ namespace BansheeEditor
 {
     public sealed class HandleSliderDisc : HandleSlider
     {
-        public HandleSliderDisc(Handle parentHandle, Vector3 normal, float radius, bool fixedScale = true, float snapValue = 0.0f)
+        public HandleSliderDisc(Handle parentHandle, Vector3 normal, float radius, bool fixedScale = true)
             :base(parentHandle)
         {
             Internal_CreateInstance(this, normal, radius, fixedScale);
         }
 
-        public float Delta
+        public Degree Delta
         {
             get
             {
@@ -22,10 +22,23 @@ namespace BansheeEditor
             }
         }
 
+        public Degree StartAngle
+        {
+            get
+            {
+                float value;
+                Internal_GetStartAngle(mCachedPtr, out value);
+                return value;
+            }
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(HandleSliderDisc instance, Vector3 normal, float radius, bool fixedScale);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_GetDelta(IntPtr nativeInstance, out float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetStartAngle(IntPtr nativeInstance, out float value);
     }
 }

+ 8 - 15
MBansheeEditor/Scene/Handles.cs

@@ -17,12 +17,6 @@ namespace BansheeEditor
             set { EditorSettings.RotateHandleSnapActive = value; }
         }
 
-        public static bool ScaleHandleSnapActive
-        {
-            get { return EditorSettings.ScaleHandleSnapActive; }
-            set { EditorSettings.ScaleHandleSnapActive = value; }
-        }
-
         public static float MoveSnapAmount
         {
             get { return EditorSettings.MoveHandleSnapAmount; }
@@ -32,13 +26,7 @@ namespace BansheeEditor
         public static Degree RotateSnapAmount
         {
             get { return EditorSettings.RotateHandleSnapAmount; }
-            set { EditorSettings.RotateHandleSnapAmount = value.GetDegrees(); }
-        }
-
-        public static float ScaleSnapAmount
-        {
-            get { return EditorSettings.ScaleHandleSnapAmount; }
-            set { EditorSettings.MoveHandleSnapAmount = value; }
+            set { EditorSettings.RotateHandleSnapAmount = value; }
         }
 
         public static float SnapValue(float value, float snapAmount)
@@ -49,12 +37,17 @@ namespace BansheeEditor
             return value;
         }
 
+        public static Degree SnapValue(Degree value, Degree snapAmount)
+        {
+            return SnapValue(value.GetDegrees(), snapAmount.GetDegrees());
+        }
+
         public static float GetHandleSize(Camera camera, Vector3 position)
         {
-            Vector3 cameraPos = camera.sceneObject.position;
+            Vector3 cameraPos = camera.sceneObject.Position;
 
 		    Vector3 diff = position - cameraPos;
-		    float distAlongViewDir = Math.Abs(Vector3.Dot(diff, camera.sceneObject.rotation.Forward));
+		    float distAlongViewDir = Math.Abs(Vector3.Dot(diff, camera.sceneObject.Rotation.Forward));
 
             return distAlongViewDir * EditorSettings.DefaultHandleSize;
         }

+ 132 - 3
MBansheeEditor/Scene/RotateHandle.cs

@@ -4,29 +4,158 @@ namespace BansheeEditor
 {
     public sealed class RotateHandle : DefaultHandle
     {
-        public RotateHandle()
-        {
+        private Quaternion delta;
+
+        private HandleSliderDisc xAxis;
+        private HandleSliderDisc yAxis;
+        private HandleSliderDisc zAxis;
 
+        public Quaternion Delta
+        {
+            get { return delta; }
         }
 
         internal override bool IsDragged()
         {
-            return false;
+            return xAxis.State == HandleSlider.StateType.Active;
+            //return xAxis.State == HandleSlider.StateType.Active ||
+            //        yAxis.State == HandleSlider.StateType.Active ||
+            //        zAxis.State == HandleSlider.StateType.Active;
+        }
+
+        public RotateHandle()
+        {
+            xAxis = new HandleSliderDisc(this, Vector3.xAxis, 1.0f);
+            //yAxis = new HandleSliderDisc(this, Vector3.yAxis, 1.0f);
+            //zAxis = new HandleSliderDisc(this, Vector3.zAxis, 1.0f);
         }
 
         protected override void PreInput()
         {
+            xAxis.Position = position;
+           // yAxis.Position = position;
+            //zAxis.Position = position;
 
+            xAxis.Rotation = rotation;
+            //yAxis.Rotation = rotation;
+            //zAxis.Rotation = rotation;
         }
 
         protected override void PostInput()
         {
+            delta = Quaternion.identity;
+
+            Degree xValue = 0.0f;
+            Degree yValue = 0.0f;
+            Degree zValue = 0.0f;
+
+            if (Handles.RotateHandleSnapActive)
+            {
+                xValue = Handles.SnapValue(xAxis.Delta, Handles.RotateSnapAmount);
+                //yValue = Handles.SnapValue(yAxis.Delta, Handles.RotateSnapAmount);
+                //zValue = Handles.SnapValue(zAxis.Delta, Handles.RotateSnapAmount);
+            }
+            else
+            {
+                xValue = xAxis.Delta;
+                //yValue = yAxis.Delta;
+               // zValue = zAxis.Delta;
+            }
 
+            delta = Quaternion.FromAxisAngle(GetXDir(), xValue) * delta;
+           // delta = Quaternion.FromAxisAngle(GetYDir(), yValue) * delta;
+           // delta = Quaternion.FromAxisAngle(GetZDir(), zValue) * delta;
         }
 
         protected override void Draw()
         {
+            HandleDrawing.SetTransform(Matrix4.TRS(Position, Rotation, Vector3.one));
+            float handleSize = Handles.GetHandleSize(EditorApplication.SceneViewCamera, position);
+
+            // Draw arcs
+            Color axisHover = new Color(0.8f, 0.8f, 0.8f, 1.0f);
+
+            if (xAxis.State == HandleSlider.StateType.Active)
+                HandleDrawing.SetColor(Color.white);
+            else if(xAxis.State == HandleSlider.StateType.Hover)
+                HandleDrawing.SetColor(Color.red * axisHover);
+            else
+                HandleDrawing.SetColor(Color.red);
+
+            Vector3 xStartDir = Vector3.Cross(EditorApplication.SceneViewCamera.sceneObject.Forward, GetXDir());
+            Degree xStartAngle = PointOnCircleToAngle(GetXDir(), xStartDir);
+
+            Debug.Log("START ARC: " + xStartDir + " -- " + xStartAngle);
+
+            HandleDrawing.DrawWireArc(Vector3.zero, GetXDir(), 1.0f, xStartAngle, 180.0f, handleSize);
+
+            //if (yAxis.State == HandleSlider.StateType.Active)
+            //    HandleDrawing.SetColor(Color.white);
+            //else if (yAxis.State == HandleSlider.StateType.Hover)
+            //    HandleDrawing.SetColor(Color.green * axisHover);
+            //else
+            //    HandleDrawing.SetColor(Color.green);
+
+            //Vector3 yStartDir = Vector3.Cross(EditorApplication.SceneViewCamera.sceneObject.Forward, GetYDir());
+            //Degree yStartAngle = PointOnCircleToAngle(GetYDir(), yStartDir);
+
+            //HandleDrawing.DrawWireArc(Vector3.zero, GetYDir(), 1.0f, yStartAngle, 180.0f, handleSize);
+
+            //if (zAxis.State == HandleSlider.StateType.Active)
+            //    HandleDrawing.SetColor(Color.white);
+            //else if (zAxis.State == HandleSlider.StateType.Hover)
+            //    HandleDrawing.SetColor(Color.blue * axisHover);
+            //else
+            //    HandleDrawing.SetColor(Color.blue);
+
+            //Vector3 zStartDir = Vector3.Cross(EditorApplication.SceneViewCamera.sceneObject.Forward, -GetZDir());
+            //Degree zStartAngle = PointOnCircleToAngle(-GetZDir(), zStartDir);
+
+            //HandleDrawing.DrawWireArc(Vector3.zero, -GetZDir(), 1.0f, zStartAngle, 180.0f, handleSize);
+
+            // Draw active rotation pie
+            Color gray = new Color(1.0f, 1.0f, 1.0f, 0.3f);
+            HandleDrawing.SetColor(gray);
+
+            if (xAxis.State == HandleSlider.StateType.Active)
+                HandleDrawing.DrawArc(Vector3.zero, GetXDir(), 1.0f, xAxis.StartAngle, xAxis.Delta, handleSize);
+            //else if (yAxis.State == HandleSlider.StateType.Active)
+            //    HandleDrawing.DrawArc(Vector3.zero, GetYDir(), 1.0f, yAxis.StartAngle, yAxis.Delta, handleSize);
+            //else if (zAxis.State == HandleSlider.StateType.Active)
+            //    HandleDrawing.DrawArc(Vector3.zero, -GetZDir(), 1.0f, zAxis.StartAngle, zAxis.Delta, handleSize);
+
+            // TODO - Free rotate handle
+        }
+
+        private Vector3 GetXDir()
+        {
+             return rotation.Rotate(Vector3.xAxis);
+        }
+
+        private Vector3 GetYDir()
+        {
+            return rotation.Rotate(Vector3.yAxis);
+        }
+
+        private Vector3 GetZDir()
+        {
+            return rotation.Rotate(Vector3.zAxis);
+        }
+
+        private Degree PointOnCircleToAngle(Vector3 up, Vector3 point)
+        {
+            Vector3[] arcBasis = new Vector3[3];
+            arcBasis[1] = up;
+            Vector3.OrthogonalComplement(arcBasis[1], out arcBasis[2], out arcBasis[0]);
+
+            Matrix4 worldToPlane = Matrix4.identity;
+            worldToPlane.SetColumn(0, (Vector4)arcBasis[0]);
+            worldToPlane.SetColumn(1, (Vector4)arcBasis[1]);
+            worldToPlane.SetColumn(2, (Vector4)arcBasis[2]);
+
+            point = worldToPlane.Multiply(point);
 
+            return (MathEx.Atan2(-point.z, -point.x) + MathEx.Pi) * MathEx.Rad2Deg;
         }
     }
 }

+ 92 - 3
MBansheeEditor/Scene/ScaleHandle.cs

@@ -4,29 +4,118 @@ namespace BansheeEditor
 {
     public sealed class ScaleHandle : DefaultHandle
     {
-        public ScaleHandle()
-        {
+        private const float SMALL_CUBE_SIZE = 0.175f;
+        private const float CENTER_CUBE_SIZE = 0.33f;
+
+        private Vector3 delta;
+
+        private HandleSliderLine xAxis;
+        private HandleSliderLine yAxis;
+        private HandleSliderLine zAxis;
 
+        public Vector3 Delta
+        {
+            get { return delta; }
         }
 
         internal override bool IsDragged()
         {
-            return false;
+            return xAxis.State == HandleSlider.StateType.Active ||
+                    yAxis.State == HandleSlider.StateType.Active ||
+                    zAxis.State == HandleSlider.StateType.Active;
+        }
+
+        public ScaleHandle()
+        {
+            xAxis = new HandleSliderLine(this, Vector3.xAxis, 1.0f);
+            yAxis = new HandleSliderLine(this, Vector3.yAxis, 1.0f);
+            zAxis = new HandleSliderLine(this, Vector3.zAxis, 1.0f);
         }
 
         protected override void PreInput()
         {
+            xAxis.Position = position;
+            yAxis.Position = position;
+            zAxis.Position = position;
 
+            xAxis.Rotation = rotation;
+            yAxis.Rotation = rotation;
+            zAxis.Rotation = rotation;
         }
 
         protected override void PostInput()
         {
+            delta = Vector3.zero;
 
+            delta += xAxis.Delta * GetXDir();
+            delta += yAxis.Delta * GetYDir();
+            delta += zAxis.Delta * GetZDir();
         }
 
         protected override void Draw()
         {
+            HandleDrawing.SetTransform(Matrix4.TRS(Position, Rotation, Vector3.one));
+            float handleSize = Handles.GetHandleSize(EditorApplication.SceneViewCamera, position);
+
+            // Draw 1D sliders
+            Vector3 smallCubeExtents = new Vector3(SMALL_CUBE_SIZE*0.5f, SMALL_CUBE_SIZE*0.5f, SMALL_CUBE_SIZE*0.5f);
+            Color axisHover = new Color(0.8f, 0.8f, 0.8f, 1.0f);
+
+            if (xAxis.State == HandleSlider.StateType.Active)
+                HandleDrawing.SetColor(Color.white);
+            else if (xAxis.State == HandleSlider.StateType.Hover)
+                HandleDrawing.SetColor(Color.red * axisHover);
+            else
+                HandleDrawing.SetColor(Color.red);
+
+            Vector3 xCubeOffset = Vector3.xAxis * SMALL_CUBE_SIZE * 0.5f;
+            Vector3 xCubeStart = Vector3.xAxis - xCubeOffset;
+            
+            HandleDrawing.DrawLine(Vector3.zero, xCubeStart, handleSize);
+            HandleDrawing.DrawCube(xCubeStart + xCubeOffset, smallCubeExtents, handleSize);
+
+            if (yAxis.State == HandleSlider.StateType.Active)
+                HandleDrawing.SetColor(Color.white);
+            else if (yAxis.State == HandleSlider.StateType.Hover)
+                HandleDrawing.SetColor(Color.green * axisHover);
+            else
+                HandleDrawing.SetColor(Color.green);
+
+            Vector3 yCubeOffset = Vector3.yAxis * SMALL_CUBE_SIZE * 0.5f;
+            Vector3 yCubeStart = Vector3.yAxis - yCubeOffset;
 
+            HandleDrawing.DrawLine(Vector3.zero, yCubeStart, handleSize);
+            HandleDrawing.DrawCube(yCubeStart + yCubeOffset, smallCubeExtents, handleSize);
+
+            if (zAxis.State == HandleSlider.StateType.Active)
+                HandleDrawing.SetColor(Color.white);
+            else if (zAxis.State == HandleSlider.StateType.Hover)
+                HandleDrawing.SetColor(Color.blue * axisHover);
+            else
+                HandleDrawing.SetColor(Color.blue);
+
+            Vector3 zCubeOffset = Vector3.zAxis * SMALL_CUBE_SIZE * 0.5f;
+            Vector3 zCubeStart = Vector3.zAxis - zCubeOffset;
+
+            HandleDrawing.DrawLine(Vector3.zero, zCubeStart, handleSize);
+            HandleDrawing.DrawCube(zCubeStart + zCubeOffset, smallCubeExtents, handleSize);
+
+            // TODO - Draw free scale handle
+        }
+
+        private Vector3 GetXDir()
+        {
+            return rotation.Rotate(Vector3.xAxis);
+        }
+
+        private Vector3 GetYDir()
+        {
+            return rotation.Rotate(Vector3.yAxis);
+        }
+
+        private Vector3 GetZDir()
+        {
+            return rotation.Rotate(Vector3.zAxis);
         }
     }
 }

+ 5 - 5
MBansheeEditor/Scene/SceneCamera.cs

@@ -89,14 +89,14 @@ namespace BansheeEditor
                 Quaternion camRot = yRot * xRot;
                 camRot.Normalize();
 
-                sceneObject.rotation = camRot;
+                sceneObject.Rotation = camRot;
 		    }
 
             Vector3 direction = Vector3.zero;
-            if (goingForward) direction += sceneObject.forward;
-            if (goingBack) direction -= sceneObject.forward;
-            if (goingRight) direction += sceneObject.right;
-            if (goingLeft) direction -= sceneObject.right;
+            if (goingForward) direction += sceneObject.Forward;
+            if (goingBack) direction -= sceneObject.Forward;
+            if (goingRight) direction += sceneObject.Right;
+            if (goingLeft) direction -= sceneObject.Right;
 
             if (direction.sqrdMagnitude != 0)
             {

+ 159 - 6
MBansheeEditor/Scene/SceneWindow.cs

@@ -24,6 +24,20 @@ namespace BansheeEditor
         private GUIToggle rotateButton;
         private GUIToggle scaleButton;
 
+        private GUIToggle localCoordButton;
+        private GUIToggle worldCoordButton;
+
+        private GUIToggle pivotButton;
+        private GUIToggle centerButton;
+
+        private GUIToggle moveSnapButton;
+        private GUIFloatField moveSnapInput;
+
+        private GUIToggle rotateSnapButton;
+        private GUIFloatField rotateSnapInput;
+
+        private int editorSettingsHash = int.MaxValue;
+
         public Camera GetCamera()
         {
             return camera;
@@ -37,23 +51,59 @@ namespace BansheeEditor
             mainLayout = GUI.layout.AddLayoutY();
 
             GUIToggleGroup handlesTG = new GUIToggleGroup();
-
             viewButton = new GUIToggle("V", handlesTG, EditorStyles.Button);
             moveButton = new GUIToggle("M", handlesTG, EditorStyles.Button);
             rotateButton = new GUIToggle("R", handlesTG, EditorStyles.Button);
             scaleButton = new GUIToggle("S", handlesTG, EditorStyles.Button);
 
-            viewButton.OnClick += () => EditorApplication.ActiveSceneTool = SceneViewTool.View;
-            moveButton.OnClick += () => EditorApplication.ActiveSceneTool = SceneViewTool.Move;
-            rotateButton.OnClick += () => EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
-            scaleButton.OnClick += () => EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
+            GUIToggleGroup coordModeTG = new GUIToggleGroup();
+            localCoordButton = new GUIToggle("L", coordModeTG, EditorStyles.Button);
+            worldCoordButton = new GUIToggle("W", coordModeTG, EditorStyles.Button);
+
+            GUIToggleGroup pivotModeTG = new GUIToggleGroup();
+            pivotButton = new GUIToggle("P", pivotModeTG, EditorStyles.Button);
+            centerButton = new GUIToggle("C", pivotModeTG, EditorStyles.Button);
+
+            moveSnapButton = new GUIToggle("MS", EditorStyles.Button);
+            moveSnapInput = new GUIFloatField();
+
+            rotateSnapButton = new GUIToggle("RS", EditorStyles.Button);
+            rotateSnapInput = new GUIFloatField();
+
+            viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
+            moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
+            rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
+            scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
+
+            localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
+            worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
+
+            pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
+            centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
+
+            moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
+            moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
+
+            rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
+            rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
 
             GUILayout handlesLayout = mainLayout.AddLayoutX();
             handlesLayout.AddElement(viewButton);
             handlesLayout.AddElement(moveButton);
             handlesLayout.AddElement(rotateButton);
             handlesLayout.AddElement(scaleButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(localCoordButton);
+            handlesLayout.AddElement(worldCoordButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(pivotButton);
+            handlesLayout.AddElement(centerButton);
             handlesLayout.AddFlexibleSpace();
+            handlesLayout.AddElement(moveSnapButton);
+            handlesLayout.AddElement(moveSnapInput);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(rotateSnapButton);
+            handlesLayout.AddElement(rotateSnapInput);
 
             UpdateRenderTexture(Width, Height - HeaderHeight);
         }
@@ -86,6 +136,14 @@ namespace BansheeEditor
 
         private void EditorUpdate()
         {
+            // Refresh GUI buttons if needed (in case someones changes the values from script)
+            if (editorSettingsHash != EditorSettings.Hash)
+            {
+                UpdateButtonStates();
+                editorSettingsHash = EditorSettings.Hash;
+            }
+
+            // Update scene view handles and selection
             sceneViewHandler.Update();
 
             bool handleActive = false;
@@ -138,6 +196,101 @@ namespace BansheeEditor
             }
         }
 
+        private void OnSceneToolButtonClicked(SceneViewTool tool)
+        {
+            EditorApplication.ActiveSceneTool = tool;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
+        {
+            EditorApplication.ActiveCoordinateMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnPivotModeButtonClicked(HandlePivotMode mode)
+        {
+            EditorApplication.ActivePivotMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnMoveSnapToggled(bool active)
+        {
+            Handles.MoveHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnMoveSnapValueChanged(float value)
+        {
+            Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnRotateSnapToggled(bool active)
+        {
+            Handles.RotateHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void OnRotateSnapValueChanged(float value)
+        {
+            Handles.RotateSnapAmount = MathEx.Clamp(value, 0.01f, 360.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        private void UpdateButtonStates()
+        {
+            switch (EditorApplication.ActiveSceneTool)
+            {
+                case SceneViewTool.View:
+                    viewButton.ToggleOn();
+                    break;
+                case SceneViewTool.Move:
+                    moveButton.ToggleOn();
+                    break;
+                case SceneViewTool.Rotate:
+                    rotateButton.ToggleOn();
+                    break;
+                case SceneViewTool.Scale:
+                    scaleButton.ToggleOn();
+                    break;
+            }
+
+            switch (EditorApplication.ActiveCoordinateMode)
+            {
+                case HandleCoordinateMode.Local:
+                    localCoordButton.ToggleOn();
+                    break;
+                case HandleCoordinateMode.World:
+                    worldCoordButton.ToggleOn();
+                    break;
+            }
+
+            switch (EditorApplication.ActivePivotMode)
+            {
+                case HandlePivotMode.Center:
+                    centerButton.ToggleOn();
+                    break;
+                case HandlePivotMode.Pivot:
+                    pivotButton.ToggleOn();
+                    break;
+            }
+
+            if (Handles.MoveHandleSnapActive)
+                moveSnapButton.ToggleOn();
+            else
+                moveSnapButton.ToggleOff();
+
+            moveSnapInput.Value = Handles.MoveSnapAmount;
+
+            if (Handles.RotateHandleSnapActive)
+                rotateSnapButton.ToggleOn();
+            else
+                rotateSnapButton.ToggleOff();
+
+            moveSnapInput.Value = Handles.RotateSnapAmount.GetDegrees();
+        }
+
         private void UpdateRenderTexture(int width, int height)
 	    {
             width = MathEx.Max(20, width);
@@ -153,7 +306,7 @@ namespace BansheeEditor
                 camera.target = renderTexture;
                 camera.viewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
 
-                sceneCameraSO.position = new Vector3(0, 0.5f, 1);
+                sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
                 sceneCameraSO.LookAt(new Vector3(0, 0, 0));
 
                 camera.priority = 1;

+ 4 - 4
MBansheeEngine/GUI/GUIToggle.cs

@@ -52,12 +52,12 @@ namespace BansheeEngine
 
         public void ToggleOn()
         {
-            Internal_ToggleOn();
+            Internal_ToggleOn(mCachedPtr);
         }
 
         public void ToggleOff()
         {
-            Internal_ToggleOff();
+            Internal_ToggleOff(mCachedPtr);
         }
 
         private void DoOnClick()
@@ -92,9 +92,9 @@ namespace BansheeEngine
         private static extern void Internal_SetContent(IntPtr nativeInstance, GUIContent content);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ToggleOn();
+        private static extern void Internal_ToggleOn(IntPtr nativeInstance);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_ToggleOff();
+        private static extern void Internal_ToggleOff(IntPtr nativeInstance);
     }
 }

+ 8 - 0
MBansheeEngine/Math/Matrix4.cs

@@ -447,6 +447,14 @@ namespace BansheeEngine
                 m30 * v.x + m31 * v.y + m32 * v.z + m33 * v.w);
         }
 
+        public void SetColumn(int columnIdx, Vector4 column)
+        {
+            this[0, columnIdx] = column.x;
+            this[1, columnIdx] = column.y;
+            this[2, columnIdx] = column.z;
+            this[3, columnIdx] = column.w;
+        }
+
         public static Matrix4 TRS(Vector3 translation, Quaternion rotation, Vector3 scale)
         {
             Matrix3 rot3x3 = rotation.ToRotationMatrix();

+ 31 - 0
MBansheeEngine/Math/Vector3.cs

@@ -83,6 +83,11 @@ namespace BansheeEngine
             this.z = z;
         }
 
+        public static explicit operator Vector4(Vector3 vec)
+        {
+            return new Vector4(vec.x, vec.y, vec.z, 0.0f);
+        }
+
         public static Vector3 operator+ (Vector3 a, Vector3 b)
         {
             return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
@@ -200,5 +205,31 @@ namespace BansheeEngine
         {
             return "(" + x + ", " + y + ", " + z + ")";
         }
+
+        public static void OrthogonalComplement(Vector3 x, out Vector3 y, out Vector3 z)
+        {
+            if (MathEx.Abs(x.x) > MathEx.Abs(x.y))
+                y = new Vector3(-x.z, 0, x.x);
+            else
+                y = new Vector3(0, x.z, -x.y);
+
+            z = Cross(x, y);
+
+            Orthonormalize(ref x, ref y, ref z);
+        }
+
+        public static void Orthonormalize(ref Vector3 x, ref Vector3 y, ref Vector3 z)
+        {
+            x.Normalize();
+
+            float dot0 = Vector3.Dot(x, y);
+            y -= dot0 * x;
+            y.Normalize();
+
+            float dot1 = Vector3.Dot(y, z);
+            dot0 = Vector3.Dot(x, z);
+            z -= dot0 * x + dot1 * y;
+            z.Normalize();
+        }
     }
 }

+ 5 - 0
MBansheeEngine/Math/Vector4.cs

@@ -90,6 +90,11 @@ namespace BansheeEngine
             this.w = w;
         }
 
+        public static explicit operator Vector3(Vector4 vec)
+        {
+            return new Vector3(vec.x, vec.y, vec.z);
+        }
+
         public static Vector4 operator+ (Vector4 a, Vector4 b)
         {
             return new Vector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);

+ 12 - 12
MBansheeEngine/SceneObject.cs

@@ -5,13 +5,13 @@ namespace BansheeEngine
 {
     public sealed class SceneObject : GameObject
     {
-        public SceneObject parent
+        public SceneObject Parent
         {
             set { Internal_SetParent(mCachedPtr, value); }
             get { return Internal_GetParent(mCachedPtr); }
         }
 
-        public Vector3 position
+        public Vector3 Position
         {
             get
             {
@@ -26,7 +26,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 localPosition
+        public Vector3 LocalPosition
         {
             get
             {
@@ -41,7 +41,7 @@ namespace BansheeEngine
             }
         }
 
-        public Quaternion rotation
+        public Quaternion Rotation
         {
             get
             {
@@ -56,7 +56,7 @@ namespace BansheeEngine
             }
         }
 
-        public Quaternion localRotation
+        public Quaternion LocalRotation
         {
             get
             {
@@ -71,7 +71,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 scale
+        public Vector3 Scale
         {
             get
             {
@@ -81,7 +81,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 localScale
+        public Vector3 LocalScale
         {
             get
             {
@@ -96,7 +96,7 @@ namespace BansheeEngine
             }
         }
 
-        public Matrix4 worldTransform
+        public Matrix4 WorldTransform
         {
             get
             {
@@ -106,7 +106,7 @@ namespace BansheeEngine
             }
         }
 
-        public Matrix4 localTransform
+        public Matrix4 LocalTransform
         {
             get
             {
@@ -116,7 +116,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 forward
+        public Vector3 Forward
         {
             get
             {
@@ -130,7 +130,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 right
+        public Vector3 Right
         {
             get
             {
@@ -140,7 +140,7 @@ namespace BansheeEngine
             }
         }
 
-        public Vector3 up
+        public Vector3 Up
         {
             get
             {

+ 7 - 4
SBansheeEditor/Include/BsScriptEditorSettings.h

@@ -17,15 +17,18 @@ namespace BansheeEngine
 		static void internal_SetMoveHandleSnapActive(bool value);
 		static bool internal_GetRotateHandleSnapActive();
 		static void internal_SetRotateHandleSnapActive(bool value);
-		static bool internal_GetScaleHandleSnapActive();
-		static void internal_SetScaleHandleSnapActive(bool value);
 		static float internal_GetMoveHandleSnapAmount();
 		static void internal_SetMoveHandleSnapAmount(float value);
 		static float internal_GetRotateHandleSnapAmount();
 		static void internal_SetRotateHandleSnapAmount(float value);
-		static float internal_GetScaleHandleSnapAmount();
-		static void internal_SetScaleHandleSnapAmount(float value);
 		static float internal_GetDefaultHandleSize();
 		static void internal_SetDefaultHandleSize(float value);
+		static UINT32 internal_GetActiveSceneTool();
+		static void internal_SetActiveSceneTool(UINT32 value);
+		static UINT32 internal_GetActiveCoordinateMode();
+		static void internal_SetActiveCoordinateMode(UINT32 value);
+		static UINT32 internal_GetActivePivotMode();
+		static void internal_SetActivePivotMode(UINT32 value);
+		static UINT32 internal_GetHash();
 	};
 }

+ 1 - 0
SBansheeEditor/Include/BsScriptHandleSliderDisc.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 	private:
 		static void internal_CreateInstance(MonoObject* instance, Vector3 normal, float radius, bool fixedScale);
 		static void internal_GetDelta(ScriptHandleSliderDisc* nativeInstance, float* value);
+		static void internal_GetStartAngle(ScriptHandleSliderDisc* nativeInstance, float* value);
 
 		ScriptHandleSliderDisc(MonoObject* instance, const Vector3& normal, float radius, bool fixedScale);
 		~ScriptHandleSliderDisc();

+ 45 - 24
SBansheeEditor/Source/BsScriptEditorSettings.cpp

@@ -18,16 +18,19 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetMoveHandleSnapActive", &ScriptEditorSettings::internal_SetMoveHandleSnapActive);
 		metaData.scriptClass->addInternalCall("Internal_GetRotateHandleSnapActive", &ScriptEditorSettings::internal_GetRotateHandleSnapActive);
 		metaData.scriptClass->addInternalCall("Internal_SetRotateHandleSnapActive", &ScriptEditorSettings::internal_SetRotateHandleSnapActive);
-		metaData.scriptClass->addInternalCall("Internal_GetScaleHandleSnapActive", &ScriptEditorSettings::internal_GetScaleHandleSnapActive);
-		metaData.scriptClass->addInternalCall("Internal_SetScaleHandleSnapActive", &ScriptEditorSettings::internal_SetScaleHandleSnapActive);
 		metaData.scriptClass->addInternalCall("Internal_GetMoveHandleSnapAmount", &ScriptEditorSettings::internal_GetMoveHandleSnapAmount);
 		metaData.scriptClass->addInternalCall("Internal_SetMoveHandleSnapAmount", &ScriptEditorSettings::internal_SetMoveHandleSnapAmount);
 		metaData.scriptClass->addInternalCall("Internal_GetRotateHandleSnapAmount", &ScriptEditorSettings::internal_GetRotateHandleSnapAmount);
 		metaData.scriptClass->addInternalCall("Internal_SetRotateHandleSnapAmount", &ScriptEditorSettings::internal_SetRotateHandleSnapAmount);
-		metaData.scriptClass->addInternalCall("Internal_GetScaleHandleSnapAmount", &ScriptEditorSettings::internal_GetScaleHandleSnapAmount);
-		metaData.scriptClass->addInternalCall("Internal_SetScaleHandleSnapAmount", &ScriptEditorSettings::internal_SetScaleHandleSnapAmount);
 		metaData.scriptClass->addInternalCall("Internal_GetDefaultHandleSize", &ScriptEditorSettings::internal_GetDefaultHandleSize);
 		metaData.scriptClass->addInternalCall("Internal_SetDefaultHandleSize", &ScriptEditorSettings::internal_SetDefaultHandleSize);
+		metaData.scriptClass->addInternalCall("Internal_GetActiveSceneTool", &ScriptEditorSettings::internal_GetActiveSceneTool);
+		metaData.scriptClass->addInternalCall("Internal_SetActiveSceneTool", &ScriptEditorSettings::internal_SetActiveSceneTool);
+		metaData.scriptClass->addInternalCall("Internal_GetActiveCoordinateMode", &ScriptEditorSettings::internal_GetActiveCoordinateMode);
+		metaData.scriptClass->addInternalCall("Internal_SetActiveCoordinateMode", &ScriptEditorSettings::internal_SetActiveCoordinateMode);
+		metaData.scriptClass->addInternalCall("Internal_GetActivePivotMode", &ScriptEditorSettings::internal_GetActivePivotMode);
+		metaData.scriptClass->addInternalCall("Internal_SetActivePivotMode", &ScriptEditorSettings::internal_SetActivePivotMode);
+		metaData.scriptClass->addInternalCall("Internal_GetHash", &ScriptEditorSettings::internal_GetHash);
 	}
 
 	bool ScriptEditorSettings::internal_GetMoveHandleSnapActive()
@@ -54,18 +57,6 @@ namespace BansheeEngine
 		settings->setRotateHandleSnapActive(value);
 	}
 
-	bool ScriptEditorSettings::internal_GetScaleHandleSnapActive()
-	{
-		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		return settings->getScaleHandleSnapActive();
-	}
-
-	void ScriptEditorSettings::internal_SetScaleHandleSnapActive(bool value)
-	{
-		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		settings->setScaleHandleSnapActive(value);
-	}
-
 	float ScriptEditorSettings::internal_GetMoveHandleSnapAmount()
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
@@ -90,27 +81,57 @@ namespace BansheeEngine
 		settings->setRotationHandleSnap(Degree(value));
 	}
 
-	float ScriptEditorSettings::internal_GetScaleHandleSnapAmount()
+	float ScriptEditorSettings::internal_GetDefaultHandleSize()
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		return settings->getScaleHandleSnap();
+		return settings->getHandleSize();
 	}
 
-	void ScriptEditorSettings::internal_SetScaleHandleSnapAmount(float value)
+	void ScriptEditorSettings::internal_SetDefaultHandleSize(float value)
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		settings->setScaleHandleSnap(value);
+		settings->setHandleSize(value);
 	}
 
-	float ScriptEditorSettings::internal_GetDefaultHandleSize()
+	UINT32 ScriptEditorSettings::internal_GetActiveSceneTool()
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		return settings->getHandleSize();
+		return settings->getActiveSceneTool();
 	}
 
-	void ScriptEditorSettings::internal_SetDefaultHandleSize(float value)
+	void ScriptEditorSettings::internal_SetActiveSceneTool(UINT32 value)
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
-		settings->setHandleSize(value);
+		settings->setActiveSceneTool(value);
+	}
+
+	UINT32 ScriptEditorSettings::internal_GetActiveCoordinateMode()
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		return settings->getActiveCoordinateMode();
+	}
+
+	void ScriptEditorSettings::internal_SetActiveCoordinateMode(UINT32 value)
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		settings->setActiveCoordinateMode(value);
+	}
+
+	UINT32 ScriptEditorSettings::internal_GetActivePivotMode()
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		return settings->getActivePivotMode();
+	}
+
+	void ScriptEditorSettings::internal_SetActivePivotMode(UINT32 value)
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		settings->setActivePivotMode(value);
+	}
+
+	UINT32 ScriptEditorSettings::internal_GetHash()
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		return settings->getHash();
 	}
 }

+ 7 - 1
SBansheeEditor/Source/BsScriptHandleSliderDisc.cpp

@@ -30,6 +30,7 @@ namespace BansheeEngine
 	{
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptHandleSliderDisc::internal_CreateInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetDelta", &ScriptHandleSliderDisc::internal_GetDelta);
+		metaData.scriptClass->addInternalCall("Internal_GetStartAngle", &ScriptHandleSliderDisc::internal_GetStartAngle);
 	}
 
 	void ScriptHandleSliderDisc::internal_CreateInstance(MonoObject* instance, Vector3 normal, float radius, bool fixedScale)
@@ -40,6 +41,11 @@ namespace BansheeEngine
 
 	void ScriptHandleSliderDisc::internal_GetDelta(ScriptHandleSliderDisc* nativeInstance, float* value)
 	{
-		*value = nativeInstance->mSlider->getDelta();
+		*value = nativeInstance->mSlider->getDelta().valueDegrees();
+	}
+
+	void ScriptHandleSliderDisc::internal_GetStartAngle(ScriptHandleSliderDisc* nativeInstance, float* value)
+	{
+		*value = nativeInstance->mSlider->getStartAngle().valueDegrees();
 	}
 }

+ 8 - 4
SBansheeEngine/Source/BsScriptGUIToggle.cpp

@@ -46,7 +46,7 @@ namespace BansheeEngine
 	}
 
 	void ScriptGUIToggle::internal_createInstance(MonoObject* instance, MonoObject* content, 
-		MonoObject* toggleGroup, MonoString* style, MonoArray* guiOptions)
+		MonoObject* monoToggleGroup, MonoString* style, MonoArray* guiOptions)
 	{
 		GUIOptions options;
 
@@ -55,11 +55,15 @@ namespace BansheeEngine
 			options.addOption(mono_array_get(guiOptions, GUIOption, i));
 
 		ScriptGUIToggleGroup* scriptToggleGroup = nullptr;
-		if (toggleGroup != nullptr)
-			scriptToggleGroup = ScriptGUIToggleGroup::toNative(toggleGroup);
+		std::shared_ptr<GUIToggleGroup> toggleGroup;
+		if (monoToggleGroup != nullptr)
+		{
+			scriptToggleGroup = ScriptGUIToggleGroup::toNative(monoToggleGroup);
+			toggleGroup = scriptToggleGroup->getInternalValue();
+		}
 
 		GUIContent nativeContent(ScriptGUIContent::getText(content), ScriptGUIContent::getImage(content), ScriptGUIContent::getTooltip(content));
-		GUIToggle* guiToggle = GUIToggle::create(nativeContent, scriptToggleGroup->getInternalValue(), options, toString(MonoUtil::monoToWString(style)));
+		GUIToggle* guiToggle = GUIToggle::create(nativeContent, toggleGroup, options, toString(MonoUtil::monoToWString(style)));
 
 		guiToggle->onClick.connect(std::bind(&ScriptGUIToggle::onClick, instance));
 		guiToggle->onHover.connect(std::bind(&ScriptGUIToggle::onHover, instance));

+ 13 - 7
TODO.txt

@@ -1,7 +1,6 @@
 --------- ALL LONG TERM TASKS / FIXES BELONG TO GOOGLE DOCS: ImplementationTODO OR PossibleImprovements ----------
 
 Do a cleanup pass on the editor? Fix the annoying little issues:
- - When GUIToggle is initialy toggled the texture is missing
  - Switching between button states seem to cause 1 frame of no texture for the gui element
  - Much more I can't think of right now
 
@@ -12,14 +11,21 @@ Possibly set up automatic refresh in debug mode after initialization? As an ad-h
 
 <<<<<<Handles>>>>>>>>
 
-When active handle in EditorApplication changes externally, update the GUI button in scene view
-Save/load active handle tool (it should also probably persist assembly refresh)
+Add free rotate handle
+Add free scale handle
 
-Movement of plane (and possible line) sliders might need tweaking
- - Move handle was moving object in the opposite direction at one point
- - It also seems to move too slow
-Need to update ShapeMeshes3D::solidQuad to render both sides of the quad
+Rotate handle:
+ - Transparent angle start is at the wrong position
+ - Red arc is on the wrong side when viewed from above
+ - Missing two free rotate arcs
 
+Cursor wrap only works when cursor moves really slowly over the border, and even then it's spotty
+When changing handle types they do not refresh until you click on the scene view
+
+The first time I mouse over a GUI button it flashes for a frame as if it has no texture
+ - It seems that the ParamBlockBuffer for the vertex shader gets updated one frame too late
+ - First frame it is set to all zeroes, but fragment data seems valid (both the param block and texture/sampler)
+ - Then syncToCore() happens next frame and data is updated.
 
 <<<<Multi-resource saving>>>>:
  - Modify Font so it doesn't contain a texture, but instead keeps a handle to it