From 045f17a274c0cd41aebd34d5759f7fe791b680e4 Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Thu, 26 Jul 2012 21:35:04 +0200 Subject: Dynamic light manager - rewrote old CLight as CLightManager --- src/graphics/common/engine.cpp | 23 ++- src/graphics/common/engine.h | 26 +-- src/graphics/common/light.cpp | 396 +++++++++++++++++++++++++++++++++++- src/graphics/common/light.h | 125 ++++++++---- src/graphics/opengl/test/README.txt | 1 + 5 files changed, 517 insertions(+), 54 deletions(-) (limited to 'src/graphics') diff --git a/src/graphics/common/engine.cpp b/src/graphics/common/engine.cpp index dd1d23f..e6dcfc7 100644 --- a/src/graphics/common/engine.cpp +++ b/src/graphics/common/engine.cpp @@ -52,7 +52,7 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app) m_iMan->AddInstance(CLASS_ENGINE, this); m_app = app; - m_light = NULL; + m_lightMan = NULL; m_text = NULL; m_particle = NULL; m_water = NULL; @@ -203,7 +203,7 @@ bool Gfx::CEngine::Create() { m_wasInit = true; - /*m_light = new Gfx::CLight(m_iMan, this); + /*m_lightMan = new Gfx::CLight(m_iMan, this); m_text = new Gfx::CText(m_iMan, this); m_particle = new Gfx::CParticle(m_iMan, this); m_water = new Gfx::CWater(m_iMan, this); @@ -222,8 +222,8 @@ void Gfx::CEngine::Destroy() { // TODO - /*delete m_light; - m_light = NULL; + /*delete m_lightMan; + m_lightMan = NULL; delete m_text; m_text = NULL; @@ -678,6 +678,21 @@ void Gfx::CEngine::DrawMouseSprite(Math::Point pos, Math::Point size, int icon) AddStatisticTriangle(2); } +bool Gfx::CEngine::GetPause() +{ + return m_pause; +} + +Math::Vector Gfx::CEngine::GetLookatPt() +{ + return m_lookatPt; +} + +Math::Vector Gfx::CEngine::GetEyePt() +{ + return m_eyePt; +} + void Gfx::CEngine::SetMouseVisible(bool visible) { m_mouseVisible = visible; diff --git a/src/graphics/common/engine.h b/src/graphics/common/engine.h index 0413816..9b6cc1f 100644 --- a/src/graphics/common/engine.h +++ b/src/graphics/common/engine.h @@ -45,7 +45,7 @@ class CSound; namespace Gfx { class CDevice; -class CLight; +class CLightManager; class CText; class CParticle; class CWater; @@ -843,18 +843,18 @@ protected: void UpdateGeometry(); protected: - CInstanceManager* m_iMan; - CApplication* m_app; - CSound* m_sound; - Gfx::CDevice* m_device; - Gfx::CText* m_text; - Gfx::CLight* m_light; - Gfx::CParticle* m_particle; - Gfx::CWater* m_water; - Gfx::CCloud* m_cloud; - Gfx::CLightning* m_lightning; - Gfx::CPlanet* m_planet; - Gfx::CTerrain* m_terrain; + CInstanceManager* m_iMan; + CApplication* m_app; + CSound* m_sound; + Gfx::CDevice* m_device; + Gfx::CText* m_text; + Gfx::CLightManager* m_lightMan; + Gfx::CParticle* m_particle; + Gfx::CWater* m_water; + Gfx::CCloud* m_cloud; + Gfx::CLightning* m_lightning; + Gfx::CPlanet* m_planet; + Gfx::CTerrain* m_terrain; bool m_wasInit; std::string m_error; diff --git a/src/graphics/common/light.cpp b/src/graphics/common/light.cpp index d938256..3ca890c 100644 --- a/src/graphics/common/light.cpp +++ b/src/graphics/common/light.cpp @@ -19,4 +19,398 @@ #include "graphics/common/light.h" -// TODO implementation \ No newline at end of file +#include "common/iman.h" +#include "graphics/common/device.h" +#include "math/geometry.h" + +#include + + +void Gfx::LightProgression::Init(float value) +{ + starting = value; + ending = value; + current = value; + progress = 0.0f; + speed = 100.0f; +} + +void Gfx::LightProgression::Update(float rTime) +{ + if (speed < 100.0f) + { + if (progress < 1.0f) + { + progress += speed * rTime; + if (progress > 1.0f) + progress = 1.0f; + } + + current = starting + progress * (ending - starting); + } + else + { + current = ending; + } +} + +void Gfx::LightProgression::SetTarget(float value) +{ + starting = current; + ending = value; + progress = 0.0f; +} + + +Gfx::DynamicLight::DynamicLight() +{ + used = enabled = false; +} + + + +Gfx::CLightManager::CLightManager(CInstanceManager* iMan, Gfx::CEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_LIGHT, this); + + m_device = NULL; + m_engine = engine; + + m_time = 0.0f; +} + +Gfx::CLightManager::~CLightManager() +{ + m_iMan->DeleteInstance(CLASS_LIGHT, this); + + m_iMan = NULL; + m_device = NULL; + m_engine = NULL; +} + +void Gfx::CLightManager::SetDevice(Gfx::CDevice* device) +{ + m_device = device; + + m_dynLights = std::vector(m_device->GetMaxLightCount(), Gfx::DynamicLight()); +} + +void Gfx::CLightManager::FlushLights() +{ + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + m_dynLights[i].used = false; + m_device->SetLightEnabled(i, false); + } +} + +/** Returns the index of light created or -1 if all lights are used. */ +int Gfx::CLightManager::CreateLight() +{ + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + if (m_dynLights[i].used) continue; + + m_dynLights[i] = Gfx::DynamicLight(); + + m_dynLights[i].used = true; + m_dynLights[i].enabled = true; + + m_dynLights[i].includeType = Gfx::ENG_OBJTYPE_NULL; + m_dynLights[i].excludeType = Gfx::ENG_OBJTYPE_NULL; + + m_dynLights[i].light.type = Gfx::LIGHT_DIRECTIONAL; + m_dynLights[i].light.diffuse = Gfx::Color(0.5f, 0.5f, 0.5f); + m_dynLights[i].light.position = Math::Vector(-100.0f, 100.0f, -100.0f); + m_dynLights[i].light.direction = Math::Vector( 1.0f, -1.0f, 1.0f); + + m_dynLights[i].intensity.Init(1.0f); // maximum + m_dynLights[i].colorRed.Init(0.5f); + m_dynLights[i].colorGreen.Init(0.5f); + m_dynLights[i].colorBlue.Init(0.5f); // gray + + return i; + } + + return -1; +} + +bool Gfx::CLightManager::DeleteLight(int lightRank) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].used = false; + m_device->SetLightEnabled(lightRank, false); + + return true; +} + +// Specifies a light. + +bool Gfx::CLightManager::SetLight(int lightRank, const Gfx::Light &light) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].light = light; + + m_dynLights[lightRank].colorRed.Init(m_dynLights[lightRank].light.diffuse.r); + m_dynLights[lightRank].colorGreen.Init(m_dynLights[lightRank].light.diffuse.g); + m_dynLights[lightRank].colorBlue.Init(m_dynLights[lightRank].light.diffuse.b); + + return true; +} + +bool Gfx::CLightManager::GetLight(int lightRank, Gfx::Light &light) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + light = m_dynLights[lightRank].light; + return true; +} + +bool Gfx::CLightManager::SetLightEnabled(int lightRank, bool enabled) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].enabled = enabled; + return true; +} + +bool Gfx::CLightManager::SetLightIncludeType(int lightRank, Gfx::EngineObjectType type) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].includeType = type; + return true; +} + +bool Gfx::CLightManager::SetLightExcludeType(int lightRank, Gfx::EngineObjectType type) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].excludeType = type; + return true; +} + +bool Gfx::CLightManager::SetLightPos(int lightRank, const Math::Vector &pos) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].light.position = pos; + return true; +} + +Math::Vector Gfx::CLightManager::GetLightPos(int lightRank) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return Math::Vector(0.0f, 0.0f, 0.0f); + + return m_dynLights[lightRank].light.position; +} + +bool Gfx::CLightManager::SetLightDir(int lightRank, const Math::Vector &dir) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].light.direction = dir; + return true; +} + +Math::Vector Gfx::CLightManager::GetLightDir(int lightRank) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return Math::Vector(0.0f, 0.0f, 0.0f); + + return m_dynLights[lightRank].light.direction; +} + +bool Gfx::CLightManager::SetLightIntensitySpeed(int lightRank, float speed) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].intensity.speed = speed; + return true; +} + +bool Gfx::CLightManager::SetLightIntensity(int lightRank, float value) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].intensity.SetTarget(value); + return true; +} + +float Gfx::CLightManager::GetLightIntensity(int lightRank) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return 0.0f; + + return m_dynLights[lightRank].intensity.current; +} + + +bool Gfx::CLightManager::SetLightColorSpeed(int lightRank, float speed) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].colorRed.speed = speed; + m_dynLights[lightRank].colorGreen.speed = speed; + m_dynLights[lightRank].colorBlue.speed = speed; + return true; +} + +bool Gfx::CLightManager::SetLightColor(int lightRank, const Gfx::Color &color) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return false; + + m_dynLights[lightRank].colorRed.SetTarget(color.r); + m_dynLights[lightRank].colorGreen.SetTarget(color.g); + m_dynLights[lightRank].colorBlue.SetTarget(color.b); + return true; +} + +Gfx::Color Gfx::CLightManager::GetLightColor(int lightRank) +{ + if ( (lightRank < 0) || (lightRank >= static_cast( m_dynLights.size() )) ) + return Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f); + + Gfx::Color color; + color.r = m_dynLights[lightRank].colorRed.current; + color.g = m_dynLights[lightRank].colorGreen.current; + color.b = m_dynLights[lightRank].colorBlue.current; + return color; +} + +void Gfx::CLightManager::AdaptLightColor(const Gfx::Color &color, float factor) +{ + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + if (! m_dynLights[i].used) + continue; + + Gfx::Color value; + value.r = m_dynLights[i].colorRed.current; + value.g = m_dynLights[i].colorGreen.current; + value.b = m_dynLights[i].colorBlue.current; + + value.r += color.r * factor; + value.g += color.g * factor; + value.b += color.b * factor; + + m_dynLights[i].colorRed.Init(value.r); + m_dynLights[i].colorGreen.Init(value.g); + m_dynLights[i].colorBlue.Init(value.b); + } + + UpdateLights(); +} + +void Gfx::CLightManager::UpdateProgression(float rTime) +{ + if (m_engine->GetPause()) + return; + + m_time += rTime; + + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + if (! m_dynLights[i].used) + continue; + + m_dynLights[i].intensity.Update(rTime); + m_dynLights[i].colorRed.Update(rTime); + m_dynLights[i].colorGreen.Update(rTime); + m_dynLights[i].colorBlue.Update(rTime); + + if (m_dynLights[i].includeType == Gfx::ENG_OBJTYPE_QUARTZ) + { + m_dynLights[i].light.direction.x = sinf(1.0f * (m_time + i*Math::PI*0.5f)); + m_dynLights[i].light.direction.z = cosf(1.1f * (m_time + i*Math::PI*0.5f)); + m_dynLights[i].light.direction.y = -1.0f + 0.5f * cosf((m_time + i*Math::PI*0.5f)*2.7f); + } + + if (m_dynLights[i].includeType == Gfx::ENG_OBJTYPE_METAL) + { + Math::Vector dir = m_engine->GetEyePt() - m_engine->GetLookatPt(); + float angle = Math::RotateAngle(dir.x, dir.z); + angle += Math::PI * 0.5f * i; + m_dynLights[i].light.direction.x = sinf(2.0f * angle); + m_dynLights[i].light.direction.z = cosf(2.0f * angle); + } + } +} + + +void Gfx::CLightManager::UpdateLights() +{ + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + if (! m_dynLights[i].used) + continue; + + bool enabled = m_dynLights[i].enabled; + if (m_dynLights[i].intensity.current == 0.0f) + enabled = false; + + if (enabled) + { + float value = m_dynLights[i].colorRed.current * m_dynLights[i].intensity.current; + m_dynLights[i].light.diffuse.r = value; + + value = m_dynLights[i].colorGreen.current * m_dynLights[i].intensity.current; + m_dynLights[i].light.diffuse.g = value; + + value = m_dynLights[i].colorBlue.current * m_dynLights[i].intensity.current; + m_dynLights[i].light.diffuse.b = value; + + m_device->SetLight(i, m_dynLights[i].light); + m_device->SetLightEnabled(i, enabled); + } + else + { + m_dynLights[i].light.diffuse.r = 0.0f; + m_dynLights[i].light.diffuse.g = 0.0f; + m_dynLights[i].light.diffuse.b = 0.0f; + + m_device->SetLightEnabled(i, enabled); + } + } +} + +void Gfx::CLightManager::UpdateLightsEnableState(Gfx::EngineObjectType type) +{ + for (int i = 0; i < static_cast( m_dynLights.size() ); i++) + { + if (! m_dynLights[i].used) + continue; + if (! m_dynLights[i].enabled) + continue; + if (m_dynLights[i].intensity.current == 0.0f) + continue; + + if (m_dynLights[i].includeType != Gfx::ENG_OBJTYPE_NULL) + { + bool enabled = (m_dynLights[i].includeType == type); + m_device->SetLightEnabled(i, enabled); + } + + if (m_dynLights[i].excludeType != Gfx::ENG_OBJTYPE_NULL) + { + bool enabled = (m_dynLights[i].excludeType != type); + m_device->SetLightEnabled(i, enabled); + } + } +} diff --git a/src/graphics/common/light.h b/src/graphics/common/light.h index e1d51ab..93e8c1b 100644 --- a/src/graphics/common/light.h +++ b/src/graphics/common/light.h @@ -27,8 +27,9 @@ namespace Gfx { -/** \enum LightType - * \brief Type of light */ +/** + \enum LightType + \brief Type of light in 3D scene */ enum LightType { LIGHT_POINT, @@ -37,11 +38,10 @@ enum LightType }; /** - * \struct Light - * \brief Light - * - * This structure was created as analog to DirectX's D3DLIGHT. - */ + \struct Light + \brief Properties of light in 3D scene + + This structure was created as analog to DirectX's D3DLIGHT. */ struct Light { //! Type of light source @@ -90,99 +90,152 @@ struct Light }; /** - * \struct LightProgression - * \brief Describes the progression of light parameters change - * - * TODO documentation - */ + \struct LightProgression + \brief Describes the progression of light parameters change */ struct LightProgression { + //! Starting value float starting; + //! Ending (destination) value float ending; + //! Current value float current; + //! Progress from start to end float progress; + //! Speed of progression float speed; LightProgression() { starting = ending = current = progress = speed = 0.0f; } + + //! Initializes the progression + void Init(float value); + + //! Updates the progression + void Update(float rTime); + + //! Sets the new end value (starting is set to current) + void SetTarget(float value); }; /** - * \struct DynamicLight - * \brief Dynamic light in 3D scene - * - * TODO documentation - */ + \struct DynamicLight + \brief Dynamic light in 3D scene + + It is an extension over standard light properties. Added are dynamic progressions for light + colors and intensity and types of objects included/excluded in lighting. */ struct DynamicLight { - //! true -> light exists + //! Whether the light is used bool used; - //! true -> light turned on + //! Whether the light is turned on bool enabled; - //! Type of all objects included - Gfx::EngineObjectType includeType; - //! Type of all objects excluded - Gfx::EngineObjectType excludeType; - //! Configuration of the light Gfx::Light light; - //! intensity (0 .. 1) + //! Progression of intensity [0, 1] Gfx::LightProgression intensity; + //! Progression of red diffuse color Gfx::LightProgression colorRed; + //! Progression of green diffuse color Gfx::LightProgression colorGreen; + //! Progression of blue diffuse color Gfx::LightProgression colorBlue; + + //! Type of objects included in lighting with this light; if Gfx::ENG_OBJTYPE_NULL is used, it is ignored + Gfx::EngineObjectType includeType; + //! Type of objects excluded from lighting with this light; if Gfx::ENG_OBJTYPE_NULL is used, it is ignored + Gfx::EngineObjectType excludeType; + + DynamicLight(); }; /** - \class CLight + \class CLightManager \brief Manager for dynamic lights in 3D scene + + (Old CLight class) + + The class is responsible for managing dynamic lights (struct Gfx::DynamicLight) used in 3D scene. + The dynamic lights are created, updated and deleted through the class' interface. + + Number of available lights depends on graphics device used. Class allocates vector + for the total number of lights, but only some are used. */ -class CLight +class CLightManager { public: - CLight(CInstanceManager *iMan, Gfx::CEngine* engine); - virtual ~CLight(); + //! Constructor + CLightManager(CInstanceManager *iMan, Gfx::CEngine* engine); + //! Destructor + virtual ~CLightManager(); + //! Sets the device to be used void SetDevice(Gfx::CDevice* device); - void FlushLight(); + //! Clears and disables all lights + void FlushLights(); + //! Creates a new dynamic light and returns its index (lightRank) int CreateLight(); + //! Deletes and disables the given dynamic light bool DeleteLight(int lightRank); + //! Sets the light parameters for dynamic light bool SetLight(int lightRank, const Gfx::Light &light); + //! Returns the light parameters for given dynamic light bool GetLight(int lightRank, Gfx::Light &light); - bool LightEnable(int lightRank, bool enable); + //! Enables/disables the given dynamic light + bool SetLightEnabled(int lightRank, bool enable); + //! Sets what objects are included in given dynamic light bool SetLightIncludeType(int lightRank, Gfx::EngineObjectType type); + //! Sets what objects are excluded from given dynamic light bool SetLightExcludeType(int lightRank, Gfx::EngineObjectType type); + //! Sets the position of dynamic light bool SetLightPos(int lightRank, const Math::Vector &pos); + //! Returns the position of dynamic light Math::Vector GetLightPos(int lightRank); + //! Sets the direction of dynamic light bool SetLightDir(int lightRank, const Math::Vector &dir); + //! Returns the direction of dynamic light Math::Vector GetLightDir(int lightRank); - bool SetLightIntensitySpeed(int lightRank, float speed); + //! Sets the destination intensity for dynamic light's intensity progression bool SetLightIntensity(int lightRank, float value); + //! Returns the current light intensity float GetLightIntensity(int lightRank); + //! Sets the rate of change for dynamic light intensity + bool SetLightIntensitySpeed(int lightRank, float speed); + + //! Adjusts the color of all dynamic lights void AdaptLightColor(const Gfx::Color &color, float factor); - bool SetLightColorSpeed(int lightRank, float speed); + //! Sets the destination color for dynamic light's color progression bool SetLightColor(int lightRank, const Gfx::Color &color); + //! Returns current light color Gfx::Color GetLightColor(int lightRank); + //! Sets the rate of change for dynamic light colors (RGB) + bool SetLightColorSpeed(int lightRank, float speed); - void FrameLight(float rTime); - void LightUpdate(); - void LightUpdate(Gfx::EngineObjectType type); + //! Updates progression of dynamic lights + void UpdateProgression(float rTime); + //! Updates (recalculates) all dynamic lights + void UpdateLights(); + //! Enables or disables dynamic lights affecting the given object type + void UpdateLightsEnableState(Gfx::EngineObjectType type); protected: CInstanceManager* m_iMan; CEngine* m_engine; CDevice* m_device; + + //! Current time float m_time; + //! List of dynamic lights std::vector m_dynLights; }; diff --git a/src/graphics/opengl/test/README.txt b/src/graphics/opengl/test/README.txt index fe6f1d7..c618415 100644 --- a/src/graphics/opengl/test/README.txt +++ b/src/graphics/opengl/test/README.txt @@ -6,3 +6,4 @@ Test programs for OpenGL engine: requires ./tex folder (or symlink) with Colobot textures viewer is controlled from keyboard - the bindings can be found in code - transform_test -> simple "walk around" test for world & view transformations + - light test -> test for lighting -- cgit v1.2.3-1-g7c22