// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // 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 namespace Nz { inline SpotLight::SpotLight() : Light(SafeCast(BasicLightType::Spot)), m_color(Color::White()), m_position(Vector3f::Zero()), m_ambientFactor(0.2f), m_diffuseFactor(1.f) { UpdateAngles(DegreeAnglef(30.f), DegreeAnglef(45.f)); UpdateRadius(5.f); UpdateRotation(Quaternionf::Identity()); } inline float SpotLight::GetAmbientFactor() const { return m_ambientFactor; } inline Color SpotLight::GetColor() const { return m_color; } inline float SpotLight::GetDiffuseFactor() const { return m_diffuseFactor; } inline const Vector3f& SpotLight::GetDirection() const { return m_direction; } inline RadianAnglef SpotLight::GetInnerAngle() const { return m_innerAngle; } inline float SpotLight::GetInnerAngleCos() const { return m_innerAngleCos; } inline float SpotLight::GetInvRadius() const { return m_invRadius; } inline RadianAnglef SpotLight::GetOuterAngle() const { return m_outerAngle; } inline float SpotLight::GetOuterAngleCos() const { return m_outerAngleCos; } inline float SpotLight::GetOuterAngleTan() const { return m_outerAngleTan; } inline const Vector3f& SpotLight::GetPosition() const { return m_position; } inline const Quaternionf& SpotLight::GetRotation() const { return m_rotation; } inline float SpotLight::GetRadius() const { return m_radius; } inline const Matrix4f& SpotLight::GetViewProjMatrix() const { return m_viewProjMatrix; } inline void SpotLight::UpdateAmbientFactor(float factor) { m_ambientFactor = factor; OnLightDataInvalided(this); } inline void SpotLight::UpdateAngles(RadianAnglef innerAngle, RadianAnglef outerAngle) { m_innerAngle = innerAngle; m_innerAngleCos = m_innerAngle.GetCos(); m_outerAngle = outerAngle; m_outerAngleCos = m_outerAngle.GetCos(); m_outerAngleTan = m_outerAngle.GetTan(); UpdateBoundingVolume(); UpdateViewProjMatrix(); OnLightDataInvalided(this); } inline void SpotLight::UpdateColor(Color color) { m_color = color; OnLightDataInvalided(this); } inline void SpotLight::UpdateDiffuseFactor(float factor) { m_diffuseFactor = factor; OnLightDataInvalided(this); } inline void SpotLight::UpdateDirection(const Vector3f& direction) { UpdateRotation(Quaternionf::RotationBetween(Vector3f::Forward(), direction)); } inline void SpotLight::UpdateInnerAngle(RadianAnglef innerAngle) { m_innerAngle = innerAngle; m_innerAngleCos = m_innerAngle.GetCos(); OnLightDataInvalided(this); } inline void SpotLight::UpdateOuterAngle(RadianAnglef outerAngle) { m_outerAngle = outerAngle; m_outerAngleCos = m_outerAngle.GetCos(); m_outerAngleTan = m_outerAngle.GetTan(); UpdateBoundingVolume(); UpdateViewProjMatrix(); OnLightDataInvalided(this); } inline void SpotLight::UpdatePosition(const Vector3f& position) { m_position = position; UpdateBoundingVolume(); UpdateViewProjMatrix(); OnLightTransformInvalided(this); } inline void SpotLight::UpdateRadius(float radius) { m_radius = radius; m_invRadius = 1.f / m_radius; UpdateBoundingVolume(); UpdateViewProjMatrix(); } inline void SpotLight::UpdateRotation(const Quaternionf& rotation) { m_rotation = rotation; m_direction = rotation * Vector3f::Forward(); UpdateBoundingVolume(); UpdateViewProjMatrix(); OnLightTransformInvalided(this); } inline void SpotLight::UpdateBoundingVolume() { // We make a box center in the origin Boxf box = Boxf::Zero(); // We compute the other points Vector3f base(Vector3f::Forward() * m_radius); // Now we need the radius of the projected circle depending on the distance // Tangent = Opposite/Adjacent <=> Opposite = Adjacent * Tangent float radius = m_radius * m_outerAngleTan; Vector3f lExtend = Vector3f::Left() * radius; Vector3f uExtend = Vector3f::Up() * radius; // And we add the four extremities of our pyramid box.ExtendTo(base + lExtend + uExtend); box.ExtendTo(base + lExtend - uExtend); box.ExtendTo(base - lExtend + uExtend); box.ExtendTo(base - lExtend - uExtend); BoundingVolumef boundingVolume(box); boundingVolume.Update(Matrix4f::Transform(m_position, m_rotation)); Light::UpdateBoundingVolume(boundingVolume); //< will trigger OnLightDataInvalided } inline void SpotLight::UpdateViewProjMatrix() { constexpr Matrix4f biasMatrix(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.0f, 1.0f); Matrix4f projection = Matrix4f::Perspective(m_outerAngle * 2.f, 1.f, 0.01f, m_radius); Matrix4f view = Matrix4f::TransformInverse(m_position, m_rotation); m_viewProjMatrix = view * projection * biasMatrix; } } #include