1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CCameraSceneNode.h"
6 #include "ISceneManager.h"
7 #include "IVideoDriver.h"
8 #include "os.h"
9 
10 namespace irr
11 {
12 namespace scene
13 {
14 
15 
16 //! constructor
CCameraSceneNode(ISceneNode * parent,ISceneManager * mgr,s32 id,const core::vector3df & position,const core::vector3df & lookat)17 CCameraSceneNode::CCameraSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,
18 	const core::vector3df& position, const core::vector3df& lookat)
19 	: ICameraSceneNode(parent, mgr, id, position),
20 	Target(lookat), UpVector(0.0f, 1.0f, 0.0f), ZNear(1.0f), ZFar(3000.0f),
21 	InputReceiverEnabled(true), TargetAndRotationAreBound(false)
22 {
23 	#ifdef _DEBUG
24 	setDebugName("CCameraSceneNode");
25 	#endif
26 
27 	// set default projection
28 	Fovy = core::PI / 2.5f;	// Field of view, in radians.
29 
30 	const video::IVideoDriver* const d = mgr?mgr->getVideoDriver():0;
31 	if (d)
32 		Aspect = (f32)d->getCurrentRenderTargetSize().Width /
33 			(f32)d->getCurrentRenderTargetSize().Height;
34 	else
35 		Aspect = 4.0f / 3.0f;	// Aspect ratio.
36 
37 	recalculateProjectionMatrix();
38 	recalculateViewArea();
39 }
40 
41 
42 //! Disables or enables the camera to get key or mouse inputs.
setInputReceiverEnabled(bool enabled)43 void CCameraSceneNode::setInputReceiverEnabled(bool enabled)
44 {
45 	InputReceiverEnabled = enabled;
46 }
47 
48 
49 //! Returns if the input receiver of the camera is currently enabled.
isInputReceiverEnabled() const50 bool CCameraSceneNode::isInputReceiverEnabled() const
51 {
52 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
53 	return InputReceiverEnabled;
54 }
55 
56 
57 //! Sets the projection matrix of the camera.
58 /** The core::matrix4 class has some methods
59 to build a projection matrix. e.g: core::matrix4::buildProjectionMatrixPerspectiveFovLH
60 \param projection: The new projection matrix of the camera. */
setProjectionMatrix(const core::matrix4 & projection,bool isOrthogonal)61 void CCameraSceneNode::setProjectionMatrix(const core::matrix4& projection, bool isOrthogonal)
62 {
63 	IsOrthogonal = isOrthogonal;
64 	ViewArea.getTransform ( video::ETS_PROJECTION ) = projection;
65 }
66 
67 
68 //! Gets the current projection matrix of the camera
69 //! \return Returns the current projection matrix of the camera.
getProjectionMatrix() const70 const core::matrix4& CCameraSceneNode::getProjectionMatrix() const
71 {
72 	return ViewArea.getTransform ( video::ETS_PROJECTION );
73 }
74 
75 
76 //! Gets the current view matrix of the camera
77 //! \return Returns the current view matrix of the camera.
getViewMatrix() const78 const core::matrix4& CCameraSceneNode::getViewMatrix() const
79 {
80 	return ViewArea.getTransform ( video::ETS_VIEW );
81 }
82 
83 
84 //! Sets a custom view matrix affector. The matrix passed here, will be
85 //! multiplied with the view matrix when it gets updated.
86 //! This allows for custom camera setups like, for example, a reflection camera.
87 /** \param affector: The affector matrix. */
setViewMatrixAffector(const core::matrix4 & affector)88 void CCameraSceneNode::setViewMatrixAffector(const core::matrix4& affector)
89 {
90 	Affector = affector;
91 }
92 
93 
94 //! Gets the custom view matrix affector.
getViewMatrixAffector() const95 const core::matrix4& CCameraSceneNode::getViewMatrixAffector() const
96 {
97 	return Affector;
98 }
99 
100 
101 //! It is possible to send mouse and key events to the camera. Most cameras
102 //! may ignore this input, but camera scene nodes which are created for
103 //! example with scene::ISceneManager::addMayaCameraSceneNode or
104 //! scene::ISceneManager::addFPSCameraSceneNode, may want to get this input
105 //! for changing their position, look at target or whatever.
OnEvent(const SEvent & event)106 bool CCameraSceneNode::OnEvent(const SEvent& event)
107 {
108 	if (!InputReceiverEnabled)
109 		return false;
110 
111 	// send events to event receiving animators
112 
113 	ISceneNodeAnimatorList::Iterator ait = Animators.begin();
114 
115 	for (; ait != Animators.end(); ++ait)
116 		if ((*ait)->isEventReceiverEnabled() && (*ait)->OnEvent(event))
117 			return true;
118 
119 	// if nobody processed the event, return false
120 	return false;
121 }
122 
123 
124 //! sets the look at target of the camera
125 //! \param pos: Look at target of the camera.
setTarget(const core::vector3df & pos)126 void CCameraSceneNode::setTarget(const core::vector3df& pos)
127 {
128 	Target = pos;
129 
130 	if(TargetAndRotationAreBound)
131 	{
132 		const core::vector3df toTarget = Target - getAbsolutePosition();
133 		ISceneNode::setRotation(toTarget.getHorizontalAngle());
134 	}
135 }
136 
137 
138 //! Sets the rotation of the node.
139 /** This only modifies the relative rotation of the node.
140 If the camera's target and rotation are bound ( @see bindTargetAndRotation() )
141 then calling this will also change the camera's target to match the rotation.
142 \param rotation New rotation of the node in degrees. */
setRotation(const core::vector3df & rotation)143 void CCameraSceneNode::setRotation(const core::vector3df& rotation)
144 {
145 	if(TargetAndRotationAreBound)
146 		Target = getAbsolutePosition() + rotation.rotationToDirection();
147 
148 	ISceneNode::setRotation(rotation);
149 }
150 
151 
152 //! Gets the current look at target of the camera
153 //! \return Returns the current look at target of the camera
getTarget() const154 const core::vector3df& CCameraSceneNode::getTarget() const
155 {
156 	return Target;
157 }
158 
159 
160 //! sets the up vector of the camera
161 //! \param pos: New upvector of the camera.
setUpVector(const core::vector3df & pos)162 void CCameraSceneNode::setUpVector(const core::vector3df& pos)
163 {
164 	UpVector = pos;
165 }
166 
167 
168 //! Gets the up vector of the camera.
169 //! \return Returns the up vector of the camera.
getUpVector() const170 const core::vector3df& CCameraSceneNode::getUpVector() const
171 {
172 	return UpVector;
173 }
174 
175 
getNearValue() const176 f32 CCameraSceneNode::getNearValue() const
177 {
178 	return ZNear;
179 }
180 
181 
getFarValue() const182 f32 CCameraSceneNode::getFarValue() const
183 {
184 	return ZFar;
185 }
186 
187 
getAspectRatio() const188 f32 CCameraSceneNode::getAspectRatio() const
189 {
190 	return Aspect;
191 }
192 
193 
getFOV() const194 f32 CCameraSceneNode::getFOV() const
195 {
196 	return Fovy;
197 }
198 
199 
setNearValue(f32 f)200 void CCameraSceneNode::setNearValue(f32 f)
201 {
202 	ZNear = f;
203 	recalculateProjectionMatrix();
204 }
205 
206 
setFarValue(f32 f)207 void CCameraSceneNode::setFarValue(f32 f)
208 {
209 	ZFar = f;
210 	recalculateProjectionMatrix();
211 }
212 
213 
setAspectRatio(f32 f)214 void CCameraSceneNode::setAspectRatio(f32 f)
215 {
216 	Aspect = f;
217 	recalculateProjectionMatrix();
218 }
219 
220 
setFOV(f32 f)221 void CCameraSceneNode::setFOV(f32 f)
222 {
223 	Fovy = f;
224 	recalculateProjectionMatrix();
225 }
226 
227 
recalculateProjectionMatrix()228 void CCameraSceneNode::recalculateProjectionMatrix()
229 {
230 	ViewArea.getTransform ( video::ETS_PROJECTION ).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar);
231 }
232 
233 
234 //! prerender
OnRegisterSceneNode()235 void CCameraSceneNode::OnRegisterSceneNode()
236 {
237 	if ( SceneManager->getActiveCamera () == this )
238 		SceneManager->registerNodeForRendering(this, ESNRP_CAMERA);
239 
240 	ISceneNode::OnRegisterSceneNode();
241 }
242 
243 
244 //! render
render()245 void CCameraSceneNode::render()
246 {
247 	core::vector3df pos = getAbsolutePosition();
248 	core::vector3df tgtv = Target - pos;
249 	tgtv.normalize();
250 
251 	// if upvector and vector to the target are the same, we have a
252 	// problem. so solve this problem:
253 	core::vector3df up = UpVector;
254 	up.normalize();
255 
256 	f32 dp = tgtv.dotProduct(up);
257 
258 	if ( core::equals(core::abs_<f32>(dp), 1.f) )
259 	{
260 		up.X += 0.5f;
261 	}
262 
263 	ViewArea.getTransform(video::ETS_VIEW).buildCameraLookAtMatrixLH(pos, Target, up);
264 	ViewArea.getTransform(video::ETS_VIEW) *= Affector;
265 	recalculateViewArea();
266 
267 	video::IVideoDriver* driver = SceneManager->getVideoDriver();
268 	if ( driver)
269 	{
270 		driver->setTransform(video::ETS_PROJECTION, ViewArea.getTransform ( video::ETS_PROJECTION) );
271 		driver->setTransform(video::ETS_VIEW, ViewArea.getTransform ( video::ETS_VIEW) );
272 	}
273 }
274 
275 
276 //! returns the axis aligned bounding box of this node
getBoundingBox() const277 const core::aabbox3d<f32>& CCameraSceneNode::getBoundingBox() const
278 {
279 	return ViewArea.getBoundingBox();
280 }
281 
282 
283 //! returns the view frustum. needed sometimes by bsp or lod render nodes.
getViewFrustum() const284 const SViewFrustum* CCameraSceneNode::getViewFrustum() const
285 {
286 	return &ViewArea;
287 }
288 
289 
recalculateViewArea()290 void CCameraSceneNode::recalculateViewArea()
291 {
292 	ViewArea.cameraPosition = getAbsolutePosition();
293 
294 	core::matrix4 m(core::matrix4::EM4CONST_NOTHING);
295 	m.setbyproduct_nocheck(ViewArea.getTransform(video::ETS_PROJECTION),
296 						ViewArea.getTransform(video::ETS_VIEW));
297 	ViewArea.setFrom(m);
298 }
299 
300 
301 //! Writes attributes of the scene node.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options) const302 void CCameraSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
303 {
304 	ICameraSceneNode::serializeAttributes(out, options);
305 
306 	out->addVector3d("Target", Target);
307 	out->addVector3d("UpVector", UpVector);
308 	out->addFloat("Fovy", Fovy);
309 	out->addFloat("Aspect", Aspect);
310 	out->addFloat("ZNear", ZNear);
311 	out->addFloat("ZFar", ZFar);
312 	out->addBool("Binding", TargetAndRotationAreBound);
313 	out->addBool("ReceiveInput", InputReceiverEnabled);
314 }
315 
316 //! Reads attributes of the scene node.
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options)317 void CCameraSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
318 {
319 	ICameraSceneNode::deserializeAttributes(in, options);
320 
321 	Target = in->getAttributeAsVector3d("Target");
322 	UpVector = in->getAttributeAsVector3d("UpVector");
323 	Fovy = in->getAttributeAsFloat("Fovy");
324 	Aspect = in->getAttributeAsFloat("Aspect");
325 	ZNear = in->getAttributeAsFloat("ZNear");
326 	ZFar = in->getAttributeAsFloat("ZFar");
327 	TargetAndRotationAreBound = in->getAttributeAsBool("Binding");
328 	if ( in->findAttribute("ReceiveInput") )
329 		InputReceiverEnabled = in->getAttributeAsBool("InputReceiverEnabled");
330 
331 	recalculateProjectionMatrix();
332 	recalculateViewArea();
333 }
334 
335 
336 //! Set the binding between the camera's rotation adn target.
bindTargetAndRotation(bool bound)337 void CCameraSceneNode::bindTargetAndRotation(bool bound)
338 {
339 	TargetAndRotationAreBound = bound;
340 }
341 
342 
343 //! Gets the binding between the camera's rotation and target.
getTargetAndRotationBinding(void) const344 bool CCameraSceneNode::getTargetAndRotationBinding(void) const
345 {
346 	return TargetAndRotationAreBound;
347 }
348 
349 
350 //! Creates a clone of this scene node and its children.
clone(ISceneNode * newParent,ISceneManager * newManager)351 ISceneNode* CCameraSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
352 {
353 	ICameraSceneNode::clone(newParent, newManager);
354 
355 	if (!newParent)
356 		newParent = Parent;
357 	if (!newManager)
358 		newManager = SceneManager;
359 
360 	CCameraSceneNode* nb = new CCameraSceneNode(newParent,
361 		newManager, ID, RelativeTranslation, Target);
362 
363 	nb->ISceneNode::cloneMembers(this, newManager);
364 	nb->ICameraSceneNode::cloneMembers(this);
365 
366 	nb->Target = Target;
367 	nb->UpVector = UpVector;
368 	nb->Fovy = Fovy;
369 	nb->Aspect = Aspect;
370 	nb->ZNear = ZNear;
371 	nb->ZFar = ZFar;
372 	nb->ViewArea = ViewArea;
373 	nb->Affector = Affector;
374 	nb->InputReceiverEnabled = InputReceiverEnabled;
375 	nb->TargetAndRotationAreBound = TargetAndRotationAreBound;
376 
377 	if ( newParent )
378 		nb->drop();
379 	return nb;
380 }
381 
382 
383 } // end namespace
384 } // end namespace
385 
386