瀏覽代碼

Merge branch 'master' of https://github.com/BearishSun/BansheeEngine.git

marco.bellan 9 年之前
父節點
當前提交
6695c24b5f

+ 4 - 8
Documentation/GitHub/license.md

@@ -1,6 +1,6 @@
 # License
 # License
 
 
-Banshee if offered using a dual-license model: LGPL v3 and Commercial.
+Banshee is offered using a dual-license model: LGPL v3 and Commercial.
 
 
 ## GNU Lesser General Public License v3
 ## GNU Lesser General Public License v3
 
 
@@ -10,8 +10,6 @@ Banshee is offered completely free for personal or commercial use under the GNU
 A commercial license is available for those that are not comfortable with LGPL terms. The commercial license is a paid per-user license that allows you to distribute your application under your own terms.
 A commercial license is available for those that are not comfortable with LGPL terms. The commercial license is a paid per-user license that allows you to distribute your application under your own terms.
 
 
 It is available under the "pay what you want" model, meaning you can purchase it for as little as $0 or as much as $1000. This is completely up to you and we won't judge if you just want a free license.
 It is available under the "pay what you want" model, meaning you can purchase it for as little as $0 or as much as $1000. This is completely up to you and we won't judge if you just want a free license.
-
-However if you like Banshee consider spending some money as this ensures we can pay the costs for developing it further. The money will be invested into hiring more developers to help with adding more features faster (see [Roadmap](roadmap.md)), as well as help with testing, polish and documentation.
  
  
 ## Third party licenses
 ## Third party licenses
 Licenses for all third party libraries used by Banshee can be found in License\ThirdParty sub-directory of the source code.
 Licenses for all third party libraries used by Banshee can be found in License\ThirdParty sub-directory of the source code.
@@ -19,18 +17,16 @@ Licenses for all third party libraries used by Banshee can be found in License\T
 ## License FAQ
 ## License FAQ
 
 
 *Why have two separate licenses?*
 *Why have two separate licenses?*
-LGPL is offered because at its core Banshee is an open source project. Commercial license is offered because we wish to fund Banshee development so we can make it bigger and better than other open source engines.
+LGPL is offered because at its core Banshee is an open source project. Commercial license is offered so we can fund Banshee development in order to make it better than other open source engines.
 
 
 *How do I get a commercial license?*
 *How do I get a commercial license?*
-Until a website is set up, you must contact me personally at [email protected]
+Commercial licenses will become available after Banshee v1.0 is released.
 
 
 *What are the differences between the two licenses?*
 *What are the differences between the two licenses?*
 Engine features for both license models are identical. The only difference are the license terms.
 Engine features for both license models are identical. The only difference are the license terms.
 
 
 *What are the limitations of the LGPL license?*
 *What are the limitations of the LGPL license?*
-When modifying the source code of the engine, or linking the engine statically with your application you will be required to release your code/application under LGPL or a compatible license.
-
-This involves providing the source code for your application, as well as giving the rights to use, modify and redistribute your code/application to anyone who acquires it.
+When modifying the source code of the engine, or linking the engine statically with your application you will be required to release your code/application under LGPL or a compatible license. This involves providing the source code for your application, as well as giving the rights to use, modify and redistribute your code/application to anyone who acquires it.
 
 
 *What are the limitations of the commercial license?*
 *What are the limitations of the commercial license?*
 You can publish binaries of your product (including all portions of Banshee, modified or original) under any terms you wish. The only limitation being that you are not allowed to publish Banshee's source code (modified or original) under a custom license (source code must be published under the LGPL).
 You can publish binaries of your product (including all portions of Banshee, modified or original) under any terms you wish. The only limitation being that you are not allowed to publish Banshee's source code (modified or original) under a custom license (source code must be published under the LGPL).

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ On top of the engine Banshee also provides a highly intuitive and customizable *
 
 
 The scripting system supports C# and comes with an extensive API ensuring you can complete your game without ever touching the C++ engine core. **C# scripting** makes your development easier by giving you access to the entire .NET library and a wide variety of pre-existing managed libraries. Integration of the scripting system with the editor and external tools like Visual Studio, as well as fast compilation times ensures that iteration times between coding and testing are minimized.
 The scripting system supports C# and comes with an extensive API ensuring you can complete your game without ever touching the C++ engine core. **C# scripting** makes your development easier by giving you access to the entire .NET library and a wide variety of pre-existing managed libraries. Integration of the scripting system with the editor and external tools like Visual Studio, as well as fast compilation times ensures that iteration times between coding and testing are minimized.
 
 
-Aside from being a fully featured game engine and toolkit, Banshee can also be used as a **low level library**, providing a powerful foundation to build new technologies with or to easily customize the engine for game specific needs. Layered and plugin based design allows developers to use only the functionality they need, and to fully remove or replace major engine systems. Banshee's code is modern, with clean interfaces that make it easy to learn and maintain. Platform specific functionality is kept at a minimum making porting as easy as possible. It is fully documented with an extensive API reference, as well as a set of manuals introducing you to most major systems.
+Aside from being a fully featured game engine and toolkit, Banshee can also be used as a **low level framework**, providing a powerful foundation to build new technologies with or to easily customize the engine for game specific needs. Layered and plugin based design allows developers to use only the functionality they need, and to fully remove or replace major engine systems. Banshee's code is modern, with clean interfaces that make it easy to learn and maintain. Platform specific functionality is kept at a minimum making porting as easy as possible. It is fully documented with an extensive API reference, as well as a set of manuals introducing you to most major systems.
 
 
 # Features
 # Features
 * [Features](Documentation/GitHub/features.md) - A list of all currently available features.
 * [Features](Documentation/GitHub/features.md) - A list of all currently available features.

+ 3 - 1
Source/BansheeCore/Include/BsCollider.h

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 #pragma once
 
 
+#include <cfloat>
+
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsPhysicsCommon.h"
 #include "BsPhysicsCommon.h"
 #include "BsVector3.h"
 #include "BsVector3.h"
@@ -155,4 +157,4 @@ namespace BansheeEngine
 	};
 	};
 
 
 	/** @} */
 	/** @} */
-}
+}

+ 16 - 1
Source/BansheeCore/Include/BsGpuProgram.h

@@ -231,4 +231,19 @@ namespace BansheeEngine
 	};
 	};
 
 
 	/** @} */
 	/** @} */
-}
+}
+
+namespace std
+{
+/** Hash value generator for GpuProgramProfile. */
+template<>
+struct hash<BansheeEngine::GpuProgramProfile>
+{
+	size_t operator()(const BansheeEngine::GpuProgramProfile& profile) const
+	{
+		size_t hash = 0;
+		BansheeEngine::hash_combine(hash, (int)profile);
+		return hash;
+	}
+};
+}

+ 3 - 1
Source/BansheeCore/Include/BsJoint.h

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 #pragma once
 
 
+#include <cfloat>
+
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsPhysicsCommon.h"
 #include "BsPhysicsCommon.h"
 #include "BsFJoint.h"
 #include "BsFJoint.h"
@@ -342,4 +344,4 @@ namespace BansheeEngine
 	};
 	};
 
 
 	/** @} */
 	/** @} */
-}
+}

+ 34 - 18
Source/BansheeEngine/Source/BsGUISliderHandle.cpp

@@ -251,9 +251,9 @@ namespace BansheeEngine
 						INT32 right = left + handleSize;
 						INT32 right = left + handleSize;
 
 
 						INT32 clickPos = ev.getPosition().x;
 						INT32 clickPos = ev.getPosition().x;
-						if(clickPos >= left && clickPos < (left + (INT32)RESIZE_HANDLE_SIZE))
+						if (clickPos >= left && clickPos < (left + (INT32)RESIZE_HANDLE_SIZE))
 							mDragState = DragState::LeftResize;
 							mDragState = DragState::LeftResize;
-						else if(clickPos >= (right - (INT32)RESIZE_HANDLE_SIZE) && clickPos < right)
+						else if (clickPos >= (right - (INT32)RESIZE_HANDLE_SIZE) && clickPos < right)
 							mDragState = DragState::RightResize;
 							mDragState = DragState::RightResize;
 						else
 						else
 							mDragState = DragState::Normal;
 							mDragState = DragState::Normal;
@@ -295,36 +295,52 @@ namespace BansheeEngine
 		{
 		{
 			if (!_isDisabled())
 			if (!_isDisabled())
 			{
 			{
-				INT32 handlePosPx;
-				if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
-					handlePosPx = ev.getPosition().x - mDragStartPos - mLayoutData.area.x;
-				else
-					handlePosPx = ev.getPosition().y - mDragStartPos - mLayoutData.area.y;
-
 				if (mDragState == DragState::Normal)
 				if (mDragState == DragState::Normal)
 				{
 				{
+					INT32 handlePosPx;
+					if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
+						handlePosPx = ev.getPosition().x - mDragStartPos - mLayoutData.area.x;
+					else
+						handlePosPx = ev.getPosition().y - mDragStartPos - mLayoutData.area.y;
+
 					setHandlePosPx(handlePosPx);
 					setHandlePosPx(handlePosPx);
 					onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
 					onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
 				}
 				}
 				else // Resizing
 				else // Resizing
 				{
 				{
+					INT32 clickPosPx;
+					if (mFlags.isSet(GUISliderHandleFlag::Horizontal))
+						clickPosPx = ev.getPosition().x - mLayoutData.area.x;
+					else
+						clickPosPx = ev.getPosition().y - mLayoutData.area.y;
+
+					INT32 left = getHandlePosPx();
+					UINT32 maxSize = getMaxSize();
+
+					INT32 newHandleSize;
+					float newHandlePos;
 					if(mDragState == DragState::LeftResize)
 					if(mDragState == DragState::LeftResize)
 					{
 					{
-						INT32 right = getHandlePosPx() + handleSize;
-						INT32 newHandleSize = right - handlePosPx;
+						INT32 newLeft = clickPosPx - mDragStartPos;
+						INT32 right = left + handleSize;
+						newLeft = Math::clamp(newLeft, 0, right);
 
 
-						_setHandleSize(newHandleSize / (float)getMaxSize());
-						setHandlePosPx(handlePosPx);
-						onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
+						newHandleSize = std::max((INT32)mMinHandleSize, right - newLeft);
+						newLeft = right - newHandleSize;
+						newHandlePos = newLeft / (float)(maxSize - newHandleSize);
 					}
 					}
-					else if(mDragState == DragState::RightResize)
+					else // Right resize
 					{
 					{
-						INT32 left = getHandlePosPx();
-						INT32 newHandleSize = handlePosPx - left;
+						INT32 newRight = clickPosPx;
+						newHandleSize = std::max((INT32)mMinHandleSize, std::min(newRight, (INT32)maxSize) - left);
 
 
-						_setHandleSize(newHandleSize / (float)getMaxSize());
-						onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
+						newHandlePos = left / (float)(maxSize - newHandleSize);
 					}
 					}
+
+					_setHandleSize(newHandleSize / (float)maxSize);
+					_setHandlePos(newHandlePos);
+
+					onHandleMovedOrResized(mPctHandlePos, _getHandleSizePct());
 				}
 				}
 
 
 				_markLayoutAsDirty();
 				_markLayoutAsDirty();

+ 2 - 2
Source/BansheeGLRenderAPI/Include/BsGLPrerequisites.h

@@ -24,7 +24,7 @@
 #   include <GL/glew.h>
 #   include <GL/glew.h>
 #   include <GL/glu.h>
 #   include <GL/glu.h>
 #   define GL_GLEXT_PROTOTYPES
 #   define GL_GLEXT_PROTOTYPES
-#elif BS_PLATFORM == BS_PLATFORM_APPLE
+#elif BS_PLATFORM == BS_PLATFORM_OSX
 #   include <GL/glew.h>
 #   include <GL/glew.h>
 #   include <OpenGL/glu.h>
 #   include <OpenGL/glu.h>
 #endif
 #endif
@@ -101,4 +101,4 @@ namespace BansheeEngine
 	};
 	};
 
 
 	/** @} */
 	/** @} */
-}
+}

+ 2 - 2
Source/BansheeOISInput/Source/BsInputHandlerOIS.cpp

@@ -73,7 +73,7 @@ namespace BansheeEngine
 		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
 		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
 		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
 		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
 		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
 		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
-#elif defined BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_APPLE
+#elif defined BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_OSX
 		pl.insert(std::make_pair(std::string("x11_mouse_grab"), std::string("false")));
 		pl.insert(std::make_pair(std::string("x11_mouse_grab"), std::string("false")));
 		pl.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("false")));
 		pl.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("false")));
 		pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
 		pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
@@ -334,4 +334,4 @@ namespace BansheeEngine
 
 
 		return (ButtonCode)(BC_GAMEPAD_BTN1 + (joystickCode - 15));
 		return (ButtonCode)(BC_GAMEPAD_BTN1 + (joystickCode - 15));
 	}
 	}
-}
+}

+ 2 - 2
Source/BansheeUtility/Include/BsAny.h

@@ -52,7 +52,7 @@ namespace BansheeEngine
 			:mData(bs_new<Data<ValueType>>(value))
 			:mData(bs_new<Data<ValueType>>(value))
 		{ }
 		{ }
 
 
-		Any(nullptr_t)
+		Any(std::nullptr_t)
 			:mData(nullptr)
 			:mData(nullptr)
 		{ }
 		{ }
 
 
@@ -186,4 +186,4 @@ namespace BansheeEngine
 	}
 	}
 
 
 	/** @} */
 	/** @} */
-}
+}

+ 1 - 7
Source/BansheeUtility/Include/BsDynLib.h

@@ -21,18 +21,12 @@ namespace BansheeEngine
 #    define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b )
 #    define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b )
 #    define DYNLIB_UNLOAD( a ) !FreeLibrary( a )
 #    define DYNLIB_UNLOAD( a ) !FreeLibrary( a )
 
 
-#elif BS_PLATFORM == BS_PLATFORM_LINUX
+#elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_OSX
 #    define DYNLIB_HANDLE void*
 #    define DYNLIB_HANDLE void*
 #    define DYNLIB_LOAD( a ) dlopen( a, RTLD_LAZY | RTLD_GLOBAL)
 #    define DYNLIB_LOAD( a ) dlopen( a, RTLD_LAZY | RTLD_GLOBAL)
 #    define DYNLIB_GETSYM( a, b ) dlsym( a, b )
 #    define DYNLIB_GETSYM( a, b ) dlsym( a, b )
 #    define DYNLIB_UNLOAD( a ) dlclose( a )
 #    define DYNLIB_UNLOAD( a ) dlclose( a )
 
 
-#elif BS_PLATFORM == BS_PLATFORM_APPLE
-#    define DYNLIB_HANDLE void*
-#    define DYNLIB_LOAD( a ) mac_loadDylib( a )
-#    define DYNLIB_GETSYM( a, b ) dlsym( a, b )
-#    define DYNLIB_UNLOAD( a ) dlclose( a )
-
 #endif
 #endif
 
 
     /** Class that holds data about a dynamic library. */
     /** Class that holds data about a dynamic library. */

+ 21 - 4
Source/BansheeUtility/Include/BsFrameAlloc.h

@@ -2,6 +2,9 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 #pragma once
 
 
+#include <limits>
+#include <new>                  /* For 'placement new' */
+
 #include "BsPrerequisitesUtil.h"
 #include "BsPrerequisitesUtil.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
@@ -151,6 +154,12 @@ namespace BansheeEngine
 	{
 	{
 	public:
 	public:
 		typedef T value_type;
 		typedef T value_type;
+		typedef value_type* pointer;
+		typedef const value_type* const_pointer;
+		typedef value_type& reference;
+		typedef const value_type& const_reference;
+		typedef std::size_t size_type;
+		typedef std::ptrdiff_t difference_type;
 
 
 		StdFrameAlloc() noexcept 
 		StdFrameAlloc() noexcept 
 			:mFrameAlloc(nullptr)
 			:mFrameAlloc(nullptr)
@@ -160,12 +169,13 @@ namespace BansheeEngine
 			:mFrameAlloc(alloc)
 			:mFrameAlloc(alloc)
 		{ }
 		{ }
 
 
-		template<class T> StdFrameAlloc(const StdFrameAlloc<T>& alloc) noexcept
+		template<class U> StdFrameAlloc(const StdFrameAlloc<U>& alloc) noexcept
 			:mFrameAlloc(alloc.mFrameAlloc)
 			:mFrameAlloc(alloc.mFrameAlloc)
 		{ }
 		{ }
 
 
-		template<class T> bool operator==(const StdFrameAlloc<T>&) const noexcept { return true; }
-		template<class T> bool operator!=(const StdFrameAlloc<T>&) const noexcept { return false; }
+		template<class U> bool operator==(const StdFrameAlloc<U>&) const noexcept { return true; }
+		template<class U> bool operator!=(const StdFrameAlloc<U>&) const noexcept { return false; }
+		template<class U> class rebind { public: typedef StdFrameAlloc<U> other; };
 
 
 		/** Allocate but don't initialize number elements of type T.*/
 		/** Allocate but don't initialize number elements of type T.*/
 		T* allocate(const size_t num) const
 		T* allocate(const size_t num) const
@@ -190,6 +200,13 @@ namespace BansheeEngine
 		}
 		}
 
 
 		FrameAlloc* mFrameAlloc;
 		FrameAlloc* mFrameAlloc;
+
+		size_t max_size() const { return std::numeric_limits<size_type>::max() / sizeof(T); }
+		void construct(pointer p, const_reference t) { new (p) T(t); }
+		void destroy(pointer p) { p->~T(); }
+		template<class U, class... Args>
+		void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
+
 	};
 	};
 
 
 	/** Return that all specializations of this allocator are interchangeable. */
 	/** Return that all specializations of this allocator are interchangeable. */
@@ -208,4 +225,4 @@ namespace BansheeEngine
 
 
 	/** @} */
 	/** @} */
 	/** @} */
 	/** @} */
-}
+}

+ 21 - 6
Source/BansheeUtility/Include/BsMemoryAllocator.h

@@ -1,11 +1,12 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 #pragma once
-
 #undef min
 #undef min
 #undef max
 #undef max
 
 
 #include <atomic>
 #include <atomic>
+#include <limits>
+#include <new>                  /* For 'placement new' */
 #include <utility>
 #include <utility>
 
 
 #if BS_PLATFORM == BS_PLATFORM_LINUX
 #if BS_PLATFORM == BS_PLATFORM_LINUX
@@ -377,16 +378,24 @@ namespace BansheeEngine
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
-    /** Allocator for the standard library that internally uses Banshee memory allocator. */
-    template <class T, class Alloc = GenAlloc>
+	/** Allocator for the standard library that internally uses Banshee memory allocator. */
+	template <class T, class Alloc = GenAlloc>
 	class StdAlloc 
 	class StdAlloc 
 	{
 	{
 	public:
 	public:
 		typedef T value_type;
 		typedef T value_type;
+		typedef T* pointer;
+		typedef const T* const_pointer;
+		typedef T& reference;
+		typedef const T& const_reference;
+		typedef std::size_t size_type;
+		typedef std::ptrdiff_t difference_type;
+
 		StdAlloc() noexcept {}
 		StdAlloc() noexcept {}
-		template<class T, class Alloc> StdAlloc(const StdAlloc<T, Alloc>&) noexcept {}
-		template<class T, class Alloc> bool operator==(const StdAlloc<T, Alloc>&) const noexcept { return true; }
-		template<class T, class Alloc> bool operator!=(const StdAlloc<T, Alloc>&) const noexcept { return false; }
+		template<class U, class Alloc2> StdAlloc(const StdAlloc<U, Alloc2>&) noexcept {}
+		template<class U, class Alloc2> bool operator==(const StdAlloc<U, Alloc2>&) const noexcept { return true; }
+		template<class U, class Alloc2> bool operator!=(const StdAlloc<U, Alloc2>&) const noexcept { return false; }
+		template<class U> class rebind { public: typedef StdAlloc<U, Alloc> other; };
 
 
 		/** Allocate but don't initialize number elements of type T. */
 		/** Allocate but don't initialize number elements of type T. */
 		T* allocate(const size_t num) const
 		T* allocate(const size_t num) const
@@ -409,6 +418,12 @@ namespace BansheeEngine
 		{
 		{
 			bs_free<Alloc>((void*)p);
 			bs_free<Alloc>((void*)p);
 		}
 		}
+
+		size_t max_size() const { return std::numeric_limits<size_type>::max() / sizeof(T); }
+		void construct(pointer p, const_reference t) { new (p) T(t); }
+		void destroy(pointer p) { p->~T(); }
+		template<class U, class... Args>
+		void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 2 - 2
Source/BansheeUtility/Include/BsPlatformDefines.h

@@ -51,7 +51,7 @@
 #if defined( __WIN32__ ) || defined( _WIN32 )
 #if defined( __WIN32__ ) || defined( _WIN32 )
 #   define BS_PLATFORM BS_PLATFORM_WIN32
 #   define BS_PLATFORM BS_PLATFORM_WIN32
 #elif defined( __APPLE_CC__)
 #elif defined( __APPLE_CC__)
-#   define BS_PLATFORM BS_PLATFORM_APPLE
+#   define BS_PLATFORM BS_PLATFORM_OSX
 #else
 #else
 #   define BS_PLATFORM BS_PLATFORM_LINUX
 #   define BS_PLATFORM BS_PLATFORM_LINUX
 #endif
 #endif
@@ -116,4 +116,4 @@
 #        define BS_THREADLOCAL __thread
 #        define BS_THREADLOCAL __thread
 #	endif
 #	endif
 
 
-#endif
+#endif

+ 2 - 2
Source/BansheeUtility/Include/BsRTTIType.h

@@ -546,7 +546,7 @@ namespace BansheeEngine
 
 
 			UINT32 typeSize = 0;
 			UINT32 typeSize = 0;
 			if(field->hasDynamicSize())
 			if(field->hasDynamicSize())
-				typeSize = field->getArrayElemDynamicSize(object, arrIdx);
+				typeSize = field->getArrayElemDynamicSize(object, index);
 			else
 			else
 				typeSize = field->getTypeSize();
 				typeSize = field->getTypeSize();
 
 
@@ -1238,4 +1238,4 @@ namespace BansheeEngine
 	}
 	}
 
 
 	/** @} */
 	/** @} */
-}
+}

+ 1 - 1
Source/BansheeUtility/Include/BsVectorNI.h

@@ -52,7 +52,7 @@ namespace BansheeEngine
 		{
 		{
 			for (UINT32 i = 0; i < N; i++)
 			for (UINT32 i = 0; i < N; i++)
 			{
 			{
-				if (v[i] != rhs.v[i])
+				if (v[i] != rhs[i])
 					return false;
 					return false;
 			}
 			}
 
 

+ 1 - 2
Source/BansheeUtility/Source/BsDynLib.cpp

@@ -12,7 +12,6 @@
 #endif
 #endif
 
 
 #if BS_PLATFORM == BS_PLATFORM_OSX
 #if BS_PLATFORM == BS_PLATFORM_OSX
-#   include "macUtils.h"
 #   include <dlfcn.h>
 #   include <dlfcn.h>
 #endif
 #endif
 
 
@@ -96,7 +95,7 @@ namespace BansheeEngine
         // Free the buffer.
         // Free the buffer.
         LocalFree(lpMsgBuf);
         LocalFree(lpMsgBuf);
         return ret;
         return ret;
-#elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_APPLE
+#elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_OSX
         return String(dlerror());
         return String(dlerror());
 #else
 #else
         return String("");
         return String("");

+ 5 - 5
Source/BansheeUtility/Source/BsPath.cpp

@@ -117,7 +117,7 @@ namespace BansheeEngine
 		default:
 		default:
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 			parseWindows(pathStr, numChars);
 			parseWindows(pathStr, numChars);
-#elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
+#elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
 			parseUnix(pathStr, numChars);
 			parseUnix(pathStr, numChars);
 #else
 #else
 			static_assert(false, "Unsupported platform for path.");
 			static_assert(false, "Unsupported platform for path.");
@@ -139,7 +139,7 @@ namespace BansheeEngine
 		default:
 		default:
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 			parseWindows(pathStr, numChars);
 			parseWindows(pathStr, numChars);
-#elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
+#elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
 			parseUnix(pathStr, numChars);
 			parseUnix(pathStr, numChars);
 #else
 #else
 			static_assert(false, "Unsupported platform for path.");
 			static_assert(false, "Unsupported platform for path.");
@@ -159,7 +159,7 @@ namespace BansheeEngine
 		default:
 		default:
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 			return buildWindows();
 			return buildWindows();
-#elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
+#elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
 			return buildUnix();
 			return buildUnix();
 #else
 #else
 			static_assert(false, "Unsupported platform for path.");
 			static_assert(false, "Unsupported platform for path.");
@@ -179,7 +179,7 @@ namespace BansheeEngine
 		default:
 		default:
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 			return BansheeEngine::toString(buildWindows());
 			return BansheeEngine::toString(buildWindows());
-#elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
+#elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
 			return BansheeEngine::toString(buildUnix());
 			return BansheeEngine::toString(buildUnix());
 #else
 #else
 			static_assert(false, "Unsupported platform for path.");
 			static_assert(false, "Unsupported platform for path.");
@@ -591,4 +591,4 @@ namespace BansheeEngine
 	{
 	{
 		pushDirectory(BansheeEngine::toWString(dir));
 		pushDirectory(BansheeEngine::toWString(dir));
 	}
 	}
-}
+}

+ 2 - 1
Source/BansheeUtility/Source/BsThreadPool.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsThreadPool.h"
 #include "BsThreadPool.h"
+#include "BsDebug.h"
 
 
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #include "windows.h"
 #include "windows.h"
@@ -344,4 +345,4 @@ namespace BansheeEngine
 
 
 		return (UINT32)mThreads.size();
 		return (UINT32)mThreads.size();
 	}
 	}
-}
+}

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -52,6 +52,7 @@
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\Animation\FieldSelectionWindow.cs" />
     <Compile Include="Windows\Animation\FieldSelectionWindow.cs" />
+    <Compile Include="Windows\Animation\GUIAnimEvents.cs" />
     <Compile Include="Windows\Animation\GUIAnimFieldDisplay.cs" />
     <Compile Include="Windows\Animation\GUIAnimFieldDisplay.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />
     <Compile Include="Windows\Animation\GUICurveEditor.cs" />
     <Compile Include="Windows\Animation\GUICurveEditor.cs" />

+ 167 - 0
Source/MBansheeEditor/Windows/Animation/GUIAnimEvents.cs

@@ -0,0 +1,167 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup AnimationEditor
+     *  @{
+     */
+
+    public class GUIAnimEvents
+    {
+        private const int EVENT_HALF_WIDTH = 2;
+
+        private float rangeLength = 60.0f;
+        private float rangeOffset = 0.0f;
+        private int fps = 1;
+
+        private GUICanvas canvas;
+        private int width;
+        private int height;
+        private int drawableWidth;
+
+        private AnimationEvent[] events = new AnimationEvent[0];
+        private bool[] selectedEvents = new bool[0];
+
+        public GUIAnimEvents(GUILayout layout, int width, int height)
+        {
+            canvas = new GUICanvas();
+            layout.AddElement(canvas);
+
+            SetSize(width, height);
+        }
+
+        public bool FindEvent(Vector2I windowCoords, out int eventIdx)
+        {
+            Rect2I bounds = canvas.Bounds;
+
+            if (windowCoords.x < (bounds.x + GUIGraphTime.PADDING) || windowCoords.x >= (bounds.x + bounds.width - GUIGraphTime.PADDING) ||
+                windowCoords.y < bounds.y || windowCoords.y >= (bounds.y + bounds.height))
+            {
+                eventIdx = -1;
+                return false;
+            }
+
+            Vector2I relativeCoords = windowCoords - new Vector2I(bounds.x + GUIGraphTime.PADDING, bounds.y);
+            for (int i = 0; i < events.Length; i++)
+            {
+                AnimationEvent evnt = events[i];
+
+                int xPos = (int)(((evnt.Time - rangeOffset) / GetRange()) * drawableWidth) + GUIGraphTime.PADDING;
+
+                if (relativeCoords.x >= (xPos - EVENT_HALF_WIDTH) || relativeCoords.y >= (xPos + EVENT_HALF_WIDTH))
+                {
+                    eventIdx = i;
+                    return true;
+                }
+            }
+
+            eventIdx = -1;
+            return false;
+        }
+
+        public void SetSize(int width, int height)
+        {
+            this.width = width;
+            this.height = height;
+
+            canvas.SetWidth(width);
+            canvas.SetHeight(height);
+
+            drawableWidth = Math.Max(0, width - GUIGraphTime.PADDING * 2);
+        }
+
+        public void SetRange(float length)
+        {
+            rangeLength = Math.Max(0.0f, length);
+        }
+
+        public void SetOffset(float offset)
+        {
+            rangeOffset = offset;
+        }
+
+        public void SetFPS(int fps)
+        {
+            this.fps = Math.Max(1, fps);
+        }
+
+        public void SetEvents(AnimationEvent[] events, bool[] selected)
+        {
+            int numEvents;
+            if (events != null)
+                numEvents = events.Length;
+            else
+                numEvents = 0;
+
+            this.events = new AnimationEvent[numEvents];
+            if(events != null)
+                Array.Copy(events, this.events, numEvents);
+
+            selectedEvents = new bool[numEvents];
+            if(selected != null)
+                Array.Copy(selected, selectedEvents, MathEx.Min(numEvents, selected.Length));
+        }
+
+        private void DrawEventMarker(float t, bool selected)
+        {
+            int xPos = (int)(((t - rangeOffset) / GetRange()) * drawableWidth) + GUIGraphTime.PADDING;
+
+            Vector2I a = new Vector2I(xPos - EVENT_HALF_WIDTH, height - 1);
+            Vector2I b = new Vector2I(xPos, 0);
+            Vector2I c = new Vector2I(xPos + EVENT_HALF_WIDTH, height - 1);
+            Vector2I d = new Vector2I(xPos, 0);
+
+            // Draw square shape
+            Vector2I[] linePoints = { a, b, c, d, a };
+            Vector2I[] trianglePoints = { b, c, a, d };
+
+            Color outerColor = selected ? Color.BansheeOrange : Color.Black;
+            canvas.DrawTriangleStrip(trianglePoints, Color.White, 101);
+            canvas.DrawPolyLine(linePoints, outerColor, 100);
+        }
+
+        private float GetRange(bool padding = false)
+        {
+            float spf = 1.0f / fps;
+
+            float range = rangeLength;
+            if (padding)
+            {
+                float lengthPerPixel = rangeLength / drawableWidth;
+                range += lengthPerPixel * GUIGraphTime.PADDING;
+            }
+
+            return ((int)range / spf) * spf;
+        }
+
+        /// <summary>
+        /// Rebuilds the internal GUI elements. Should be called whenever timeline properties change.
+        /// </summary>
+        public void Rebuild()
+        {
+            canvas.Clear();
+
+            float range = GetRange();
+
+            float lengthPerPixel = rangeLength / drawableWidth;
+            float eventHalfWidth = lengthPerPixel * EVENT_HALF_WIDTH;
+            for (int i = 0; i < events.Length; i++)
+            {
+                float t = events[i].Time;
+
+                float min = t - eventHalfWidth;
+                float max = t + eventHalfWidth;
+
+                if (max < rangeOffset || min > (rangeOffset + range))
+                    continue;
+
+                DrawEventMarker(t, selectedEvents[i]);
+            }
+        }
+    }
+
+    /** @} */
+}

+ 2 - 2
Source/MBansheeEditor/Windows/Animation/GUICurveDrawing.cs

@@ -275,7 +275,7 @@ namespace BansheeEditor
         /// <param name="pixelCoords">Coordinates relative to this GUI element, in pixels.</param>
         /// <param name="pixelCoords">Coordinates relative to this GUI element, in pixels.</param>
         /// <param name="curveCoords">Curve coordinates within the range as specified by <see cref="SetRange"/>. Only
         /// <param name="curveCoords">Curve coordinates within the range as specified by <see cref="SetRange"/>. Only
         ///                           valid when function returns true.</param>
         ///                           valid when function returns true.</param>
-        /// <returns>True if the window coordinates were within the curve area, false otherwise.</returns>
+        /// <returns>True if the coordinates are within the curve area, false otherwise.</returns>
         public bool PixelToCurveSpace(Vector2I pixelCoords, out Vector2 curveCoords)
         public bool PixelToCurveSpace(Vector2I pixelCoords, out Vector2 curveCoords)
         {
         {
             Rect2I bounds = canvas.Bounds;
             Rect2I bounds = canvas.Bounds;
@@ -501,7 +501,7 @@ namespace BansheeEditor
             if (curves == null)
             if (curves == null)
                 return;
                 return;
 
 
-            tickHandler.SetRange(offset.x, GetRange(true), drawableWidth + GUIGraphTime.PADDING);
+            tickHandler.SetRange(offset.x, offset.x + GetRange(true), drawableWidth + GUIGraphTime.PADDING);
 
 
             // Draw vertical frame markers
             // Draw vertical frame markers
             int numTickLevels = tickHandler.NumLevels;
             int numTickLevels = tickHandler.NumLevels;

+ 52 - 8
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -37,14 +37,16 @@ namespace BansheeEditor
         }
         }
 
 
         private const int TIMELINE_HEIGHT = 20;
         private const int TIMELINE_HEIGHT = 20;
+        private const int EVENTS_HEIGHT = 10;
         private const int SIDEBAR_WIDTH = 30;
         private const int SIDEBAR_WIDTH = 30;
         private const int DRAG_START_DISTANCE = 3;
         private const int DRAG_START_DISTANCE = 3;
 
 
         private EditorWindow window;
         private EditorWindow window;
         private GUILayout gui;
         private GUILayout gui;
         private GUIPanel drawingPanel;
         private GUIPanel drawingPanel;
-        private GUIPanel sidebarPanel;
+
         private GUIGraphTime guiTimeline;
         private GUIGraphTime guiTimeline;
+        private GUIAnimEvents guiEvents;
         private GUICurveDrawing guiCurveDrawing;
         private GUICurveDrawing guiCurveDrawing;
         private GUIGraphValues guiSidebar;
         private GUIGraphValues guiSidebar;
 
 
@@ -90,6 +92,7 @@ namespace BansheeEditor
                 yRange = value.y;
                 yRange = value.y;
 
 
                 guiTimeline.SetRange(xRange);
                 guiTimeline.SetRange(xRange);
+                guiEvents.SetRange(xRange);
                 guiCurveDrawing.SetRange(xRange, yRange * 2.0f);
                 guiCurveDrawing.SetRange(xRange, yRange * 2.0f);
                 guiSidebar.SetRange(offset.y - yRange, offset.y + yRange);
                 guiSidebar.SetRange(offset.y - yRange, offset.y + yRange);
 
 
@@ -108,8 +111,11 @@ namespace BansheeEditor
                 offset = value;
                 offset = value;
 
 
                 guiTimeline.SetOffset(offset.x);
                 guiTimeline.SetOffset(offset.x);
+                guiEvents.SetOffset(offset.x);
                 guiCurveDrawing.SetOffset(offset);
                 guiCurveDrawing.SetOffset(offset);
                 guiSidebar.SetRange(offset.y - yRange, offset.y + yRange);
                 guiSidebar.SetRange(offset.y - yRange, offset.y + yRange);
+
+                Redraw();
             }
             }
         }
         }
 
 
@@ -155,19 +161,46 @@ namespace BansheeEditor
 
 
             guiTimeline = new GUIGraphTime(gui, width, TIMELINE_HEIGHT);
             guiTimeline = new GUIGraphTime(gui, width, TIMELINE_HEIGHT);
 
 
+            GUIPanel eventsPanel = gui.AddPanel();
+            eventsPanel.SetPosition(0, TIMELINE_HEIGHT);
+            guiEvents = new GUIAnimEvents(eventsPanel, width, EVENTS_HEIGHT);
+            
             drawingPanel = gui.AddPanel();
             drawingPanel = gui.AddPanel();
-            drawingPanel.SetPosition(0, TIMELINE_HEIGHT);
+            drawingPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT);
 
 
-            guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT, curves);
+            guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT - EVENTS_HEIGHT, curves);
             guiCurveDrawing.SetRange(60.0f, 20.0f);
             guiCurveDrawing.SetRange(60.0f, 20.0f);
 
 
-            sidebarPanel = gui.AddPanel(-10);
-            sidebarPanel.SetPosition(0, TIMELINE_HEIGHT);
+            GUIPanel sidebarPanel = gui.AddPanel(-10);
+            sidebarPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT);
 
 
-            guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
+            guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT - EVENTS_HEIGHT);
             guiSidebar.SetRange(-10.0f, 10.0f);
             guiSidebar.SetRange(-10.0f, 10.0f);
         }
         }
 
 
+        /// <summary>
+        /// Converts pixel coordinates relative to the curve drawing area into coordinates in curve space.
+        /// </summary>
+        /// <param name="pixelCoords">Coordinates relative to this GUI element, in pixels.</param>
+        /// <param name="curveCoords">Curve coordinates within the range as specified by <see cref="Range"/>. Only
+        ///                           valid when function returns true.</param>
+        /// <returns>True if the coordinates are within the curve area, false otherwise.</returns>
+        public bool PixelToCurveSpace(Vector2I pixelCoords, out Vector2 curveCoords)
+        {
+            return guiCurveDrawing.PixelToCurveSpace(pixelCoords, out curveCoords);
+        }
+
+        /// <summary>
+        /// Converts coordinate in curve space (time, value) into pixel coordinates relative to the curve drawing area
+        /// origin.
+        /// </summary>
+        /// <param name="curveCoords">Time and value of the location to convert.</param>
+        /// <returns>Coordinates relative to curve drawing area's origin, in pixels.</returns>
+        public Vector2I CurveToPixelSpace(Vector2 curveCoords)
+        {
+            return guiCurveDrawing.CurveToPixelSpace(curveCoords);
+        }
+
         public bool WindowToCurveSpace(Vector2I windowPos, out Vector2 curveCoord)
         public bool WindowToCurveSpace(Vector2I windowPos, out Vector2 curveCoord)
         {
         {
             Rect2I elementBounds = GUIUtility.CalculateBounds(gui, window.GUI);
             Rect2I elementBounds = GUIUtility.CalculateBounds(gui, window.GUI);
@@ -232,6 +265,14 @@ namespace BansheeEditor
 
 
                     if (frameIdx != -1)
                     if (frameIdx != -1)
                         SetMarkedFrame(frameIdx);
                         SetMarkedFrame(frameIdx);
+                    else
+                    {
+                        int eventIdx;
+                        if (guiEvents.FindEvent(pointerPos, out eventIdx))
+                        {
+                            // TODO - Select event
+                        }
+                    }
 
 
                     OnFrameSelected?.Invoke(frameIdx);
                     OnFrameSelected?.Invoke(frameIdx);
                 }
                 }
@@ -448,8 +489,9 @@ namespace BansheeEditor
             this.height = height;
             this.height = height;
 
 
             guiTimeline.SetSize(width, TIMELINE_HEIGHT);
             guiTimeline.SetSize(width, TIMELINE_HEIGHT);
-            guiCurveDrawing.SetSize(width, height - TIMELINE_HEIGHT);
-            guiSidebar.SetSize(SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
+            guiEvents.SetSize(height, EVENTS_HEIGHT);
+            guiCurveDrawing.SetSize(width, height - TIMELINE_HEIGHT - EVENTS_HEIGHT);
+            guiSidebar.SetSize(SIDEBAR_WIDTH, height - TIMELINE_HEIGHT - EVENTS_HEIGHT);
 
 
             Redraw();
             Redraw();
         }
         }
@@ -461,6 +503,7 @@ namespace BansheeEditor
         public void SetFPS(int fps)
         public void SetFPS(int fps)
         {
         {
             guiTimeline.SetFPS(fps);
             guiTimeline.SetFPS(fps);
+            guiEvents.SetFPS(fps);
             guiCurveDrawing.SetFPS(fps);
             guiCurveDrawing.SetFPS(fps);
 
 
             Redraw();
             Redraw();
@@ -512,6 +555,7 @@ namespace BansheeEditor
         {
         {
             guiCurveDrawing.Rebuild();
             guiCurveDrawing.Rebuild();
             guiTimeline.Rebuild();
             guiTimeline.Rebuild();
+            guiEvents.Rebuild();
             guiSidebar.Rebuild();
             guiSidebar.Rebuild();
         }
         }
         
         

+ 6 - 6
Source/MBansheeEditor/Windows/Animation/GUIGraphTime.cs

@@ -96,7 +96,7 @@ namespace BansheeEditor
             tickHeight = (int)(height * TICK_HEIGHT_PCT);
             tickHeight = (int)(height * TICK_HEIGHT_PCT);
             drawableWidth = Math.Max(0, width - PADDING * 2);
             drawableWidth = Math.Max(0, width - PADDING * 2);
 
 
-            tickHandler.SetRange(rangeOffset, GetRange(true), drawableWidth + PADDING);
+            tickHandler.SetRange(rangeOffset, rangeOffset + GetRange(true), drawableWidth + PADDING);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -107,7 +107,7 @@ namespace BansheeEditor
         {
         {
             rangeLength = Math.Max(0.0f, length);
             rangeLength = Math.Max(0.0f, length);
 
 
-            tickHandler.SetRange(rangeOffset, GetRange(true), drawableWidth + PADDING);
+            tickHandler.SetRange(rangeOffset, rangeOffset + GetRange(true), drawableWidth + PADDING);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -118,7 +118,7 @@ namespace BansheeEditor
         {
         {
             rangeOffset = offset;
             rangeOffset = offset;
 
 
-            tickHandler.SetRange(rangeOffset, GetRange(true), drawableWidth + PADDING);
+            tickHandler.SetRange(rangeOffset, rangeOffset + GetRange(true), drawableWidth + PADDING);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -129,7 +129,7 @@ namespace BansheeEditor
         {
         {
             this.fps = Math.Max(1, fps);
             this.fps = Math.Max(1, fps);
 
 
-            tickHandler.SetRange(rangeOffset, GetRange(true), drawableWidth + PADDING);
+            tickHandler.SetRange(rangeOffset, rangeOffset + GetRange(true), drawableWidth + PADDING);
         }
         }
         
         
         /// <summary>
         /// <summary>
@@ -206,12 +206,12 @@ namespace BansheeEditor
         /// <returns>Time range rounded to a multiple of FPS.</returns>
         /// <returns>Time range rounded to a multiple of FPS.</returns>
         private float GetRange(bool padding = false)
         private float GetRange(bool padding = false)
         {
         {
-            float spf = 1.0f/fps;
+            float spf = 1.0f / fps;
 
 
             float range = rangeLength;
             float range = rangeLength;
             if (padding)
             if (padding)
             {
             {
-                float lengthPerPixel = rangeLength/drawableWidth;
+                float lengthPerPixel = rangeLength / drawableWidth;
                 range += lengthPerPixel * PADDING;
                 range += lengthPerPixel * PADDING;
             }
             }
 
 

+ 39 - 28
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -19,7 +19,7 @@ namespace BansheeEditor
         private const int FIELD_DISPLAY_WIDTH = 200;
         private const int FIELD_DISPLAY_WIDTH = 200;
         private const int DRAG_START_DISTANCE = 3;
         private const int DRAG_START_DISTANCE = 3;
         private const float DRAG_SCALE = 10.0f;
         private const float DRAG_SCALE = 10.0f;
-        private const float ZOOM_SCALE = 15.0f;
+        private const float ZOOM_SCALE = 0.1f/120.0f; // One scroll step is usually 120 units, we want 1/10 of that
 
 
         private bool isInitialized;
         private bool isInitialized;
         private SceneObject selectedSO;
         private SceneObject selectedSO;
@@ -114,7 +114,12 @@ namespace BansheeEditor
             curves.Clear();
             curves.Clear();
             isInitialized = false;
             isInitialized = false;
 
 
-            selectedSO = Selection.SceneObject;
+            if (selectedSO != Selection.SceneObject)
+            {
+                zoomAmount = 0.0f;
+                selectedSO = Selection.SceneObject;
+            }
+
             if (selectedSO == null)
             if (selectedSO == null)
             {
             {
                 GUILabel warningLbl = new GUILabel(new LocEdString("Select an object to animate in the Hierarchy or Scene windows."));
                 GUILabel warningLbl = new GUILabel(new LocEdString("Select an object to animate in the Hierarchy or Scene windows."));
@@ -361,7 +366,7 @@ namespace BansheeEditor
             float scrollableRange = totalRange.y - visibleRange.y;
             float scrollableRange = totalRange.y - visibleRange.y;
 
 
             Vector2 offset = guiCurveEditor.Offset;
             Vector2 offset = guiCurveEditor.Offset;
-            offset.y = scrollableRange * (position * 2.0f - 1.0f);
+            offset.y = -scrollableRange * (position * 2.0f - 1.0f);
 
 
             guiCurveEditor.Offset = offset;
             guiCurveEditor.Offset = offset;
         }
         }
@@ -398,12 +403,22 @@ namespace BansheeEditor
             Vector2 scrollableRange = totalRange - visibleRange;
             Vector2 scrollableRange = totalRange - visibleRange;
 
 
             Vector2 offset = guiCurveEditor.Offset;
             Vector2 offset = guiCurveEditor.Offset;
-            // Transform Y from [-x, +x] range to [0, x]
-            offset.y += visibleRange.y;
-            offset.y /= 2.0f;
+            if (scrollableRange.x > 0.0f)
+                horzScrollBar.Position = offset.x / scrollableRange.x;
+            else
+                horzScrollBar.Position = 0.0f;
 
 
-            horzScrollBar.Position = offset.x / scrollableRange.x;
-            vertScrollBar.Position = offset.y / scrollableRange.y;
+            if (scrollableRange.y > 0.0f)
+            {
+                float pos = offset.y/scrollableRange.y;
+                float sign = MathEx.Sign(pos);
+                pos = sign*MathEx.Clamp01(MathEx.Abs(pos));
+                pos = (1.0f - pos) /2.0f;
+
+                vertScrollBar.Position = pos;
+            }
+            else
+                vertScrollBar.Position = 0.0f;
         }
         }
 
 
         private Vector2 GetZoomedRange()
         private Vector2 GetZoomedRange()
@@ -416,41 +431,37 @@ namespace BansheeEditor
 
 
         private Vector2 GetTotalRange()
         private Vector2 GetTotalRange()
         {
         {
-            Vector2 visibleRange = guiCurveEditor.Range;
-            Vector2 totalRange = guiCurveEditor.Offset;
-            totalRange.x += visibleRange.x;
-            totalRange.y = Math.Abs(totalRange.y) + visibleRange.y;
-
+            // Return optimal range (that covers the visible curve)
             Vector2 optimalRange = GetOptimalRange();
             Vector2 optimalRange = GetOptimalRange();
-            return Vector2.Max(totalRange, optimalRange);
+
+            // Increase range in case user zoomed out
+            Vector2 zoomedRange = GetZoomedRange();
+            return Vector2.Max(optimalRange, zoomedRange);
         }
         }
 
 
         private void Zoom(Vector2 curvePos, float amount)
         private void Zoom(Vector2 curvePos, float amount)
         {
         {
+            // Increase or decrease the visible range depending on zoom level
             Vector2 oldZoomedRange = GetZoomedRange();
             Vector2 oldZoomedRange = GetZoomedRange();
-            zoomAmount += amount;
+            zoomAmount = MathEx.Clamp(zoomAmount + amount, -10.0f, 10.0f);
             Vector2 zoomedRange = GetZoomedRange();
             Vector2 zoomedRange = GetZoomedRange();
 
 
             Vector2 zoomedDiff = zoomedRange - oldZoomedRange;
             Vector2 zoomedDiff = zoomedRange - oldZoomedRange;
-            zoomedDiff.y *= 0.5f;
 
 
             Vector2 currentRange = guiCurveEditor.Range;
             Vector2 currentRange = guiCurveEditor.Range;
             Vector2 newRange = currentRange + zoomedDiff;
             Vector2 newRange = currentRange + zoomedDiff;
+            guiCurveEditor.Range = newRange;
 
 
-            Vector2 offset = guiCurveEditor.Offset;
-            Vector2 relativePos = curvePos - offset;
-
-            relativePos.x /= currentRange.x;
-            relativePos.y /= currentRange.y;
-
-            relativePos.x = relativePos.x * 2.0f - 1.0f;
-            relativePos.y = relativePos.y * 2.0f - 1.0f;
+            // When zooming, make sure to focus on the point provided, so adjust the offset
+            Vector2 rangeScale = newRange;
+            rangeScale.x /= currentRange.x;
+            rangeScale.y /= currentRange.y;
 
 
-            offset.x += relativePos.x * zoomedDiff.x;
-            offset.y += relativePos.y * zoomedDiff.y * 2.0f;
+            Vector2 relativeCurvePos = curvePos - guiCurveEditor.Offset;
+            Vector2 newCurvePos = relativeCurvePos * rangeScale;
+            Vector2 diff = newCurvePos - relativeCurvePos;
 
 
-            guiCurveEditor.Offset = offset;
-            guiCurveEditor.Range = newRange;
+            guiCurveEditor.Offset -= diff;
 
 
             UpdateScrollBarSize();
             UpdateScrollBarSize();
             UpdateScrollBarPosition();
             UpdateScrollBarPosition();