/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "core/camera.h" #include "core/light.h" #include "log/log.h" using namespace GS; using namespace GS::Core; //------------------------------------------------------------------------------ void Camera::AlignTo(const Light &l) { aspect_ratio = 1; SnapshotTransformation(l.GetMatrix()); SetNearClippingPlane(l.GetNearClippingPlane()); SetFarClippingPlane(l.GetFarClippingPlane()); float fov = l.cone_angle + l.edge_angle; SetZoomFactor(Math::Cos(fov) / Math::Sin(fov)); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Camera::WorldToScreen(const fRect &v, const Vector4 &in, Vector4 &out, bool normalize) { Vector4 dp = in * GetInverseMatrix(); if (dp.z <= 0) return false; dp.x /= dp.z / zoom_factor; dp.y /= dp.z / zoom_factor; if (normalize) { if (aspect_ratio_ref_yaxis) dp.x /= v.GetWidth() / v.GetHeight(); else dp.y /= v.GetWidth() / v.GetHeight(); } out.Set((1 + dp.x) * 0.5f, (1 - dp.y) * 0.5f, 0); return true; } Vector4 Camera::ScreenToWorld(const fRect &v, float x, float y, float z, float ar) { Vector4 sw((x - 0.5f) * 2.f, -(y - 0.5f) * 2.f, zoom_factor); sw *= z / sw.z; if (ar < 0) { if (aspect_ratio_ref_yaxis) sw.x *= v.GetWidth() / v.GetHeight(); else sw.y *= v.GetWidth() / v.GetHeight(); } else sw.x *= ar; return sw * GetMatrix(); } Vector4 Camera::ComputeAspectRatioCorrection(const fRect &v, float global_ar) const { float k_ar = aspect_ratio; if (k_ar <= 0.f) // Square AR. k_ar = v.GetHeight() / v.GetWidth(); k_ar *= global_ar; if (aspect_ratio_ref_yaxis) return Vector4(k_ar, 1, 1); return Vector4(1, 1.f / k_ar, 1); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Camera::ComputeProjectionMatrix(const fRect &v, Matrix4 &m) const { #if __PLATFORM_NINTENDO_WII__ const float za = z_far; #else const float za = z_near; #endif if (is_occulus_camera) { m = custom_projection; /* // create scale and offset float projXScale = 2.0f / (tan_left + tan_right); float projXOffset = (tan_left - tan_right) * projXScale * 0.5f; float projYScale = 2.0f / (tan_up + tan_down); float projYOffset = (tan_up - tan_down) * projYScale * 0.5f; //result.Scale = GS::Vector2(projXScale, projYScale); //result.Offset = GS::Vector2(projXOffset, projYOffset); // Hey - why is that Y.Offset negated? // It's because a projection matrix transforms from world coords with Y=up, // whereas this is from NDC which is Y=down. m.Set ( projXScale, 0, 0, 0, 0, projYScale, 0, 0, 0, 0, z_far / (z_far - z_near), 1, projXOffset, -projYOffset, -(z_far * z_near) / (z_far - z_near), 0 ); /* float idx = 1.0f / (tan_right - tan_left); float idy = 1.0f / (tan_down - tan_up); float idz = 1.0f / (z_far - z_near); float sx = tan_right + tan_left; float sy = tan_down + tan_up; m.Set ( 2 * idx, 0, 0, 0, 0, 2 * idy, 0, 0, 0, 0, z_far / (z_far - z_near), 1, sx*idx, -sy*idy, -(z_far * z_near) / (z_far - z_near), 0 ); */ /* float(*p)[4] = pmProj->m; p[0][0] = 2 * idx; p[0][1] = 0; p[0][2] = sx*idx; p[0][3] = 0; p[1][0] = 0; p[1][1] = 2 * idy; p[1][2] = sy*idy; p[1][3] = 0; p[2][0] = 0; p[2][1] = 0; p[2][2] = -zFar*idz; p[2][3] = -zFar*zNear*idz; p[3][0] = 0; p[3][1] = 0; p[3][2] = -1.0f; p[3][3] = 0; */ } else { if (is_orthographic) { const float q = 1.f / (z_far - z_near); m.Set ( 2.f / ortho_w, 0, 0, 0, 0, 2.f / ortho_h, 0, 0, 0, 0, q, 0, 0, 0, -q * za, 1 ); } else { m.Set ( zoom_factor, 0, 0, 0, 0, zoom_factor, 0, 0, 0, 0, z_far / (z_far - z_near), 1, 0, 0, -(z_far * z_near) / (z_far - z_near), 0 ); } Vector4 ar = ComputeAspectRatioCorrection(v); m.m[0][0] *= ar.x; m.m[1][1] *= ar.y; } } void Camera::ComputeFrustum(Frustum &f, const fRect &viewport, float zn, float zf) const { Vector4 k_ar = ComputeAspectRatioCorrection(viewport); if (is_orthographic) f.SetOrthographic(ortho_w, ortho_h, zn != -1 ? zn : z_near, zf != -1 ? zf : z_far, &GetMatrix(), k_ar.x, k_ar.y); else f.SetPerspective(GetFov(), zn != -1 ? zn : z_near, zf != -1 ? zf : z_far, &GetMatrix(), k_ar.x, k_ar.y); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Camera::ComputeMatrix() { bool update_world = item_flags.IsSet(ItemFlagWorldMatrixDirty); Item::ComputeMatrix(); // Remove scale from world matrix. if (update_world) { Vector4 u = matrix.GetRow(0).Normalized(); matrix.m[0][0] = u.x; matrix.m[1][0] = u.y; matrix.m[2][0] = u.z; Vector4 v = matrix.GetRow(1).Normalized(); matrix.m[0][1] = v.x; matrix.m[1][1] = v.y; matrix.m[2][1] = v.z; Vector4 w = matrix.GetRow(2).Normalized(); matrix.m[0][2] = w.x; matrix.m[1][2] = w.y; matrix.m[2][2] = w.z; } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ float Camera::GetFov() const { return Math::ATan(1.f / zoom_factor) * 2.f; } void Camera::SetZoomFactor(float z) { zoom_factor = z; /*Types::Max(0.1f, z);*/ } void Camera::SetFov(float fov) { fov = Types::Clamp(fov, Units::Deg(0.5f), Units::Deg(179.95f)); SetZoomFactor(1.f / Math::Tan(fov * 0.5f)); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Camera::Camera() { using namespace Units; SetZoomFactor(3.2f); is_orthographic = false; ortho_w = Mtr(1.f); ortho_h = Mtr(1.f); z_near = Cm(10.f); z_far = Km(50.f); aspect_ratio = -1.f; aspect_ratio_ref_yaxis = true; is_occulus_camera = false; } //------------------------------------------------------------------------------