245 lines
7.3 KiB
C++
245 lines
7.3 KiB
C++
// Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
|
|
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
|
|
|
#include "NavLights.h"
|
|
|
|
#include "FileSystem.h"
|
|
#include "GameSaveError.h"
|
|
#include "Json.h"
|
|
#include "core/IniConfig.h"
|
|
#include "graphics/RenderState.h"
|
|
#include "graphics/TextureBuilder.h"
|
|
#include "scenegraph/FindNodeVisitor.h"
|
|
#include "scenegraph/SceneGraph.h"
|
|
#include "utils.h"
|
|
|
|
const float BILLBOARD_SIZE = 2.5f;
|
|
|
|
static RefCountedPtr<Graphics::Texture> texHalos4x4;
|
|
static RefCountedPtr<Graphics::Material> matHalos4x4;
|
|
|
|
static bool g_initted = false;
|
|
static vector2f m_lightColorsUVoffsets[(NavLights::NAVLIGHT_YELLOW + 1)] = {
|
|
vector2f(0.0f, 0.0f),
|
|
vector2f(0.5f, 0.0f),
|
|
vector2f(0.0f, 0.5f),
|
|
vector2f(0.5f, 0.5f)
|
|
};
|
|
|
|
static vector2f get_color(Uint8 c)
|
|
{
|
|
return m_lightColorsUVoffsets[c];
|
|
}
|
|
|
|
static inline vector2f LoadLightColorUVoffset(const std::string &spec)
|
|
{
|
|
std::vector<float> v(2);
|
|
SplitSpec(spec, v);
|
|
return vector2f(v[0], v[1]);
|
|
}
|
|
|
|
NavLights::LightBulb::LightBulb(Uint8 _group, Uint8 _mask, Uint8 _color, SceneGraph::Billboard *_bb) :
|
|
group(_group),
|
|
mask(_mask),
|
|
color(_color),
|
|
billboard(_bb)
|
|
{
|
|
}
|
|
|
|
void NavLights::Init(Graphics::Renderer *renderer)
|
|
{
|
|
PROFILE_SCOPED()
|
|
assert(!g_initted);
|
|
|
|
IniConfig cfg;
|
|
// set defaults
|
|
cfg.SetString("LeftOrOccupiedUVOffset", "0,0");
|
|
cfg.SetString("RightOrFreeUVOffset", "0.5,0");
|
|
cfg.SetString("StaticUVOffset", "0,0.5");
|
|
cfg.SetString("DockingUVOffset", "0.5,0.5");
|
|
// load
|
|
cfg.Read(FileSystem::gameDataFiles, "textures/NavLights.ini");
|
|
|
|
m_lightColorsUVoffsets[NAVLIGHT_RED] = LoadLightColorUVoffset(cfg.String("LeftOrOccupiedUVOffset"));
|
|
m_lightColorsUVoffsets[NAVLIGHT_GREEN] = LoadLightColorUVoffset(cfg.String("RightOrFreeUVOffset"));
|
|
m_lightColorsUVoffsets[NAVLIGHT_BLUE] = LoadLightColorUVoffset(cfg.String("StaticUVOffset"));
|
|
m_lightColorsUVoffsets[NAVLIGHT_YELLOW] = LoadLightColorUVoffset(cfg.String("DockingUVOffset"));
|
|
|
|
g_initted = true;
|
|
}
|
|
|
|
void NavLights::Uninit()
|
|
{
|
|
assert(g_initted);
|
|
|
|
g_initted = false;
|
|
}
|
|
|
|
NavLights::NavLights(SceneGraph::Model *model, float period) :
|
|
m_time(0.f),
|
|
m_period(period),
|
|
m_enabled(false),
|
|
m_billboardTris(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_NORMAL),
|
|
m_billboardRS(nullptr)
|
|
{
|
|
PROFILE_SCOPED();
|
|
assert(g_initted);
|
|
|
|
Graphics::Renderer *renderer = model->GetRenderer();
|
|
|
|
using SceneGraph::Billboard;
|
|
using SceneGraph::MatrixTransform;
|
|
using SceneGraph::Node;
|
|
|
|
//This will find all matrix transforms meant for navlights.
|
|
SceneGraph::FindNodeVisitor lightFinder(SceneGraph::FindNodeVisitor::MATCH_NAME_STARTSWITH, "navlight_");
|
|
model->GetRoot()->Accept(lightFinder);
|
|
const std::vector<Node *> &results = lightFinder.GetResults();
|
|
|
|
//attach light billboards
|
|
for (unsigned int i = 0; i < results.size(); i++) {
|
|
MatrixTransform *mt = dynamic_cast<MatrixTransform *>(results.at(i));
|
|
assert(mt);
|
|
Billboard *bblight = new Billboard(m_billboardTris, renderer, BILLBOARD_SIZE);
|
|
Uint32 group = 0;
|
|
Uint8 mask = 0xff; //always on
|
|
Uint8 color = NAVLIGHT_BLUE;
|
|
|
|
if (mt->GetName().substr(9, 3) == "red") {
|
|
mask = 0x0f;
|
|
color = NAVLIGHT_RED;
|
|
} else if (mt->GetName().substr(9, 5) == "green") {
|
|
mask = 0xf0;
|
|
color = NAVLIGHT_GREEN;
|
|
} else if (mt->GetName().substr(9, 3) == "pad") {
|
|
//group by pad number
|
|
// due to this problem: http://stackoverflow.com/questions/15825254/why-is-scanfhhu-char-overwriting-other-variables-when-they-are-local
|
|
// where MSVC is still using a C89 compiler the format identifer %hhu is not recognised. Therefore I've switched to Uint32 for group.
|
|
PiVerify(1 == sscanf(mt->GetName().c_str(), "navlight_pad%u", &group));
|
|
mask = 0xf8;
|
|
}
|
|
bblight->SetColorUVoffset(get_color(color));
|
|
|
|
// automagically create a new group if one doesn't exist.
|
|
m_groupLights[group].push_back(LightBulb(group, mask, color, bblight));
|
|
mt->SetNodeMask(SceneGraph::NODE_TRANSPARENT);
|
|
mt->AddChild(bblight);
|
|
}
|
|
}
|
|
|
|
NavLights::~NavLights()
|
|
{
|
|
}
|
|
|
|
void NavLights::SaveToJson(Json &jsonObj)
|
|
{
|
|
Json navLightsObj = Json::object(); // Create JSON object to contain nav lights data.
|
|
|
|
navLightsObj["time"] = m_time;
|
|
navLightsObj["enabled"] = m_enabled;
|
|
|
|
jsonObj["nav_lights"] = navLightsObj; // Add nav lights object to supplied object.
|
|
}
|
|
|
|
void NavLights::LoadFromJson(const Json &jsonObj)
|
|
{
|
|
try {
|
|
Json navLightsObj = jsonObj["nav_lights"];
|
|
|
|
m_time = navLightsObj["time"];
|
|
m_enabled = navLightsObj["enabled"];
|
|
} catch (Json::type_error &) {
|
|
throw SavedGameCorruptException();
|
|
}
|
|
}
|
|
|
|
void NavLights::Update(float time)
|
|
{
|
|
PROFILE_SCOPED();
|
|
if (!m_enabled) {
|
|
for (const auto &group : m_groupLights)
|
|
for (const LightBulb &light : group.second)
|
|
light.billboard->SetNodeMask(0x0);
|
|
return;
|
|
}
|
|
|
|
m_time += time;
|
|
|
|
const int phase((fmod(m_time, m_period) / m_period) * 8);
|
|
const Uint8 mask = 1 << phase;
|
|
|
|
for (const auto &pair : m_groupLights) {
|
|
for (const LightBulb &light : pair.second) {
|
|
if (light.mask & mask && light.color != LightColor::NAVLIGHT_OFF)
|
|
light.billboard->SetNodeMask(SceneGraph::NODE_TRANSPARENT);
|
|
else
|
|
light.billboard->SetNodeMask(0x0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NavLights::Render(Graphics::Renderer *renderer)
|
|
{
|
|
if (!m_billboardRS) {
|
|
Graphics::MaterialDescriptor desc;
|
|
desc.effect = Graphics::EFFECT_BILLBOARD_ATLAS;
|
|
desc.textures = 1;
|
|
matHalos4x4.Reset(renderer->CreateMaterial(desc));
|
|
texHalos4x4.Reset(Graphics::TextureBuilder::Billboard("textures/halo_4x4.dds").GetOrCreateTexture(renderer, std::string("billboard")));
|
|
matHalos4x4->texture0 = texHalos4x4.Get();
|
|
|
|
Graphics::RenderStateDesc rsd;
|
|
rsd.blendMode = Graphics::BLEND_ADDITIVE;
|
|
rsd.depthWrite = false;
|
|
m_billboardRS = renderer->CreateRenderState(rsd);
|
|
}
|
|
|
|
const bool isVBValid = m_billboardVB.Valid();
|
|
const bool hasVerts = !m_billboardTris.IsEmpty();
|
|
const bool isVertCountEnough = isVBValid && (m_billboardTris.GetNumVerts() <= m_billboardVB->GetCapacity());
|
|
if (hasVerts && (!isVBValid || !isVertCountEnough)) {
|
|
//create buffer
|
|
// NB - we're (ab)using the normal type to hold (uv coordinate offset value + point size)
|
|
Graphics::VertexBufferDesc vbd;
|
|
vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
|
|
vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
|
|
vbd.attrib[1].semantic = Graphics::ATTRIB_NORMAL;
|
|
vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT3;
|
|
vbd.numVertices = m_billboardTris.GetNumVerts();
|
|
vbd.usage = Graphics::BUFFER_USAGE_DYNAMIC; // we could be updating this per-frame
|
|
m_billboardVB.Reset(renderer->CreateVertexBuffer(vbd));
|
|
}
|
|
|
|
if (m_billboardVB.Valid()) {
|
|
if (hasVerts) {
|
|
m_billboardVB->Populate(m_billboardTris);
|
|
renderer->SetTransform(matrix4x4f::Identity());
|
|
renderer->DrawBuffer(m_billboardVB.Get(), m_billboardRS, matHalos4x4.Get(), Graphics::POINTS);
|
|
renderer->GetStats().AddToStatCount(Graphics::Stats::STAT_BILLBOARD, 1);
|
|
}
|
|
m_billboardTris.Clear();
|
|
}
|
|
}
|
|
|
|
void NavLights::SetColor(unsigned int group, LightColor c)
|
|
{
|
|
PROFILE_SCOPED();
|
|
if (!m_groupLights.count(group)) return;
|
|
for (LightBulb &light : m_groupLights[group]) {
|
|
if (light.group != group || light.color == c) continue;
|
|
if (c != LightColor::NAVLIGHT_OFF)
|
|
light.billboard->SetColorUVoffset(get_color(c));
|
|
|
|
light.color = c;
|
|
}
|
|
}
|
|
|
|
void NavLights::SetMask(unsigned int group, uint8_t mask)
|
|
{
|
|
PROFILE_SCOPED()
|
|
if (!m_groupLights.count(group)) return;
|
|
for (LightBulb &light : m_groupLights[group]) {
|
|
light.mask = mask;
|
|
}
|
|
}
|