// Copyright (C) 2013 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 #include #include #include #include #include #include #include #include #include ///TODO: Utilisation des UBOs NzLight::NzLight(nzLightType type) : m_type(type), m_color(NzColor::White), m_boundingVolumeUpdated(false), m_ambientFactor((type == nzLightType_Directional) ? 0.2f : 0.f), m_attenuation(0.9f), m_diffuseFactor(1.f), m_innerAngle(15.f), m_outerAngle(45.f), m_radius(5.f) { } NzLight::NzLight(const NzLight& light) : NzSceneNode(light), m_type(light.m_type), m_boundingVolume(light.m_boundingVolume), m_color(light.m_color), m_boundingVolumeUpdated(light.m_boundingVolumeUpdated), m_ambientFactor(light.m_ambientFactor), m_attenuation(light.m_attenuation), m_diffuseFactor(light.m_diffuseFactor), m_innerAngle(light.m_innerAngle), m_outerAngle(light.m_outerAngle), m_radius(light.m_radius) { } void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const { renderQueue->AddLight(this); } void NzLight::Enable(const NzShaderProgram* program, unsigned int lightUnit) const { /* struct Light { int type; vec4 color; vec2 factors; vec4 parameters1; vec4 parameters2; vec2 parameters3; }; Directional -P1: vec3 direction Point -P1: vec3 position + float attenuation -P2: float invRadius Spot -P1: vec3 position + float attenuation -P2: vec3 direction + float invRadius -P3: float cosInnerAngle + float cosOuterAngle */ ///TODO: Optimiser int typeLocation = program->GetUniformLocation("Lights[0].type"); int colorLocation = program->GetUniformLocation("Lights[0].color"); int factorsLocation = program->GetUniformLocation("Lights[0].factors"); int parameters1Location = program->GetUniformLocation("Lights[0].parameters1"); int parameters2Location = program->GetUniformLocation("Lights[0].parameters2"); int parameters3Location = program->GetUniformLocation("Lights[0].parameters3"); if (lightUnit > 0) { int type2Location = program->GetUniformLocation("Lights[1].type"); int offset = lightUnit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure // On applique cet offset typeLocation += offset; colorLocation += offset; factorsLocation += offset; parameters1Location += offset; parameters2Location += offset; parameters3Location += offset; } program->SendInteger(typeLocation, m_type); program->SendColor(colorLocation, m_color); program->SendVector(factorsLocation, NzVector2f(m_ambientFactor, m_diffuseFactor)); if (!m_derivedUpdated) UpdateDerived(); switch (m_type) { case nzLightType_Directional: program->SendVector(parameters1Location, NzVector4f(m_derivedRotation * NzVector3f::Forward())); break; case nzLightType_Point: program->SendVector(parameters1Location, NzVector4f(m_derivedPosition, m_attenuation)); program->SendVector(parameters2Location, NzVector4f(1.f/m_radius, 0.f, 0.f, 0.f)); break; case nzLightType_Spot: program->SendVector(parameters1Location, NzVector4f(m_derivedPosition, m_attenuation)); program->SendVector(parameters2Location, NzVector4f(m_derivedRotation * NzVector3f::Forward(), 1.f/m_radius)); program->SendVector(parameters3Location, NzVector2f(std::cos(NzDegreeToRadian(m_innerAngle)), std::cos(NzDegreeToRadian(m_outerAngle)))); break; } } float NzLight::GetAmbientFactor() const { return m_ambientFactor; } float NzLight::GetAttenuation() const { return m_attenuation; } const NzBoundingVolumef& NzLight::GetBoundingVolume() const { if (!m_boundingVolumeUpdated) UpdateBoundingVolume(); return m_boundingVolume; } NzColor NzLight::GetColor() const { return m_color; } float NzLight::GetDiffuseFactor() const { return m_diffuseFactor; } float NzLight::GetInnerAngle() const { return m_innerAngle; } nzLightType NzLight::GetLightType() const { return m_type; } float NzLight::GetOuterAngle() const { return m_outerAngle; } float NzLight::GetRadius() const { return m_radius; } nzSceneNodeType NzLight::GetSceneNodeType() const { return nzSceneNodeType_Light; } bool NzLight::IsDrawable() const { return true; } void NzLight::SetAmbientFactor(float factor) { m_ambientFactor = factor; } void NzLight::SetAttenuation(float attenuation) { m_attenuation = attenuation; } void NzLight::SetColor(const NzColor& color) { m_color = color; } void NzLight::SetDiffuseFactor(float factor) { m_diffuseFactor = factor; } void NzLight::SetInnerAngle(float innerAngle) { m_innerAngle = innerAngle; } void NzLight::SetOuterAngle(float outerAngle) { m_outerAngle = outerAngle; m_boundingVolume.MakeNull(); m_boundingVolumeUpdated = false; } void NzLight::SetRadius(float radius) { m_radius = radius; m_boundingVolume.MakeNull(); m_boundingVolumeUpdated = false; } NzLight& NzLight::operator=(const NzLight& light) { NzSceneNode::operator=(light); m_ambientFactor = light.m_ambientFactor; m_attenuation = light.m_attenuation; m_boundingVolume = light.m_boundingVolume; m_boundingVolumeUpdated = light.m_boundingVolumeUpdated; m_color = light.m_color; m_diffuseFactor = light.m_diffuseFactor; m_innerAngle = light.m_innerAngle; m_outerAngle = light.m_outerAngle; m_radius = light.m_radius; m_type = light.m_type; return *this; } void NzLight::Disable(const NzShaderProgram* program, unsigned int lightUnit) { ///TODO: Optimiser program->SendInteger(program->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1); } bool NzLight::FrustumCull(const NzFrustumf& frustum) { switch (m_type) { case nzLightType_Directional: return true; // Toujours visible case nzLightType_Point: if (!m_derivedUpdated) UpdateDerived(); // Un test sphérique est bien plus rapide et précis que celui de la bounding box return frustum.Contains(NzSpheref(m_derivedPosition, m_radius)); case nzLightType_Spot: if (!m_boundingVolumeUpdated) UpdateBoundingVolume(); return frustum.Contains(m_boundingVolume); } NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')'); return false; } void NzLight::Invalidate() { NzSceneNode::Invalidate(); m_boundingVolumeUpdated = false; } void NzLight::Register() { } void NzLight::Unregister() { } void NzLight::UpdateBoundingVolume() const { if (m_boundingVolume.IsNull()) { switch (m_type) { case nzLightType_Directional: m_boundingVolume.MakeInfinite(); m_boundingVolumeUpdated = true; return; // Rien d'autre à faire case nzLightType_Point: { NzVector3f radius(m_radius); m_boundingVolume.Set(-radius, radius); break; } case nzLightType_Spot: { // On forme une boite sur l'origine NzBoxf box(NzVector3f::Zero()); // On calcule le reste des points float height = m_radius; NzVector3f base(NzVector3f::Forward()*height); // Il nous faut maintenant le rayon du cercle projeté à cette distance // Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente float radius = height*std::tan(NzDegreeToRadian(m_outerAngle)); NzVector3f lExtend = NzVector3f::Left()*radius; NzVector3f uExtend = NzVector3f::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; } } } switch (m_type) { case nzLightType_Directional: break; case nzLightType_Point: if (!m_derivedUpdated) UpdateDerived(); m_boundingVolume.Update(NzMatrix4f::Translate(m_derivedPosition)); // Notre BoundingBox ne changera que selon la position break; case nzLightType_Spot: if (!m_transformMatrixUpdated) UpdateTransformMatrix(); m_boundingVolume.Update(m_transformMatrix); break; } m_boundingVolumeUpdated = true; }