mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 20:02:40 +01:00
Implement new VisualFX system for particles etc.
- VisualFX stores data about effects like particles and lighting - Only particles initial implementation - World stores active VisualFX
This commit is contained in:
parent
46cd7b8f51
commit
2985a70354
@ -19,6 +19,8 @@ class CharacterObject;
|
||||
class InstanceObject;
|
||||
class VehicleObject;
|
||||
|
||||
#include <render/VisualFX.hpp>
|
||||
|
||||
struct WeaponScan;
|
||||
|
||||
class ScriptMachine;
|
||||
@ -138,6 +140,16 @@ public:
|
||||
*/
|
||||
void doWeaponScan(const WeaponScan &scan );
|
||||
|
||||
/**
|
||||
* Allocates a new VisualFX of the given type
|
||||
*/
|
||||
VisualFX* createEffect(VisualFX::EffectType type);
|
||||
|
||||
/**
|
||||
* Immediately destoys the given effect
|
||||
*/
|
||||
void destroyEffect(VisualFX* effect);
|
||||
|
||||
/**
|
||||
* Returns the current hour
|
||||
*/
|
||||
@ -209,6 +221,12 @@ public:
|
||||
*/
|
||||
AIGraph aigraph;
|
||||
|
||||
/**
|
||||
* Visual Effects
|
||||
* @todo Consider using lighter handing mechanism
|
||||
*/
|
||||
std::vector<VisualFX*> effects;
|
||||
|
||||
/**
|
||||
* Randomness Engine
|
||||
*/
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class VisualFX;
|
||||
class CharacterObject;
|
||||
|
||||
/**
|
||||
@ -20,6 +21,7 @@ class PickupObject : public GameObject
|
||||
float _enableTimer;
|
||||
bool collected;
|
||||
int _modelID;
|
||||
VisualFX* corona;
|
||||
public:
|
||||
|
||||
PickupObject(GameWorld* world, const glm::vec3& position, int modelID);
|
||||
|
@ -75,64 +75,6 @@ class GameRenderer
|
||||
/** Internal non-descript VAOs */
|
||||
GLuint vao, debugVAO;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Stores particle effect instance data
|
||||
*/
|
||||
struct FXParticle {
|
||||
|
||||
/** Initial world position */
|
||||
glm::vec3 position;
|
||||
|
||||
/** Direction of particle */
|
||||
glm::vec3 direction;
|
||||
|
||||
/** Velocity of particle */
|
||||
float velocity;
|
||||
|
||||
/** Particle orientation modes */
|
||||
enum Orientation {
|
||||
Free, /** faces direction using up */
|
||||
Camera, /** Faces towards the camera @todo implement */
|
||||
UpCamera /** Face closes point in camera's look direction */
|
||||
};
|
||||
Orientation orientation;
|
||||
|
||||
/** Game time at particle instantiation */
|
||||
float starttime;
|
||||
float lifetime;
|
||||
|
||||
/** Texture name */
|
||||
GLuint texture;
|
||||
|
||||
/** Size of particle */
|
||||
glm::vec2 size;
|
||||
|
||||
/** Up direction (only used in Free mode) */
|
||||
glm::vec3 up;
|
||||
|
||||
/** Render tint colour */
|
||||
glm::vec4 colour;
|
||||
|
||||
/** Internal cache value */
|
||||
glm::vec3 _currentPosition;
|
||||
|
||||
/** Constructs a particle */
|
||||
FXParticle(const glm::vec3& p, const glm::vec3& d, float v,
|
||||
Orientation o, float st, float lt, GLuint texture,
|
||||
const glm::vec2& size, const glm::vec3& up = {0.f, 0.f, 1.f},
|
||||
const glm::vec4& colour = {1.f, 1.f, 1.f, 1.f})
|
||||
: position(p), direction(d), velocity(v), orientation(o),
|
||||
starttime(st), lifetime(lt), texture(texture), size(size),
|
||||
up(up), colour(colour), _currentPosition(p) {}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/** Particles in flight */
|
||||
std::vector<FXParticle> _particles;
|
||||
|
||||
/** Camera values passed to renderWorld() */
|
||||
ViewCamera _camera;
|
||||
|
||||
@ -153,8 +95,6 @@ public:
|
||||
Renderer::ShaderProgram* waterProg;
|
||||
Renderer::ShaderProgram* particleProg;
|
||||
|
||||
GLuint particleProgram;
|
||||
|
||||
GLuint ssRectProgram;
|
||||
GLint ssRectTexture, ssRectColour, ssRectSize, ssRectOffset;
|
||||
|
||||
@ -211,9 +151,9 @@ public:
|
||||
void renderItem(InventoryItem* item, const glm::mat4& modelMatrix);
|
||||
|
||||
/**
|
||||
* @brief renders all visible particles and removes expired
|
||||
* Renders the effects (Particles, Lighttrails etc)
|
||||
*/
|
||||
void renderParticles();
|
||||
void renderEffects();
|
||||
|
||||
/**
|
||||
* @brief Draws the current on screen text.
|
||||
@ -236,9 +176,6 @@ public:
|
||||
/** Increases cinematic value */
|
||||
void renderLetterbox();
|
||||
|
||||
/** Adds a particle to the rendering */
|
||||
void addParticle(const FXParticle& particle);
|
||||
|
||||
Renderer* getRenderer()
|
||||
{
|
||||
return renderer;
|
||||
|
83
rwengine/include/render/VisualFX.hpp
Normal file
83
rwengine/include/render/VisualFX.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "TextureData.hpp"
|
||||
|
||||
/**
|
||||
* Represents a scene effect: lighting, particles etc.
|
||||
*/
|
||||
class VisualFX
|
||||
{
|
||||
public:
|
||||
enum EffectType
|
||||
{
|
||||
Light,
|
||||
Particle,
|
||||
Trail
|
||||
};
|
||||
|
||||
struct LightData
|
||||
{
|
||||
~LightData();
|
||||
};
|
||||
struct ParticleData
|
||||
{
|
||||
/** Initial world position */
|
||||
glm::vec3 position;
|
||||
|
||||
/** Direction of particle */
|
||||
glm::vec3 direction;
|
||||
|
||||
/** Particle orientation modes */
|
||||
enum Orientation {
|
||||
Free, /** faces direction using up */
|
||||
Camera, /** Faces towards the camera @todo implement */
|
||||
UpCamera /** Face closes point in camera's look direction */
|
||||
};
|
||||
Orientation orientation;
|
||||
|
||||
/** Game time at particle instantiation */
|
||||
float starttime;
|
||||
/** Number of seconds particle should exist for, negative values = forever */
|
||||
float lifetime;
|
||||
|
||||
/** Texture name */
|
||||
TextureData::Handle texture;
|
||||
|
||||
/** Size of particle */
|
||||
glm::vec2 size;
|
||||
|
||||
/** Up direction (only used in Free mode) */
|
||||
glm::vec3 up;
|
||||
|
||||
/** Render tint colour */
|
||||
glm::vec4 colour;
|
||||
|
||||
/** Constructs a particle */
|
||||
ParticleData()
|
||||
: orientation(Free), starttime(0.f), lifetime(-1.f), size(1.f, 1.f),
|
||||
up(0.f, 0.f, 1.f), colour(1.f, 1.f, 1.f, 1.f) { }
|
||||
~ParticleData();
|
||||
};
|
||||
struct TrailData
|
||||
{
|
||||
~TrailData();
|
||||
};
|
||||
|
||||
/// @todo stop abusing unions
|
||||
union {
|
||||
LightData light;
|
||||
ParticleData particle;
|
||||
TrailData trail;
|
||||
};
|
||||
|
||||
VisualFX(EffectType type);
|
||||
~VisualFX();
|
||||
|
||||
EffectType getType() const { return type; }
|
||||
|
||||
const glm::vec3& getPosition() const;
|
||||
|
||||
private:
|
||||
EffectType type;
|
||||
};
|
@ -504,6 +504,28 @@ void GameWorld::destroyQueuedObjects()
|
||||
}
|
||||
}
|
||||
|
||||
VisualFX* GameWorld::createEffect(VisualFX::EffectType type)
|
||||
{
|
||||
auto effect = new VisualFX( type );
|
||||
effects.push_back(effect);
|
||||
return effect;
|
||||
}
|
||||
|
||||
void GameWorld::destroyEffect(VisualFX* effect)
|
||||
{
|
||||
for( auto it = effects.begin(); it != effects.end(); )
|
||||
{
|
||||
if( *it == effect )
|
||||
{
|
||||
it = effects.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameWorld::doWeaponScan(const WeaponScan &scan)
|
||||
{
|
||||
if( scan.type == WeaponScan::RADIUS ) {
|
||||
|
@ -17,6 +17,13 @@ PickupObject::PickupObject(GameWorld *world, const glm::vec3 &position, int mode
|
||||
_ghost->setCollisionShape(_shape);
|
||||
_ghost->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT|btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
|
||||
corona = world->createEffect(VisualFX::Particle);
|
||||
corona->particle.position = getPosition();
|
||||
corona->particle.direction = glm::vec3(0.f, 0.f, 1.f);
|
||||
corona->particle.orientation = VisualFX::ParticleData::Camera;
|
||||
corona->particle.colour = glm::vec4(1.0f, 0.3f, 0.3f, 0.3f);
|
||||
corona->particle.texture = engine->gameData.findTexture("coronacircle");
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@ -24,6 +31,7 @@ PickupObject::~PickupObject()
|
||||
{
|
||||
if(_ghost) {
|
||||
setEnabled(false);
|
||||
engine->destroyEffect(corona);
|
||||
delete _ghost;
|
||||
delete _shape;
|
||||
}
|
||||
@ -64,20 +72,6 @@ void PickupObject::tick(float dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto tex = engine->gameData.findTexture("coronacircle")->getName();
|
||||
|
||||
/// @TODO move this into rendering logic.
|
||||
/*engine->renderer.addParticle({
|
||||
position,
|
||||
{0.f, 0.f, 1.f},
|
||||
0.f,
|
||||
GameRenderer::FXParticle::Camera,
|
||||
engine->gameTime, dt,
|
||||
tex,
|
||||
{1.f, 1.f},
|
||||
{}, {0.75f, 0.f, 0.f, 1.f}
|
||||
});*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,9 +79,11 @@ void PickupObject::setEnabled(bool enabled)
|
||||
{
|
||||
if( ! _enabled && enabled ) {
|
||||
engine->dynamicsWorld->addCollisionObject(_ghost, btBroadphaseProxy::SensorTrigger);
|
||||
corona->particle.size = glm::vec2(1.5f, 1.5f);
|
||||
}
|
||||
else if( _enabled && ! enabled ) {
|
||||
engine->dynamicsWorld->removeCollisionObject(_ghost);
|
||||
corona->particle.size = glm::vec2(0.f, 0.f);
|
||||
}
|
||||
|
||||
_enabled = enabled;
|
||||
|
@ -71,17 +71,17 @@ void ProjectileObject::explode()
|
||||
});
|
||||
}
|
||||
|
||||
auto tex = engine->gameData.findTexture("explo02")->getName();
|
||||
auto tex = engine->gameData.findTexture("explo02");
|
||||
|
||||
/*engine->renderer.addParticle({
|
||||
position,
|
||||
{0.f, 0.f, 1.f},
|
||||
0.f,
|
||||
GameRenderer::FXParticle::Camera,
|
||||
engine->gameTime, 0.5f,
|
||||
tex,
|
||||
{exp_size, exp_size}
|
||||
});*/
|
||||
auto explosion = engine->createEffect(VisualFX::Particle);
|
||||
explosion->particle.size = glm::vec2(exp_size);
|
||||
explosion->particle.texture = tex;
|
||||
explosion->particle.starttime = engine->gameTime;
|
||||
explosion->particle.lifetime = 0.5f;
|
||||
explosion->particle.orientation = VisualFX::ParticleData::Camera;
|
||||
explosion->particle.colour = glm::vec4(1.0f);
|
||||
explosion->particle.position = getPosition();
|
||||
explosion->particle.direction = glm::vec3(0.f, 0.f, 1.f);
|
||||
|
||||
_exploded = true;
|
||||
engine->destroyObjectQueued(this);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <deque>
|
||||
#include <cmath>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
const size_t skydomeSegments = 8, skydomeRows = 10;
|
||||
|
||||
@ -100,16 +101,6 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
||||
renderer->setProgramBlockBinding(particleProg, "SceneData", 1);
|
||||
renderer->setProgramBlockBinding(particleProg, "ObjectData", 2);
|
||||
|
||||
particleProgram = compileProgram(GameShaders::WorldObject::VertexShader,
|
||||
GameShaders::Particle::FragmentShader);
|
||||
|
||||
/*uniTexture = glGetUniformLocation(particleProgram, "texture");
|
||||
ubiScene = glGetUniformBlockIndex(particleProgram, "SceneData");
|
||||
ubiObject = glGetUniformBlockIndex(particleProgram, "ObjectData");*/
|
||||
|
||||
//glUniformBlockBinding(particleProgram, ubiScene, 1);
|
||||
//glUniformBlockBinding(particleProgram, ubiObject, 2);
|
||||
|
||||
skyProg = renderer->createShader(
|
||||
GameShaders::Sky::VertexShader,
|
||||
GameShaders::Sky::FragmentShader);
|
||||
@ -204,6 +195,7 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
||||
{-0.5f,-0.5f, 0.f, 0.f, 1.f, 1.f, 1.f}
|
||||
});
|
||||
particleDraw.addGeometry(&particleGeom);
|
||||
particleDraw.setFaceType(GL_TRIANGLE_STRIP);
|
||||
|
||||
ssRectGeom.uploadVertices(sspaceRect);
|
||||
ssRectDraw.addGeometry(&ssRectGeom);
|
||||
@ -485,7 +477,7 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
||||
|
||||
renderer->draw(glm::mat4(), &skyDbuff, dp);
|
||||
|
||||
renderParticles();
|
||||
renderEffects();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
@ -997,47 +989,43 @@ void GameRenderer::renderAreaIndicator(const AreaIndicatorInfo* info)
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderParticles()
|
||||
void GameRenderer::renderEffects()
|
||||
{
|
||||
_particles.erase( std::remove_if(_particles.begin(), _particles.end(),
|
||||
[&](FXParticle& p) {
|
||||
if ( ( engine->gameTime - p.starttime ) > p.lifetime ) {
|
||||
return true;
|
||||
}
|
||||
float t = engine->gameTime - p.starttime;
|
||||
p._currentPosition = p.position + (p.direction * p.velocity) * t;
|
||||
return false;
|
||||
}), _particles.end() );
|
||||
|
||||
glUseProgram( particleProgram );
|
||||
glBindVertexArray( particleDraw.getVAOName() );
|
||||
renderer->useProgram( particleProg );
|
||||
|
||||
auto cpos = _camera.position;
|
||||
auto cfwd = glm::normalize(glm::inverse(_camera.rotation) * glm::vec3(0.f, 1.f, 0.f));
|
||||
|
||||
std::sort( _particles.begin(), _particles.end(),
|
||||
[&](const FXParticle& a, const FXParticle& b) {
|
||||
return glm::distance( a._currentPosition, cpos ) > glm::distance( b._currentPosition, cpos );
|
||||
auto& effects = engine->effects;
|
||||
|
||||
std::sort( effects.begin(), effects.end(),
|
||||
[&](const VisualFX* a, const VisualFX* b) {
|
||||
return glm::distance( a->getPosition(), cpos ) > glm::distance( b->getPosition(), cpos );
|
||||
});
|
||||
|
||||
for(FXParticle& part : _particles) {
|
||||
glBindTexture(GL_TEXTURE_2D, part.texture);
|
||||
auto& p = part._currentPosition;
|
||||
for(VisualFX* fx : effects) {
|
||||
// Other effects not implemented yet
|
||||
if( fx->getType() != VisualFX::Particle ) continue;
|
||||
|
||||
auto& particle = fx->particle;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, particle.texture->getName());
|
||||
auto& p = particle.position;
|
||||
|
||||
glm::mat4 m(1.f);
|
||||
|
||||
// Figure the direction to the camera center.
|
||||
auto amp = cpos - p;
|
||||
glm::vec3 ptc = part.up;
|
||||
glm::vec3 ptc = particle.up;
|
||||
|
||||
if( part.orientation == FXParticle::UpCamera ) {
|
||||
if( particle.orientation == VisualFX::ParticleData::UpCamera ) {
|
||||
ptc = glm::normalize(amp - (glm::dot(amp, cfwd))*cfwd);
|
||||
}
|
||||
else if( part.orientation == FXParticle::Camera ) {
|
||||
else if( particle.orientation == VisualFX::ParticleData::Camera ) {
|
||||
ptc = amp;
|
||||
}
|
||||
|
||||
glm::vec3 f = glm::normalize(part.direction);
|
||||
glm::vec3 f = glm::normalize(particle.direction);
|
||||
glm::vec3 s = glm::cross(f, glm::normalize(ptc));
|
||||
glm::vec3 u = glm::cross(s, f);
|
||||
m[0][0] = s.x;
|
||||
@ -1052,16 +1040,19 @@ void GameRenderer::renderParticles()
|
||||
m[3][0] =-glm::dot(s, p);
|
||||
m[3][1] = glm::dot(f, p);
|
||||
m[3][2] =-glm::dot(u, p);
|
||||
m = glm::scale(glm::inverse(m), glm::vec3(particle.size, 1.f));
|
||||
|
||||
m = glm::scale(glm::inverse(m), glm::vec3(part.size, 1.f));
|
||||
/*uploadUBO<ObjectUniformData>(
|
||||
uboObject, {
|
||||
m,
|
||||
part.colour,
|
||||
1.f, 1.f, 1.f
|
||||
});*/
|
||||
//m = glm::translate(m, p);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
Renderer::DrawParameters dp;
|
||||
dp.texture = particle.texture->getName();
|
||||
dp.ambient = 1.f;
|
||||
dp.colour = glm::u8vec4(particle.colour * 255.f);
|
||||
dp.start = 0;
|
||||
dp.count = 4;
|
||||
dp.diffuse = 1.f;
|
||||
|
||||
renderer->drawArrays(m, &particleDraw, dp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1214,7 +1205,3 @@ void GameRenderer::renderLetterbox()
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void GameRenderer::addParticle(const FXParticle &particle)
|
||||
{
|
||||
_particles.push_back(particle);
|
||||
}
|
||||
|
@ -224,7 +224,8 @@ void main()
|
||||
if(c.a <= ALPHA_DISCARD_THRESHOLD) discard;
|
||||
float fogZ = (gl_FragCoord.z / gl_FragCoord.w);
|
||||
float fogfac = clamp( (fogStart-fogZ)/(fogEnd-fogStart), 0.0, 1.0 );
|
||||
outColour = mix(ambient, c * colour * Colour, 1.f);
|
||||
vec4 tint = vec4(colour.rgb, visibility);
|
||||
outColour = c * tint;
|
||||
})";
|
||||
|
||||
|
||||
|
64
rwengine/src/render/VisualFX.cpp
Normal file
64
rwengine/src/render/VisualFX.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <render/VisualFX.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
VisualFX::LightData::~LightData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VisualFX::ParticleData::~ParticleData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VisualFX::TrailData::~TrailData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VisualFX::VisualFX(VisualFX::EffectType type)
|
||||
: type(type)
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case VisualFX::Light:
|
||||
new (&light) LightData;
|
||||
break;
|
||||
case VisualFX::Particle:
|
||||
new (&particle) ParticleData;
|
||||
break;
|
||||
case VisualFX::Trail:
|
||||
new (&trail) TrailData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VisualFX::~VisualFX()
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case VisualFX::Light:
|
||||
light.~LightData();
|
||||
break;
|
||||
case VisualFX::Particle:
|
||||
particle.~ParticleData();
|
||||
break;
|
||||
case VisualFX::Trail:
|
||||
trail.~TrailData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const glm::vec3& VisualFX::getPosition() const
|
||||
{
|
||||
static glm::vec3 errorRef;
|
||||
switch( type )
|
||||
{
|
||||
case VisualFX::Particle:
|
||||
return particle.position;
|
||||
default:
|
||||
return errorRef;
|
||||
}
|
||||
}
|
||||
|
15
rwengine/tests/test_VisualFX.cpp
Normal file
15
rwengine/tests/test_VisualFX.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <test_globals.hpp>
|
||||
#include <render/VisualFX.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(VisualFXTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_light_data)
|
||||
{
|
||||
VisualFX fx(VisualFX::Light);
|
||||
|
||||
BOOST_CHECK_EQUAL(fx.getType(), VisualFX::Light);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -193,6 +193,22 @@ void RWGame::tick(float dt)
|
||||
clockAccumulator -= 1.f;
|
||||
}
|
||||
|
||||
// Clean up old VisualFX
|
||||
for( int i = 0; i < engine->effects.size(); ++i )
|
||||
{
|
||||
VisualFX* effect = engine->effects[i];
|
||||
if( effect->getType() == VisualFX::Particle )
|
||||
{
|
||||
auto& part = effect->particle;
|
||||
if( part.lifetime < 0.f ) continue;
|
||||
if( engine->gameTime >= part.starttime + part.lifetime )
|
||||
{
|
||||
engine->destroyEffect( effect );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( GameObject* object : engine->objects ) {
|
||||
object->_updateLastTransform();
|
||||
object->tick(dt);
|
||||
|
Loading…
Reference in New Issue
Block a user