228 lines
5.4 KiB
C++
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();
|
|
}
|
|
//------------------------------------------------------------------------------
|