1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "Renderer/BsCamera.h" |
4 | #include "Private/RTTI/BsCameraRTTI.h" |
5 | #include "Math/BsMath.h" |
6 | #include "Math/BsMatrix3.h" |
7 | #include "Math/BsVector2.h" |
8 | #include "Math/BsAABox.h" |
9 | #include "Math/BsSphere.h" |
10 | #include "Error/BsException.h" |
11 | #include "RenderAPI/BsRenderAPI.h" |
12 | #include "Scene/BsSceneObject.h" |
13 | #include "Renderer/BsRendererManager.h" |
14 | #include "Renderer/BsRenderer.h" |
15 | #include "Scene/BsSceneManager.h" |
16 | #include "CoreThread/BsCoreObjectSync.h" |
17 | |
18 | namespace bs |
19 | { |
20 | const float CameraBase::INFINITE_FAR_PLANE_ADJUST = 0.00001f; |
21 | |
22 | CameraBase::CameraBase() |
23 | : mRecalcFrustum(true), mRecalcFrustumPlanes(true), mRecalcView(true) |
24 | { |
25 | mRenderSettings = bs_shared_ptr_new<RenderSettings>(); |
26 | |
27 | invalidateFrustum(); |
28 | } |
29 | |
30 | void CameraBase::setHorzFOV(const Radian& fov) |
31 | { |
32 | mHorzFOV = fov; |
33 | invalidateFrustum(); |
34 | _markCoreDirty(); |
35 | } |
36 | |
37 | const Radian& CameraBase::getHorzFOV() const |
38 | { |
39 | return mHorzFOV; |
40 | } |
41 | |
42 | void CameraBase::setFarClipDistance(float farPlane) |
43 | { |
44 | mFarDist = farPlane; |
45 | invalidateFrustum(); |
46 | _markCoreDirty(); |
47 | } |
48 | |
49 | float CameraBase::getFarClipDistance() const |
50 | { |
51 | return mFarDist; |
52 | } |
53 | |
54 | void CameraBase::setNearClipDistance(float nearPlane) |
55 | { |
56 | if (nearPlane <= 0) |
57 | { |
58 | LOGERR("Near clip distance must be greater than zero." ); |
59 | return; |
60 | } |
61 | |
62 | mNearDist = nearPlane; |
63 | invalidateFrustum(); |
64 | _markCoreDirty(); |
65 | } |
66 | |
67 | float CameraBase::getNearClipDistance() const |
68 | { |
69 | return mNearDist; |
70 | } |
71 | |
72 | const Matrix4& CameraBase::getProjectionMatrix() const |
73 | { |
74 | updateFrustum(); |
75 | |
76 | return mProjMatrix; |
77 | } |
78 | |
79 | const Matrix4& CameraBase::getProjectionMatrixInv() const |
80 | { |
81 | updateFrustum(); |
82 | |
83 | return mProjMatrixInv; |
84 | } |
85 | |
86 | const Matrix4& CameraBase::getProjectionMatrixRS() const |
87 | { |
88 | updateFrustum(); |
89 | |
90 | return mProjMatrixRS; |
91 | } |
92 | |
93 | const Matrix4& CameraBase::getProjectionMatrixRSInv() const |
94 | { |
95 | updateFrustum(); |
96 | |
97 | return mProjMatrixRSInv; |
98 | } |
99 | |
100 | const Matrix4& CameraBase::getViewMatrix() const |
101 | { |
102 | updateView(); |
103 | |
104 | return mViewMatrix; |
105 | } |
106 | |
107 | const Matrix4& CameraBase::getViewMatrixInv() const |
108 | { |
109 | updateView(); |
110 | |
111 | return mViewMatrixInv; |
112 | } |
113 | |
114 | const ConvexVolume& CameraBase::getFrustum() const |
115 | { |
116 | // Make any pending updates to the calculated frustum planes |
117 | updateFrustumPlanes(); |
118 | |
119 | return mFrustum; |
120 | } |
121 | |
122 | ConvexVolume CameraBase::getWorldFrustum() const |
123 | { |
124 | const Vector<Plane>& frustumPlanes = getFrustum().getPlanes(); |
125 | |
126 | const Transform& tfrm = getTransform(); |
127 | |
128 | Matrix4 worldMatrix; |
129 | worldMatrix.setTRS(tfrm.getPosition(), tfrm.getRotation(), Vector3::ONE); |
130 | |
131 | Vector<Plane> worldPlanes(frustumPlanes.size()); |
132 | UINT32 i = 0; |
133 | for (auto& plane : frustumPlanes) |
134 | { |
135 | worldPlanes[i] = worldMatrix.multiplyAffine(plane); |
136 | i++; |
137 | } |
138 | |
139 | return ConvexVolume(worldPlanes); |
140 | } |
141 | |
142 | void CameraBase::calcProjectionParameters(float& left, float& right, float& bottom, float& top) const |
143 | { |
144 | if (mCustomProjMatrix) |
145 | { |
146 | // Convert clipspace corners to camera space |
147 | Matrix4 invProj = mProjMatrix.inverse(); |
148 | Vector3 topLeft(-0.5f, 0.5f, 0.0f); |
149 | Vector3 bottomRight(0.5f, -0.5f, 0.0f); |
150 | |
151 | topLeft = invProj.multiply(topLeft); |
152 | bottomRight = invProj.multiply(bottomRight); |
153 | |
154 | left = topLeft.x; |
155 | top = topLeft.y; |
156 | right = bottomRight.x; |
157 | bottom = bottomRight.y; |
158 | } |
159 | else |
160 | { |
161 | if (mFrustumExtentsManuallySet) |
162 | { |
163 | left = mLeft; |
164 | right = mRight; |
165 | top = mTop; |
166 | bottom = mBottom; |
167 | } |
168 | else if (mProjType == PT_PERSPECTIVE) |
169 | { |
170 | Radian thetaX(mHorzFOV * 0.5f); |
171 | float tanThetaX = Math::tan(thetaX); |
172 | float tanThetaY = tanThetaX / mAspect; |
173 | |
174 | float half_w = tanThetaX * mNearDist; |
175 | float half_h = tanThetaY * mNearDist; |
176 | |
177 | left = -half_w; |
178 | right = half_w; |
179 | bottom = -half_h; |
180 | top = half_h; |
181 | |
182 | mLeft = left; |
183 | mRight = right; |
184 | mTop = top; |
185 | mBottom = bottom; |
186 | } |
187 | else |
188 | { |
189 | float half_w = getOrthoWindowWidth() * 0.5f; |
190 | float half_h = getOrthoWindowHeight() * 0.5f; |
191 | |
192 | left = -half_w; |
193 | right = half_w; |
194 | bottom = -half_h; |
195 | top = half_h; |
196 | |
197 | mLeft = left; |
198 | mRight = right; |
199 | mTop = top; |
200 | mBottom = bottom; |
201 | } |
202 | } |
203 | } |
204 | |
205 | void CameraBase::updateFrustum() const |
206 | { |
207 | if (isFrustumOutOfDate()) |
208 | { |
209 | float left, right, bottom, top; |
210 | |
211 | calcProjectionParameters(left, right, bottom, top); |
212 | |
213 | if (!mCustomProjMatrix) |
214 | { |
215 | float inv_w = 1 / (right - left); |
216 | float inv_h = 1 / (top - bottom); |
217 | float inv_d = 1 / (mFarDist - mNearDist); |
218 | |
219 | if (mProjType == PT_PERSPECTIVE) |
220 | { |
221 | float A = 2 * mNearDist * inv_w; |
222 | float B = 2 * mNearDist * inv_h; |
223 | float C = (right + left) * inv_w; |
224 | float D = (top + bottom) * inv_h; |
225 | float q, qn; |
226 | |
227 | if (mFarDist == 0) |
228 | { |
229 | // Infinite far plane |
230 | q = CameraBase::INFINITE_FAR_PLANE_ADJUST - 1; |
231 | qn = mNearDist * (CameraBase::INFINITE_FAR_PLANE_ADJUST - 2); |
232 | } |
233 | else |
234 | { |
235 | q = -(mFarDist + mNearDist) * inv_d; |
236 | qn = -2 * (mFarDist * mNearDist) * inv_d; |
237 | } |
238 | |
239 | mProjMatrix = Matrix4::ZERO; |
240 | mProjMatrix[0][0] = A; |
241 | mProjMatrix[0][2] = C; |
242 | mProjMatrix[1][1] = B; |
243 | mProjMatrix[1][2] = D; |
244 | mProjMatrix[2][2] = q; |
245 | mProjMatrix[2][3] = qn; |
246 | mProjMatrix[3][2] = -1; |
247 | } |
248 | else if (mProjType == PT_ORTHOGRAPHIC) |
249 | { |
250 | float A = 2 * inv_w; |
251 | float B = 2 * inv_h; |
252 | float C = -(right + left) * inv_w; |
253 | float D = -(top + bottom) * inv_h; |
254 | float q, qn; |
255 | |
256 | if (mFarDist == 0) |
257 | { |
258 | // Can not do infinite far plane here, avoid divided zero only |
259 | q = -CameraBase::INFINITE_FAR_PLANE_ADJUST / mNearDist; |
260 | qn = -CameraBase::INFINITE_FAR_PLANE_ADJUST - 1; |
261 | } |
262 | else |
263 | { |
264 | q = -2 * inv_d; |
265 | qn = -(mFarDist + mNearDist) * inv_d; |
266 | } |
267 | |
268 | mProjMatrix = Matrix4::ZERO; |
269 | mProjMatrix[0][0] = A; |
270 | mProjMatrix[0][3] = C; |
271 | mProjMatrix[1][1] = B; |
272 | mProjMatrix[1][3] = D; |
273 | mProjMatrix[2][2] = q; |
274 | mProjMatrix[2][3] = qn; |
275 | mProjMatrix[3][3] = 1; |
276 | } |
277 | } |
278 | |
279 | ct::RenderAPI* renderAPI = ct::RenderAPI::instancePtr(); |
280 | renderAPI->convertProjectionMatrix(mProjMatrix, mProjMatrixRS); |
281 | mProjMatrixInv = mProjMatrix.inverse(); |
282 | mProjMatrixRSInv = mProjMatrixRS.inverse(); |
283 | |
284 | // Calculate bounding box (local) |
285 | // Box is from 0, down -Z, max dimensions as determined from far plane |
286 | // If infinite view frustum just pick a far value |
287 | float farDist = (mFarDist == 0) ? 100000 : mFarDist; |
288 | |
289 | // Near plane bounds |
290 | Vector3 min(left, bottom, -farDist); |
291 | Vector3 max(right, top, 0); |
292 | |
293 | if (mCustomProjMatrix) |
294 | { |
295 | // Some custom projection matrices can have unusual inverted settings |
296 | // So make sure the AABB is the right way around to start with |
297 | Vector3 tmp = min; |
298 | min.min(max); |
299 | max.max(tmp); |
300 | } |
301 | |
302 | if (mProjType == PT_PERSPECTIVE) |
303 | { |
304 | // Merge with far plane bounds |
305 | float radio = farDist / mNearDist; |
306 | min.min(Vector3(left * radio, bottom * radio, -farDist)); |
307 | max.max(Vector3(right * radio, top * radio, 0)); |
308 | } |
309 | |
310 | mBoundingBox.setExtents(min, max); |
311 | |
312 | mRecalcFrustum = false; |
313 | mRecalcFrustumPlanes = true; |
314 | } |
315 | } |
316 | |
317 | bool CameraBase::isFrustumOutOfDate() const |
318 | { |
319 | return mRecalcFrustum; |
320 | } |
321 | |
322 | void CameraBase::updateView() const |
323 | { |
324 | if (!mCustomViewMatrix && mRecalcView) |
325 | { |
326 | mViewMatrix.makeView(mTransform.getPosition(), mTransform.getRotation()); |
327 | mViewMatrixInv = mViewMatrix.inverseAffine(); |
328 | mRecalcView = false; |
329 | } |
330 | } |
331 | |
332 | void CameraBase::updateFrustumPlanes() const |
333 | { |
334 | updateFrustum(); |
335 | |
336 | if (mRecalcFrustumPlanes) |
337 | { |
338 | mFrustum = ConvexVolume(mProjMatrix); |
339 | mRecalcFrustumPlanes = false; |
340 | } |
341 | } |
342 | |
343 | float CameraBase::getAspectRatio() const |
344 | { |
345 | return mAspect; |
346 | } |
347 | |
348 | void CameraBase::setAspectRatio(float r) |
349 | { |
350 | mAspect = r; |
351 | invalidateFrustum(); |
352 | _markCoreDirty(); |
353 | } |
354 | |
355 | const AABox& CameraBase::getBoundingBox() const |
356 | { |
357 | updateFrustum(); |
358 | |
359 | return mBoundingBox; |
360 | } |
361 | |
362 | void CameraBase::setProjectionType(ProjectionType pt) |
363 | { |
364 | mProjType = pt; |
365 | invalidateFrustum(); |
366 | _markCoreDirty(); |
367 | } |
368 | |
369 | ProjectionType CameraBase::getProjectionType() const |
370 | { |
371 | return mProjType; |
372 | } |
373 | |
374 | void CameraBase::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix) |
375 | { |
376 | mCustomViewMatrix = enable; |
377 | if (enable) |
378 | { |
379 | mViewMatrix = viewMatrix; |
380 | mViewMatrixInv = mViewMatrix.inverseAffine(); |
381 | } |
382 | |
383 | _markCoreDirty(); |
384 | } |
385 | |
386 | void CameraBase::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix) |
387 | { |
388 | mCustomProjMatrix = enable; |
389 | |
390 | if (enable) |
391 | mProjMatrix = projMatrix; |
392 | |
393 | invalidateFrustum(); |
394 | _markCoreDirty(); |
395 | } |
396 | |
397 | void CameraBase::setOrthoWindow(float w, float h) |
398 | { |
399 | mOrthoHeight = h; |
400 | mAspect = w / h; |
401 | |
402 | invalidateFrustum(); |
403 | _markCoreDirty(); |
404 | } |
405 | |
406 | void CameraBase::setOrthoWindowHeight(float h) |
407 | { |
408 | mOrthoHeight = h; |
409 | |
410 | invalidateFrustum(); |
411 | _markCoreDirty(); |
412 | } |
413 | |
414 | void CameraBase::setOrthoWindowWidth(float w) |
415 | { |
416 | mOrthoHeight = w / mAspect; |
417 | |
418 | invalidateFrustum(); |
419 | _markCoreDirty(); |
420 | } |
421 | |
422 | float CameraBase::getOrthoWindowHeight() const |
423 | { |
424 | return mOrthoHeight; |
425 | } |
426 | |
427 | float CameraBase::getOrthoWindowWidth() const |
428 | { |
429 | return mOrthoHeight * mAspect; |
430 | } |
431 | |
432 | void CameraBase::setFrustumExtents(float left, float right, float top, float bottom) |
433 | { |
434 | mFrustumExtentsManuallySet = true; |
435 | mLeft = left; |
436 | mRight = right; |
437 | mTop = top; |
438 | mBottom = bottom; |
439 | |
440 | invalidateFrustum(); |
441 | _markCoreDirty(); |
442 | } |
443 | |
444 | void CameraBase::resetFrustumExtents() |
445 | { |
446 | mFrustumExtentsManuallySet = false; |
447 | |
448 | invalidateFrustum(); |
449 | _markCoreDirty(); |
450 | } |
451 | |
452 | void CameraBase::getFrustumExtents(float& outleft, float& outright, float& outtop, float& outbottom) const |
453 | { |
454 | updateFrustum(); |
455 | |
456 | outleft = mLeft; |
457 | outright = mRight; |
458 | outtop = mTop; |
459 | outbottom = mBottom; |
460 | } |
461 | |
462 | void CameraBase::setTransform(const Transform& transform) |
463 | { |
464 | SceneActor::setTransform(transform); |
465 | |
466 | mRecalcView = true; |
467 | } |
468 | |
469 | void CameraBase::invalidateFrustum() const |
470 | { |
471 | mRecalcFrustum = true; |
472 | mRecalcFrustumPlanes = true; |
473 | } |
474 | |
475 | Vector2I CameraBase::worldToScreenPoint(const Vector3& worldPoint) const |
476 | { |
477 | Vector2 ndcPoint = worldToNdcPoint(worldPoint); |
478 | return ndcToScreenPoint(ndcPoint); |
479 | } |
480 | |
481 | Vector2 CameraBase::worldToNdcPoint(const Vector3& worldPoint) const |
482 | { |
483 | Vector3 viewPoint = worldToViewPoint(worldPoint); |
484 | return viewToNdcPoint(viewPoint); |
485 | } |
486 | |
487 | Vector3 CameraBase::worldToViewPoint(const Vector3& worldPoint) const |
488 | { |
489 | return getViewMatrix().multiplyAffine(worldPoint); |
490 | } |
491 | |
492 | Vector3 CameraBase::screenToWorldPoint(const Vector2I& screenPoint, float depth) const |
493 | { |
494 | Vector2 ndcPoint = screenToNdcPoint(screenPoint); |
495 | return ndcToWorldPoint(ndcPoint, depth); |
496 | } |
497 | |
498 | Vector3 CameraBase::screenToWorldPointDeviceDepth(const Vector2I& screenPoint, float deviceDepth) const |
499 | { |
500 | Vector2 ndcPoint = screenToNdcPoint(screenPoint); |
501 | Vector4 worldPoint(ndcPoint.x, ndcPoint.y, deviceDepth, 1.0f); |
502 | worldPoint = getProjectionMatrixRS().inverse().multiply(worldPoint); |
503 | |
504 | Vector3 worldPoint3D; |
505 | if (Math::abs(worldPoint.w) > 1e-7f) |
506 | { |
507 | float invW = 1.0f / worldPoint.w; |
508 | |
509 | worldPoint3D.x = worldPoint.x * invW; |
510 | worldPoint3D.y = worldPoint.y * invW; |
511 | worldPoint3D.z = worldPoint.z * invW; |
512 | } |
513 | |
514 | return viewToWorldPoint(worldPoint3D); |
515 | } |
516 | |
517 | Vector3 CameraBase::screenToViewPoint(const Vector2I& screenPoint, float depth) const |
518 | { |
519 | Vector2 ndcPoint = screenToNdcPoint(screenPoint); |
520 | return ndcToViewPoint(ndcPoint, depth); |
521 | } |
522 | |
523 | Vector2 CameraBase::screenToNdcPoint(const Vector2I& screenPoint) const |
524 | { |
525 | Rect2I viewport = getViewportRect(); |
526 | |
527 | Vector2 ndcPoint; |
528 | ndcPoint.x = (float)(((screenPoint.x - viewport.x) / (float)viewport.width) * 2.0f - 1.0f); |
529 | |
530 | const Conventions& rapiConventions = ct::gCaps().conventions; |
531 | if(rapiConventions.ndcYAxis == Conventions::Axis::Down) |
532 | ndcPoint.y = (float)(((screenPoint.y - viewport.y) / (float)viewport.height) * 2.0f - 1.0f); |
533 | else |
534 | ndcPoint.y = (float)((1.0f - ((screenPoint.y - viewport.y) / (float)viewport.height)) * 2.0f - 1.0f); |
535 | |
536 | return ndcPoint; |
537 | } |
538 | |
539 | Vector3 CameraBase::viewToWorldPoint(const Vector3& viewPoint) const |
540 | { |
541 | return getViewMatrix().inverseAffine().multiplyAffine(viewPoint); |
542 | } |
543 | |
544 | Vector2I CameraBase::viewToScreenPoint(const Vector3& viewPoint) const |
545 | { |
546 | Vector2 ndcPoint = viewToNdcPoint(viewPoint); |
547 | return ndcToScreenPoint(ndcPoint); |
548 | } |
549 | |
550 | Vector2 CameraBase::viewToNdcPoint(const Vector3& viewPoint) const |
551 | { |
552 | Vector3 projPoint = projectPoint(viewPoint); |
553 | |
554 | return Vector2(projPoint.x, projPoint.y); |
555 | } |
556 | |
557 | Vector3 CameraBase::ndcToWorldPoint(const Vector2& ndcPoint, float depth) const |
558 | { |
559 | Vector3 viewPoint = ndcToViewPoint(ndcPoint, depth); |
560 | return viewToWorldPoint(viewPoint); |
561 | } |
562 | |
563 | Vector3 CameraBase::ndcToViewPoint(const Vector2& ndcPoint, float depth) const |
564 | { |
565 | return unprojectPoint(Vector3(ndcPoint.x, ndcPoint.y, depth)); |
566 | } |
567 | |
568 | Vector2I CameraBase::ndcToScreenPoint(const Vector2& ndcPoint) const |
569 | { |
570 | Rect2I viewport = getViewportRect(); |
571 | |
572 | Vector2I screenPoint; |
573 | screenPoint.x = Math::roundToInt(viewport.x + ((ndcPoint.x + 1.0f) * 0.5f) * viewport.width); |
574 | screenPoint.y = Math::roundToInt(viewport.y + (1.0f - (ndcPoint.y + 1.0f) * 0.5f) * viewport.height); |
575 | |
576 | return screenPoint; |
577 | } |
578 | |
579 | Ray CameraBase::screenPointToRay(const Vector2I& screenPoint) const |
580 | { |
581 | Vector2 ndcPoint = screenToNdcPoint(screenPoint); |
582 | |
583 | Vector3 near = unprojectPoint(Vector3(ndcPoint.x, ndcPoint.y, mNearDist)); |
584 | Vector3 far = unprojectPoint(Vector3(ndcPoint.x, ndcPoint.y, mNearDist + 1.0f)); |
585 | |
586 | Ray ray(near, Vector3::normalize(far - near)); |
587 | ray.transformAffine(getViewMatrix().inverseAffine()); |
588 | |
589 | return ray; |
590 | } |
591 | |
592 | Vector3 CameraBase::projectPoint(const Vector3& point) const |
593 | { |
594 | Vector4 projPoint4(point.x, point.y, point.z, 1.0f); |
595 | projPoint4 = getProjectionMatrixRS().multiply(projPoint4); |
596 | |
597 | if (Math::abs(projPoint4.w) > 1e-7f) |
598 | { |
599 | float invW = 1.0f / projPoint4.w; |
600 | projPoint4.x *= invW; |
601 | projPoint4.y *= invW; |
602 | projPoint4.z *= invW; |
603 | } |
604 | else |
605 | { |
606 | projPoint4.x = 0.0f; |
607 | projPoint4.y = 0.0f; |
608 | projPoint4.z = 0.0f; |
609 | } |
610 | |
611 | return Vector3(projPoint4.x, projPoint4.y, projPoint4.z); |
612 | } |
613 | |
614 | Vector3 CameraBase::unprojectPoint(const Vector3& point) const |
615 | { |
616 | // Point.z is expected to be in view space, so we need to do some extra work to get the proper coordinates |
617 | // (as opposed to if point.z was in device coordinates, in which case we could just inverse project) |
618 | |
619 | // Get world position for a point near the far plane (0.95f) |
620 | Vector4 farAwayPoint(point.x, point.y, 0.95f, 1.0f); |
621 | farAwayPoint = getProjectionMatrixRS().inverse().multiply(farAwayPoint); |
622 | |
623 | // Can't proceed if w is too small |
624 | if (Math::abs(farAwayPoint.w) > 1e-7f) |
625 | { |
626 | // Perspective divide, to get the values that make sense in 3D space |
627 | float invW = 1.0f / farAwayPoint.w; |
628 | |
629 | Vector3 farAwayPoint3D; |
630 | farAwayPoint3D.x = farAwayPoint.x * invW; |
631 | farAwayPoint3D.y = farAwayPoint.y * invW; |
632 | farAwayPoint3D.z = farAwayPoint.z * invW; |
633 | |
634 | // Find the distance to the far point along the camera's viewing axis |
635 | float distAlongZ = farAwayPoint3D.dot(-Vector3::UNIT_Z); |
636 | |
637 | // Do nothing if point is behind the camera |
638 | if (distAlongZ >= 0.0f) |
639 | { |
640 | if (mProjType == PT_PERSPECTIVE) |
641 | { |
642 | // Direction from origin to our point |
643 | Vector3 dir = farAwayPoint3D; // Camera is at (0, 0, 0) so it's the same vector |
644 | |
645 | // Our view space depth (point.z) is distance along the camera's viewing axis. Since our direction |
646 | // vector is not parallel to the viewing axis, instead of normalizing it with its own length, we |
647 | // "normalize" with the length projected along the camera's viewing axis. |
648 | dir /= distAlongZ; |
649 | |
650 | // And now we just find the final position along the direction |
651 | return dir * point.z; |
652 | } |
653 | else // Ortographic |
654 | { |
655 | // Depth difference between our arbitrary point and actual depth |
656 | float depthDiff = distAlongZ - point.z; |
657 | |
658 | // Depth difference along viewing direction |
659 | Vector3 depthDiffVec = depthDiff * -Vector3::UNIT_Z; |
660 | |
661 | // Return point that is depthDiff closer than our arbitrary point |
662 | return farAwayPoint3D - depthDiffVec; |
663 | } |
664 | } |
665 | } |
666 | |
667 | return Vector3(0.0f, 0.0f, 0.0f); |
668 | } |
669 | |
670 | template <bool Core> |
671 | template <class P> |
672 | void TCamera<Core>::rttiEnumFields(P p) |
673 | { |
674 | p(mLayers); |
675 | p(mProjType); |
676 | p(mHorzFOV); |
677 | p(mFarDist); |
678 | p(mNearDist); |
679 | p(mAspect); |
680 | p(mOrthoHeight); |
681 | p(mPriority); |
682 | p(mCustomViewMatrix); |
683 | p(mCustomProjMatrix); |
684 | p(mFrustumExtentsManuallySet); |
685 | p(mMSAA); |
686 | p(mMain); |
687 | p(*mRenderSettings); |
688 | } |
689 | |
690 | SPtr<ct::Camera> Camera::getCore() const |
691 | { |
692 | return std::static_pointer_cast<ct::Camera>(mCoreSpecific); |
693 | } |
694 | |
695 | SPtr<Camera> Camera::create() |
696 | { |
697 | Camera* handler = new (bs_alloc<Camera>()) Camera(); |
698 | SPtr<Camera> handlerPtr = bs_core_ptr<Camera>(handler); |
699 | handlerPtr->_setThisPtr(handlerPtr); |
700 | handlerPtr->initialize(); |
701 | |
702 | return handlerPtr; |
703 | } |
704 | |
705 | SPtr<Camera> Camera::createEmpty() |
706 | { |
707 | Camera* handler = new (bs_alloc<Camera>()) Camera(); |
708 | SPtr<Camera> handlerPtr = bs_core_ptr<Camera>(handler); |
709 | handlerPtr->_setThisPtr(handlerPtr); |
710 | |
711 | return handlerPtr; |
712 | } |
713 | |
714 | SPtr<ct::CoreObject> Camera::createCore() const |
715 | { |
716 | ct::Camera* handler = new (bs_alloc<ct::Camera>()) ct::Camera(mViewport->getCore()); |
717 | SPtr<ct::Camera> handlerPtr = bs_shared_ptr<ct::Camera>(handler); |
718 | handlerPtr->_setThisPtr(handlerPtr); |
719 | |
720 | return handlerPtr; |
721 | } |
722 | |
723 | void Camera::initialize() |
724 | { |
725 | mViewport = Viewport::create(nullptr); |
726 | |
727 | CoreObject::initialize(); |
728 | |
729 | gSceneManager()._registerCamera(std::static_pointer_cast<Camera>(getThisPtr())); |
730 | } |
731 | |
732 | void Camera::destroy() |
733 | { |
734 | if(isInitialized()) |
735 | gSceneManager()._unregisterCamera(std::static_pointer_cast<Camera>(getThisPtr())); |
736 | |
737 | CoreObject::destroy(); |
738 | } |
739 | |
740 | void Camera::setMain(bool main) |
741 | { |
742 | mMain = main; |
743 | gSceneManager()._notifyMainCameraStateChanged(std::static_pointer_cast<Camera>(getThisPtr())); |
744 | } |
745 | |
746 | Rect2I Camera::getViewportRect() const |
747 | { |
748 | return mViewport->getPixelArea(); |
749 | } |
750 | |
751 | CoreSyncData Camera::syncToCore(FrameAlloc* allocator) |
752 | { |
753 | UINT32 dirtyFlag = getCoreDirtyFlags(); |
754 | |
755 | UINT32 size = rttiGetElemSize(dirtyFlag); |
756 | size += coreSyncGetElemSize((SceneActor&)*this); |
757 | |
758 | if (dirtyFlag != (UINT32)ActorDirtyFlag::Transform) |
759 | size += coreSyncGetElemSize(*this); |
760 | |
761 | UINT8* buffer = allocator->alloc(size); |
762 | |
763 | char* dataPtr = (char*)buffer; |
764 | dataPtr = rttiWriteElem(dirtyFlag, dataPtr); |
765 | dataPtr = coreSyncWriteElem((SceneActor&)*this, dataPtr); |
766 | |
767 | if (dirtyFlag != (UINT32)ActorDirtyFlag::Transform) |
768 | dataPtr = coreSyncWriteElem(*this, dataPtr); |
769 | |
770 | return CoreSyncData(buffer, size); |
771 | } |
772 | |
773 | void Camera::getCoreDependencies(Vector<CoreObject*>& dependencies) |
774 | { |
775 | dependencies.push_back(mViewport.get()); |
776 | } |
777 | |
778 | void Camera::_markCoreDirty(ActorDirtyFlag flag) |
779 | { |
780 | markCoreDirty((UINT32)flag); |
781 | } |
782 | |
783 | RTTITypeBase* Camera::getRTTIStatic() |
784 | { |
785 | return CameraRTTI::instance(); |
786 | } |
787 | |
788 | RTTITypeBase* Camera::getRTTI() const |
789 | { |
790 | return Camera::getRTTIStatic(); |
791 | } |
792 | |
793 | namespace ct |
794 | { |
795 | Camera::~Camera() |
796 | { |
797 | RendererManager::instance().getActive()->notifyCameraRemoved(this); |
798 | } |
799 | |
800 | Camera::Camera(SPtr<RenderTarget> target, float left, float top, float width, float height) |
801 | : mRendererId(0) |
802 | { |
803 | mViewport = Viewport::create(target, left, top, width, height); |
804 | } |
805 | |
806 | Camera::Camera(const SPtr<Viewport>& viewport) |
807 | : mRendererId(0) |
808 | { |
809 | mViewport = viewport; |
810 | } |
811 | |
812 | void Camera::initialize() |
813 | { |
814 | RendererManager::instance().getActive()->notifyCameraAdded(this); |
815 | |
816 | CoreObject::initialize(); |
817 | } |
818 | |
819 | Rect2I Camera::getViewportRect() const |
820 | { |
821 | return mViewport->getPixelArea(); |
822 | } |
823 | |
824 | void Camera::syncToCore(const CoreSyncData& data) |
825 | { |
826 | char* dataPtr = (char*)data.getBuffer(); |
827 | |
828 | UINT32 dirtyFlag; |
829 | dataPtr = rttiReadElem(dirtyFlag, dataPtr); |
830 | dataPtr = coreSyncReadElem((SceneActor&)*this, dataPtr); |
831 | |
832 | if (dirtyFlag != (UINT32)ActorDirtyFlag::Transform) |
833 | dataPtr = coreSyncReadElem(*this, dataPtr); |
834 | |
835 | mRecalcFrustum = true; |
836 | mRecalcFrustumPlanes = true; |
837 | mRecalcView = true; |
838 | |
839 | RendererManager::instance().getActive()->notifyCameraUpdated(this, (UINT32)dirtyFlag); |
840 | } |
841 | } |
842 | } |
843 | |