Game Programming 05 3D math for Games 2010 년 2 학기 디지털콘텐츠전공
Unbuffered Input
Call-back Driven Framework Framework: –A partially-constructed application Encapsulating knowledge of how to use the low-level libraries Providing functions for the game flow But functions are mostly empty –Need to be completed by a programmer Fill-in the missing details Overide call-back functions
Call-back Driven Framework Example from Ogre3D’s game loop while (true) { for (each frameListener) { frameListener.frameStarted(); } renderCurrentScene(); for (each frameListener) { frameListener.frameEnded(); } while (true) { for (each frameListener) { frameListener.frameStarted(); } renderCurrentScene(); for (each frameListener) { frameListener.frameEnded(); }
Call-back Driven Framework Using Ogre’s FrameListener Class GameFrameListener : public Ogre::FrameListener { public: virtual void frameStarted(const FrameEvent &event) { // Do things happening before rendering pollJoypad(event); updatePlayerControls(events); updateDynamicSimulation(events); resolveCollisions(events); updateCamera(events); // etc. } virtual void frameEnded(const FrameEvent &event) { // Do things after rendering drawHud(events); } Class GameFrameListener : public Ogre::FrameListener { public: virtual void frameStarted(const FrameEvent &event) { // Do things happening before rendering pollJoypad(event); updatePlayerControls(events); updateDynamicSimulation(events); resolveCollisions(events); updateCamera(events); // etc. } virtual void frameEnded(const FrameEvent &event) { // Do things after rendering drawHud(events); }
OGRE3D: Frame Listener 한 장면이 화면에 렌더링되기 직전 / 직후에 해야 할 일을 정의하고 있는 객체 추가 작업을 위해서는 frame listener 를 추가 생성 하고, 이를 root 에 등록시켜 사용 여러 개의 frame listener 가 존재할 수 있다.
Frame Listener 의 맴버함수 frameStarted Called just before a frame is rendered. frameRenderingQueued Called after all render targets have had their r endering commands issued, but before the render windows have been asked to flip th eir buffers over frameEnded Called just after a frame has been rendered. virtual bool frameStarted(const FrameEvent& evt); virtual bool frameRenderingQueued(const FrameEvent& evt); virtual bool frameEnded(const FrameEvent& evt) virtual bool frameStarted(const FrameEvent& evt); virtual bool frameRenderingQueued(const FrameEvent& evt); virtual bool frameEnded(const FrameEvent& evt) FrameEvent structure: Real timeSinceLastEvent, Real timeSinceLastFrame 값을 기억하는 구조체
Frame Listener 의 호출 Root 객체의 rendering loop 내에서 호출 bool Root::renderOneFrame(void) { if(!_fireFrameStarted()) return false; if (!_updateAllRenderTargets()) return false; return _fireFrameEnded(); } bool Root::renderOneFrame(void) { if(!_fireFrameStarted()) return false; if (!_updateAllRenderTargets()) return false; return _fireFrameEnded(); }
Ogre Engine 의 Rendering Loop Root::startRendering() 함수에서 이뤄짐 Redering Loop 실행 – 매 프레임 마다 renderOneFrame 실행 : Loop 의 중단 –FrameStarted, frameRenderingQueued, FrameEnded 에서 하나라도 false 를 return 할 경우 1. Frame Listener 들의 FrameStarted() 함수 호출 3. Frame Listener 들의 FrameEnded() 함수 호출 Frame Listener 들의 frameRenderingQueued() 함수 호출 2. 한 프레임 렌더링
Input Handling Types of input handling –Unbuffered Input Testing states of Mouse/Keyboard every frame Do action if necessary –Buffered Input Event(message)-driven Event is queued into a buffer Call message pump routines per frame
Ninja 움직이기 Add two functions in Application class: class OgreApp : public BaseApplication { public: BasicTutorial4(void); virtual ~BasicTutorial4(void); protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt); private: bool processUnbufferedInput(const Ogre::FrameEvent& evt); } class OgreApp : public BaseApplication { public: BasicTutorial4(void); virtual ~BasicTutorial4(void); protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt); private: bool processUnbufferedInput(const Ogre::FrameEvent& evt); }
Ninja 움직이기 Add two functions in Application class: class OgreApp : public BaseApplication { public: BasicTutorial4(void); virtual ~BasicTutorial4(void); protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt); private: bool processUnbufferedInput(const Ogre::FrameEvent& evt); } class OgreApp : public BaseApplication { public: BasicTutorial4(void); virtual ~BasicTutorial4(void); protected: virtual void createScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt); private: bool processUnbufferedInput(const Ogre::FrameEvent& evt); }
CreateScene 수정 Ninja 를 포함하고 light 를 세팅 mSceneMgr->setAmbientLight(Ogre::ColourValue(0.25, 0.25, 0.25)); Ogre::Entity* ninjaEntity = mSceneMgr->createEntity("Ninja", "ninja.mesh"); Ogre::SceneNode *node = mSceneMgr->getRootSceneNode() ->createChildSceneNode("NinjaNode"); node->attachObject(ninjaEntity); Ogre::Light* pointLight = mSceneMgr->createLight("pointLight"); pointLight->setType(Ogre::Light::LT_POINT); pointLight->setPosition(Ogre::Vector3(250, 150, 250)); pointLight->setDiffuseColour(Ogre::ColourValue::White); pointLight->setSpecularColour(Ogre::ColourValue::White); mSceneMgr->setAmbientLight(Ogre::ColourValue(0.25, 0.25, 0.25)); Ogre::Entity* ninjaEntity = mSceneMgr->createEntity("Ninja", "ninja.mesh"); Ogre::SceneNode *node = mSceneMgr->getRootSceneNode() ->createChildSceneNode("NinjaNode"); node->attachObject(ninjaEntity); Ogre::Light* pointLight = mSceneMgr->createLight("pointLight"); pointLight->setType(Ogre::Light::LT_POINT); pointLight->setPosition(Ogre::Vector3(250, 150, 250)); pointLight->setDiffuseColour(Ogre::ColourValue::White); pointLight->setSpecularColour(Ogre::ColourValue::White);
Frame Listner 수정 frameRenderingQueued 함수 override bool OgreApp::frameRenderingQueued(const Ogre::FrameEvent& evt) { bool ret = BaseApplication::frameRenderingQueued(evt); if(!processUnbufferedInput(evt)) return false; return ret; } bool OgreApp::frameRenderingQueued(const Ogre::FrameEvent& evt) { bool ret = BaseApplication::frameRenderingQueued(evt); if(!processUnbufferedInput(evt)) return false; return ret; }
Processing Input bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt) { static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt) { static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant
Processing Input UnbufferedInput 구현 –Mouse 와 keyboard 의 현재 상태를 얻어옴 (BaseApplication::frameRenderingQueued 에서 수행 ) 다음과 같이 사용할 준비를 한다. bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt) { static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant bool OgreApp::processUnbufferedInput(const Ogre::FrameEvent& evt) { static bool mMouseDown = false; // If a mouse button is depressed static Ogre::Real mToggle = 0.0; // The time left until next toggle static Ogre::Real mRotate = 0.13; // The rotate constant static Ogre::Real mMove = 250; // The movement constant mMouse->capture(); mKeyboard->capture(); mMouse->capture(); mKeyboard->capture();
Toggle 기능 구현 현재 mouse 와 마지막 mouse 상태의 비교를 통 해 구현 bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left); if (currMouse && ! mMouseDown) { Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible()); } mMouseDown = currMouse; bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left); if (currMouse && ! mMouseDown) { Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible()); } mMouseDown = currMouse;
Delayed Toggle 구현 눌린 시간을 기억하여 어느 일정시간이상 눌렸으 면 실행 mToggle -= evt.timeSinceLastFrame; if ((mToggle isKeyDown(OIS::KC_1)) { mToggle = 0.5; Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible()); } mToggle -= evt.timeSinceLastFrame; if ((mToggle isKeyDown(OIS::KC_1)) { mToggle = 0.5; Ogre::Light* light = mSceneMgr->getLight("pointLight"); light->setVisible(! light->isVisible()); }
Keyboard 입력 구현 키보드의 상태를 찾아내어 구현 Ogre::Vector3 transVector = Ogre::Vector3::ZERO; if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward transVector.z -= mMove; if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward transVector.z += mMove; if (mKeyboard->isKeyDown(OIS::KC_U)) // Up transVector.y += mMove; if (mKeyboard->isKeyDown(OIS::KC_O)) // Down transVector.y -= mMove; mSceneMgr->getSceneNode("NinjaNode") ->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL); Ogre::Vector3 transVector = Ogre::Vector3::ZERO; if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward transVector.z -= mMove; if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward transVector.z += mMove; if (mKeyboard->isKeyDown(OIS::KC_U)) // Up transVector.y += mMove; if (mKeyboard->isKeyDown(OIS::KC_O)) // Down transVector.y -= mMove; mSceneMgr->getSceneNode("NinjaNode") ->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
Rotate 구현 SHIFT 키와 조합하여 구현 if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe { if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw left mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(mRotate * 5)); } else { transVector.x -= mMove; // Strafe left } if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe { if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw right mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(-mRotate * 5)); } else { transVector.x += mMove; // Strafe right } if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe { if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw left mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(mRotate * 5)); } else { transVector.x -= mMove; // Strafe left } if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe { if(mKeyboard->isKeyDown( OIS::KC_LSHIFT )) { // Yaw right mSceneMgr->getSceneNode("NinjaNode")->yaw(Ogre::Degree(-mRotate * 5)); } else { transVector.x += mMove; // Strafe right }
Buffered Input Unbuffered Input: –Every frame, the state of OIS::Keyboard/OIS::Mouse is queried Buffered Input –Event-driven –Event: key-pressed/released Mouse button pressed/released Mouse move
Event-Driven Updating Event: –Any interesting change in the state of the game –Examples: The player pressing a keyboard The explosion going off An enemy character spotting the player
Event-Driven Updating Event System –Similar to windows GUI –A subsystem of a game engine Register events of interests Respond to the events when they occur –Periodic services can be implemented by an event Example: Posting a event for every 1/30 sec. Using the event que for posting a event for the future
Keyboard 입력 구현 키보드의 상태를 찾아내어 구현 Ogre::Vector3 transVector = Ogre::Vector3::ZERO; if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward transVector.z -= mMove; if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward transVector.z += mMove; if (mKeyboard->isKeyDown(OIS::KC_U)) // Up transVector.y += mMove; if (mKeyboard->isKeyDown(OIS::KC_O)) // Down transVector.y -= mMove; mSceneMgr->getSceneNode("NinjaNode") ->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL); Ogre::Vector3 transVector = Ogre::Vector3::ZERO; if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward transVector.z -= mMove; if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward transVector.z += mMove; if (mKeyboard->isKeyDown(OIS::KC_U)) // Up transVector.y += mMove; if (mKeyboard->isKeyDown(OIS::KC_O)) // Down transVector.y -= mMove; mSceneMgr->getSceneNode("NinjaNode") ->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
3D Math for Games A game is: –a mathematical model of a virtual world simulated in real-time on a computer. So, Mathematics provides everything ! Games programmers must use of: –All branches of mathematics: Trigonometry, algebra, statistics, calculus, and so on
Points Point: a location in n-dimensional space Coordinate systems: –Cartesian coordinate –Cylindrical coordinate –Spherical coordinate
Vectors Point: absolute Vector: relative (direction and magnitude) Basic Operations: –Multiplication by a scalar –Addition and Subtraction –Magnitude (length, norm) (in a cartesian coordinate system)
Vectors Normalization and Unit vector Normal Vector
Vectors Dot product –Dot product in action: Collinear test Collinear but opposite Perpendicular test Same direction (same side) Opposite direction (opposite side) Compute height from a plane
Vectors Cross product –Cross product in action: Find perpendicular direction from the given two vectors Compute torque (rotational force)
Vectors in Action! Ogre::Vector3 Class OgreVector3.h
Linear Interpolation LERP (linear interpolation) –Given two points (vectors), finding inbetweens P Q v = Q - P P + α v = P + α (Q – P) = (1- α) P + α Q α
Matrices Basic Operations: –Addition –Scalar multiplication –Transpose
Matrices Matrix Multiplication Identity Matrix AB ≠ BA
Matrices Vectors as a Matrix –Row vector –Column vector =
Homogeneous Coordinates Any affine transformation between 3D spaces can be represented by a 4x4 matrix
Transformations 2D rotation 2D scaling
Transformations 2D shear 2D reflection
Affine Transformations 2D translation
Examples of Affine Transformations 2D transformation for vectors –Translation is simply ignored
Affine Transformations 3D rotation
Pivot-Point Rotation Rotation with respect to a pivot point (x,y)
Fixed-Point Scaling Scaling with respect to a fixed point (x,y)
Scaling Direction Scaling along an arbitrary axis
Matrices in Action Ogre::Matrix3, Ogre::Matrix4
Euler angles Arbitrary rotation can be represented by three rotation along x,y,z axis
Gimble Hardware implementation of Euler angles Aircraft, Camera
Euler Angles Rotation about three orthogonal axes –12 combinations XYZ, XYX, XZY, XZX YZX, YZY, YXZ, YXY ZXY, ZXZ, ZYX, ZYZ Gimble lock –Coincidence of inner most and outmost gimbles’ rotation axes –Loss of degree of freedom
Euler Angles Euler angles are ambiguous –Two different Euler angles can represent the same orientation –This ambiguity brings unexpected results of animation where frames are generated by interpolation.
Rotation and orientation Euler theorem –Every 3D frame can be written as a spinning by θ about a fixed axis n, the eigenvector of the rotation matrix.
Problems with Euler angles Gimbal lock –Losing a degree of freedom y z x y z x y z x
Problems with Euler angles How to Interpolate orientations with Euler angles? y z x y z x
Problems with Euler angles What we want is –A parameterization of orientation enabling us to Resolve the gimbal lock problem. Get a simple steady rotation between any two orientation. (Euler theorem) Define rotational moves that are independent of the choice of the coordinate system. Such a parameterization exists? –Yes, Unit quaternions.
Quaternion A complex number of four numbers Unit quaternion –A quaternion of unit length –A point on the unit hyper-sphere S 3
Representation Grouping the four components into a scalar and a 3D vector
Quaternion and Rotation Representing an rotation by a unit quaternion
Antipodal Points in S 3 and are the same orientation
Algebra of quaternions Addition Multiplication –Non-commutative under multiplication
Algebra of quaternions Inverse operation
Algebra of quaternions Rotation with a unit quaternion q Successive rotation with quaternions
Into and Out of Quaternion Space unit quaternion to rotation matrix
Into and Out of Quaternion Space Rotation matrix to unit quaternion
Quaternions in Action Ogre::Quaternion OgreQuaternion.h