/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "core/light.h" #include "core/resource_factories.h" #include "core/render_resource_factory.h" #include "log/log.h" using namespace GS; using namespace GS::Core; //------------------------------------------------------------------------------ void Light::ComputeProjectionMatrix(Matrix4 &m) const { switch (model) { case Model_Spot: { const float q = volume_range / (volume_range - z_near), fov = (cone_angle + edge_angle), zoom_factor = float(Math::Cos(fov) / Math::Sin(fov)); // Default aspect ration: PC(1:1). m.Set ( zoom_factor, 0, 0, 0, 0, zoom_factor, 0, 0, 0, 0, q, 1, 0, 0, -q * z_near, 0 ); } break; default: m = Matrix4::IdentityMatrix(); break; } } void Light::ComputeFrustrum(Frustum &f, float zn, float zf) const { if (model == Model_Spot) f.SetPerspective((cone_angle + edge_angle) * 2, zn != -1 ? zn : z_near, zf != -1 ? zf : volume_range, &GetMatrix(), 1, 1); } void Light::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; } ComputeFrustrum(frustum); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Light::SampleColor(const Vector4 &location, const Vector4 &normal, Color &diff, Color &spec, Vector4 *view, float gloss) { float idiff, ispec; if (!SampleEnergy(location, normal, &idiff, &ispec, view, gloss, true, false)) { diff.Set(); spec.Set(); return false; } diff = diffuse_color * diffuse_intensity * idiff; spec = specular_color * specular_intensity * ispec; return true; } bool Light::SampleEnergy(const Vector4 &loc, const Vector4 &normal, float *diff, float *spec, Vector4 *view, float gloss, bool halfway, bool cooktorrance) { if (!diff && !spec) return false; Vector4 dt; float a = 1; switch (model) { case Model_Linear: dt = GetMatrix().GetRow(2).Reversed().Normalized(); break; default: { dt = GetMatrix().GetRow(3) - loc; float dt_length = dt.Len(); dt /= dt_length; // Compute light attenuation. if (range) { float distance = dt_length / range; switch (falloff) { /* case Falloff_InvDist: if (distance) a = 1 / distance; break; case Falloff_InvDist2: if (distance) a = 1 / (distance * distance); break; */ default: case Falloff_Linear: a = 1 - distance; break; } if (a <= 0) return false; } if (model == Model_Spot) { float c = Math::ACos(dt.Reversed().Dot(GetMatrix().GetRow(2).Normalized())); if ((c < 0) || (c > (cone_angle + edge_angle))) return false; if (c > cone_angle) a *= 1 - (c - cone_angle) / edge_angle; } } break; } // Sample diffuse. float e = normal.Dot(dt); if (e < 0) return false; if (diff) *diff = e * a * diffuse_intensity; /* Sample specular. Use the halfway vector method if you'd like to avoid a vector reflection. */ if (spec) { *spec = 0; if (view) { Vector4 local_view = view->Normalized(); float e = 0; if (cooktorrance) __LOG_W__ << "Cook-Torrance specular model not implemented.\n"; else { if (halfway) e = (dt - local_view).Normalized().Dot(normal); else e = local_view.Reflected(normal).Dot(dt); e = (e > 0) ? Math::Pow(e, gloss * 96.f) : 0; } *spec = e * a * specular_intensity; } } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Light::RenderSetup(ResourceFactories *f) { render_data = new RenderData; if (f && f->render) if (!projection_texture.IsEmpty()) render_data->projection_texture = f->render->LoadTexture(projection_texture); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Light::SetDefaults() { model = Light::Model_Point; shadow = Light::Shadow_None; range = Units::Mtr(0.f); volume_range = Units::Mtr(500.f); clip_distance = Units::Mtr(300.f); cone_angle = Units::Deg(30.f); edge_angle = Units::Deg(30.f); shadow_cast_all = false; shadow_bias = 0.01f; shadow_distribution = 0.9f; shadow_range = Units::Mtr(100.f); shadow_color.Set(0, 0, 0); volumetric = false; volumetric_sample_step = 0.5f; volumetric_range = 8.0f; volumetric_thickness = 1.0f; z_near = Units::Cm(2.f); falloff = Falloff_Linear; diffuse_color = Color::White; diffuse_intensity = 1; specular_color = Color::White; specular_intensity = 0; } Light::Light() { SetDefaults(); } //------------------------------------------------------------------------------