Files
Webcam/include/engine/core/light.cpp

228 lines
5.4 KiB
C++

/* -----------------------------------------------------------------------------
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();
}
//------------------------------------------------------------------------------