1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-22 10:22:52 +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:
Daniel Evans 2015-03-06 16:55:46 +00:00
parent 46cd7b8f51
commit 2985a70354
12 changed files with 279 additions and 138 deletions

View File

@ -19,6 +19,8 @@ class CharacterObject;
class InstanceObject;
class VehicleObject;
#include <render/VisualFX.hpp>
struct WeaponScan;
class ScriptMachine;
@ -137,7 +139,17 @@ public:
* Performs a weapon scan against things in the world
*/
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
*/
@ -208,6 +220,12 @@ public:
* AI Graph
*/
AIGraph aigraph;
/**
* Visual Effects
* @todo Consider using lighter handing mechanism
*/
std::vector<VisualFX*> effects;
/**
* Randomness Engine

View File

@ -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);

View File

@ -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;
@ -209,11 +149,11 @@ public:
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);
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;

View 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;
};

View File

@ -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 ) {

View File

@ -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;

View File

@ -71,17 +71,17 @@ void ProjectileObject::explode()
});
}
auto tex = engine->gameData.findTexture("explo02")->getName();
/*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 tex = engine->gameData.findTexture("explo02");
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);

View File

@ -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));
auto& effects = engine->effects;
std::sort( _particles.begin(), _particles.end(),
[&](const FXParticle& a, const FXParticle& b) {
return glm::distance( a._currentPosition, cpos ) > glm::distance( b._currentPosition, cpos );
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);
}

View File

@ -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;
})";

View 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;
}
}

View 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()

View File

@ -192,6 +192,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();