colobot/colobot-base/graphics/engine/lightman.cpp

533 lines
15 KiB
C++
Raw Normal View History

/*
* This file is part of the Colobot: Gold Edition source code
2023-08-06 21:15:48 +00:00
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
2015-08-22 14:40:02 +00:00
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "graphics/engine/lightman.h"
#include "common/logger.h"
2012-09-19 21:50:28 +00:00
#include "graphics/core/device.h"
2012-09-19 21:50:28 +00:00
#include "graphics/engine/engine.h"
#include "math/geometry.h"
2012-09-19 21:50:28 +00:00
#include <cmath>
#include <algorithm>
// Graphics module namespace
2015-08-02 09:40:47 +00:00
namespace Gfx
{
2012-09-19 21:50:28 +00:00
void LightProgression::Init(float value)
{
starting = value;
ending = value;
current = value;
progress = 0.0f;
speed = 100.0f;
}
2012-09-19 21:50:28 +00:00
void 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;
}
}
2012-09-19 21:50:28 +00:00
void LightProgression::SetTarget(float value)
{
starting = current;
ending = value;
progress = 0.0f;
}
CLightManager::CLightManager(CEngine* engine)
{
m_device = nullptr;
m_engine = engine;
m_time = 0.0f;
}
2012-09-19 21:50:28 +00:00
CLightManager::~CLightManager()
{
m_device = nullptr;
m_engine = nullptr;
}
2012-09-19 21:50:28 +00:00
void CLightManager::SetDevice(CDevice* device)
{
m_device = device;
m_lightMap = std::vector<int>(8, -1);
}
void CLightManager::DebugDumpLights()
{
CLogger* l = GetLogger();
l->Debug("Dynamic lights:\n");
for (int i = 0; i < static_cast<int>( m_dynLights.size() ); ++i)
{
const DynamicLight& dynLight = m_dynLights[i];
if (!dynLight.used)
continue;
int deviceLight = -1;
for (int j = 0; j < static_cast<int>( m_lightMap.size() ); ++j)
{
if (m_lightMap[j] == i)
{
deviceLight = j;
break;
}
}
l->Debug(" light %d\n", i);
l->Debug(" enabled = %s\n", dynLight.enabled ? "true" : "false");
l->Debug(" priority = %d\n", dynLight.priority);
l->Debug(" device light = %d\n", deviceLight);
l->Debug(" light:\n");
const Light& light = dynLight.light;
std::string str;
l->Debug(" type = %d\n", light.type);
str = light.ambient.ToString();
l->Debug(" ambient = %s\n", str.c_str());
str = light.diffuse.ToString();
l->Debug(" diffuse = %s\n", str.c_str());
str = light.specular.ToString();
l->Debug(" specular = %s\n", str.c_str());
str = Math::ToString(light.position);
l->Debug(" position = %s\n", str.c_str());
str = Math::ToString(light.direction);
l->Debug(" direction = %s\n", str.c_str());
l->Debug(" attenuation0 = %f\n", light.attenuation0);
l->Debug(" attenuation1 = %f\n", light.attenuation1);
l->Debug(" attenuation2 = %f\n", light.attenuation2);
l->Debug(" spotAngle = %f\n", light.spotAngle);
l->Debug(" spotIntensity = %f\n", light.spotIntensity);
l->Debug(" intensity: %f\n", dynLight.intensity.current);
l->Debug(" color: %f %f %f\n", dynLight.colorRed.current, dynLight.colorGreen.current, dynLight.colorBlue.current);
l->Debug(" includeType: %d\n", dynLight.includeType);
l->Debug(" excludeType: %d\n", dynLight.excludeType);
}
}
2012-09-19 21:50:28 +00:00
void CLightManager::FlushLights()
{
m_dynLights.clear();
}
/** Returns the index of light created. */
int CLightManager::CreateLight(LightPriority priority)
{
int index = 0;
for (; index < static_cast<int>( m_dynLights.size() ); index++)
{
if (! m_dynLights[index].used)
break;
}
if (index == static_cast<int>(m_dynLights.size()))
m_dynLights.push_back(DynamicLight());
m_dynLights[index] = DynamicLight();
m_dynLights[index].rank = index;
m_dynLights[index].used = true;
m_dynLights[index].enabled = true;
m_dynLights[index].priority = priority;
m_dynLights[index].includeType = ENG_OBJTYPE_NULL;
m_dynLights[index].excludeType = ENG_OBJTYPE_NULL;
m_dynLights[index].light.type = LIGHT_DIRECTIONAL;
m_dynLights[index].light.diffuse = Color(0.5f, 0.5f, 0.5f);
m_dynLights[index].light.ambient = Color(0.0f, 0.0f, 0.0f);
m_dynLights[index].light.position = glm::vec3(-100.0f, 100.0f, -100.0f);
m_dynLights[index].light.direction = glm::vec3( 1.0f, -1.0f, 1.0f);
m_dynLights[index].intensity.Init(1.0f); // maximum
m_dynLights[index].colorRed.Init(0.5f);
m_dynLights[index].colorGreen.Init(0.5f);
m_dynLights[index].colorBlue.Init(0.5f); // gray
return index;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::DeleteLight(int lightRank)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].used = false;
return true;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLight(int lightRank, const Light &light)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( 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;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::GetLight(int lightRank, Light &light)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
light = m_dynLights[lightRank].light;
return true;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightEnabled(int lightRank, bool enabled)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].enabled = enabled;
return true;
}
bool CLightManager::SetLightPriority(int lightRank, LightPriority priority)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].priority = priority;
return true;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightIncludeType(int lightRank, EngineObjectType type)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].includeType = type;
return true;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightExcludeType(int lightRank, EngineObjectType type)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].excludeType = type;
return true;
}
bool CLightManager::SetLightPos(int lightRank, const glm::vec3 &pos)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].light.position = pos;
return true;
}
glm::vec3 CLightManager::GetLightPos(int lightRank)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return glm::vec3(0.0f, 0.0f, 0.0f);
return m_dynLights[lightRank].light.position;
}
bool CLightManager::SetLightDir(int lightRank, const glm::vec3 &dir)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].light.direction = dir;
return true;
}
glm::vec3 CLightManager::GetLightDir(int lightRank)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return glm::vec3(0.0f, 0.0f, 0.0f);
return m_dynLights[lightRank].light.direction;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightIntensitySpeed(int lightRank, float speed)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].intensity.speed = speed;
return true;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightIntensity(int lightRank, float value)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return false;
m_dynLights[lightRank].intensity.SetTarget(value);
return true;
}
2012-09-19 21:50:28 +00:00
float CLightManager::GetLightIntensity(int lightRank)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
return 0.0f;
return m_dynLights[lightRank].intensity.current;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightColorSpeed(int lightRank, float speed)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( 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;
}
2012-09-19 21:50:28 +00:00
bool CLightManager::SetLightColor(int lightRank, const Color &color)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( 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;
}
2012-09-19 21:50:28 +00:00
Color CLightManager::GetLightColor(int lightRank)
{
if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) )
2012-09-19 21:50:28 +00:00
return Color(0.5f, 0.5f, 0.5f, 0.5f);
2012-09-19 21:50:28 +00:00
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;
}
2012-09-19 21:50:28 +00:00
void CLightManager::AdaptLightColor(const Color &color, float factor)
{
for (int i = 0; i < static_cast<int>( m_dynLights.size() ); i++)
{
if (! m_dynLights[i].used)
continue;
2012-09-19 21:50:28 +00:00
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();
}
2012-09-19 21:50:28 +00:00
void CLightManager::UpdateProgression(float rTime)
{
if (m_engine->GetPause())
return;
m_time += rTime;
for (int i = 0; i < static_cast<int>( 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);
2012-09-19 21:50:28 +00:00
if (m_dynLights[i].includeType == 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);
}
2012-09-19 21:50:28 +00:00
if (m_dynLights[i].includeType == ENG_OBJTYPE_METAL)
{
glm::vec3 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);
}
}
}
2012-09-19 21:50:28 +00:00
void CLightManager::UpdateLights()
{
for (int i = 0; i < static_cast<int>( m_dynLights.size() ); i++)
{
if (! m_dynLights[i].used)
continue;
bool enabled = m_dynLights[i].enabled;
if (Math::IsZero(m_dynLights[i].intensity.current))
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;
}
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;
}
}
}
void CLightManager::UpdateDeviceLights(EngineObjectType type)
{
for (int i = 0; i < static_cast<int>( m_lightMap.size() ); ++i)
m_lightMap[i] = -1;
std::vector<DynamicLight> sortedLights = m_dynLights;
CLightsComparator lightsComparator(m_engine->GetEyePt(), type);
std::sort(sortedLights.begin(), sortedLights.end(), lightsComparator);
int lightMapIndex = 0;
for (int i = 0; i < static_cast<int>( sortedLights.size() ); i++)
{
if (! sortedLights[i].used)
continue;
if (! sortedLights[i].enabled)
continue;
if (sortedLights[i].intensity.current == 0.0f)
continue;
bool enabled = true;
if (sortedLights[i].includeType != ENG_OBJTYPE_NULL)
enabled = (sortedLights[i].includeType == type);
if (sortedLights[i].excludeType != ENG_OBJTYPE_NULL)
enabled = (sortedLights[i].excludeType != type);
if (enabled)
{
m_lightMap[lightMapIndex] = sortedLights[i].rank;
++lightMapIndex;
}
if (lightMapIndex >= static_cast<int>( m_lightMap.size() ))
break;
}
for (int i = 0; i < static_cast<int>( m_lightMap.size() ); ++i)
{
int rank = m_lightMap[i];
if (rank != -1)
{
Light light = m_dynLights[rank].light;
//m_device->SetLight(i, light);
//m_device->SetLightEnabled(i, true);
}
else
{
//m_device->SetLightEnabled(i, false);
}
}
}
2012-09-19 21:50:28 +00:00
// -----------
CLightManager::CLightsComparator::CLightsComparator(glm::vec3 eyePos, EngineObjectType objectType)
{
m_eyePos = eyePos;
m_objectType = objectType;
}
float CLightManager::CLightsComparator::GetLightWeight(const DynamicLight& dynLight)
{
if (dynLight.priority == LIGHT_PRI_HIGHEST)
return -1.0f;
bool enabled = true;
if (!dynLight.used || !dynLight.enabled || dynLight.intensity.current == 0.0f)
enabled = false;
else if (dynLight.includeType != ENG_OBJTYPE_NULL)
enabled = dynLight.includeType == m_objectType;
else if (dynLight.excludeType != ENG_OBJTYPE_NULL)
enabled = dynLight.excludeType != m_objectType;
return enabled ? ( glm::length(dynLight.light.position - m_eyePos) * dynLight.priority ) : 10000.0f;
}
bool CLightManager::CLightsComparator::operator()(const DynamicLight& left, const DynamicLight& right)
{
float leftWeight = GetLightWeight(left);
float rightWeight = GetLightWeight(right);
return leftWeight < rightWeight;
}
2012-09-19 21:50:28 +00:00
} // namespace Gfx