// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CSceneNodeAnimatorCollisionResponse.h" #include "ISceneCollisionManager.h" #include "ISceneManager.h" #include "ICameraSceneNode.h" #include "os.h" namespace irr { namespace scene { //! constructor CSceneNodeAnimatorCollisionResponse::CSceneNodeAnimatorCollisionResponse( ISceneManager* scenemanager, ITriangleSelector* world, ISceneNode* object, const core::vector3df& ellipsoidRadius, const core::vector3df& gravityPerSecond, const core::vector3df& ellipsoidTranslation, f32 slidingSpeed) : Radius(ellipsoidRadius), Gravity(gravityPerSecond), Translation(ellipsoidTranslation), World(world), Object(object), SceneManager(scenemanager), LastTime(0), SlidingSpeed(slidingSpeed), CollisionNode(0), CollisionCallback(0), Falling(false), IsCamera(false), AnimateCameraTarget(true), CollisionOccurred(false), FirstUpdate(true) { #ifdef _DEBUG setDebugName("CSceneNodeAnimatorCollisionResponse"); #endif if (World) World->grab(); setNode(Object); } //! destructor CSceneNodeAnimatorCollisionResponse::~CSceneNodeAnimatorCollisionResponse() { if (World) World->drop(); if (CollisionCallback) CollisionCallback->drop(); } //! Returns if the attached scene node is falling, which means that //! there is no blocking wall from the scene node in the direction of //! the gravity. bool CSceneNodeAnimatorCollisionResponse::isFalling() const { return Falling; } //! Sets the radius of the ellipsoid with which collision detection and //! response is done. void CSceneNodeAnimatorCollisionResponse::setEllipsoidRadius( const core::vector3df& radius) { Radius = radius; FirstUpdate = true; } //! Returns the radius of the ellipsoid with which the collision detection and //! response is done. core::vector3df CSceneNodeAnimatorCollisionResponse::getEllipsoidRadius() const { return Radius; } //! Sets the gravity of the environment. void CSceneNodeAnimatorCollisionResponse::setGravity(const core::vector3df& gravity) { Gravity = gravity; FirstUpdate = true; } //! Returns current vector of gravity. core::vector3df CSceneNodeAnimatorCollisionResponse::getGravity() const { return Gravity; } //! 'Jump' the animator, by adding a jump speed opposite to its gravity void CSceneNodeAnimatorCollisionResponse::jump(f32 jumpSpeed) { FallingVelocity -= (core::vector3df(Gravity).normalize()) * jumpSpeed; Falling = true; } //! Sets the translation of the ellipsoid for collision detection. void CSceneNodeAnimatorCollisionResponse::setEllipsoidTranslation(const core::vector3df &translation) { Translation = translation; } //! Returns the translation of the ellipsoid for collision detection. core::vector3df CSceneNodeAnimatorCollisionResponse::getEllipsoidTranslation() const { return Translation; } //! Sets a triangle selector holding all triangles of the world with which //! the scene node may collide. void CSceneNodeAnimatorCollisionResponse::setWorld(ITriangleSelector* newWorld) { if (newWorld) newWorld->grab(); if (World) World->drop(); World = newWorld; FirstUpdate = true; } //! Returns the current triangle selector containing all triangles for //! collision detection. ITriangleSelector* CSceneNodeAnimatorCollisionResponse::getWorld() const { return World; } void CSceneNodeAnimatorCollisionResponse::animateNode(ISceneNode* node, u32 timeMs) { CollisionOccurred = false; if (node != Object) setNode(node); if(!Object || !World) return; // trigger reset if ( timeMs == 0 ) { FirstUpdate = true; timeMs = LastTime; } if ( FirstUpdate ) { LastPosition = Object->getPosition(); Falling = false; LastTime = timeMs; FallingVelocity.set ( 0, 0, 0 ); FirstUpdate = false; } const f32 diffSec = (f32)(timeMs - LastTime)*0.001f; LastTime = timeMs; CollisionResultPosition = Object->getPosition(); core::vector3df vel = CollisionResultPosition - LastPosition; FallingVelocity += Gravity * diffSec; CollisionTriangle = RefTriangle; CollisionPoint = core::vector3df(); CollisionResultPosition = core::vector3df(); CollisionNode = 0; // core::vector3df force = vel + FallingVelocity; if ( AnimateCameraTarget ) { // TODO: divide SlidingSpeed by frame time bool f = false; CollisionResultPosition = SceneManager->getSceneCollisionManager()->getCollisionResultPosition( World, LastPosition-Translation, Radius, vel, CollisionTriangle, CollisionPoint, f, CollisionNode, SlidingSpeed, FallingVelocity*diffSec); CollisionOccurred = (CollisionTriangle != RefTriangle); CollisionResultPosition += Translation; if ( diffSec > 0 ) // don't change the state when there was no time { if (f)//CollisionTriangle == RefTriangle) { Falling = true; } else { if ( CollisionOccurred ) // f can also happen to be false when FallingVelocity was already 0 (p.e. at top of a jump) Falling = false; FallingVelocity.set(0, 0, 0); } } bool collisionConsumed = false; if (CollisionOccurred && CollisionCallback) collisionConsumed = CollisionCallback->onCollision(*this); if(!collisionConsumed) Object->setPosition(CollisionResultPosition); } // move camera target if (AnimateCameraTarget && IsCamera) { const core::vector3df pdiff = Object->getPosition() - LastPosition - vel; ICameraSceneNode* cam = (ICameraSceneNode*)Object; cam->setTarget(cam->getTarget() + pdiff); } LastPosition = Object->getPosition(); } void CSceneNodeAnimatorCollisionResponse::setNode(ISceneNode* node) { Object = node; if (Object) { LastPosition = Object->getPosition(); IsCamera = (Object->getType() == ESNT_CAMERA); } LastTime = os::Timer::getTime(); } //! Writes attributes of the scene node animator. void CSceneNodeAnimatorCollisionResponse::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const { ISceneNodeAnimatorCollisionResponse::serializeAttributes(out, options); out->addVector3d("Radius", Radius); out->addVector3d("Gravity", Gravity); out->addVector3d("Translation", Translation); out->addBool("AnimateCameraTarget", AnimateCameraTarget); } //! Reads attributes of the scene node animator. void CSceneNodeAnimatorCollisionResponse::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) { ISceneNodeAnimatorCollisionResponse::deserializeAttributes(in, options); Radius = in->getAttributeAsVector3d("Radius", Radius); Gravity = in->getAttributeAsVector3d("Gravity", Gravity); Translation = in->getAttributeAsVector3d("Translation", Translation); AnimateCameraTarget = in->getAttributeAsBool("AnimateCameraTarget", AnimateCameraTarget); } ISceneNodeAnimator* CSceneNodeAnimatorCollisionResponse::createClone(ISceneNode* node, ISceneManager* newManager) { if (!newManager) newManager = SceneManager; CSceneNodeAnimatorCollisionResponse * newAnimator = new CSceneNodeAnimatorCollisionResponse(newManager, World, Object, Radius, Gravity, Translation, SlidingSpeed); newAnimator->cloneMembers(this); return newAnimator; } void CSceneNodeAnimatorCollisionResponse::setCollisionCallback(ICollisionCallback* callback) { if ( CollisionCallback == callback ) return; if (CollisionCallback) CollisionCallback->drop(); CollisionCallback = callback; if (CollisionCallback) CollisionCallback->grab(); } //! Should the Target react on collision ( default = true ) void CSceneNodeAnimatorCollisionResponse::setAnimateTarget ( bool enable ) { AnimateCameraTarget = enable; FirstUpdate = true; } //! Should the Target react on collision ( default = true ) bool CSceneNodeAnimatorCollisionResponse::getAnimateTarget () const { return AnimateCameraTarget; } } // end namespace scene } // end namespace irr