2020-01-03 20:05:16 +01:00
// 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 "CCameraSceneNode.h"
# include "ISceneManager.h"
# include "IVideoDriver.h"
# include "os.h"
namespace irr
{
namespace scene
{
//! constructor
CCameraSceneNode : : CCameraSceneNode ( ISceneNode * parent , ISceneManager * mgr , s32 id ,
const core : : vector3df & position , const core : : vector3df & lookat )
: ICameraSceneNode ( parent , mgr , id , position ) ,
BoundingBox ( core : : vector3df ( 0 , 0 , 0 ) ) , // Camera has no size. Still not sure if FLT_MAX might be the better variant
Target ( lookat ) , UpVector ( 0.0f , 1.0f , 0.0f ) , ZNear ( 1.0f ) , ZFar ( 3000.0f ) ,
InputReceiverEnabled ( true ) , TargetAndRotationAreBound ( false ) ,
HasD3DStyleProjectionMatrix ( true )
{
# ifdef _DEBUG
setDebugName ( " CCameraSceneNode " ) ;
# endif
// set default projection
Fovy = core : : PI / 2.5f ; // Field of view, in radians.
2022-09-22 23:55:03 +02:00
Aspect = 4.0f / 3.0f ; // Aspect ratio.
2020-01-03 20:05:16 +01:00
const video : : IVideoDriver * const d = mgr ? mgr - > getVideoDriver ( ) : 0 ;
if ( d )
{
2022-09-22 23:55:03 +02:00
if ( d - > getCurrentRenderTargetSize ( ) . Height )
Aspect = ( f32 ) d - > getCurrentRenderTargetSize ( ) . Width /
( f32 ) d - > getCurrentRenderTargetSize ( ) . Height ;
2020-01-03 20:05:16 +01:00
HasD3DStyleProjectionMatrix = d - > getDriverType ( ) ! = video : : EDT_OPENGL ;
}
ViewArea . setFarNearDistance ( ZFar - ZNear ) ;
recalculateProjectionMatrix ( ) ;
recalculateViewArea ( ) ;
}
//! Disables or enables the camera to get key or mouse inputs.
void CCameraSceneNode : : setInputReceiverEnabled ( bool enabled )
{
InputReceiverEnabled = enabled ;
}
//! Returns if the input receiver of the camera is currently enabled.
bool CCameraSceneNode : : isInputReceiverEnabled ( ) const
{
return InputReceiverEnabled ;
}
//! Sets the projection matrix of the camera.
/** The core::matrix4 class has some methods
to build a projection matrix . e . g : core : : matrix4 : : buildProjectionMatrixPerspectiveFovLH
\ param projection : The new projection matrix of the camera . */
void CCameraSceneNode : : setProjectionMatrix ( const core : : matrix4 & projection , bool isOrthogonal )
{
IsOrthogonal = isOrthogonal ;
ViewArea . getTransform ( video : : ETS_PROJECTION ) = projection ;
}
//! Gets the current projection matrix of the camera
//! \return Returns the current projection matrix of the camera.
const core : : matrix4 & CCameraSceneNode : : getProjectionMatrix ( ) const
{
return ViewArea . getTransform ( video : : ETS_PROJECTION ) ;
}
//! Gets the current view matrix of the camera
//! \return Returns the current view matrix of the camera.
const core : : matrix4 & CCameraSceneNode : : getViewMatrix ( ) const
{
return ViewArea . getTransform ( video : : ETS_VIEW ) ;
}
//! Sets a custom view matrix affector. The matrix passed here, will be
//! multiplied with the view matrix when it gets updated.
//! This allows for custom camera setups like, for example, a reflection camera.
/** \param affector: The affector matrix. */
void CCameraSceneNode : : setViewMatrixAffector ( const core : : matrix4 & affector )
{
Affector = affector ;
}
//! Gets the custom view matrix affector.
const core : : matrix4 & CCameraSceneNode : : getViewMatrixAffector ( ) const
{
return Affector ;
}
//! It is possible to send mouse and key events to the camera. Most cameras
//! may ignore this input, but camera scene nodes which are created for
//! example with scene::ISceneManager::addMayaCameraSceneNode or
//! scene::ISceneManager::addFPSCameraSceneNode, may want to get this input
//! for changing their position, look at target or whatever.
bool CCameraSceneNode : : OnEvent ( const SEvent & event )
{
if ( ! InputReceiverEnabled )
return false ;
// send events to event receiving animators
ISceneNodeAnimatorList : : Iterator ait = Animators . begin ( ) ;
for ( ; ait ! = Animators . end ( ) ; + + ait )
if ( ( * ait ) - > isEventReceiverEnabled ( ) & & ( * ait ) - > OnEvent ( event ) )
return true ;
// if nobody processed the event, return false
return false ;
}
//! sets the look at target of the camera
//! \param pos: Look at target of the camera.
void CCameraSceneNode : : setTarget ( const core : : vector3df & pos )
{
Target = pos ;
if ( TargetAndRotationAreBound )
{
const core : : vector3df toTarget = Target - getAbsolutePosition ( ) ;
ISceneNode : : setRotation ( toTarget . getHorizontalAngle ( ) ) ;
}
}
//! Sets the rotation of the node.
/** This only modifies the relative rotation of the node.
If the camera ' s target and rotation are bound ( @ see bindTargetAndRotation ( ) )
then calling this will also change the camera ' s target to match the rotation .
\ param rotation New rotation of the node in degrees . */
void CCameraSceneNode : : setRotation ( const core : : vector3df & rotation )
{
if ( TargetAndRotationAreBound )
Target = getAbsolutePosition ( ) + rotation . rotationToDirection ( ) ;
ISceneNode : : setRotation ( rotation ) ;
}
//! Gets the current look at target of the camera
//! \return Returns the current look at target of the camera
const core : : vector3df & CCameraSceneNode : : getTarget ( ) const
{
return Target ;
}
//! sets the up vector of the camera
//! \param pos: New upvector of the camera.
void CCameraSceneNode : : setUpVector ( const core : : vector3df & pos )
{
UpVector = pos ;
}
//! Gets the up vector of the camera.
//! \return Returns the up vector of the camera.
const core : : vector3df & CCameraSceneNode : : getUpVector ( ) const
{
return UpVector ;
}
f32 CCameraSceneNode : : getNearValue ( ) const
{
return ZNear ;
}
f32 CCameraSceneNode : : getFarValue ( ) const
{
return ZFar ;
}
f32 CCameraSceneNode : : getAspectRatio ( ) const
{
return Aspect ;
}
f32 CCameraSceneNode : : getFOV ( ) const
{
return Fovy ;
}
void CCameraSceneNode : : setNearValue ( f32 f )
{
ZNear = f ;
recalculateProjectionMatrix ( ) ;
ViewArea . setFarNearDistance ( ZFar - ZNear ) ;
}
void CCameraSceneNode : : setFarValue ( f32 f )
{
ZFar = f ;
recalculateProjectionMatrix ( ) ;
ViewArea . setFarNearDistance ( ZFar - ZNear ) ;
}
void CCameraSceneNode : : setAspectRatio ( f32 f )
{
Aspect = f ;
recalculateProjectionMatrix ( ) ;
}
void CCameraSceneNode : : setFOV ( f32 f )
{
Fovy = f ;
recalculateProjectionMatrix ( ) ;
}
void CCameraSceneNode : : recalculateProjectionMatrix ( )
{
ViewArea . getTransform ( video : : ETS_PROJECTION ) . buildProjectionMatrixPerspectiveFovLH ( Fovy , Aspect , ZNear , ZFar , HasD3DStyleProjectionMatrix ) ;
IsOrthogonal = false ;
}
//! prerender
void CCameraSceneNode : : OnRegisterSceneNode ( )
{
if ( SceneManager - > getActiveCamera ( ) = = this )
SceneManager - > registerNodeForRendering ( this , ESNRP_CAMERA ) ;
ISceneNode : : OnRegisterSceneNode ( ) ;
}
//! render
void CCameraSceneNode : : render ( )
{
2022-09-22 23:55:03 +02:00
updateAbsolutePosition ( ) ; // depending on that call in onAnimate is risky (might not be in SceneManager or it or it's parent might be invisible and still should render)
2020-01-03 20:05:16 +01:00
updateMatrices ( ) ;
video : : IVideoDriver * driver = SceneManager - > getVideoDriver ( ) ;
if ( driver )
{
driver - > setTransform ( video : : ETS_PROJECTION , ViewArea . getTransform ( video : : ETS_PROJECTION ) ) ;
driver - > setTransform ( video : : ETS_VIEW , ViewArea . getTransform ( video : : ETS_VIEW ) ) ;
}
}
//! update
void CCameraSceneNode : : updateMatrices ( )
{
2022-05-04 23:38:12 +02:00
# if defined(_IRR_COMPILE_WITH_90_DEGREE_CAMERA)
ViewArea . getTransform ( video : : ETS_VIEW ) . buildCameraLookAtMatrixLH ( getAbsolutePosition ( ) , Target , UpVector ) ;
# else
2020-01-03 20:05:16 +01:00
core : : vector3df pos = getAbsolutePosition ( ) ;
core : : vector3df tgtv = Target - pos ;
tgtv . normalize ( ) ;
// if upvector and vector to the target are the same, we have a
// problem. so solve this problem:
core : : vector3df up = UpVector ;
up . normalize ( ) ;
f32 dp = tgtv . dotProduct ( up ) ;
if ( core : : equals ( core : : abs_ < f32 > ( dp ) , 1.f ) )
{
up . X + = 0.5f ;
}
ViewArea . getTransform ( video : : ETS_VIEW ) . buildCameraLookAtMatrixLH ( pos , Target , up ) ;
2022-05-04 23:38:12 +02:00
# endif
2020-01-03 20:05:16 +01:00
ViewArea . getTransform ( video : : ETS_VIEW ) * = Affector ;
recalculateViewArea ( ) ;
}
//! returns the axis aligned bounding box of this node
const core : : aabbox3d < f32 > & CCameraSceneNode : : getBoundingBox ( ) const
{
// NOTE: We deliberately don't return the boundingbox of the ViewArea. People can access that already.
// We want to prevent cameras from having their bounding box colliding in the SceneCollisionManager.
// If another boundingbox is ever necessary then please move BoundingBox to ICameraSceneNode and make it accessible (via a setter or an enum with options).
return BoundingBox ;
}
//! returns the view frustum.
const SViewFrustum * CCameraSceneNode : : getViewFrustum ( ) const
{
return & ViewArea ;
}
void CCameraSceneNode : : recalculateViewArea ( )
{
ViewArea . cameraPosition = getAbsolutePosition ( ) ;
core : : matrix4 m ( core : : matrix4 : : EM4CONST_NOTHING ) ;
m . setbyproduct_nocheck ( ViewArea . getTransform ( video : : ETS_PROJECTION ) ,
ViewArea . getTransform ( video : : ETS_VIEW ) ) ;
ViewArea . setFrom ( m , HasD3DStyleProjectionMatrix ) ;
}
//! Writes attributes of the scene node.
void CCameraSceneNode : : serializeAttributes ( io : : IAttributes * out , io : : SAttributeReadWriteOptions * options ) const
{
ICameraSceneNode : : serializeAttributes ( out , options ) ;
out - > addVector3d ( " Target " , Target ) ;
out - > addVector3d ( " UpVector " , UpVector ) ;
out - > addFloat ( " Fovy " , Fovy ) ;
out - > addFloat ( " Aspect " , Aspect ) ;
out - > addFloat ( " ZNear " , ZNear ) ;
out - > addFloat ( " ZFar " , ZFar ) ;
out - > addBool ( " Binding " , TargetAndRotationAreBound ) ;
out - > addBool ( " ReceiveInput " , InputReceiverEnabled ) ;
}
//! Reads attributes of the scene node.
void CCameraSceneNode : : deserializeAttributes ( io : : IAttributes * in , io : : SAttributeReadWriteOptions * options )
{
ICameraSceneNode : : deserializeAttributes ( in , options ) ;
Target = in - > getAttributeAsVector3d ( " Target " ) ;
UpVector = in - > getAttributeAsVector3d ( " UpVector " ) ;
Fovy = in - > getAttributeAsFloat ( " Fovy " ) ;
Aspect = in - > getAttributeAsFloat ( " Aspect " ) ;
ZNear = in - > getAttributeAsFloat ( " ZNear " ) ;
ZFar = in - > getAttributeAsFloat ( " ZFar " ) ;
TargetAndRotationAreBound = in - > getAttributeAsBool ( " Binding " ) ;
if ( in - > findAttribute ( " ReceiveInput " ) )
InputReceiverEnabled = in - > getAttributeAsBool ( " ReceiveInput " ) ;
recalculateProjectionMatrix ( ) ;
recalculateViewArea ( ) ;
}
//! Set the binding between the camera's rotation adn target.
void CCameraSceneNode : : bindTargetAndRotation ( bool bound )
{
TargetAndRotationAreBound = bound ;
}
//! Gets the binding between the camera's rotation and target.
bool CCameraSceneNode : : getTargetAndRotationBinding ( void ) const
{
return TargetAndRotationAreBound ;
}
//! Creates a clone of this scene node and its children.
ISceneNode * CCameraSceneNode : : clone ( ISceneNode * newParent , ISceneManager * newManager )
{
ICameraSceneNode : : clone ( newParent , newManager ) ;
if ( ! newParent )
newParent = Parent ;
if ( ! newManager )
newManager = SceneManager ;
CCameraSceneNode * nb = new CCameraSceneNode ( newParent ,
newManager , ID , RelativeTranslation , Target ) ;
nb - > ISceneNode : : cloneMembers ( this , newManager ) ;
nb - > ICameraSceneNode : : cloneMembers ( this ) ;
nb - > Target = Target ;
nb - > UpVector = UpVector ;
nb - > Fovy = Fovy ;
nb - > Aspect = Aspect ;
nb - > ZNear = ZNear ;
nb - > ZFar = ZFar ;
nb - > ViewArea = ViewArea ;
nb - > Affector = Affector ;
nb - > InputReceiverEnabled = InputReceiverEnabled ;
nb - > TargetAndRotationAreBound = TargetAndRotationAreBound ;
if ( newParent )
nb - > drop ( ) ;
return nb ;
}
} // end namespace
} // end namespace