pioneer/src/scenegraph/Thruster.cpp

230 lines
6.7 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 "Thruster.h"
#include "BaseLoader.h"
#include "Easing.h"
#include "NodeVisitor.h"
#include "Serializer.h"
#include "graphics/Material.h"
#include "graphics/RenderState.h"
#include "graphics/Renderer.h"
#include "graphics/TextureBuilder.h"
#include "graphics/VertexArray.h"
namespace SceneGraph {
static const std::string thrusterTextureFilename("textures/thruster.dds");
static const std::string thrusterGlowTextureFilename("textures/halo.dds");
static Color baseColor(178, 153, 255, 255);
Thruster::Thruster(Graphics::Renderer *r, bool _linear, const vector3f &_pos, const vector3f &_dir) :
Node(r, NODE_TRANSPARENT),
linearOnly(_linear),
dir(_dir),
pos(_pos),
currentColor(baseColor)
{
//set up materials
Graphics::MaterialDescriptor desc;
desc.textures = 1;
m_tMat.Reset(r->CreateMaterial(desc));
m_tMat->texture0 = Graphics::TextureBuilder::Billboard(thrusterTextureFilename).GetOrCreateTexture(r, "billboard");
m_tMat->diffuse = baseColor;
m_glowMat.Reset(r->CreateMaterial(desc));
m_glowMat->texture0 = Graphics::TextureBuilder::Billboard(thrusterGlowTextureFilename).GetOrCreateTexture(r, "billboard");
m_glowMat->diffuse = baseColor;
Graphics::RenderStateDesc rsd;
rsd.blendMode = Graphics::BLEND_ALPHA_ONE;
rsd.depthWrite = false;
rsd.cullMode = Graphics::CULL_NONE;
m_renderState = r->CreateRenderState(rsd);
}
Thruster::Thruster(const Thruster &thruster, NodeCopyCache *cache) :
Node(thruster, cache),
m_tMat(thruster.m_tMat),
m_renderState(thruster.m_renderState),
linearOnly(thruster.linearOnly),
dir(thruster.dir),
pos(thruster.pos),
currentColor(thruster.currentColor)
{
}
Node *Thruster::Clone(NodeCopyCache *cache)
{
return this; //thrusters are shared
}
void Thruster::Accept(NodeVisitor &nv)
{
nv.ApplyThruster(*this);
}
void Thruster::Render(const matrix4x4f &trans, const RenderData *rd)
{
PROFILE_SCOPED()
float power = -dir.Dot(vector3f(rd->linthrust));
if (!linearOnly) {
// pitch X
// yaw Y
// roll Z
//model center is at 0,0,0, no need for invSubModelMat stuff
const vector3f at = vector3f(rd->angthrust);
const vector3f angdir = pos.Cross(dir);
const float xp = angdir.x * at.x;
const float yp = angdir.y * at.y;
const float zp = angdir.z * at.z;
if (xp + yp + zp > 0) {
if (xp > yp && xp > zp && fabs(at.x) > power)
power = fabs(at.x);
else if (yp > xp && yp > zp && fabs(at.y) > power)
power = fabs(at.y);
else if (zp > xp && zp > yp && fabs(at.z) > power)
power = fabs(at.z);
}
}
if (power < 0.001f) return;
m_tMat->diffuse = m_glowMat->diffuse = currentColor * power;
//directional fade
vector3f cdir = vector3f(trans * -dir).Normalized();
vector3f vdir = vector3f(trans[2], trans[6], -trans[10]).Normalized();
// XXX check this for transition to new colors.
m_glowMat->diffuse.a = Easing::Circ::EaseIn(Clamp(vdir.Dot(cdir), 0.f, 1.f), 0.f, 1.f, 1.f) * 255;
m_tMat->diffuse.a = 255 - m_glowMat->diffuse.a;
Graphics::Renderer *r = GetRenderer();
if (!m_tBuffer.Valid()) {
m_tBuffer.Reset(CreateThrusterGeometry(r, m_tMat.Get()));
m_glowBuffer.Reset(CreateGlowGeometry(r, m_glowMat.Get()));
}
r->SetTransform(trans);
r->DrawBuffer(m_tBuffer.Get(), m_renderState, m_tMat.Get());
r->DrawBuffer(m_glowBuffer.Get(), m_renderState, m_glowMat.Get());
}
void Thruster::Save(NodeDatabase &db)
{
Node::Save(db);
db.wr->Bool(linearOnly);
db.wr->Vector3f(dir);
db.wr->Vector3f(pos);
}
Thruster *Thruster::Load(NodeDatabase &db)
{
const bool linear = db.rd->Bool();
const vector3f dir = db.rd->Vector3f();
const vector3f pos = db.rd->Vector3f();
Thruster *t = new Thruster(db.loader->GetRenderer(), linear, pos, dir);
return t;
}
Graphics::VertexBuffer *Thruster::CreateThrusterGeometry(Graphics::Renderer *r, Graphics::Material *mat)
{
Graphics::VertexArray verts(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_UV0);
//zero at thruster center
//+x down
//+y right
//+z backwards (or thrust direction)
const float w = 0.5f;
vector3f one(0.f, -w, 0.f); //top left
vector3f two(0.f, w, 0.f); //top right
vector3f three(0.f, w, 1.f); //bottom right
vector3f four(0.f, -w, 1.f); //bottom left
//uv coords
const vector2f topLeft(0.f, 1.f);
const vector2f topRight(1.f, 1.f);
const vector2f botLeft(0.f, 0.f);
const vector2f botRight(1.f, 0.f);
//add four intersecting planes to create a volumetric effect
for (int i = 0; i < 4; i++) {
verts.Add(one, topLeft);
verts.Add(two, topRight);
verts.Add(three, botRight);
verts.Add(three, botRight);
verts.Add(four, botLeft);
verts.Add(one, topLeft);
one.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
two.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
three.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
four.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
}
//create buffer and upload data
Graphics::VertexBufferDesc vbd;
vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
vbd.attrib[1].semantic = Graphics::ATTRIB_UV0;
vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT2;
vbd.numVertices = verts.GetNumVerts();
vbd.usage = Graphics::BUFFER_USAGE_STATIC;
Graphics::VertexBuffer *vb = r->CreateVertexBuffer(vbd);
vb->Populate(verts);
return vb;
}
Graphics::VertexBuffer *Thruster::CreateGlowGeometry(Graphics::Renderer *r, Graphics::Material *mat)
{
Graphics::VertexArray verts(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_UV0);
//create glow billboard for linear thrusters
const float w = 0.2;
vector3f one(-w, -w, 0.f); //top left
vector3f two(-w, w, 0.f); //top right
vector3f three(w, w, 0.f); //bottom right
vector3f four(w, -w, 0.f); //bottom left
//uv coords
const vector2f topLeft(0.f, 1.f);
const vector2f topRight(1.f, 1.f);
const vector2f botLeft(0.f, 0.f);
const vector2f botRight(1.f, 0.f);
for (int i = 0; i < 5; i++) {
verts.Add(one, topLeft);
verts.Add(two, topRight);
verts.Add(three, botRight);
verts.Add(three, botRight);
verts.Add(four, botLeft);
verts.Add(one, topLeft);
one.z += .1f;
two.z = three.z = four.z = one.z;
}
//create buffer and upload data
Graphics::VertexBufferDesc vbd;
vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
vbd.attrib[1].semantic = Graphics::ATTRIB_UV0;
vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT2;
vbd.numVertices = verts.GetNumVerts();
vbd.usage = Graphics::BUFFER_USAGE_STATIC;
Graphics::VertexBuffer *vb = r->CreateVertexBuffer(vbd);
vb->Populate(verts);
return vb;
}
} // namespace SceneGraph