Files
noggit-red/src/noggit/Sky.cpp
2025-01-17 21:25:24 +00:00

1739 lines
50 KiB
C++
Executable File

// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include <noggit/DBC.h>
#include <noggit/Log.h>
#include <noggit/MapHeaders.h>
#include <noggit/Model.h> // Model
#include <noggit/ModelManager.h> // ModelManager
#include <noggit/Sky.h>
#include <noggit/application/NoggitApplication.hpp>
#include <noggit/application/Configuration/NoggitApplicationConfiguration.hpp>
#include <opengl/shader.hpp>
#include <glm/glm.hpp>
#include <noggit/project/CurrentProject.hpp>
#include <math/trig.hpp>
#include <math/bounding_box.hpp>
#include <algorithm>
#include <string>
#include <array>
#include <unordered_map>
#include <QFile>
#include <QTextStream>
#include <QStringList>
const float skymul = 36.0f;
namespace skyparams
{
// Map to store SkyParams globally
std::unordered_map<unsigned int, SkyParam> skyParamMap;
// get or create SkyParam from the global map
SkyParam* getOrCreateParam(unsigned int id, Noggit::NoggitRenderContext context) {
if (!id)
return nullptr;
auto it = skyParamMap.find(id);
if (it != skyParamMap.end()) {
return &(it->second); // Return existing SkyParam
}
// Try to create new SkyParam and insert into map
SkyParam newParam = SkyParam(id, context);
// Dbc loading failed
assert(newParam.Id != 0);
if (newParam.Id == 0)
return nullptr;
auto [newIt, inserted] = skyParamMap.emplace(id, std::move(newParam));
return &(newIt->second); // Return newly created SkyParam
}
}
SkyColor::SkyColor(int t, int col)
{
time = t;
color.z = ((col & 0x0000ff)) / 255.0f;
color.y = ((col & 0x00ff00) >> 8) / 255.0f;
color.x = ((col & 0xff0000) >> 16) / 255.0f;
}
SkyFloatParam::SkyFloatParam(int t, float val)
: time(t)
, value(val)
{
}
SkyParam::SkyParam(int paramId, Noggit::NoggitRenderContext context)
: _context(context)
{
Id = paramId;
if (Id == 0)
{
// shouldn't happen in the new system, we don't load params with no valid id.
assert(false);
return; // don't initialise entry
}
try
{
DBCFile::Record light_param = gLightParamsDB.getByID(paramId);
int skybox_id = light_param.getInt(LightParamsDB::skybox);
_highlight_sky = light_param.getInt(LightParamsDB::highlightSky);
_river_shallow_alpha = light_param.getFloat(LightParamsDB::water_shallow_alpha);
_river_deep_alpha = light_param.getFloat(LightParamsDB::water_deep_alpha);
_ocean_shallow_alpha = light_param.getFloat(LightParamsDB::ocean_shallow_alpha);
_ocean_deep_alpha = light_param.getFloat(LightParamsDB::ocean_deep_alpha);
_glow = light_param.getFloat(LightParamsDB::glow);
if (skybox_id)
{
try
{
auto skyboxRec = gLightSkyboxDB.getByID(skybox_id);
skybox.emplace(skyboxRec.getString(LightSkyboxDB::filename), _context);
skyboxFlags = skyboxRec.getInt(LightSkyboxDB::flags);
}
catch (...)
{
LogError << "When trying to get the skybox id " << skybox_id << "for the param " << paramId << " in LightSkybox.dbc." << std::endl;
}
}
}
catch (...)
{
LogError << "When trying to initialize Params the entry " << paramId << " in LightParams.dbc." << std::endl;
Id = 0;
}
// initialize colors (lightIntBand.dbc)
int light_int_start = (paramId * NUM_SkyColorNames) - (NUM_SkyColorNames - 1);
for (int i = 0; i < NUM_SkyColorNames; ++i)
{
try
{
DBCFile::Record rec = gLightIntBandDB.getByID(light_int_start + i);
int entries = rec.getInt(LightIntBandDB::Entries);
if (entries == 0)
{
// mmin[i] = -1;
}
else
{
// smallest/first time value
// mmin[i] = rec.getInt(LightIntBandDB::Times);
for (int l = 0; l < entries; l++)
{
SkyColor sc(rec.getInt(LightIntBandDB::Times + l), rec.getInt(LightIntBandDB::Values + l));
colorRows[i].push_back(sc);
}
}
}
catch (...)
{
assert(false);
LogError << "When trying to intialize sky, there was an error with getting an entry in LightIntBand.dbc id (" << i << "). Lightparam id : " << paramId << std::endl;
/*
DBCFile::Record rec = gLightIntBandDB.getByID(i);
int entries = rec.getInt(LightIntBandDB::Entries);
if (entries == 0)
{
mmin[i] = -1;
}
else
{
mmin[i] = rec.getInt(LightIntBandDB::Times);
for (int l = 0; l < entries; l++)
{
SkyColor sc(rec.getInt(LightIntBandDB::Times + l), rec.getInt(LightIntBandDB::Values + l));
colorRows[i].push_back(sc);
}
}*/
}
}
// initialize float params
int light_float_start = (paramId * NUM_SkyFloatParamsNames) - (NUM_SkyFloatParamsNames - 1);
for (int i = 0; i < NUM_SkyFloatParamsNames; ++i)
{
try
{
DBCFile::Record rec = gLightFloatBandDB.getByID(light_float_start + i);
int entries = rec.getInt(LightFloatBandDB::Entries);
if (entries == 0)
{
// mmin_float[i] = -1;
}
else
{
// mmin_float[i] = rec.getInt(LightFloatBandDB::Times);
for (int l = 0; l < entries; l++)
{
SkyFloatParam sc(rec.getInt(LightFloatBandDB::Times + l), rec.getFloat(LightFloatBandDB::Values + l));
floatParams[i].push_back(sc);
}
}
}
catch (...)
{
assert(false);
LogError << "When trying to intialize sky, there was an error with getting an entry in LightFloatBand.dbc id (" << i << "). Lightparam id : " << paramId << std::endl;
/*
DBCFile::Record rec = gLightFloatBandDB.getByID(i + 1);
int entries = rec.getInt(LightFloatBandDB::Entries);
if (entries == 0)
{
mmin_float[i] = -1;
}
else
{
mmin_float[i] = rec.getInt(LightFloatBandDB::Times);
for (int l = 0; l < entries; l++)
{
SkyFloatParam sc(rec.getInt(LightFloatBandDB::Times + l), rec.getFloat(LightFloatBandDB::Values + l));
floatParams[i].push_back(sc);
}
}*/
}
}
}
bool SkyParam::highlight_sky() const
{
return _highlight_sky;
}
float SkyParam::river_shallow_alpha() const
{
return _river_shallow_alpha;
}
float SkyParam::river_deep_alpha() const
{
return _river_deep_alpha;
}
float SkyParam::ocean_shallow_alpha() const
{
return _ocean_shallow_alpha;
}
float SkyParam::ocean_deep_alpha() const
{
return _ocean_deep_alpha;
}
float SkyParam::glow() const
{
return _glow;
}
void SkyParam::set_glow(float glow)
{
_glow = glow;
}
void SkyParam::set_highlight_sky(bool state)
{
_highlight_sky = state;
}
void SkyParam::set_river_shallow_alpha(float alpha)
{
_river_shallow_alpha = alpha;
}
void SkyParam::set_river_deep_alpha(float alpha)
{
_river_deep_alpha = alpha;
}
void SkyParam::set_ocean_shallow_alpha(float alpha)
{
_ocean_shallow_alpha = alpha;
}
void SkyParam::set_ocean_deep_alpha(float alpha)
{
_ocean_deep_alpha = alpha;
}
Sky::Sky(DBCFile::Iterator data, Noggit::NoggitRenderContext context)
: _context(context)
, _selected(false)
{
Id = data->getInt(LightDB::ID);
mapId = data->getInt(LightDB::Map);
pos = glm::vec3(data->getFloat(LightDB::PositionX) / skymul, data->getFloat(LightDB::PositionY) / skymul, data->getFloat(LightDB::PositionZ) / skymul);
r1 = data->getFloat(LightDB::RadiusInner) / skymul;
r2 = data->getFloat(LightDB::RadiusOuter) / skymul;
global = (pos.x == 0.0f && pos.y == 0.0f && pos.z == 0.0f);
for (int i = 0; i < NUM_SkyParamsNames; ++i)
{
int sky_param_id = data->getInt(LightDB::DataIDs + i);
skyParams[i] = sky_param_id;
// initialize param to map, shouldn't even be needed
// if (sky_param_id > 0)
// {
// getOrCreateParam(sky_param_id, _context);
// }
}
}
int Sky::getId() const
{
return Id;
}
std::optional<SkyParam*> Sky::getParam(int param_index) const
{
unsigned int param_id = skyParams[param_index];
if (param_id == 0)
return std::nullopt;
if (cachedCurrentParam && cachedCurrentParam->Id == param_id) {
return cachedCurrentParam;
}
SkyParam* param_ptr = skyparams::getOrCreateParam(param_id, _context);
if (param_ptr)
{
cachedCurrentParam = param_ptr;
return cachedCurrentParam;
}
else
{
cachedCurrentParam = nullptr;
return std::nullopt;
}
}
std::optional<SkyParam*> Sky::getCurrentParam() const
{
return getParam(curr_sky_param);
}
float Sky::floatParamFor(int r, int t) const
{
auto param_opt = getCurrentParam();
if (!param_opt.has_value())
return 0.0f;
SkyParam* const sky_param = param_opt.value();
if (sky_param->floatParams[r].empty())
{
return 0.0f;
}
float c1, c2;
int t1, t2;
size_t last = sky_param->floatParams[r].size() - 1;
if (t < sky_param->floatParams[r].front().time)
{
// reverse interpolate
c1 = sky_param->floatParams[r][last].value;
c2 = sky_param->floatParams[r][0].value;
t1 = sky_param->floatParams[r][last].time;
t2 = sky_param->floatParams[r][0].time + DAY_DURATION;
t += DAY_DURATION;
}
else
{
for (size_t i = last; true; i--)
{ //! \todo iterator this.
if (sky_param->floatParams[r][i].time <= t)
{
c1 = sky_param->floatParams[r][i].value;
t1 = sky_param->floatParams[r][i].time;
if (i == last)
{
c2 = sky_param->floatParams[r][0].value;
t2 = sky_param->floatParams[r][0].time + DAY_DURATION;
}
else
{
c2 = sky_param->floatParams[r][i + 1].value;
t2 = sky_param->floatParams[r][i + 1].time;
}
break;
}
}
}
float tt = static_cast<float>(t - t1) / static_cast<float>(t2 - t1);
return c1 + ((c2 - c1) * tt);
}
glm::vec3 Sky::colorFor(int r, int t) const
{
auto param_opt = getCurrentParam();
if (!param_opt.has_value())
return glm::vec3(0.0f, 0.0f, 0.0f);
SkyParam* const sky_param = param_opt.value();
if (sky_param->colorRows[r].empty())
{
return glm::vec3(0.0f, 0.0f, 0.0f);
}
glm::vec3 c1, c2;
int t1, t2;
int last = static_cast<int>(sky_param->colorRows[r].size()) - 1;
if (last == 0)
{
c1 = sky_param->colorRows[r][last].color;
c2 = sky_param->colorRows[r][0].color;
t1 = sky_param->colorRows[r][last].time;
t2 = sky_param->colorRows[r][0].time + DAY_DURATION;
t += DAY_DURATION;
}
else
{
// if (t < sky_param->mmin[r])
if (t < sky_param->colorRows[r].front().time)
{
// reverse interpolate
c1 = sky_param->colorRows[r][last].color;
c2 = sky_param->colorRows[r][0].color;
t1 = sky_param->colorRows[r][last].time;
t2 = sky_param->colorRows[r][0].time + DAY_DURATION;
t += DAY_DURATION;
}
else
{
for (int i = last; true; i--)
{ //! \todo iterator this.
if (sky_param->colorRows[r][i].time <= t)
{
c1 = sky_param->colorRows[r][i].color;
t1 = sky_param->colorRows[r][i].time;
if (i == last)
{
c2 = sky_param->colorRows[r][0].color;
t2 = sky_param->colorRows[r][0].time + DAY_DURATION;
}
else
{
c2 = sky_param->colorRows[r][i + 1].color;
t2 = sky_param->colorRows[r][i + 1].time;
}
break;
}
}
}
}
float tt = static_cast<float>(t - t1) / static_cast<float>(t2 - t1);
return c1*(1.0f - tt) + c2*tt;
}
const float rad = 400.0f;
//...............................top....med....medh........horiz..........bottom
const math::degrees angles[] = { math::degrees (90.0f)
, math::degrees (18.0f)
, math::degrees (10.0f)
, math::degrees (3.0f)
, math::degrees (0.0f)
, math::degrees (-30.0f)
, math::degrees (-90.0f)
};
const int cnum = 7;
const int skycolors[cnum] = { SKY_COLOR_TOP, SKY_COLOR_MIDDLE, SKY_COLOR_BAND1, SKY_COLOR_BAND2, SKY_COLOR_SMOG, SKY_FOG_COLOR, SKY_FOG_COLOR };
const int hseg = 32;
void Skies::loadZoneLights(int map_id)
{
// read zone lights from csv file
{
std::string zonelight_db_path = Noggit::Application::NoggitApplication::instance()->getConfiguration()->ApplicationNoggitDefinitionsPath
+ "\\ZoneLight.3.4.3.56262.csv";
QString qPath = QString::fromStdString(zonelight_db_path);
QFile file(qPath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file);
// Skip the header line
std::string headerLine = in.readLine().toStdString();
assert(headerLine == "ID,Name,MapID,LightID");
while (!in.atEnd())
{
QString line = in.readLine();
// Split the line by comma
QStringList fields = line.split(',');
assert(fields.size() == 4);
if (fields.size() != 4)
{
continue;
}
bool ok;
int light_map_id = fields[2].toInt(&ok);
assert(ok);
// only load this map
if (map_id != light_map_id)
continue;
ZoneLight zone_light_entry;
zone_light_entry.id = fields[0].toInt(&ok);
zone_light_entry.name = fields[1].toStdString();
// zone_light_entry.mapId = light_map_id;
zone_light_entry.lightId = fields[3].toInt(&ok);
// zoneLightsWotlk[zone_light_entry.id] = zone_light_entry;
zoneLightsWotlk.push_back(zone_light_entry);
// get the light reference
Sky* light_ptr = findSkyById(zone_light_entry.lightId);
// if light was not loaded, most likely missing or not in map.
assert(light_ptr != nullptr);
if (!light_ptr)
continue;
// zoneLightsWotlk[zone_light_entry.id].light = findSkyById(zone_light_entry.lightId);
light_ptr->zone_light = true;
}
file.close();
}
else
{
LogError << "Failed loading Zone Lights. Can't open " << zonelight_db_path << std::endl;
return;
}
}
// load zone light points to temporary object
std::unordered_map<int, std::vector<ZoneLightPoint>> zoneLightPoints;
{
std::string zonelightpoints_db_path = Noggit::Application::NoggitApplication::instance()->getConfiguration()->ApplicationNoggitDefinitionsPath
+ "\\ZoneLightPoint.3.4.3.56262.csv";
QString qPath = QString::fromStdString(zonelightpoints_db_path);
QFile file(qPath);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file);
// Skip the header line
std::string headerLine = in.readLine().toStdString();
assert(headerLine == "ID,Pos_0,Pos_1,PointOrder,ZoneLightID");
while (!in.atEnd())
{
QString line = in.readLine();
// Split the line by comma
QStringList fields = line.split(',');
assert(fields.size() == 5);
if (fields.size() != 5)
{
continue;
}
bool ok;
int zone_light_id = fields[4].toUInt(&ok);
assert(ok);
// Check if the vector contains the zone_light_id
// if (!zoneLightsWotlk.contains(zone_light_id))
auto it = std::find_if(zoneLightsWotlk.begin(), zoneLightsWotlk.end(),
[zone_light_id](const ZoneLight& zoneLight) {
return zoneLight.id == zone_light_id;
});
if (it == zoneLightsWotlk.end())
continue;
ZoneLightPoint zone_light_point_entry;
zone_light_point_entry.id = fields[0].toUInt(&ok);
zone_light_point_entry.zoneLightId = zone_light_id;
// convert server to client coords
// need to swap X and Y
zone_light_point_entry.posX = -fields[2].toFloat(&ok) + ZEROPOINT;
zone_light_point_entry.posY = -fields[1].toFloat(&ok) + ZEROPOINT;
zone_light_point_entry.pointOrder = fields[3].toUInt(&ok);
// Automatically create a vector if the key doesn't exist, and add the entry
zoneLightPoints[zone_light_id].push_back(zone_light_point_entry);
// bad idea, this blindly trusts the ordering
// zoneLightsWotlk[zone_light_id].points.push_back(glm::vec2(zone_light_point_entry.posX, zone_light_point_entry.posY));
}
file.close();
}
else
{
LogError << "Failed loading Zone Light Points. Can't open " << zonelightpoints_db_path << std::endl;
return;
}
}
// re order points, because we don't trust the storage order
for (auto& points_list : zoneLightPoints)
{
// if (!zoneLightsWotlk.contains(points_list.first))
// continue;
// polygon must have at least 3 points
assert(points_list.second.size() > 2);
// Check for duplicate pointOrder values
for (int i = 1; i < points_list.second.size(); ++i) {
if (points_list.second[i].pointOrder == points_list.second[i - 1].pointOrder)
{
assert(false);
continue;
}
}
// Sort the vector based on pointOrder (ascending)
std::sort(points_list.second.begin(), points_list.second.end(), [](const ZoneLightPoint& a, const ZoneLightPoint& b) {
return a.pointOrder < b.pointOrder;
});
}
for (auto& zone_light : zoneLightsWotlk)
{
auto& list = zoneLightPoints[zone_light.id];
// insert reordered points
for (auto& point : list)
{
zone_light.points.push_back(glm::vec2(point.posX, point.posY));
}
// calculate 2d extents
math::aabb_2d const bounds (zone_light.points);
zone_light._extents[0] = bounds.min;
zone_light._extents[1] = bounds.max;
}
}
Sky* Skies::findSkyById(int sky_id)
{
for (auto& sky : skies)
{
if (sky.getId() == sky_id)
{
return &sky;
}
}
return nullptr;
}
Skies::Skies(unsigned int mapid, Noggit::NoggitRenderContext context)
: stars (ModelInstance("Environments\\Stars\\Stars.mdx", context))
, _context(context)
, _indices_count(0)
, _last_pos(glm::vec3(0.0f, 0.0f, 0.0f))
{
bool has_global = false;
for (DBCFile::Iterator i = gLightDB.begin(); i != gLightDB.end(); ++i)
{
if (mapid == i->getUInt(LightDB::Map))
{
Sky s(i, _context);
skies.push_back(s);
numSkies++;
if (s.pos == glm::vec3(0, 0, 0))
has_global = true;
}
}
if (!has_global)
{
LogDebug << "No global light data found for the current map (id :" << mapid
<< ") using light id 1 as a fallback" << std::endl;
for (DBCFile::Iterator i = gLightDB.begin(); i != gLightDB.end(); ++i)
{
if (1 == i->getUInt(LightDB::ID))
{
Sky s(i, _context);
s.global = true;
skies.push_back(s);
numSkies++;
break;
}
}
using_fallback_global = true;
}
// sort skies from smallest to largest; global last.
// smaller skies will have precedence when calculating weights to achieve smooth transitions etc.
std::sort(skies.begin(), skies.end());
// load Zone Lights data, was hardcoded in 3.3.5 and moved to a dbc in Cata. Noggit stores them in a csv
if (Noggit::Project::CurrentProject::get()->projectVersion == Noggit::Project::ProjectVersion::WOTLK)
{
loadZoneLights(mapid);
}
}
Sky* Skies::createNewSky(Sky* old_sky, unsigned int new_id, glm::vec3& pos)
{
// TODO
Sky new_sky_copy = *old_sky;
new_sky_copy.Id = new_id;
new_sky_copy.pos = pos;
new_sky_copy.weight = 0.f;
new_sky_copy.is_new_record = true;
new_sky_copy.save_to_dbc();
skies.push_back(new_sky_copy);
numSkies++;
// refresh rendering & weights
std::sort(skies.begin(), skies.end());
force_update();
for (Sky& sky : skies)
{
if (sky.Id == new_id)
{
return &sky;
}
}
return nullptr;
}
// returns the global light, not the highest weight
Sky* Skies::findSkyWeights(glm::vec3 pos)
{
Sky* default_sky = nullptr;
for (auto& sky : skies)
{
if (sky.pos == glm::vec3(0, 0, 0))
{
default_sky = &sky;
break;
}
}
std::sort(skies.begin(), skies.end(), [=](Sky& a, Sky& b)
{
return glm::distance(pos, a.pos) > glm::distance(pos, b.pos);
});
for (auto& sky : skies)
{
float distance_to_light = glm::distance(pos, sky.pos);
if (default_sky == &sky || distance_to_light > sky.r2)
{
sky.weight = 0.f;
continue;
}
float length_of_falloff = sky.r2 - sky.r1;
sky.weight = (sky.r2 - distance_to_light) / length_of_falloff;
if (distance_to_light <= sky.r1)
{
sky.weight = 1.0f;
}
}
// Light zones
glm::vec2 const pos_2d = glm::vec2(pos.x, pos.z);
for (auto& lightzone : zoneLightsWotlk)
{
if (math::is_inside_of_aabb_2d(pos_2d, lightzone._extents[0], lightzone._extents[1]))
{
bool inside = math::is_inside_of_polygon(pos_2d, lightzone.points);
if (inside)
{
Sky* sky = findSkyById(lightzone.lightId);
if (sky)
sky->weight = 1.0f;
}
}
}
return default_sky;
}
Sky* Skies::findClosestSkyByWeight()
{
// gets the highest weight sky
if (skies.size() == 0)
return nullptr;
Sky* closest_sky = &skies[0];
for (auto& sky : skies)
{
// use >= to make sure when we have multiple with the same weight,
// last one has priority, because it is the closest
// skies is sorted by distance to center
if (sky.weight > 0.0f && sky.weight >= closest_sky->weight)
closest_sky = &sky;
}
return closest_sky;
}
Sky* Skies::findClosestSkyByDistance(glm::vec3 pos)
{
if (skies.size() == 0)
return nullptr;
Sky* closest = &skies[0];
float distance = 1000000.f;
for (auto& sky : skies)
{
float distanceToCenter = glm::distance(pos, sky.pos);
if (distanceToCenter <= sky.r2 && distanceToCenter < distance)
{
distance = distanceToCenter;
closest = &sky;
}
}
return closest;
}
void Skies::setCurrentParam(int param_id)
{
assert(param_id < NUM_SkyParamsNames);
for (auto& sky : skies)
{
Sky* skyptr = &sky;
skyptr->curr_sky_param = param_id;
}
}
void Skies::update_sky_colors(glm::vec3 pos, int time, bool global_only)
{
if (numSkies == 0 || (_last_time == time && _last_pos == pos && !_force_update))
{
return;
}
_force_update = false;
Sky* default_sky = findSkyWeights(pos);
// initialize lightning with default(global) light
if (default_sky && default_sky->getCurrentParam().has_value())
{
for (int i = 0; i < NUM_SkyColorNames; ++i)
{
color_set[i] = default_sky->colorFor(i, time);
}
// float values
float fog_distance = default_sky->floatParamFor(SKY_FOG_DISTANCE, time);
_fog_distance = fog_distance == 0.0f ? 6500.0f : fog_distance;
float fog_multiplier = default_sky->floatParamFor(SKY_FOG_MULTIPLIER, time);
_fog_multiplier = fog_multiplier == 0.0f ? 0.1f : fog_multiplier;
_celestial_glow = default_sky->floatParamFor(SKY_CELESTIAL_GLOW, time);
_cloud_density = default_sky->floatParamFor(SKY_CLOUD_DENSITY, time);
_unknown_float_param4 = default_sky->floatParamFor(SKY_UNK_FLOAT_PARAM_4, time);
_unknown_float_param5 = default_sky->floatParamFor(SKY_UNK_FLOAT_PARAM_5, time);
// param values
auto param_opt = default_sky->getCurrentParam();
if (param_opt.has_value())
{
SkyParam* const default_sky_param = param_opt.value();
_river_shallow_alpha = default_sky_param->river_shallow_alpha();
_river_deep_alpha = default_sky_param->river_deep_alpha();
_ocean_shallow_alpha = default_sky_param->ocean_shallow_alpha();
_ocean_deep_alpha = default_sky_param->ocean_deep_alpha();
_glow = default_sky_param->glow();
}
else
{
// if no param data, use some default. TODO : check how client does it.
_river_shallow_alpha = 0.5f;
_river_deep_alpha = 1.0f;
_ocean_shallow_alpha = 0.75f;
_ocean_deep_alpha = 1.0f;
_glow = 0.5f;
}
}
else
{
LogError << "Failed to load default light. Something went seriously wrong. Potentially corrupt Light.dbc" << std::endl;
for (int i = 0; i < NUM_SkyColorNames; ++i)
{
color_set[i] = glm::vec3(1.0f, 1.0f, 1.0f);
}
_fog_multiplier = 0.1f;
_fog_distance = 6500.0f;
_celestial_glow = 1.0f;
_cloud_density = 1.0f;
_unknown_float_param4 = 1.0f;
_unknown_float_param5 = 1.0f;
_river_shallow_alpha = 0.5f;
_river_deep_alpha = 1.0f;
_ocean_shallow_alpha = 0.75f;
_ocean_deep_alpha = 1.0f;
_glow = 0.5f;
}
if (!global_only)
{
// Blending interpolation with local lights
for (size_t j = 0; j<skies.size(); j++)
{
Sky const& sky = skies[j];
if (sky.weight > 0.f)
{
// now calculate the color rows
for (int i = 0; i < NUM_SkyColorNames; ++i)
{
if ((sky.colorFor(i, time).x>1.0f) || (sky.colorFor(i, time).y>1.0f) || (sky.colorFor(i, time).z>1.0f))
{
LogDebug << "Sky " << j << " " << i << " is out of bounds!" << std::endl;
continue;
}
auto timed_color = sky.colorFor(i, time);
color_set[i] = glm::mix(color_set[i], timed_color, sky.weight);
}
auto param_opt = sky.getCurrentParam();
if (param_opt.has_value())
{
SkyParam* default_sky_param = param_opt.value();
float sky_weight_remain = (1.0f - sky.weight);
float fog_distance = sky.floatParamFor(SKY_FOG_DISTANCE, time);
if (fog_distance != 0.0f)
_fog_distance = (_fog_distance * sky_weight_remain) + (fog_distance * sky.weight);
float fog_multiplier = sky.floatParamFor(SKY_FOG_MULTIPLIER, time);
if (fog_multiplier != 0.0f)
_fog_multiplier = (_fog_multiplier * sky_weight_remain) + (fog_multiplier * sky.weight);
_celestial_glow = (_celestial_glow * sky_weight_remain) + (sky.floatParamFor(SKY_CELESTIAL_GLOW, time) * sky.weight);
_cloud_density = (_cloud_density * sky_weight_remain) + (sky.floatParamFor(SKY_CLOUD_DENSITY, time) * sky.weight);
_unknown_float_param4 = (_unknown_float_param4 * sky_weight_remain) + (sky.floatParamFor(SKY_UNK_FLOAT_PARAM_4, time) * sky.weight);
_unknown_float_param5 = (_unknown_float_param5 * sky_weight_remain) + (sky.floatParamFor(SKY_UNK_FLOAT_PARAM_5, time) * sky.weight);
_river_shallow_alpha = (_river_shallow_alpha * sky_weight_remain) + (default_sky_param->river_shallow_alpha() * sky.weight);
_river_deep_alpha = (_river_deep_alpha * sky_weight_remain) + (default_sky_param->river_deep_alpha() * sky.weight);
_ocean_shallow_alpha = (_ocean_shallow_alpha * sky_weight_remain) + (default_sky_param->ocean_shallow_alpha() * sky.weight);
_ocean_deep_alpha = (_ocean_deep_alpha * sky_weight_remain) + (default_sky_param->ocean_deep_alpha() * sky.weight);
_glow = (_glow * sky_weight_remain) + (default_sky_param->glow() * sky.weight);
}
else
{
// if no data for param index, it just uses default values from global light, no blending
}
}
}
}
const float fogEnd = _fog_distance / 36.f;
const float fogStart = _fog_multiplier * fogEnd;
const float fogRange = fogEnd - fogStart;
// constexpr float fogFarClip = 500.f; // Max fog farclip possible
constexpr float fogFarClip = 1583.333374f; // 1583.333374 for wrath/tbc zones, 791.666687 for vanilla zones
if (fogRange <= fogFarClip)
{
_fog_rate = ((1.0f - (fogRange / fogFarClip)) * 5.5f) + 1.5f;
} else
{
_fog_rate = 1.5f;
}
_last_pos = pos;
_last_time = time;
_need_color_buffer_update = true;
}
bool Skies::draw(glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::vec3 const& camera_pos
, OpenGL::Scoped::use_program& m2_shader
, math::frustum const& frustum
, const float& cull_distance
, int animtime
, int time
/*, bool draw_particles*/
, bool draw_skybox
, OutdoorLightStats const& light_stats
)
{
if (numSkies == 0)
{
return false;
}
if (!_uploaded)
{
upload();
}
if (_need_color_buffer_update)
{
update_color_buffer();
}
{
OpenGL::Scoped::use_program shader {*_program.get()};
if(_need_vao_update)
{
update_vao(shader);
}
{
OpenGL::Scoped::vao_binder const _ (_vao);
shader.uniform("model_view_projection", projection * model_view);
shader.uniform("camera_pos", glm::vec3(camera_pos.x, camera_pos.y, camera_pos.z));
gl.drawElements(GL_TRIANGLES, _indices_count, GL_UNSIGNED_SHORT, nullptr);
}
}
if (draw_skybox)
{
bool combine_flag = false;
bool has_skybox = false;
// only draw one skybox model ?
for (Sky& sky : skies)
{
auto param_opt = sky.getCurrentParam();
if (sky.weight > 0.f && param_opt.has_value() && param_opt.value()->skybox)
{
has_skybox = true;
SkyParam* curr_param = param_opt.value();
if ((curr_param->skyboxFlags & LIGHT_SKYBOX_COMBINE))
combine_flag = true; // flag 0x2 = still render stars, sun and moons and clouds
auto& model = curr_param->skybox.value();
model.model->trans = sky.weight;
model.pos = camera_pos;
model.scale = 0.1f;
model.recalcExtents();
OpenGL::M2RenderState model_render_state;
model_render_state.tex_arrays = {0, 0};
model_render_state.tex_indices = {0, 0};
model_render_state.tex_unit_lookups = {-1, -1};
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.disable(GL_BLEND);
gl.depthMask(GL_TRUE);
m2_shader.uniform("blend_mode", 0);
m2_shader.uniform("unfogged", static_cast<int>(model_render_state.unfogged));
m2_shader.uniform("unlit", static_cast<int>(model_render_state.unlit));
m2_shader.uniform("tex_unit_lookup_1", 0);
m2_shader.uniform("tex_unit_lookup_2", 0);
m2_shader.uniform("pixel_shader", 0);
int skyboxtime = animtime;
if ((curr_param->skyboxFlags & LIGHT_SKYBOX_FULL_DAY))
{
unsigned int anim_lenght = model.model->get_anim_lenght(0);
// Calculate the normalized time within the day (0.0 to 1.0)
float day_fraction = static_cast<float>(time % DAY_DURATION) / DAY_DURATION;
// animation time from day time %
skyboxtime = static_cast<int>(day_fraction * anim_lenght);
}
model.model->renderer()->draw(model_view
, model
, m2_shader
, model_render_state
, frustum
, 1000000
, camera_pos
, skyboxtime
, display_mode::in_3D
, true
, true);
}
}
// if it's night, draw the stars
if (light_stats.nightIntensity > 0 && (combine_flag || !has_skybox))
{
stars.model->trans = light_stats.nightIntensity;
stars.pos = camera_pos;
stars.scale = 0.1f;
stars.recalcExtents();
OpenGL::M2RenderState model_render_state;
model_render_state.tex_arrays = {0, 0};
model_render_state.tex_indices = {0, 0};
model_render_state.tex_unit_lookups = {-1, -1};
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.disable(GL_BLEND);
gl.depthMask(GL_TRUE);
m2_shader.uniform("blend_mode", 0);
m2_shader.uniform("unfogged", static_cast<int>(model_render_state.unfogged));
m2_shader.uniform("unlit", static_cast<int>(model_render_state.unlit));
m2_shader.uniform("tex_unit_lookup_1", 0);
m2_shader.uniform("tex_unit_lookup_2", 0);
m2_shader.uniform("pixel_shader", 0);
stars.model->renderer()->draw(model_view
, stars
, m2_shader
, model_render_state
, frustum
, 1000000
, camera_pos
, animtime
, display_mode::in_3D
, true
, true);
}
}
return true;
}
void Skies::drawLightingSpheres (glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::vec3 const& camera_pos
, math::frustum const& frustum
, const float& cull_distance
)
{
for (Sky& sky : skies)
{
if (glm::distance(sky.pos, camera_pos) <= cull_distance) // TODO: frustum cull here
{
glm::vec4 diffuse = { color_set[LIGHT_GLOBAL_DIFFUSE], 1.f };
glm::vec4 ambient = { color_set[LIGHT_GLOBAL_AMBIENT], 1.f };
Log << sky.getId() << " <=> (x,y,z) : " << sky.pos.x << "," << sky.pos.y << "," << sky.pos.z << " -- r1 : " << sky.r1 << " -- r2 : " << sky.r2 << std::endl;
_sphere_render.draw(model_view * projection, sky.pos, ambient, sky.r1, 32, 18, 1.f);
_sphere_render.draw(model_view * projection, sky.pos, diffuse, sky.r2, 32, 18, 1.f);
}
}
}
void Skies::drawLightingSphereHandles (glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::vec3 const& camera_pos
, math::frustum const& frustum
, const float& cull_distance
, bool draw_spheres)
{
for (Sky& sky : skies)
{
if (glm::distance(sky.pos, camera_pos) - sky.r2 <= cull_distance) // TODO: frustum cull here
{
_sphere_render.draw(model_view * projection, sky.pos, {1.f, 0.f, 0.f, 1.f}, 5.f);
if (sky.selected())
{
glm::vec3 diffuse = color_set[LIGHT_GLOBAL_DIFFUSE];
glm::vec3 ambient = color_set[LIGHT_GLOBAL_AMBIENT];
_sphere_render.draw(model_view * projection, sky.pos, {ambient.x, ambient.y, ambient.z, 0.3}, sky.r1);
_sphere_render.draw(model_view * projection, sky.pos, {diffuse.x, diffuse.y, diffuse.z, 0.3}, sky.r2);
}
}
}
}
bool Skies::hasSkies() const
{
return numSkies > 0;
}
float Skies::river_shallow_alpha() const
{
return _river_shallow_alpha;
}
float Skies::river_deep_alpha() const
{
return _river_deep_alpha;
}
float Skies::ocean_shallow_alpha() const
{
return _ocean_shallow_alpha;
}
float Skies::ocean_deep_alpha() const
{
return _ocean_deep_alpha;
}
float Skies::fog_distance_end() const
{
return _fog_distance / 36.f;
}
float Skies::fog_distance_start() const
{
return (_fog_distance / 36.f) * _fog_multiplier;
}
float Skies::fog_distance_multiplier() const
{
return _fog_multiplier;
}
float Skies::celestial_glow() const
{
return _celestial_glow;
}
float Skies::cloud_density() const
{
return _cloud_density;
}
float Skies::unknown_float_param4() const
{
return _unknown_float_param4;
}
float Skies::unknown_float_param5() const
{
return _unknown_float_param5;
}
float Skies::glow() const
{
return _glow;
}
float Skies::fogRate() const
{
return _fog_rate;
}
void Skies::unload()
{
_program.reset();
_vertex_array.unload();
_buffers.unload();
_sphere_render.unload();
_uploaded = false;
_need_vao_update = true;
}
void Skies::force_update()
{
_force_update = true;
}
void Skies::upload()
{
_program.reset(new OpenGL::program(
{
{GL_VERTEX_SHADER, R"code(
#version 330 core
uniform mat4 model_view_projection;
uniform vec3 camera_pos;
in vec3 position;
in vec3 color;
out vec3 f_color;
void main()
{
vec4 pos = vec4(position + camera_pos, 1.f);
gl_Position = model_view_projection * pos;
f_color = color;
}
)code" }
, {GL_FRAGMENT_SHADER, R"code(
#version 330 core
in vec3 f_color;
out vec4 out_color;
void main()
{
out_color = vec4(f_color, 1.);
}
)code" }
}
));
_vertex_array.upload();
_buffers.upload();
std::vector<glm::vec3> vertices;
std::vector<std::uint16_t> indices;
glm::vec3 basepos1[cnum], basepos2[cnum];
for (int h = 0; h < hseg; h++)
{
for (int i = 0; i < cnum; ++i)
{
basepos1[i] = basepos2[i] = glm::vec3(glm::cos(math::radians(angles[i])._) * rad, glm::sin(math::radians(angles[i])._)*rad, 0);
math::rotate(0, 0, &basepos1[i].x, &basepos1[i].z, math::radians(glm::pi<float>() *2.0f / hseg * h));
math::rotate(0, 0, &basepos2[i].x, &basepos2[i].z, math::radians(glm::pi<float>() *2.0f / hseg * (h + 1)));
}
for (int v = 0; v < cnum - 1; v++)
{
int start = static_cast<int>(vertices.size());
vertices.push_back(basepos2[v]);
vertices.push_back(basepos1[v]);
vertices.push_back(basepos1[v + 1]);
vertices.push_back(basepos2[v + 1]);
indices.push_back(start+0);
indices.push_back(start+1);
indices.push_back(start+2);
indices.push_back(start+2);
indices.push_back(start+3);
indices.push_back(start+0);
}
}
gl.bufferData<GL_ARRAY_BUFFER, glm::vec3>(_vertices_vbo, vertices, GL_STATIC_DRAW);
gl.bufferData<GL_ELEMENT_ARRAY_BUFFER, std::uint16_t>(_indices_vbo, indices, GL_STATIC_DRAW);
_indices_count = static_cast<int>(indices.size());
_uploaded = true;
_need_vao_update = true;
}
void Skies::update_vao(OpenGL::Scoped::use_program& shader)
{
OpenGL::Scoped::index_buffer_manual_binder indices_binder (_indices_vbo);
{
OpenGL::Scoped::vao_binder const _ (_vao);
OpenGL::Scoped::buffer_binder<GL_ARRAY_BUFFER> vertices_buffer (_vertices_vbo);
shader.attrib("position", 3, GL_FLOAT, GL_FALSE, 0, 0);
OpenGL::Scoped::buffer_binder<GL_ARRAY_BUFFER> colors_buffer (_colors_vbo);
shader.attrib("color", 3, GL_FLOAT, GL_FALSE, 0, 0);
indices_binder.bind();
}
_need_vao_update = false;
}
void Skies::update_color_buffer()
{
std::vector<glm::vec3> colors;
for (int h = 0; h < hseg; h++)
{
for (int v = 0; v < cnum - 1; v++)
{
colors.push_back(color_set[skycolors[v]]);
colors.push_back(color_set[skycolors[v]]);
colors.push_back(color_set[skycolors[v + 1]]);
colors.push_back(color_set[skycolors[v + 1]]);
}
}
gl.bufferData<GL_ARRAY_BUFFER, glm::vec3>(_colors_vbo, colors, GL_STATIC_DRAW);
_need_vao_update = true;
}
void OutdoorLightStats::interpolate(OutdoorLightStats *a, OutdoorLightStats *b, float r)
{
static constexpr unsigned DayNight_SecondsPerDay = 86400;
float progressDayAndNight = r / DayNight_SecondsPerDay;
float phiValue = 0;
const float thetaValue = 3.926991f;
const float phiTable[4] =
{
2.2165682f,
1.9198623f,
2.2165682f,
1.9198623f
};
unsigned currentPhiIndex = static_cast<unsigned>(progressDayAndNight / 0.25f);
unsigned nextPhiIndex = 0;
if (currentPhiIndex < 3)
nextPhiIndex = currentPhiIndex + 1;
// Lerp between the current value of phi and the next value of phi
{
float transitionProgress = (progressDayAndNight / 0.25f) - currentPhiIndex;
float currentPhiValue = phiTable[currentPhiIndex];
float nextPhiValue = phiTable[nextPhiIndex];
phiValue = glm::mix(currentPhiValue, nextPhiValue, transitionProgress);
}
// Convert from Spherical Position to Cartesian coordinates
float sinPhi = glm::sin(phiValue);
float cosPhi = glm::cos(phiValue);
float sinTheta = glm::sin(thetaValue);
float cosTheta = glm::cos(thetaValue);
dayDir.x = sinPhi * cosTheta;
dayDir.y = sinPhi * sinTheta;
dayDir.z = cosPhi;
float ir = 1.0f - progressDayAndNight;
nightIntensity = a->nightIntensity * ir + b->nightIntensity * progressDayAndNight;
}
OutdoorLighting::OutdoorLighting()
{
static constexpr std::array<int, 24> night_hours =
{1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1};
for (int i = 0; i < 24; ++i)
{
OutdoorLightStats ols;
ols.nightIntensity = night_hours[i];
lightStats.push_back(ols);
}
}
OutdoorLightStats OutdoorLighting::getLightStats(int time)
{
// ASSUME: only 24 light info records, one for each whole hour
//! \todo generalize this if the data file changes in the future
int normalized_time ((static_cast<int>(time) % DAY_DURATION) / 2);
static constexpr unsigned DayNight_SecondsPerDay = 86400;
long progressDayAndNight = (static_cast<float>(normalized_time) * 120);
while (progressDayAndNight < 0 || progressDayAndNight > DayNight_SecondsPerDay)
{
if (progressDayAndNight > DayNight_SecondsPerDay)
progressDayAndNight -= DayNight_SecondsPerDay;
if (progressDayAndNight < 0)
progressDayAndNight += DayNight_SecondsPerDay;
}
OutdoorLightStats out;
OutdoorLightStats *a, *b;
int ta = normalized_time / 60;
int tb = (ta + 1) % 24;
a = &lightStats[ta];
b = &lightStats[tb];
out.interpolate(a, b, progressDayAndNight);
return out;
}
bool Sky::operator<(const Sky& s) const
{
if (global) return false;
else if (s.global) return true;
else return r2 < s.r2;
}
bool Sky::selected() const
{
return _selected;
}
void Sky::save_to_dbc()
{
// Save Light.dbc record
// find new empty ID : gLightDB.getEmptyRecordID(); .prob do it when creating new light instead.
try
{
// assuming a new unused id is already set with is_new_record
DBCFile::Record lightDbRecord = is_new_record ? gLightDB.addRecord(Id) : gLightDB.getByID(Id);
if (is_new_record)
lightDbRecord.write(LightDB::Map, mapId);
lightDbRecord.write(LightDB::PositionX, pos.x * skymul);
lightDbRecord.write(LightDB::PositionY, pos.y * skymul);
lightDbRecord.write(LightDB::PositionZ, pos.z * skymul);
lightDbRecord.write(LightDB::RadiusInner, r1 * skymul);
lightDbRecord.write(LightDB::RadiusOuter,r2 * skymul);
bool save_param_dbc = false;
bool save_colors_dbc = false;
bool save_floats_dbc = false;
bool save_skybox_dbc = false;
for (int param_id = 0; param_id < NUM_SkyFloatParamsNames; param_id++)
{
auto param_opt = getCurrentParam();
if (!param_opt.has_value())
continue;
SkyParam* sky_param = param_opt.value();
if (sky_param == nullptr)
continue;
assert(sky_param->Id > 0);
// This is weird. Id assignation for new records need to be done either now or before.
// int lightParam_dbc_id = sky_param->_is_new_param_record ? gLightParamsDB.getEmptyRecordID()
// : sky_param->Id;
// assuming Id was properly set
int lightParam_dbc_id = sky_param->Id;
lightDbRecord.write(LightDB::DataIDs + param_id, lightParam_dbc_id);
if (lightParam_dbc_id == 0)
{
continue;
}
// save lightparams.dbc
if (sky_param->_need_save || sky_param->_is_new_param_record)
{
save_param_dbc = true;
try
{
DBCFile::Record light_param = sky_param->_is_new_param_record ? gLightParamsDB.addRecord(lightParam_dbc_id)
: gLightParamsDB.getByID(lightParam_dbc_id);
light_param.write(LightParamsDB::highlightSky, int(sky_param->highlight_sky()));
light_param.write(LightParamsDB::water_shallow_alpha, sky_param->river_shallow_alpha());
light_param.write(LightParamsDB::water_deep_alpha, sky_param->river_deep_alpha());
light_param.write(LightParamsDB::ocean_shallow_alpha, sky_param->ocean_shallow_alpha());
light_param.write(LightParamsDB::ocean_deep_alpha, sky_param->ocean_deep_alpha());
light_param.write(LightParamsDB::glow, sky_param->glow());
if (sky_param->skybox.has_value()) // TODO skybox dbc
{
// try to find an existing record with those params
bool exists = false;
for (DBCFile::Iterator i = gLightSkyboxDB.begin(); i != gLightSkyboxDB.end(); ++i)
{
if (i->getString(LightSkyboxDB::filename) == sky_param->skybox.value().model->file_key().filepath()
&& i->getInt(LightSkyboxDB::flags) == sky_param->skyboxFlags)
{
int id = i->getInt(LightSkyboxDB::ID);
light_param.write(LightParamsDB::skybox, id);
exists = true;
break;
}
}
if (!exists) // doesn't exist, create a new record
{
int new_skybox_dbc_id = gLightSkyboxDB.getEmptyRecordID();
DBCFile::Record rec = gLightSkyboxDB.addRecord(new_skybox_dbc_id);
rec.writeString(LightSkyboxDB::filename, sky_param->skybox.value().model->file_key().filepath());
rec.write(LightSkyboxDB::flags, sky_param->skyboxFlags);
gLightSkyboxDB.save();
light_param.write(LightParamsDB::skybox, new_skybox_dbc_id);
}
}
else
light_param.write(LightParamsDB::skybox, 0);
}
catch (DBCFile::NotFound)
{
assert(false);
LogError << "When trying to get the lightparams for the entry " << lightParam_dbc_id << " in LightParams.dbc" << std::endl;
// failsafe, don't point to new id that couldn't be created
if (sky_param->_is_new_param_record)
lightDbRecord.write(LightDB::DataIDs + param_id, 0);
}
}
// save LightIntBand.dbc
if (sky_param->_colors_need_save || sky_param->_is_new_param_record)
{
save_colors_dbc = true;
int light_int_start = (lightParam_dbc_id * NUM_SkyColorNames) - (NUM_SkyColorNames - 1);
for (int i = 0; i < NUM_SkyColorNames; ++i)
{
try
{
DBCFile::Record rec = sky_param->_is_new_param_record ? gLightIntBandDB.addRecord(light_int_start + i)
: gLightIntBandDB.getByID(light_int_start + i);
int entries = static_cast<int>(sky_param->colorRows[i].size());
rec.write(LightIntBandDB::Entries, entries); // nb of entries
// write all entries to cleanup data
for (int l = 0; l < 16; l++)
{
if (l >= entries)
{
rec.write(LightIntBandDB::Times + l, 0);
rec.write(LightIntBandDB::Values + l, 0);
}
else
{
rec.write(LightIntBandDB::Times + l, sky_param->colorRows[i][l].time);
int rebuilt_color_int = static_cast<int>(sky_param->colorRows[i][l].color.z * 255.0f)
+ (static_cast<int>(sky_param->colorRows[i][l].color.y * 255.0f) << 8)
+ (static_cast<int>(sky_param->colorRows[i][l].color.x * 255.0f) << 16);
rec.write(LightIntBandDB::Values + l, rebuilt_color_int);
}
}
// sky_param->_colors_need_save = false;
}
catch (...)
{
assert(false);
LogError << "When trying to save sky colors, sky id : " << lightDbRecord.getInt(LightDB::ID) << std::endl;
}
}
}
// save LightFloatBand.dbc
if (sky_param->_floats_need_save || sky_param->_is_new_param_record)
{
save_floats_dbc = true;
int light_float_start = (lightParam_dbc_id * NUM_SkyFloatParamsNames) - (NUM_SkyFloatParamsNames - 1);
for (int i = 0; i < NUM_SkyFloatParamsNames; ++i)
{
try
{
DBCFile::Record rec = sky_param->_is_new_param_record ? gLightFloatBandDB.addRecord(light_float_start + i)
: gLightFloatBandDB.getByID(light_float_start + i);
int entries = static_cast<int>(sky_param->floatParams[i].size());
rec.write(LightFloatBandDB::Entries, entries); // nb of entries
// write all entries to cleanup data
for (int l = 0; l < 16; l++)
{
if (l >= entries)
{
rec.write(LightFloatBandDB::Times + l, 0);
rec.write(LightFloatBandDB::Values + l, 0.0f);
}
else
{
rec.write(LightFloatBandDB::Times + l, sky_param->floatParams[i][l].time);
rec.write(LightFloatBandDB::Values + l, sky_param->floatParams[i][l].value);
}
}
// sky_param->_floats_need_save = false;
}
catch (...)
{
LogError << "Error when trying to save sky float params, sky id : " << lightDbRecord.getInt(LightDB::ID) << std::endl;
}
}
}
// sky_param->_need_save = false;
sky_param->_is_new_param_record = false;
}
gLightDB.save();
if (save_colors_dbc)
gLightIntBandDB.save();
if (save_colors_dbc)
gLightFloatBandDB.save();
if (save_param_dbc)
gLightParamsDB.save();
is_new_record = false;
}
catch (DBCFile::AlreadyExists)
{
LogError << "DBCFile::AlreadyExists When trying to add light.dbc record for the entry " << Id << std::endl;
assert(false);
}
catch (DBCFile::NotFound)
{
LogError << "DBCFile::NotFound When trying to add light.dbc record for the entry " << Id << std::endl;
assert(false);
}
catch (...)
{
LogError << "Unknown exception When trying to add light.dbc record for the entry " << Id << std::endl;
assert(false);
}
}