1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../Graphics/Camera.h"
27 #include "../Graphics/DebugRenderer.h"
28 #include "../Graphics/Drawable.h"
29 #include "../Scene/Node.h"
30
31 #include "../DebugNew.h"
32
33 namespace Urho3D
34 {
35
36 extern const char* SCENE_CATEGORY;
37
38 static const char* fillModeNames[] =
39 {
40 "Solid",
41 "Wireframe",
42 "Point",
43 0
44 };
45
46 static const Matrix4 flipMatrix(
47 1.0f, 0.0f, 0.0f, 0.0f,
48 0.0f, -1.0f, 0.0f, 0.0f,
49 0.0f, 0.0f, 1.0f, 0.0f,
50 0.0f, 0.0f, 0.0f, 1.0f
51 );
52
Camera(Context * context)53 Camera::Camera(Context* context) :
54 Component(context),
55 viewDirty_(true),
56 projectionDirty_(true),
57 frustumDirty_(true),
58 orthographic_(false),
59 nearClip_(DEFAULT_NEARCLIP),
60 farClip_(DEFAULT_FARCLIP),
61 fov_(DEFAULT_CAMERA_FOV),
62 orthoSize_(DEFAULT_ORTHOSIZE),
63 aspectRatio_(1.0f),
64 zoom_(1.0f),
65 lodBias_(1.0f),
66 viewMask_(DEFAULT_VIEWMASK),
67 viewOverrideFlags_(VO_NONE),
68 fillMode_(FILL_SOLID),
69 projectionOffset_(Vector2::ZERO),
70 reflectionPlane_(Plane::UP),
71 clipPlane_(Plane::UP),
72 autoAspectRatio_(true),
73 flipVertical_(false),
74 useReflection_(false),
75 useClipping_(false),
76 customProjection_(false)
77 {
78 reflectionMatrix_ = reflectionPlane_.ReflectionMatrix();
79 }
80
~Camera()81 Camera::~Camera()
82 {
83 }
84
RegisterObject(Context * context)85 void Camera::RegisterObject(Context* context)
86 {
87 context->RegisterFactory<Camera>(SCENE_CATEGORY);
88
89 URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
90 URHO3D_ACCESSOR_ATTRIBUTE("Near Clip", GetNearClip, SetNearClip, float, DEFAULT_NEARCLIP, AM_DEFAULT);
91 URHO3D_ACCESSOR_ATTRIBUTE("Far Clip", GetFarClip, SetFarClip, float, DEFAULT_FARCLIP, AM_DEFAULT);
92 URHO3D_ACCESSOR_ATTRIBUTE("FOV", GetFov, SetFov, float, DEFAULT_CAMERA_FOV, AM_DEFAULT);
93 URHO3D_ACCESSOR_ATTRIBUTE("Aspect Ratio", GetAspectRatio, SetAspectRatioInternal, float, 1.0f, AM_DEFAULT);
94 URHO3D_ENUM_ATTRIBUTE("Fill Mode", fillMode_, fillModeNames, FILL_SOLID, AM_DEFAULT);
95 URHO3D_ATTRIBUTE("Auto Aspect Ratio", bool, autoAspectRatio_, true, AM_DEFAULT);
96 URHO3D_ACCESSOR_ATTRIBUTE("Orthographic", IsOrthographic, SetOrthographic, bool, false, AM_DEFAULT);
97 URHO3D_ACCESSOR_ATTRIBUTE("Orthographic Size", GetOrthoSize, SetOrthoSizeAttr, float, DEFAULT_ORTHOSIZE, AM_DEFAULT);
98 URHO3D_ACCESSOR_ATTRIBUTE("Zoom", GetZoom, SetZoom, float, 1.0f, AM_DEFAULT);
99 URHO3D_ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
100 URHO3D_ATTRIBUTE("View Mask", int, viewMask_, DEFAULT_VIEWMASK, AM_DEFAULT);
101 URHO3D_ATTRIBUTE("View Override Flags", int, viewOverrideFlags_, VO_NONE, AM_DEFAULT);
102 URHO3D_ACCESSOR_ATTRIBUTE("Projection Offset", GetProjectionOffset, SetProjectionOffset, Vector2, Vector2::ZERO, AM_DEFAULT);
103 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Reflection Plane", GetReflectionPlaneAttr, SetReflectionPlaneAttr, Vector4,
104 Vector4(0.0f, 1.0f, 0.0f, 0.0f), AM_DEFAULT);
105 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Clip Plane", GetClipPlaneAttr, SetClipPlaneAttr, Vector4, Vector4(0.0f, 1.0f, 0.0f, 0.0f),
106 AM_DEFAULT);
107 URHO3D_ACCESSOR_ATTRIBUTE("Use Reflection", GetUseReflection, SetUseReflection, bool, false, AM_DEFAULT);
108 URHO3D_ACCESSOR_ATTRIBUTE("Use Clipping", GetUseClipping, SetUseClipping, bool, false, AM_DEFAULT);
109 }
110
DrawDebugGeometry(DebugRenderer * debug,bool depthTest)111 void Camera::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
112 {
113 debug->AddFrustum(GetFrustum(), Color::WHITE, depthTest);
114 }
115
SetNearClip(float nearClip)116 void Camera::SetNearClip(float nearClip)
117 {
118 nearClip_ = Max(nearClip, M_MIN_NEARCLIP);
119 frustumDirty_ = true;
120 projectionDirty_ = true;
121 MarkNetworkUpdate();
122 }
123
SetFarClip(float farClip)124 void Camera::SetFarClip(float farClip)
125 {
126 farClip_ = Max(farClip, M_MIN_NEARCLIP);
127 frustumDirty_ = true;
128 projectionDirty_ = true;
129 MarkNetworkUpdate();
130 }
131
SetFov(float fov)132 void Camera::SetFov(float fov)
133 {
134 fov_ = Clamp(fov, 0.0f, M_MAX_FOV);
135 frustumDirty_ = true;
136 projectionDirty_ = true;
137 MarkNetworkUpdate();
138 }
139
SetOrthoSize(float orthoSize)140 void Camera::SetOrthoSize(float orthoSize)
141 {
142 orthoSize_ = orthoSize;
143 aspectRatio_ = 1.0f;
144 frustumDirty_ = true;
145 projectionDirty_ = true;
146 MarkNetworkUpdate();
147 }
148
SetOrthoSize(const Vector2 & orthoSize)149 void Camera::SetOrthoSize(const Vector2& orthoSize)
150 {
151 autoAspectRatio_ = false;
152 orthoSize_ = orthoSize.y_;
153 aspectRatio_ = orthoSize.x_ / orthoSize.y_;
154 frustumDirty_ = true;
155 projectionDirty_ = true;
156 MarkNetworkUpdate();
157 }
158
SetAspectRatio(float aspectRatio)159 void Camera::SetAspectRatio(float aspectRatio)
160 {
161 autoAspectRatio_ = false;
162 SetAspectRatioInternal(aspectRatio);
163 }
164
SetZoom(float zoom)165 void Camera::SetZoom(float zoom)
166 {
167 zoom_ = Max(zoom, M_EPSILON);
168 frustumDirty_ = true;
169 projectionDirty_ = true;
170 MarkNetworkUpdate();
171 }
172
SetLodBias(float bias)173 void Camera::SetLodBias(float bias)
174 {
175 lodBias_ = Max(bias, M_EPSILON);
176 MarkNetworkUpdate();
177 }
178
SetViewMask(unsigned mask)179 void Camera::SetViewMask(unsigned mask)
180 {
181 viewMask_ = mask;
182 MarkNetworkUpdate();
183 }
184
SetViewOverrideFlags(unsigned flags)185 void Camera::SetViewOverrideFlags(unsigned flags)
186 {
187 viewOverrideFlags_ = flags;
188 MarkNetworkUpdate();
189 }
190
SetFillMode(FillMode mode)191 void Camera::SetFillMode(FillMode mode)
192 {
193 fillMode_ = mode;
194 MarkNetworkUpdate();
195 }
196
SetOrthographic(bool enable)197 void Camera::SetOrthographic(bool enable)
198 {
199 orthographic_ = enable;
200 frustumDirty_ = true;
201 projectionDirty_ = true;
202 MarkNetworkUpdate();
203 }
204
SetAutoAspectRatio(bool enable)205 void Camera::SetAutoAspectRatio(bool enable)
206 {
207 autoAspectRatio_ = enable;
208 MarkNetworkUpdate();
209 }
210
SetProjectionOffset(const Vector2 & offset)211 void Camera::SetProjectionOffset(const Vector2& offset)
212 {
213 projectionOffset_ = offset;
214 projectionDirty_ = true;
215 MarkNetworkUpdate();
216 }
217
SetUseReflection(bool enable)218 void Camera::SetUseReflection(bool enable)
219 {
220 useReflection_ = enable;
221 viewDirty_ = true;
222 frustumDirty_ = true;
223 MarkNetworkUpdate();
224 }
225
SetReflectionPlane(const Plane & plane)226 void Camera::SetReflectionPlane(const Plane& plane)
227 {
228 reflectionPlane_ = plane;
229 reflectionMatrix_ = reflectionPlane_.ReflectionMatrix();
230 viewDirty_ = true;
231 frustumDirty_ = true;
232 MarkNetworkUpdate();
233 }
234
SetUseClipping(bool enable)235 void Camera::SetUseClipping(bool enable)
236 {
237 useClipping_ = enable;
238 projectionDirty_ = true;
239 MarkNetworkUpdate();
240 }
241
SetClipPlane(const Plane & plane)242 void Camera::SetClipPlane(const Plane& plane)
243 {
244 clipPlane_ = plane;
245 MarkNetworkUpdate();
246 }
247
SetFlipVertical(bool enable)248 void Camera::SetFlipVertical(bool enable)
249 {
250 flipVertical_ = enable;
251 MarkNetworkUpdate();
252 }
253
SetProjection(const Matrix4 & projection)254 void Camera::SetProjection(const Matrix4& projection)
255 {
256 projection_ = projection;
257 Matrix4 projInverse = projection_.Inverse();
258
259 // Calculate the actual near & far clip from the custom matrix
260 projNearClip_ = (projInverse * Vector3(0.0f, 0.0f, 0.0f)).z_;
261 projFarClip_ = (projInverse * Vector3(0.0f, 0.0f, 1.0f)).z_;
262 projectionDirty_ = false;
263 autoAspectRatio_ = false;
264 frustumDirty_ = true;
265 customProjection_ = true;
266 // Called due to autoAspectRatio changing state, the projection itself is not serialized
267 MarkNetworkUpdate();
268 }
269
GetNearClip() const270 float Camera::GetNearClip() const
271 {
272 if (projectionDirty_)
273 UpdateProjection();
274
275 return projNearClip_;
276 }
277
GetFarClip() const278 float Camera::GetFarClip() const
279 {
280 if (projectionDirty_)
281 UpdateProjection();
282
283 return projFarClip_;
284 }
285
GetFrustum() const286 const Frustum& Camera::GetFrustum() const
287 {
288 // Use projection_ instead of GetProjection() so that Y-flip has no effect. Update first if necessary
289 if (projectionDirty_)
290 UpdateProjection();
291
292 if (frustumDirty_)
293 {
294 if (customProjection_)
295 frustum_.Define(projection_ * GetView());
296 else
297 {
298 // If not using a custom projection, prefer calculating frustum from projection parameters instead of matrix
299 // for better accuracy
300 if (!orthographic_)
301 frustum_.Define(fov_, aspectRatio_, zoom_, GetNearClip(), GetFarClip(), GetEffectiveWorldTransform());
302 else
303 frustum_.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), GetFarClip(), GetEffectiveWorldTransform());
304 }
305
306 frustumDirty_ = false;
307 }
308
309 return frustum_;
310 }
311
GetSplitFrustum(float nearClip,float farClip) const312 Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const
313 {
314 if (projectionDirty_)
315 UpdateProjection();
316
317 nearClip = Max(nearClip, projNearClip_);
318 farClip = Min(farClip, projFarClip_);
319 if (farClip < nearClip)
320 farClip = nearClip;
321
322 Frustum ret;
323
324 if (customProjection_)
325 {
326 // DefineSplit() needs to project the near & far distances, so can not use a combined view-projection matrix.
327 // Transform to world space afterward instead
328 ret.DefineSplit(projection_, nearClip, farClip);
329 ret.Transform(GetEffectiveWorldTransform());
330 }
331 else
332 {
333 if (!orthographic_)
334 ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform());
335 else
336 ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform());
337 }
338
339 return ret;
340 }
341
GetViewSpaceFrustum() const342 Frustum Camera::GetViewSpaceFrustum() const
343 {
344 if (projectionDirty_)
345 UpdateProjection();
346
347 Frustum ret;
348
349 if (customProjection_)
350 ret.Define(projection_);
351 else
352 {
353 if (!orthographic_)
354 ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), GetFarClip());
355 else
356 ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), GetFarClip());
357 }
358
359 return ret;
360 }
361
GetViewSpaceSplitFrustum(float nearClip,float farClip) const362 Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const
363 {
364 if (projectionDirty_)
365 UpdateProjection();
366
367 nearClip = Max(nearClip, projNearClip_);
368 farClip = Min(farClip, projFarClip_);
369 if (farClip < nearClip)
370 farClip = nearClip;
371
372 Frustum ret;
373
374 if (customProjection_)
375 ret.DefineSplit(projection_, nearClip, farClip);
376 else
377 {
378 if (!orthographic_)
379 ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip);
380 else
381 ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip);
382 }
383
384 return ret;
385 }
386
GetScreenRay(float x,float y) const387 Ray Camera::GetScreenRay(float x, float y) const
388 {
389 Ray ret;
390
391 // If projection is invalid, just return a ray pointing forward
392 if (!IsProjectionValid())
393 {
394 ret.origin_ = node_ ? node_->GetWorldPosition() : Vector3::ZERO;
395 ret.direction_ = node_ ? node_->GetWorldDirection() : Vector3::FORWARD;
396 return ret;
397 }
398
399 Matrix4 viewProjInverse = (GetProjection() * GetView()).Inverse();
400
401 // The parameters range from 0.0 to 1.0. Expand to normalized device coordinates (-1.0 to 1.0) & flip Y axis
402 x = 2.0f * x - 1.0f;
403 y = 1.0f - 2.0f * y;
404 Vector3 near(x, y, 0.0f);
405 Vector3 far(x, y, 1.0f);
406
407 ret.origin_ = viewProjInverse * near;
408 ret.direction_ = ((viewProjInverse * far) - ret.origin_).Normalized();
409 return ret;
410 }
411
WorldToScreenPoint(const Vector3 & worldPos) const412 Vector2 Camera::WorldToScreenPoint(const Vector3& worldPos) const
413 {
414 Vector3 eyeSpacePos = GetView() * worldPos;
415 Vector2 ret;
416
417 if (eyeSpacePos.z_ > 0.0f)
418 {
419 Vector3 screenSpacePos = GetProjection() * eyeSpacePos;
420 ret.x_ = screenSpacePos.x_;
421 ret.y_ = screenSpacePos.y_;
422 }
423 else
424 {
425 ret.x_ = (-eyeSpacePos.x_ > 0.0f) ? -1.0f : 1.0f;
426 ret.y_ = (-eyeSpacePos.y_ > 0.0f) ? -1.0f : 1.0f;
427 }
428
429 ret.x_ = (ret.x_ / 2.0f) + 0.5f;
430 ret.y_ = 1.0f - ((ret.y_ / 2.0f) + 0.5f);
431 return ret;
432 }
433
ScreenToWorldPoint(const Vector3 & screenPos) const434 Vector3 Camera::ScreenToWorldPoint(const Vector3& screenPos) const
435 {
436 Ray ray = GetScreenRay(screenPos.x_, screenPos.y_);
437 Vector3 viewSpaceDir = (GetView() * Vector4(ray.direction_, 0.0f));
438 float rayDistance = (Max(screenPos.z_ - GetNearClip(), 0.0f) / viewSpaceDir.z_);
439 return ray.origin_ + ray.direction_ * rayDistance;
440 }
441
GetProjection() const442 Matrix4 Camera::GetProjection() const
443 {
444 if (projectionDirty_)
445 UpdateProjection();
446
447 return flipVertical_ ? flipMatrix * projection_ : projection_;
448 }
449
GetGPUProjection() const450 Matrix4 Camera::GetGPUProjection() const
451 {
452 #ifndef URHO3D_OPENGL
453 return GetProjection(); // Already matches API-specific format
454 #else
455 // See formulation for depth range conversion at http://www.ogre3d.org/forums/viewtopic.php?f=4&t=13357
456 Matrix4 ret = GetProjection();
457
458 ret.m20_ = 2.0f * ret.m20_ - ret.m30_;
459 ret.m21_ = 2.0f * ret.m21_ - ret.m31_;
460 ret.m22_ = 2.0f * ret.m22_ - ret.m32_;
461 ret.m23_ = 2.0f * ret.m23_ - ret.m33_;
462
463 return ret;
464 #endif
465 }
466
GetFrustumSize(Vector3 & near,Vector3 & far) const467 void Camera::GetFrustumSize(Vector3& near, Vector3& far) const
468 {
469 Frustum viewSpaceFrustum = GetViewSpaceFrustum();
470 near = viewSpaceFrustum.vertices_[0];
471 far = viewSpaceFrustum.vertices_[4];
472
473 /// \todo Necessary? Explain this
474 if (flipVertical_)
475 {
476 near.y_ = -near.y_;
477 far.y_ = -far.y_;
478 }
479 }
480
GetHalfViewSize() const481 float Camera::GetHalfViewSize() const
482 {
483 if (!orthographic_)
484 return tanf(fov_ * M_DEGTORAD * 0.5f) / zoom_;
485 else
486 return orthoSize_ * 0.5f / zoom_;
487 }
488
GetDistance(const Vector3 & worldPos) const489 float Camera::GetDistance(const Vector3& worldPos) const
490 {
491 if (!orthographic_)
492 {
493 const Vector3& cameraPos = node_ ? node_->GetWorldPosition() : Vector3::ZERO;
494 return (worldPos - cameraPos).Length();
495 }
496 else
497 return Abs((GetView() * worldPos).z_);
498 }
499
GetDistanceSquared(const Vector3 & worldPos) const500 float Camera::GetDistanceSquared(const Vector3& worldPos) const
501 {
502 if (!orthographic_)
503 {
504 const Vector3& cameraPos = node_ ? node_->GetWorldPosition() : Vector3::ZERO;
505 return (worldPos - cameraPos).LengthSquared();
506 }
507 else
508 {
509 float distance = (GetView() * worldPos).z_;
510 return distance * distance;
511 }
512 }
513
GetLodDistance(float distance,float scale,float bias) const514 float Camera::GetLodDistance(float distance, float scale, float bias) const
515 {
516 float d = Max(lodBias_ * bias * scale * zoom_, M_EPSILON);
517 if (!orthographic_)
518 return distance / d;
519 else
520 return orthoSize_ / d;
521 }
522
GetFaceCameraRotation(const Vector3 & position,const Quaternion & rotation,FaceCameraMode mode,float minAngle)523 Quaternion Camera::GetFaceCameraRotation(const Vector3& position, const Quaternion& rotation, FaceCameraMode mode, float minAngle)
524 {
525 if (!node_)
526 return rotation;
527
528 switch (mode)
529 {
530 case FC_ROTATE_XYZ:
531 return node_->GetWorldRotation();
532
533 case FC_ROTATE_Y:
534 {
535 Vector3 euler = rotation.EulerAngles();
536 euler.y_ = node_->GetWorldRotation().EulerAngles().y_;
537 return Quaternion(euler.x_, euler.y_, euler.z_);
538 }
539
540 case FC_LOOKAT_XYZ:
541 {
542 Quaternion lookAt;
543 lookAt.FromLookRotation(position - node_->GetWorldPosition());
544 return lookAt;
545 }
546
547 case FC_LOOKAT_Y:
548 case FC_LOOKAT_MIXED:
549 {
550 // Mixed mode needs true look-at vector
551 const Vector3 lookAtVec(position - node_->GetWorldPosition());
552 // While Y-only lookat happens on an XZ plane to make sure there are no unwanted transitions or singularities
553 const Vector3 lookAtVecXZ(lookAtVec.x_, 0.0f, lookAtVec.z_);
554
555 Quaternion lookAt;
556 lookAt.FromLookRotation(lookAtVecXZ);
557
558 Vector3 euler = rotation.EulerAngles();
559 if (mode == FC_LOOKAT_MIXED)
560 {
561 const float angle = lookAtVec.Angle(rotation * Vector3::UP);
562 if (angle > 180 - minAngle)
563 euler.x_ += minAngle - (180 - angle);
564 else if (angle < minAngle)
565 euler.x_ -= minAngle - angle;
566 }
567 euler.y_ = lookAt.EulerAngles().y_;
568 return Quaternion(euler.x_, euler.y_, euler.z_);
569 }
570
571 default:
572 return rotation;
573 }
574 }
575
GetEffectiveWorldTransform() const576 Matrix3x4 Camera::GetEffectiveWorldTransform() const
577 {
578 Matrix3x4 worldTransform = node_ ? Matrix3x4(node_->GetWorldPosition(), node_->GetWorldRotation(), 1.0f) : Matrix3x4::IDENTITY;
579 return useReflection_ ? reflectionMatrix_ * worldTransform : worldTransform;
580 }
581
IsProjectionValid() const582 bool Camera::IsProjectionValid() const
583 {
584 return GetFarClip() > GetNearClip();
585 }
586
GetView() const587 const Matrix3x4& Camera::GetView() const
588 {
589 if (viewDirty_)
590 {
591 // Note: view matrix is unaffected by node or parent scale
592 view_ = GetEffectiveWorldTransform().Inverse();
593 viewDirty_ = false;
594 }
595
596 return view_;
597 }
598
SetAspectRatioInternal(float aspectRatio)599 void Camera::SetAspectRatioInternal(float aspectRatio)
600 {
601 if (aspectRatio != aspectRatio_)
602 {
603 aspectRatio_ = aspectRatio;
604 frustumDirty_ = true;
605 projectionDirty_ = true;
606 }
607 MarkNetworkUpdate();
608 }
609
SetOrthoSizeAttr(float orthoSize)610 void Camera::SetOrthoSizeAttr(float orthoSize)
611 {
612 orthoSize_ = orthoSize;
613 frustumDirty_ = true;
614 projectionDirty_ = true;
615 MarkNetworkUpdate();
616 }
617
SetReflectionPlaneAttr(const Vector4 & value)618 void Camera::SetReflectionPlaneAttr(const Vector4& value)
619 {
620 SetReflectionPlane(Plane(value));
621 }
622
SetClipPlaneAttr(const Vector4 & value)623 void Camera::SetClipPlaneAttr(const Vector4& value)
624 {
625 SetClipPlane(Plane(value));
626 }
627
GetReflectionPlaneAttr() const628 Vector4 Camera::GetReflectionPlaneAttr() const
629 {
630 return reflectionPlane_.ToVector4();
631 }
632
GetClipPlaneAttr() const633 Vector4 Camera::GetClipPlaneAttr() const
634 {
635 return clipPlane_.ToVector4();
636 }
637
OnNodeSet(Node * node)638 void Camera::OnNodeSet(Node* node)
639 {
640 if (node)
641 node->AddListener(this);
642 }
643
OnMarkedDirty(Node * node)644 void Camera::OnMarkedDirty(Node* node)
645 {
646 frustumDirty_ = true;
647 viewDirty_ = true;
648 }
649
UpdateProjection() const650 void Camera::UpdateProjection() const
651 {
652 // Start from a zero matrix in case it was custom previously
653 projection_ = Matrix4::ZERO;
654
655 if (!orthographic_)
656 {
657 float h = (1.0f / tanf(fov_ * M_DEGTORAD * 0.5f)) * zoom_;
658 float w = h / aspectRatio_;
659 float q = farClip_ / (farClip_ - nearClip_);
660 float r = -q * nearClip_;
661
662 projection_.m00_ = w;
663 projection_.m02_ = projectionOffset_.x_ * 2.0f;
664 projection_.m11_ = h;
665 projection_.m12_ = projectionOffset_.y_ * 2.0f;
666 projection_.m22_ = q;
667 projection_.m23_ = r;
668 projection_.m32_ = 1.0f;
669 projNearClip_ = nearClip_;
670 projFarClip_ = farClip_;
671 }
672 else
673 {
674 float h = (1.0f / (orthoSize_ * 0.5f)) * zoom_;
675 float w = h / aspectRatio_;
676 float q = 1.0f / farClip_;
677 float r = 0.0f;
678
679 projection_.m00_ = w;
680 projection_.m03_ = projectionOffset_.x_ * 2.0f;
681 projection_.m11_ = h;
682 projection_.m13_ = projectionOffset_.y_ * 2.0f;
683 projection_.m22_ = q;
684 projection_.m23_ = r;
685 projection_.m33_ = 1.0f;
686 // Near clip does not affect depth accuracy in ortho projection, so let it stay 0 to avoid problems with shader depth parameters
687 projNearClip_ = 0.0f;
688 projFarClip_ = farClip_;
689 }
690
691 projectionDirty_ = false;
692 customProjection_ = false;
693 }
694
695 }
696