Marko Pintera 13 years ago
parent
commit
62cc09827b

+ 3 - 0
CamelotRenderer/CamelotRenderer.vcxproj

@@ -113,6 +113,8 @@
     <ClInclude Include="Include\CmHighLevelGpuProgram.h" />
     <ClInclude Include="Include\CmHighLevelGpuProgramManager.h" />
     <ClInclude Include="Include\CmImporter.h" />
+    <ClInclude Include="Include\CmInput.h" />
+    <ClInclude Include="Include\CmInputHandler.h" />
     <ClInclude Include="Include\CmMesh.h" />
     <ClInclude Include="Include\CmMeshData.h" />
     <ClInclude Include="Include\CmMeshDataRTTI.h" />
@@ -162,6 +164,7 @@
     <ClCompile Include="Source\CmHighLevelGpuProgram.cpp" />
     <ClCompile Include="Source\CmHighLevelGpuProgramManager.cpp" />
     <ClCompile Include="Source\CmImporter.cpp" />
+    <ClCompile Include="Source\CmInput.cpp" />
     <ClCompile Include="Source\CmMesh.cpp" />
     <ClCompile Include="Source\CmMeshData.cpp" />
     <ClCompile Include="Source\CmRenderSystem.cpp" />

+ 15 - 0
CamelotRenderer/CamelotRenderer.vcxproj.filters

@@ -55,6 +55,12 @@
     <Filter Include="Header Files\RTTI">
       <UniqueIdentifier>{75249db9-4f2e-43c3-8df4-37250c4b60a2}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Header Files\Input">
+      <UniqueIdentifier>{724588b9-04e2-4e9b-9467-b064ed44f05e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\Input">
+      <UniqueIdentifier>{7f8e94f3-6990-4723-965a-2b4f9346a7ee}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <Text Include="TODO.txt" />
@@ -208,6 +214,12 @@
     <ClInclude Include="Include\CmVertexDeclarationRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmInput.h">
+      <Filter>Header Files\Input</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmInputHandler.h">
+      <Filter>Header Files\Input</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CamelotRenderer.cpp">
@@ -321,5 +333,8 @@
     <ClCompile Include="Source\CmMeshData.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmInput.cpp">
+      <Filter>Source Files\Input</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 86 - 0
CamelotRenderer/Include/CmInput.h

@@ -0,0 +1,86 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmModule.h"
+#include "CmRect.h"
+#include "CmInputHandler.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT Input : public Module<Input>
+	{
+	public:
+		Input();
+		~Input();
+
+		boost::signal<void(KeyCode)> onKeyDown;
+		boost::signal<void(KeyCode)> onKeyUp;
+
+		boost::signal<void(const MouseEvent&)> onMouseMoved;
+		boost::signal<void(const MouseEvent&, MouseButton)> onMouseDown;
+		boost::signal<void(const MouseEvent&, MouseButton)> onMouseUp;
+
+		/**
+		 * @brief	Initializes the input manager with a specific input handler that sends input events
+		 * 			to the manager. Should only be called by Application.
+		 *
+		 * @param [in]	inputHandler	Input handler that determines how do we capture input.
+		 * @param [in]	clipRect		If non-zero, mouse input will be ignored unless within the specified area.
+		 */
+		void init(std::shared_ptr<InputHandler> inputHandler, Rect& clipRect);
+
+		/**
+		 * @brief	Called every frame. Should only be called by Application.
+		 */
+		void update();
+
+		/**
+		 * @brief	Returns smoothed mouse/joystick input in the horizontal axis.
+		 *
+		 * @return	The horizontal axis value ranging [-1.0f, 1.0f].
+		 */
+		float getHorizontalAxis() const;
+
+		/**
+		 * @brief	Returns smoothed mouse/joystick input in the vertical axis.
+		 *
+		 * @return	The vertical axis value ranging [-1.0f, 1.0f].
+		 */
+		float getVerticalAxis() const;
+
+	private:
+		std::shared_ptr<InputHandler> mInputHandler;
+
+		float mSmoothHorizontalAxis;
+		float mSmoothVerticalAxis;
+
+		float* mHorizontalHistoryBuffer;
+		float* mVerticalHistoryBuffer;
+		float* mTimesHistoryBuffer;
+		int	mCurrentBufferIdx;
+
+		Point mMouseLastRel;
+		Rect mClipRect;
+		bool mUsingClipRect;
+
+		void keyDown(KeyCode keyCode);
+		void keyUp(KeyCode keyCode);
+
+		void mouseMoved(const MouseEvent& event);
+		void mouseDown(const MouseEvent& event, MouseButton buttonID);
+		void mouseUp(const MouseEvent& event, MouseButton buttonID);
+
+		/**
+		 * @brief	Updates the input values that need smoothing.
+		 */
+		void updateSmoothInput();
+
+		/************************************************************************/
+		/* 								STATICS		                      		*/
+		/************************************************************************/
+		static const int HISTORY_BUFFER_SIZE; // Size of buffer used for input smoothing
+		static const float WEIGHT_MODIFIER;
+	};
+
+	CM_EXPORT Input& gInput();
+}

+ 194 - 0
CamelotRenderer/Include/CmInputHandler.h

@@ -0,0 +1,194 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include <boost/signal.hpp>
+#include "CmPoint.h"
+
+namespace CamelotEngine
+{
+	enum KeyCode
+	{
+		KC_UNASSIGNED  = 0x00,
+		KC_ESCAPE      = 0x01,
+		KC_1           = 0x02,
+		KC_2           = 0x03,
+		KC_3           = 0x04,
+		KC_4           = 0x05,
+		KC_5           = 0x06,
+		KC_6           = 0x07,
+		KC_7           = 0x08,
+		KC_8           = 0x09,
+		KC_9           = 0x0A,
+		KC_0           = 0x0B,
+		KC_MINUS       = 0x0C,    // - on main keyboard
+		KC_EQUALS      = 0x0D,
+		KC_BACK        = 0x0E,    // backspace
+		KC_TAB         = 0x0F,
+		KC_Q           = 0x10,
+		KC_W           = 0x11,
+		KC_E           = 0x12,
+		KC_R           = 0x13,
+		KC_T           = 0x14,
+		KC_Y           = 0x15,
+		KC_U           = 0x16,
+		KC_I           = 0x17,
+		KC_O           = 0x18,
+		KC_P           = 0x19,
+		KC_LBRACKET    = 0x1A,
+		KC_RBRACKET    = 0x1B,
+		KC_RETURN      = 0x1C,    // Enter on main keyboard
+		KC_LCONTROL    = 0x1D,
+		KC_A           = 0x1E,
+		KC_S           = 0x1F,
+		KC_D           = 0x20,
+		KC_F           = 0x21,
+		KC_G           = 0x22,
+		KC_H           = 0x23,
+		KC_J           = 0x24,
+		KC_K           = 0x25,
+		KC_L           = 0x26,
+		KC_SEMICOLON   = 0x27,
+		KC_APOSTROPHE  = 0x28,
+		KC_GRAVE       = 0x29,    // accent
+		KC_LSHIFT      = 0x2A,
+		KC_BACKSLASH   = 0x2B,
+		KC_Z           = 0x2C,
+		KC_X           = 0x2D,
+		KC_C           = 0x2E,
+		KC_V           = 0x2F,
+		KC_B           = 0x30,
+		KC_N           = 0x31,
+		KC_M           = 0x32,
+		KC_COMMA       = 0x33,
+		KC_PERIOD      = 0x34,    // . on main keyboard
+		KC_SLASH       = 0x35,    // / on main keyboard
+		KC_RSHIFT      = 0x36,
+		KC_MULTIPLY    = 0x37,    // * on numeric keypad
+		KC_LMENU       = 0x38,    // left Alt
+		KC_SPACE       = 0x39,
+		KC_CAPITAL     = 0x3A,
+		KC_F1          = 0x3B,
+		KC_F2          = 0x3C,
+		KC_F3          = 0x3D,
+		KC_F4          = 0x3E,
+		KC_F5          = 0x3F,
+		KC_F6          = 0x40,
+		KC_F7          = 0x41,
+		KC_F8          = 0x42,
+		KC_F9          = 0x43,
+		KC_F10         = 0x44,
+		KC_NUMLOCK     = 0x45,
+		KC_SCROLL      = 0x46,    // Scroll Lock
+		KC_NUMPAD7     = 0x47,
+		KC_NUMPAD8     = 0x48,
+		KC_NUMPAD9     = 0x49,
+		KC_SUBTRACT    = 0x4A,    // - on numeric keypad
+		KC_NUMPAD4     = 0x4B,
+		KC_NUMPAD5     = 0x4C,
+		KC_NUMPAD6     = 0x4D,
+		KC_ADD         = 0x4E,    // + on numeric keypad
+		KC_NUMPAD1     = 0x4F,
+		KC_NUMPAD2     = 0x50,
+		KC_NUMPAD3     = 0x51,
+		KC_NUMPAD0     = 0x52,
+		KC_DECIMAL     = 0x53,    // . on numeric keypad
+		KC_OEM_102     = 0x56,    // < > | on UK/Germany keyboards
+		KC_F11         = 0x57,
+		KC_F12         = 0x58,
+		KC_F13         = 0x64,    //                     (NEC PC98)
+		KC_F14         = 0x65,    //                     (NEC PC98)
+		KC_F15         = 0x66,    //                     (NEC PC98)
+		KC_KANA        = 0x70,    // (Japanese keyboard)
+		KC_ABNT_C1     = 0x73,    // / ? on Portugese (Brazilian) keyboards
+		KC_CONVERT     = 0x79,    // (Japanese keyboard)
+		KC_NOCONVERT   = 0x7B,    // (Japanese keyboard)
+		KC_YEN         = 0x7D,    // (Japanese keyboard)
+		KC_ABNT_C2     = 0x7E,    // Numpad . on Portugese (Brazilian) keyboards
+		KC_NUMPADEQUALS= 0x8D,    // = on numeric keypad (NEC PC98)
+		KC_PREVTRACK   = 0x90,    // Previous Track (KC_CIRCUMFLEX on Japanese keyboard)
+		KC_AT          = 0x91,    //                     (NEC PC98)
+		KC_COLON       = 0x92,    //                     (NEC PC98)
+		KC_UNDERLINE   = 0x93,    //                     (NEC PC98)
+		KC_KANJI       = 0x94,    // (Japanese keyboard)
+		KC_STOP        = 0x95,    //                     (NEC PC98)
+		KC_AX          = 0x96,    //                     (Japan AX)
+		KC_UNLABELED   = 0x97,    //                        (J3100)
+		KC_NEXTTRACK   = 0x99,    // Next Track
+		KC_NUMPADENTER = 0x9C,    // Enter on numeric keypad
+		KC_RCONTROL    = 0x9D,
+		KC_MUTE        = 0xA0,    // Mute
+		KC_CALCULATOR  = 0xA1,    // Calculator
+		KC_PLAYPAUSE   = 0xA2,    // Play / Pause
+		KC_MEDIASTOP   = 0xA4,    // Media Stop
+		KC_VOLUMEDOWN  = 0xAE,    // Volume -
+		KC_VOLUMEUP    = 0xB0,    // Volume +
+		KC_WEBHOME     = 0xB2,    // Web home
+		KC_NUMPADCOMMA = 0xB3,    // , on numeric keypad (NEC PC98)
+		KC_DIVIDE      = 0xB5,    // / on numeric keypad
+		KC_SYSRQ       = 0xB7,
+		KC_RMENU       = 0xB8,    // right Alt
+		KC_PAUSE       = 0xC5,    // Pause
+		KC_HOME        = 0xC7,    // Home on arrow keypad
+		KC_UP          = 0xC8,    // UpArrow on arrow keypad
+		KC_PGUP        = 0xC9,    // PgUp on arrow keypad
+		KC_LEFT        = 0xCB,    // LeftArrow on arrow keypad
+		KC_RIGHT       = 0xCD,    // RightArrow on arrow keypad
+		KC_END         = 0xCF,    // End on arrow keypad
+		KC_DOWN        = 0xD0,    // DownArrow on arrow keypad
+		KC_PGDOWN      = 0xD1,    // PgDn on arrow keypad
+		KC_INSERT      = 0xD2,    // Insert on arrow keypad
+		KC_DELETE      = 0xD3,    // Delete on arrow keypad
+		KC_LWIN        = 0xDB,    // Left Windows key
+		KC_RWIN        = 0xDC,    // Right Windows key
+		KC_APPS        = 0xDD,    // AppMenu key
+		KC_POWER       = 0xDE,    // System Power
+		KC_SLEEP       = 0xDF,    // System Sleep
+		KC_WAKE        = 0xE3,    // System Wake
+		KC_WEBSEARCH   = 0xE5,    // Web Search
+		KC_WEBFAVORITES= 0xE6,    // Web Favorites
+		KC_WEBREFRESH  = 0xE7,    // Web Refresh
+		KC_WEBSTOP     = 0xE8,    // Web Stop
+		KC_WEBFORWARD  = 0xE9,    // Web Forward
+		KC_WEBBACK     = 0xEA,    // Web Back
+		KC_MYCOMPUTER  = 0xEB,    // My Computer
+		KC_MAIL        = 0xEC,    // Mail
+		KC_MEDIASELECT = 0xED     // Media Select
+	};
+
+	enum MouseButton
+	{
+		MB_Left = 0, MB_Right, MB_Middle,
+		MB_Button3, MB_Button4,	MB_Button5, MB_Button6,	MB_Button7
+	};
+
+	struct MouseEvent
+	{
+		Point coords;
+		Point relCoords;
+
+		int z;
+		int relZ;
+	};
+
+	/**
+	 * @brief	Represents a specific way of acquiring low-level input. InputManager (which provides a higher level input)
+	 * 			must have at least one InputHandler attached. Attach events handler to the provided signals to handle input.
+	 */
+	class InputHandler
+	{
+	public:
+		virtual ~InputHandler() {}
+
+		boost::signal<void(KeyCode)> onKeyDown;
+		boost::signal<void(KeyCode)> onKeyUp;
+
+		boost::signal<void(const MouseEvent&)> onMouseMoved;
+		boost::signal<void(const MouseEvent&, MouseButton)> onMouseDown;
+		boost::signal<void(const MouseEvent&, MouseButton)> onMouseUp;
+
+		/**
+		 * @brief	Called every frame by InputManager. Capture input here if needed.
+		 */
+		virtual void update() {}
+	};
+}

+ 8 - 0
CamelotRenderer/Source/CmApplication.cpp

@@ -18,6 +18,8 @@
 #include "CmResources.h"
 #include "CmMesh.h"
 #include "CmGameObject.h"
+#include "CmTime.h"
+#include "CmInput.h"
 
 namespace CamelotEngine
 {
@@ -27,6 +29,8 @@ namespace CamelotEngine
 
 	void Application::startUp(String renderSystemDll)
 	{
+		Time::startUp(new Time());
+		Input::startUp(new Input());
 		DynLibManager::startUp(new DynLibManager());
 		HighLevelGpuProgramManager::startUp(new HighLevelGpuProgramManager());
 
@@ -174,6 +178,8 @@ namespace CamelotEngine
 			WindowEventUtilities::messagePump();
 
 			DBG_renderSimpleFrame();
+
+			gTime().update();
 		}
 	}
 
@@ -187,6 +193,8 @@ namespace CamelotEngine
 		HighLevelGpuProgramManager::shutDown();
 		DynLibManager::shutDown();
 		Resources::shutDown();
+		Input::shutDown();
+		Time::shutDown();
 	}
 
 	void Application::DBG_renderSimpleFrame()

+ 152 - 0
CamelotRenderer/Source/CmInput.cpp

@@ -0,0 +1,152 @@
+#include "CmInput.h"
+#include "CmTime.h"
+#include "CmMath.h"
+#include "CmRect.h"
+
+#include <boost/bind.hpp>
+
+namespace CamelotEngine
+{
+	const int Input::HISTORY_BUFFER_SIZE = 10; // Size of buffer used for input smoothing
+	const float Input::WEIGHT_MODIFIER = 0.5f;
+
+	Input::Input()
+		:mSmoothHorizontalAxis(0.0f), mSmoothVerticalAxis(0.0f), mCurrentBufferIdx(0), mMouseLastRel(0, 0),
+		mUsingClipRect(false), mClipRect(0, 0, 0, 0)
+	{ 
+		mHorizontalHistoryBuffer = new float[HISTORY_BUFFER_SIZE];
+		mVerticalHistoryBuffer = new float[HISTORY_BUFFER_SIZE];
+		mTimesHistoryBuffer = new float[HISTORY_BUFFER_SIZE];
+
+		for(int i = 0; i < HISTORY_BUFFER_SIZE; i++)
+		{
+			mHorizontalHistoryBuffer[i] = 0.0f;
+			mVerticalHistoryBuffer[i] = 0.0f;
+			mTimesHistoryBuffer[i] = 0.0f;
+		}
+	}
+
+	Input::~Input()
+	{
+		delete[] mHorizontalHistoryBuffer;
+		delete[] mVerticalHistoryBuffer;
+		delete[] mTimesHistoryBuffer;
+	}
+
+	void Input::init(std::shared_ptr<InputHandler> inputHandler, Rect& clipRect)
+	{
+		mInputHandler = inputHandler;
+		mClipRect = clipRect;
+
+		mUsingClipRect = (clipRect.width > 0 && clipRect.height > 0);
+
+		mInputHandler->onKeyDown.connect(boost::bind(&Input::keyDown, this, _1));
+		mInputHandler->onKeyUp.connect(boost::bind(&Input::keyUp, this, _1));
+
+		mInputHandler->onMouseMoved.connect(boost::bind(&Input::mouseMoved, this, _1));
+		mInputHandler->onMouseDown.connect(boost::bind(&Input::mouseDown, this, _1, _2));
+		mInputHandler->onMouseUp.connect(boost::bind(&Input::mouseUp, this, _1, _2));
+	}
+
+	void Input::update()
+	{
+		mInputHandler->update();
+
+		updateSmoothInput();
+	}
+
+	void Input::keyDown(KeyCode keyCode)
+	{
+		onKeyDown(keyCode);
+	}
+
+	void Input::keyUp(KeyCode keyCode)
+	{
+		onKeyUp(keyCode);
+	}
+
+	void Input::mouseMoved(const MouseEvent& event)
+	{
+		if(mUsingClipRect)
+		{
+			if(!mClipRect.contains(event.coords))
+				return;
+		}
+
+		onMouseMoved(event);
+
+		mMouseLastRel = Point(-event.relCoords.x, -event.relCoords.y);
+	}
+
+	void Input::mouseDown(const MouseEvent& event, MouseButton buttonID)
+	{
+		if(mUsingClipRect)
+		{
+			if(!mClipRect.contains(event.coords))
+				return;
+		}
+
+		onMouseDown(event, buttonID);
+	}
+
+	void Input::mouseUp(const MouseEvent& event, MouseButton buttonID)
+	{
+		if(mUsingClipRect)
+		{
+			if(!mClipRect.contains(event.coords))
+				return;
+		}
+
+		onMouseUp(event, buttonID);
+	}
+
+	float Input::getHorizontalAxis() const
+	{
+		return mSmoothHorizontalAxis;
+	}
+
+	float Input::getVerticalAxis() const
+	{
+		return mSmoothVerticalAxis;
+	}
+
+	void Input::updateSmoothInput()
+	{
+		float currentTime = gTime().getTimeSinceApplicationStart();
+
+		mHorizontalHistoryBuffer[mCurrentBufferIdx] = (float)mMouseLastRel.x;
+		mVerticalHistoryBuffer[mCurrentBufferIdx] = (float)mMouseLastRel.y;
+		mTimesHistoryBuffer[mCurrentBufferIdx] = currentTime;
+
+		int i = 0;
+		int idx = mCurrentBufferIdx;
+		float currentWeight = 1.0f;
+		float horizontalTotal = 0.0f;
+		float verticalTotal = 0.0f;
+		while(i < HISTORY_BUFFER_SIZE)
+		{
+			float timeWeight = 1.0f - (currentTime - mTimesHistoryBuffer[idx]) * 10.0f;
+			if(timeWeight < 0.0f)
+				timeWeight = 0.0f;
+
+			horizontalTotal += mHorizontalHistoryBuffer[idx] * currentWeight * timeWeight;
+			verticalTotal += mVerticalHistoryBuffer[idx] * currentWeight * timeWeight;
+
+			currentWeight *= WEIGHT_MODIFIER;
+			idx = (idx + 1) % HISTORY_BUFFER_SIZE;
+			i++;
+		}
+
+		mCurrentBufferIdx = (mCurrentBufferIdx + 1) % HISTORY_BUFFER_SIZE;
+
+		mSmoothHorizontalAxis = Math::Clamp(horizontalTotal / HISTORY_BUFFER_SIZE, -1.0f, 1.0f);
+		mSmoothVerticalAxis = Math::Clamp(verticalTotal / HISTORY_BUFFER_SIZE, -1.0f, 1.0f);
+
+		mMouseLastRel = Point(0, 0);
+	}
+
+	Input& gInput()
+	{
+		return Input::instance();
+	}
+}

+ 38 - 80
CamelotRenderer/TODO.txt

@@ -1,87 +1,50 @@
-PARSED FILES:
 
-GpuProgram - PARSED!
-TextureState - PARSED!
- - This is just an utility class representing texture state
-HardwareVertexBuffer - PARSED!
-Viewport - PARSED!
-HardwareBufferManager - PARSED!
-HighLevelGpuProgram - PARSED!
-VertexIndexData - Just data structures
- - VertexCacheProfiler is a weird one, but I think I don't have to remove it as it might come in handy
-RenderTexture - Everything OK
-RenderWindow - Everything OK
-HardwareIndexBuffer - Everything OK
-RenderSystemCapabilities - I think I need all of that
-HardwareBuffer - Base class for all buffers
- - Everything seems fine although I /might/ want to explore how the shadow buffer works and if I want to implement it differently?
-DefaultHardwareBufferManager - Provides emulation of hardware buffers
- - I think this class is okay to stay, although I'm not sure where its used, but its simple enough
-Texture - PARSED!
-HardwareOcclusionQuery - OK, but I might consider a more generic interface for queries (I'll need profiling queries too for example)
-
-TODO FILES:
-
-Camera - MERGE WITH VIEWPORT
- - Position/rotation should be inherit from transform
- - Viewport needs to be updated when I call RenderTarget::setFullscreen/finishSwitchingFullscreen/updateWindowRect/windowMovedOrResized. Currently it's not
-
-Frustum
- - World space methods need to be updated. I still need them but they need to get their data from Transform
- - Depending on how I implement the frustum, I might be able to remove those "out-of-date" checks
-
-GpuProgramParams - Holds all parameters used in a shader program and allows us to set them by index or name
- - Explore if we can remove GpuSharedParameters. I'm not exactly sure what they're used for
-
-HardwarePixelBuffer 
- - I might consider giving PixelBox a better name. Also rename PixelFormat.h to PixelUtil.h or similar. Makes more sense.
- - _clearSliceRTT method I'm not sure what is it used for, but I might want to remove it
-
-RenderTarget:
- - update() causes rendering to start - This probably need to be moved to camera, as I want it to be responsible for rendering
- - keeps a viewport list, which I also don't think I'll need
+High-level TODO:
+ - Debug tools 
+   - Camera controls + world grid
+ - Renderable (contains mesh/material)
+ - RenderManager (iterated through Renderables and displays them on screen)
+   - And its plugin implementation ForwardRenderingManager
+ - Material/Pass
+ - Resource loading thread safety and background loading
+   - Make sure all resources have default resource that will be used before actual resource is loaded
+ - Integrate with Camelot Editor
+ - SceneManager plugin
+   - Frustum culling and octree (or some other) acceleration structure
+   - Render queue and sorting
+ - DX11 render system
+
+HIGH PRIORITY TODO:
+ - GetRenderOperation doesn't consider sub-meshes
+ - HLSL & Cg don't handle include files yet
 
-TODO:
- - Make sure to parse D3D9 implementations of above classes and see what needs removing there as well
- - OpenGL too
+Mid priority TODO:
+ - Mesh loading:
+  - Example Freefall mesh has one index per vertex, and there are 17k+ vertices. I think I need a post-process step that optimizes them.
+  - Imported FBX meshes are too big
+  - Search for all remaining "TODO PORT" comments and fix them
+  - How do I serialize derived classes, without rewriting all the base class serialization?
+  - Ogre performed special DDS loading. I removed that. I'm not sure if I'll need to re-add it?
 
-TOMORROW:
- - DEBUG TOOLS
-   - Input & camera controls
-   - World grid
+Low priority TODO:
  - Mesh loading:
-   - GetRenderOperation doesn't consider sub-meshes
-   - Imported FBX meshes are too big
-   - Example Freefall mesh has one index per vertex, and there are 17k+ vertices. I think I need a post-process step that optimizes them.
+  - Sub-meshes aren't being transformed by world matrices of their nodes
+  - FBX importer can be greatly sped up by implementing a better allocator
+ - Serializable callbacks can't be null otherwise compiler complains
+ - Depth test is disabled by default (OpenGL renderer at least)
+ - Are resource getting properly unloaded? e.g. when shared_ptr destroys a texture is it removed from gpu?
  - Remove template from RTTIType and move it to IReflectable? This way i can hopefully move GetRTTITypeStatic and GetRTTIType to IReflectable so I don't
    need to manually implement those for every method.
- - Are resource getting properly unloaded? e.g. when shared_ptr destroys a texture is it removed from gpu?
- - Depth test is disabled by default (OpenGL renderer at least)
- - Serializable callbacks can't be null otherwise compiler complains
- - Ogre performed special DDS loading. I removed that. I'm not sure if I'll need to re-add it?
- - How do I serialize derived classes, without rewriting all the base class serialization?
-   - Unless something better dawns on me by Monday, just inherit from parents SerializableType and manually make sure no IDs overlap.
-     - We can improve later if needed. I though about it too much for now.
- Try to fully implement free image and maybe FBX importers
-  - Less important notes:
-    - All of this must be thread safe to allow for background loading
-    - Make sure all resources have default resource that will be used before actual resource is loaded
- - Add precompiled headers to all projects
- - Make sure they include Exception & Log classes
- - HLSL & Cg don't handle include files yet
- - Shader profiles need to be manually added. Check GpuProgramProfiler, and conversions from/to that type in HLSL/Cg files. Cg also needs some special profile setup.
+ - Removed unused methods from D3D9 and GL render systems (mostly fixed function stuff)
+ - Viewport needs to be updated when I call RenderTarget::setFullscreen/finishSwitchingFullscreen/updateWindowRect/windowMovedOrResized. Currently it's not
+ - D3D9Texture::createTextureResources is commented out at the moment. It gets called on device reset, and at that point I should reload texture resources.
+ - Device reset and resource re-loading in general
 
-Other notes:
- - Search for all remaining "TODO PORT" comments and fix them
- - How am I notified on device reset? (When I need to reload my resources)
- - D3D9Texture::createTextureResources is commented out at the moment. It gets called on device reset, and at that point
-     I should reload texture resources.
+Optional TODO:
+ - Add precompiled headers to all projects
  - If possible, make sure GLSL uses EntryPoint and Profile fields I have added to GpuProgram
- - Remove all fixed pipeline methods from D3D9 and OpenGL renderers
- - Make CamelotRenderer a project on its own?
 
- At one point rebuild everything and make sure all warnings are gone
-  - Also scan everything for "Ogre" and remove any references to it
+ -----------------------------------------------------------------------------------------------
 
 After everything is polished:
  - Make sure the renderer can run on a separate thread
@@ -90,8 +53,6 @@ After everything is polished:
     - Maybe just add support for Cg and force everyone to use that? - I'd like to be able to just switch out renderer in a single location and that everything keeps on working without 
 	  further modifications.
  - Port boost threads to std threads (CmThreadDefines.h)
- - Also, create all file format parsers and also make them reside in CamelotUtility
-   (Actual importers will still be implemented in Editor, but parsing capabilities should be available for camelotutility)
  
  - Go to Game Engine Architecture book and make a list of Utility systems we will need (Config files, Parsers, File I/O etc)
  - Go to GEA book and read about resource managers before implementing them
@@ -102,6 +63,3 @@ After everything is polished:
 
  - Textures and all other buffers keep a copy of their data in system memory. If there are memory constraints we might need a way to avoid this.
 
-
- Low priority:
-  FBX import needs a custom allocator, because its very slow and does a lot of heap allocs

+ 6 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -82,6 +82,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="Source\Win32\CmTimer.cpp" />
     <ClInclude Include="Include\CmBinarySerializer.h" />
     <ClInclude Include="Include\CmBitwise.h" />
     <ClInclude Include="Include\CmBox.h" />
@@ -98,6 +99,8 @@
     <ClInclude Include="Include\CmModule.h" />
     <ClInclude Include="Include\CmPath.h" />
     <ClInclude Include="Include\CmPixelUtil.h" />
+    <ClInclude Include="Include\CmPoint.h" />
+    <ClInclude Include="Include\CmRect.h" />
     <ClInclude Include="Include\CmRTTIField.h" />
     <ClInclude Include="Include\CmRTTIManagedDataBlockField.h" />
     <ClInclude Include="Include\CmRTTIPlainField.h" />
@@ -107,6 +110,8 @@
     <ClInclude Include="Include\CmString.h" />
     <ClInclude Include="Include\CmTextureDataRTTI.h" />
     <ClInclude Include="Include\CmThreadDefines.h" />
+    <ClInclude Include="Include\CmTime.h" />
+    <ClInclude Include="Include\CmTimer.h" />
     <ClInclude Include="Include\CmTypes.h" />
     <ClInclude Include="Include\CmFwdDeclUtil.h" />
     <ClInclude Include="Include\CmAxisAlignedBox.h" />
@@ -151,6 +156,7 @@
     <ClCompile Include="Source\CmPlane.cpp" />
     <ClCompile Include="Source\CmQuaternion.cpp" />
     <ClCompile Include="Source\CmString.cpp" />
+    <ClCompile Include="Source\CmTime.cpp" />
     <ClCompile Include="Source\CmVector2.cpp" />
     <ClCompile Include="Source\CmVector3.cpp" />
     <ClCompile Include="Source\CmVector4.cpp" />

+ 18 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -177,6 +177,18 @@
     <ClInclude Include="Include\CmTextureDataRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmPoint.h">
+      <Filter>Header Files\Math</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmRect.h">
+      <Filter>Header Files\Math</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmTimer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmTime.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">
@@ -254,5 +266,11 @@
     <ClCompile Include="Source\CmFileSystem.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmTime.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Win32\CmTimer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 3 - 3
CamelotUtility/Include/CmAxisAlignedBox.h

@@ -69,9 +69,9 @@ namespace CamelotEngine {
 
 	public:
 		/*
-		1-----2
-		/|    /|
-		/ |   / |
+		   1-----2
+		  /|    /|
+		 / |   / |
 		5-----4  |
 		|  0--|--3
 		| /   | /

+ 1 - 0
CamelotUtility/Include/CmFwdDeclUtil.h

@@ -27,6 +27,7 @@ namespace CamelotEngine {
 	class TextureData;
 	class MeshData;
 	class FileSystem;
+	class Timer;
 	// Reflection
 	class IReflectable;
 	class RTTITypeBase;

+ 19 - 0
CamelotUtility/Include/CmPoint.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+
+namespace CamelotEngine
+{
+	class Point
+	{
+	public:
+		Point()
+			:x(0), y(0)
+		{ }
+		Point(int _x, int _y)
+			:x(_x), y(_y)
+		{ }
+
+		int x, y;
+	};
+}

+ 33 - 0
CamelotUtility/Include/CmRect.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+#include "CmPoint.h"
+
+namespace CamelotEngine
+{
+	/**
+	 * @brief	A rectangle. Although you may use any coordinate system, Camelot assumes
+	 * 			that X, Y values represent its bottom left corner, where X increases to the right,
+	 * 			and Y increases upwards.
+	 */
+	class Rect
+	{
+	public:
+		Rect(int _x, int _y, int _width, int _height)
+			:x(_x), y(_y), width(_width), height(_height)
+		{ }
+
+		bool contains(Point point)
+		{
+			if(point.x >= x && point.x <= (x + width))
+			{
+				if(point.y >= y && point.y <= (y + height))
+					return true;
+			}
+
+			return false;
+		}
+
+		int x, y, width, height;
+	};
+}

+ 62 - 0
CamelotUtility/Include/CmTime.h

@@ -0,0 +1,62 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+#include "CmModule.h"
+
+namespace CamelotEngine
+{
+	/**
+	 * @brief	Manages all time related functionality
+	 */
+	class CM_UTILITY_EXPORT Time : public Module<Time>
+	{
+	public:
+		Time();
+		~Time();
+
+		/**
+		 * @brief	Gets the time elapsed since application start.
+		 *
+		 * @return	The time since application start, in seconds.
+		 */
+		float getTimeSinceApplicationStart() { return mTimeSinceStart; }
+
+		/**
+		 * @brief	Gets the time since last frame was executed.
+		 *
+		 * @return	Time since last frame was executed, in seconds.
+		 */
+		float getFrameDelta() { return mFrameDelta; }
+
+		/**
+		 * @brief	Returns the number of the current frame. First frame is 0.
+		 *
+		 * @return	The current frame.
+		 */
+		unsigned long getCurrentFrameNumber() { return mCurrentFrame; }
+
+		/**
+		 * @brief	Called when the application is first started. Should only be called by Application.
+		 */
+		void init();
+
+		/**
+		 * @brief	Called every frame. Should only be called by Application.
+		 */
+		void update();
+
+	private:
+		float mFrameDelta; // Frame delta in seconds
+		float mTimeSinceStart; // Time since start in seconds
+
+		unsigned long mAppStartTime; // Time the application started, in microseconds
+		unsigned long mLastFrameTime; // Time since last runOneFrame call, In microseconds
+		unsigned long mCurrentFrame;
+
+		Timer* mTimer;
+
+		static const double MICROSEC_TO_SEC;
+	};
+
+	CM_UTILITY_EXPORT Time& gTime();
+}

+ 39 - 0
CamelotUtility/Include/CmTimer.h

@@ -0,0 +1,39 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+    (Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2011 Torus Knot Software Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-----------------------------------------------------------------------------
+*/
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+
+//Bring in the specific platform's header file
+#if CM_PLATFORM == CM_PLATFORM_WIN32
+# include "Win32/CmTimerImp.h"
+#elif (CM_PLATFORM == CM_PLATFORM_LINUX)
+# include "GLX/CmTimerImp.h"
+#elif CM_PLATFORM == CM_PLATFORM_APPLE
+# include "OSX/CmTimerImp.h"
+#endif

+ 40 - 0
CamelotUtility/Source/CmTime.cpp

@@ -0,0 +1,40 @@
+#include "CmTime.h"
+#include "CmTimer.h"
+
+namespace CamelotEngine
+{
+	const double Time::MICROSEC_TO_SEC = 1.0/1000000.0;
+
+	Time::Time()
+		:mAppStartTime(0), mLastFrameTime(0), mFrameDelta(0.0f), mTimeSinceStart(0.0f), mCurrentFrame(0)
+	{
+		mTimer = new Timer();
+	}
+
+	Time::~Time()
+	{
+		delete mTimer;
+	}
+
+	void Time::init()
+	{
+		mAppStartTime = mTimer->getMicroseconds();
+	}
+
+	void Time::update()
+	{
+		unsigned long currentFrameTime = mTimer->getMicroseconds();
+
+		mFrameDelta = (float)((currentFrameTime - mLastFrameTime) * MICROSEC_TO_SEC);
+		mTimeSinceStart = (float)(currentFrameTime * MICROSEC_TO_SEC);
+
+		mLastFrameTime = currentFrameTime;
+
+		mCurrentFrame++;
+	}
+
+	Time& gTime()
+	{
+		return Time::instance();
+	}
+}

+ 211 - 0
CamelotUtility/Source/Win32/CmTimer.cpp

@@ -0,0 +1,211 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+    (Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2011 Torus Knot Software Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-----------------------------------------------------------------------------
+*/
+
+#include "CmTimer.h"
+#include "CmBitwise.h"
+
+using namespace CamelotEngine;
+
+//-------------------------------------------------------------------------
+Timer::Timer()
+	: mTimerMask( 0 )
+{
+	reset();
+}
+
+//-------------------------------------------------------------------------
+Timer::~Timer()
+{
+}
+
+//-------------------------------------------------------------------------
+bool Timer::setOption( const String & key, const void * val )
+{
+	if ( key == "QueryAffinityMask" )
+	{
+		// Telling timer what core to use for a timer read
+		DWORD newTimerMask = * static_cast < const DWORD * > ( val );
+
+		// Get the current process core mask
+		DWORD_PTR procMask;
+		DWORD_PTR sysMask;
+		GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
+
+		// If new mask is 0, then set to default behavior, otherwise check
+		// to make sure new timer core mask overlaps with process core mask
+		// and that new timer core mask is a power of 2 (i.e. a single core)
+		if( ( newTimerMask == 0 ) ||
+			( ( ( newTimerMask & procMask ) != 0 ) && Bitwise::isPO2( newTimerMask ) ) )
+		{
+			mTimerMask = newTimerMask;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//-------------------------------------------------------------------------
+void Timer::reset()
+{
+    // Get the current process core mask
+	DWORD_PTR procMask;
+	DWORD_PTR sysMask;
+	GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
+
+	// If procMask is 0, consider there is only one core available
+	// (using 0 as procMask will cause an infinite loop below)
+	if (procMask == 0)
+		procMask = 1;
+
+	// Find the lowest core that this process uses
+	if( mTimerMask == 0 )
+	{
+		mTimerMask = 1;
+		while( ( mTimerMask & procMask ) == 0 )
+		{
+			mTimerMask <<= 1;
+		}
+	}
+
+	HANDLE thread = GetCurrentThread();
+
+	// Set affinity to the first core
+	DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask);
+
+	// Get the constant frequency
+	QueryPerformanceFrequency(&mFrequency);
+
+	// Query the timer
+	QueryPerformanceCounter(&mStartTime);
+	mStartTick = GetTickCount();
+
+	// Reset affinity
+	SetThreadAffinityMask(thread, oldMask);
+
+	mLastTime = 0;
+	mZeroClock = clock();
+}
+
+//-------------------------------------------------------------------------
+unsigned long Timer::getMilliseconds()
+{
+    LARGE_INTEGER curTime;
+
+	HANDLE thread = GetCurrentThread();
+
+	// Set affinity to the first core
+	DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask);
+
+	// Query the timer
+	QueryPerformanceCounter(&curTime);
+
+	// Reset affinity
+	SetThreadAffinityMask(thread, oldMask);
+
+    LONGLONG newTime = curTime.QuadPart - mStartTime.QuadPart;
+    
+    // scale by 1000 for milliseconds
+    unsigned long newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
+
+    // detect and compensate for performance counter leaps
+    // (surprisingly common, see Microsoft KB: Q274323)
+    unsigned long check = GetTickCount() - mStartTick;
+    signed long msecOff = (signed long)(newTicks - check);
+    if (msecOff < -100 || msecOff > 100)
+    {
+        // We must keep the timer running forward :)
+        LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
+        mStartTime.QuadPart += adjust;
+        newTime -= adjust;
+
+        // Re-calculate milliseconds
+        newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
+    }
+
+    // Record last time for adjust
+    mLastTime = newTime;
+
+    return newTicks;
+}
+
+//-------------------------------------------------------------------------
+unsigned long Timer::getMicroseconds()
+{
+    LARGE_INTEGER curTime;
+
+	HANDLE thread = GetCurrentThread();
+
+	// Set affinity to the first core
+	DWORD_PTR oldMask = SetThreadAffinityMask(thread, mTimerMask);
+
+	// Query the timer
+	QueryPerformanceCounter(&curTime);
+
+	// Reset affinity
+	SetThreadAffinityMask(thread, oldMask);
+
+	LONGLONG newTime = curTime.QuadPart - mStartTime.QuadPart;
+    
+    // get milliseconds to check against GetTickCount
+    unsigned long newTicks = (unsigned long) (1000 * newTime / mFrequency.QuadPart);
+    
+    // detect and compensate for performance counter leaps
+    // (surprisingly common, see Microsoft KB: Q274323)
+    unsigned long check = GetTickCount() - mStartTick;
+    signed long msecOff = (signed long)(newTicks - check);
+    if (msecOff < -100 || msecOff > 100)
+    {
+        // We must keep the timer running forward :)
+        LONGLONG adjust = (std::min)(msecOff * mFrequency.QuadPart / 1000, newTime - mLastTime);
+        mStartTime.QuadPart += adjust;
+        newTime -= adjust;
+    }
+
+    // Record last time for adjust
+    mLastTime = newTime;
+
+    // scale by 1000000 for microseconds
+    unsigned long newMicro = (unsigned long) (1000000 * newTime / mFrequency.QuadPart);
+
+    return newMicro;
+}
+
+//-------------------------------------------------------------------------
+unsigned long Timer::getMillisecondsCPU()
+{
+	clock_t newClock = clock();
+	return (unsigned long)( (float)( newClock - mZeroClock ) / ( (float)CLOCKS_PER_SEC / 1000.0 ) ) ;
+}
+
+//-------------------------------------------------------------------------
+unsigned long Timer::getMicrosecondsCPU()
+{
+	clock_t newClock = clock();
+	return (unsigned long)( (float)( newClock - mZeroClock ) / ( (float)CLOCKS_PER_SEC / 1000000.0 ) ) ;
+}