irrlicht/src/CCameraSceneNode.cpp

315 lines
8.4 KiB
C++

// 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)
{
#ifdef _DEBUG
setDebugName("CCameraSceneNode");
#endif
// set default projection
Fovy = core::PI / 2.5f; // Field of view, in radians.
Aspect = 4.0f / 3.0f; // Aspect ratio.
const video::IVideoDriver *const d = mgr ? mgr->getVideoDriver() : 0;
if (d) {
if (d->getCurrentRenderTargetSize().Height)
Aspect = (f32)d->getCurrentRenderTargetSize().Width /
(f32)d->getCurrentRenderTargetSize().Height;
}
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)
{
// animators have been deleted; nothing happens here now!
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, false);
IsOrthogonal = false;
}
//! prerender
void CCameraSceneNode::OnRegisterSceneNode()
{
if (SceneManager->getActiveCamera() == this)
SceneManager->registerNodeForRendering(this, ESNRP_CAMERA);
ISceneNode::OnRegisterSceneNode();
}
//! render
void CCameraSceneNode::render()
{
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()
{
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);
ViewArea.getTransform(video::ETS_VIEW) *= Affector;
recalculateViewArea();
}
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32> &CCameraSceneNode::getBoundingBox() const
{
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, false);
}
//! 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