Files
NazaraEngine/src/Nazara/Graphics/Light.cpp
Lynix df8da275c4 Switch from Nz prefix to namespace Nz
What a huge commit


Former-commit-id: 38ac5eebf70adc1180f571f6006192d28fb99897
2015-09-25 19:20:05 +02:00

182 lines
4.6 KiB
C++

// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Math/Algorithm.hpp>
#include <Nazara/Math/Sphere.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/Shader.hpp>
#include <cstring>
#include <Nazara/Graphics/Debug.hpp>
///TODO: Utilisation des UBOs
///TODO: Scale ?
namespace Nz
{
Light::Light(LightType type) :
m_type(type)
{
SetAmbientFactor((type == LightType_Directional) ? 0.2f : 0.f);
SetAttenuation(0.9f);
SetColor(Color::White);
SetDiffuseFactor(1.f);
SetInnerAngle(15.f);
SetOuterAngle(45.f);
SetRadius(5.f);
}
void Light::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const
{
switch (m_type)
{
case LightType_Directional:
{
AbstractRenderQueue::DirectionalLight light;
light.ambientFactor = m_ambientFactor;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
renderQueue->AddDirectionalLight(light);
break;
}
case LightType_Point:
{
AbstractRenderQueue::PointLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.invRadius = m_invRadius;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddPointLight(light);
break;
}
case LightType_Spot:
{
AbstractRenderQueue::SpotLight light;
light.ambientFactor = m_ambientFactor;
light.attenuation = m_attenuation;
light.color = m_color;
light.diffuseFactor = m_diffuseFactor;
light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f);
light.innerAngleCosine = m_innerAngleCosine;
light.invRadius = m_invRadius;
light.outerAngleCosine = m_outerAngleCosine;
light.outerAngleTangent = m_outerAngleTangent;
light.position = transformMatrix.GetTranslation();
light.radius = m_radius;
renderQueue->AddSpotLight(light);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
Light* Light::Clone() const
{
return new Light(*this);
}
Light* Light::Create() const
{
return new Light;
}
bool Light::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const
{
switch (m_type)
{
case LightType_Directional:
return true; // Always visible
case LightType_Point:
return frustum.Contains(Spheref(transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise)
case LightType_Spot:
return frustum.Contains(m_boundingVolume);
}
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
return false;
}
void Light::UpdateBoundingVolume(const Matrix4f& transformMatrix)
{
switch (m_type)
{
case LightType_Directional:
break; // Nothing to do (bounding volume should be infinite)
case LightType_Point:
m_boundingVolume.Update(transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted
break;
case LightType_Spot:
m_boundingVolume.Update(transformMatrix);
break;
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
void Light::MakeBoundingVolume() const
{
switch (m_type)
{
case LightType_Directional:
m_boundingVolume.MakeInfinite();
break;
case LightType_Point:
{
Vector3f radius(m_radius * float(M_SQRT3));
m_boundingVolume.Set(-radius, radius);
break;
}
case LightType_Spot:
{
// On forme une boite sur l'origine
Boxf box(Vector3f::Zero());
// On calcule le reste des points
Vector3f base(Vector3f::Forward()*m_radius);
// Il nous faut maintenant le rayon du cercle projeté à cette distance
// Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente
float radius = m_radius * m_outerAngleTangent;
Vector3f lExtend = Vector3f::Left()*radius;
Vector3f uExtend = Vector3f::Up()*radius;
// Et on ajoute ensuite les quatres extrémités de la pyramide
box.ExtendTo(base + lExtend + uExtend);
box.ExtendTo(base + lExtend - uExtend);
box.ExtendTo(base - lExtend + uExtend);
box.ExtendTo(base - lExtend - uExtend);
m_boundingVolume.Set(box);
break;
}
default:
NazaraError("Invalid light type (0x" + String::Number(m_type, 16) + ')');
break;
}
}
}