mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-22 02:12:45 +01:00
Improve weapon and animation dynamics
Fix issue with 0-rotation animation frames Improve weapon shot origin alignment (still bad)
This commit is contained in:
parent
b8860722fd
commit
67fbc5afa3
@ -61,14 +61,16 @@ struct WeaponScan
|
||||
|
||||
glm::vec3 end;
|
||||
|
||||
WeaponData* weapon;
|
||||
|
||||
// Constructor for a RADIUS hitscan
|
||||
WeaponScan( float damage, const glm::vec3& center, float radius )
|
||||
: type(RADIUS), damage(damage), center(center), radius(radius)
|
||||
WeaponScan( float damage, const glm::vec3& center, float radius, WeaponData* weapon = nullptr )
|
||||
: type(RADIUS), damage(damage), center(center), radius(radius), weapon(weapon)
|
||||
{}
|
||||
|
||||
// Constructor for a ray hitscan
|
||||
WeaponScan( float damage, const glm::vec3& start, const glm::vec3& end)
|
||||
: type(HITSCAN), damage(damage), center(start), end(end)
|
||||
WeaponScan( float damage, const glm::vec3& start, const glm::vec3& end, WeaponData* weapon = nullptr )
|
||||
: type(HITSCAN), damage(damage), center(start), end(end), weapon(weapon)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -35,6 +35,14 @@ class Animator
|
||||
AnimationKeyframe second;
|
||||
// Used if bone is null and entry exists.
|
||||
glm::quat orientation;
|
||||
|
||||
/// Construct from animation data
|
||||
FrameInstanceData(AnimationBone* bone, const AnimationKeyframe& a, const AnimationKeyframe& b)
|
||||
: visible(true), bone(bone), first(a), second(b) {}
|
||||
|
||||
/// Construct from procedural data
|
||||
FrameInstanceData(bool visible, const glm::quat& orientation = {})
|
||||
: visible(visible), bone(nullptr), orientation(orientation) {}
|
||||
};
|
||||
|
||||
std::map<ModelFrame*, FrameInstanceData> _frameInstances;
|
||||
@ -48,6 +56,7 @@ class Animator
|
||||
float serverTime;
|
||||
float lastServerTime;
|
||||
|
||||
bool playing;
|
||||
bool repeat;
|
||||
|
||||
void reset();
|
||||
@ -82,6 +91,8 @@ public:
|
||||
void setFrameOrientation(ModelFrame* frame, const glm::quat& orientation);
|
||||
glm::quat getFrameOrientation(ModelFrame* frame) const;
|
||||
|
||||
FrameInstanceData* getFrameInstance(ModelFrame* frame);
|
||||
|
||||
/**
|
||||
* @brief getFrameMatrix returns the matrix for frame at the given time
|
||||
* @param t
|
||||
@ -124,6 +135,8 @@ public:
|
||||
|
||||
float getAnimationTime(float alpha = 0.f) const;
|
||||
void setAnimationTime(float time);
|
||||
|
||||
void setPlaying( bool play ) { playing = play; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -14,6 +14,7 @@ struct AnimationKeyframe
|
||||
glm::vec3 position;
|
||||
glm::vec3 scale;
|
||||
float starttime;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
struct AnimationBone
|
||||
@ -33,6 +34,7 @@ struct AnimationBone
|
||||
std::vector<AnimationKeyframe> frames;
|
||||
|
||||
AnimationKeyframe getInterpolatedKeyframe(float time);
|
||||
AnimationKeyframe getKeyframe(float time);
|
||||
};
|
||||
|
||||
struct Animation
|
||||
|
@ -51,7 +51,42 @@ class GameRenderer
|
||||
|
||||
float _renderAlpha;
|
||||
|
||||
std::vector<std::pair<glm::vec3, glm::vec3>> _tracers;
|
||||
public:
|
||||
|
||||
struct FXParticle {
|
||||
glm::vec3 position;
|
||||
glm::vec3 direction;
|
||||
float velocity;
|
||||
|
||||
enum Orientation {
|
||||
Free,
|
||||
Camera,
|
||||
UpCamera
|
||||
};
|
||||
Orientation orientation;
|
||||
|
||||
float starttime;
|
||||
float lifetime;
|
||||
|
||||
/// @TODO convert use of TextureInfo to a pointer so it can be used here
|
||||
GLuint texture;
|
||||
|
||||
glm::vec2 size;
|
||||
|
||||
glm::vec3 up;
|
||||
|
||||
glm::vec3 _currentPosition;
|
||||
|
||||
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})
|
||||
: position(p), direction(d), velocity(v), orientation(o),
|
||||
starttime(st), lifetime(lt), texture(texture), size(size),
|
||||
up(up), _currentPosition(p) {}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<FXParticle> _particles;
|
||||
|
||||
public:
|
||||
|
||||
@ -72,6 +107,7 @@ public:
|
||||
GLuint skyProgram;
|
||||
GLuint waterProgram, waterMVP, waterHeight, waterTexture, waterSize, waterTime, waterPosition, waterWave;
|
||||
GLint skyUniView, skyUniProj, skyUniTop, skyUniBottom;
|
||||
GLuint particleProgram;
|
||||
|
||||
/// Internal VAO to avoid clobbering global state.
|
||||
GLuint vao, debugVAO;
|
||||
@ -96,6 +132,8 @@ public:
|
||||
|
||||
void renderGeometry(Model*, size_t geom, const glm::mat4& modelMatrix, GameObject* = nullptr);
|
||||
|
||||
void renderParticles();
|
||||
|
||||
|
||||
/**
|
||||
* Renders a model (who'd have thought)
|
||||
@ -107,9 +145,7 @@ public:
|
||||
*/
|
||||
void renderPaths();
|
||||
|
||||
void addTracer(const glm::vec3& from, const glm::vec3& to) {
|
||||
_tracers.push_back({from, to});
|
||||
}
|
||||
void addParticle(const FXParticle& particle);
|
||||
|
||||
static GLuint currentUBO;
|
||||
template<class T> void uploadUBO(GLuint buffer, const T& data)
|
||||
|
@ -19,6 +19,11 @@ struct WorldObject {
|
||||
static const char* FragmentShader;
|
||||
};
|
||||
|
||||
struct Particle {
|
||||
/* Shares vertex with WorldObject */
|
||||
static const char* FragmentShader;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -172,6 +172,7 @@ bool Activities::ExitVehicle::update(CharacterObject *character, CharacterContro
|
||||
}
|
||||
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <render/Model.hpp>
|
||||
bool Activities::ShootWeapon::update(CharacterObject *character, CharacterController *controller)
|
||||
{
|
||||
auto& wepdata = _item->getWeaponData();
|
||||
@ -182,17 +183,98 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
|
||||
auto shootanim = character->engine->gameData.animations[wepdata->animation1];
|
||||
if( shootanim ) {
|
||||
character->animator->setAnimation(shootanim, false);
|
||||
if( character->animator->getAnimationTime() >= wepdata->animLoopEnd / 100.f ) {
|
||||
character->animator->setAnimationTime( wepdata->animLoopStart / 100.f );
|
||||
|
||||
auto loopstart = wepdata->animLoopStart / 100.f;
|
||||
auto loopend = wepdata->animLoopEnd / 100.f;
|
||||
auto firetime = wepdata->animFirePoint / 100.f;
|
||||
|
||||
auto currID = character->animator->getAnimationTime();
|
||||
|
||||
if( currID >= loopend ) {
|
||||
character->animator->setAnimationTime( loopstart );
|
||||
_fired = false;
|
||||
}
|
||||
if( !_fired && character->animator->getAnimationTime() >= wepdata->animFirePoint / 100.f ) {
|
||||
else if( currID >= firetime && ! _fired ) {
|
||||
|
||||
auto handFrame = character->model->model->findFrame("srhand");
|
||||
glm::mat4 handMatrix;
|
||||
if( handFrame ) {
|
||||
while( handFrame->getParent() ) {
|
||||
handMatrix = character->animator->getFrameMatrix(handFrame) * handMatrix;
|
||||
handFrame = handFrame->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
auto farTarget = character->getPosition() +
|
||||
character->getRotation() * glm::vec3(0.f, wepdata->hitRange, 0.f);
|
||||
auto handPos = glm::vec3(handMatrix * glm::vec4(0.f, 0.f, 0.f, 1.f));
|
||||
auto fireOrigin = character->getPosition() +
|
||||
character->getRotation() * wepdata->fireOffset;
|
||||
character->getRotation() * handPos;
|
||||
auto flashDir = character->getRotation() * glm::vec3{0.f, 0.f, 1.f};
|
||||
auto flashUp = character->getRotation() * glm::vec3{0.f, -1.f, 0.f};
|
||||
|
||||
character->engine->doWeaponScan(WeaponScan(wepdata->damage, fireOrigin, farTarget));
|
||||
character->engine->doWeaponScan(WeaponScan(wepdata->damage, fireOrigin, farTarget, wepdata.get()));
|
||||
|
||||
// Particle FX involved:
|
||||
// - smokeII emited around barrel
|
||||
// - Some circle particle used for the tracer
|
||||
// - smoke emited at hit point
|
||||
// - gunflash
|
||||
|
||||
auto tracerTex = character->engine->gameData.textures[{"shad_exp",""}].texName;
|
||||
auto flashTex = character->engine->gameData.textures[{"gunflash2",""}].texName;
|
||||
auto flashTex1 = character->engine->gameData.textures[{"gunflash1",""}].texName;
|
||||
|
||||
float tracertime = 0.1f;
|
||||
auto distance = glm::distance(fireOrigin, farTarget);
|
||||
const float tracerspeed = distance / tracertime * 0.5f;
|
||||
float tracersize = wepdata->hitRange / 4.f;
|
||||
float flashtime = 0.015f;
|
||||
auto shotdir = glm::normalize(farTarget - fireOrigin);
|
||||
|
||||
character->engine->renderer.addParticle({
|
||||
fireOrigin + shotdir * tracersize / 2.f,
|
||||
shotdir,
|
||||
tracerspeed,
|
||||
GameRenderer::FXParticle::UpCamera,
|
||||
character->engine->gameTime, tracertime,
|
||||
tracerTex,
|
||||
{0.04f, tracersize},
|
||||
{0.f, 0.f, 0.f}
|
||||
});
|
||||
|
||||
character->engine->renderer.addParticle({
|
||||
fireOrigin,
|
||||
flashDir,
|
||||
0.f,
|
||||
GameRenderer::FXParticle::Free,
|
||||
character->engine->gameTime, flashtime,
|
||||
flashTex,
|
||||
{0.2f, 0.2f},
|
||||
flashUp
|
||||
});
|
||||
|
||||
character->engine->renderer.addParticle({
|
||||
fireOrigin + shotdir * 0.1f,
|
||||
flashDir,
|
||||
0.f,
|
||||
GameRenderer::FXParticle::Free,
|
||||
character->engine->gameTime, flashtime,
|
||||
flashTex,
|
||||
{0.2f, 0.2f},
|
||||
flashUp
|
||||
});
|
||||
|
||||
character->engine->renderer.addParticle({
|
||||
fireOrigin + shotdir * 0.2f,
|
||||
flashDir,
|
||||
0.f,
|
||||
GameRenderer::FXParticle::Free,
|
||||
character->engine->gameTime, flashtime,
|
||||
flashTex1,
|
||||
{0.2f, 0.2f},
|
||||
flashUp
|
||||
});
|
||||
|
||||
_fired = true;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Animator::Animator()
|
||||
: model(nullptr), time(0.f), serverTime(0.f), lastServerTime(0.f), repeat(true)
|
||||
: model(nullptr), time(0.f), serverTime(0.f), lastServerTime(0.f), playing(true), repeat(true)
|
||||
{
|
||||
|
||||
}
|
||||
@ -23,7 +23,8 @@ void Animator::reset()
|
||||
if( it == getAnimation()->bones.end() ) continue;
|
||||
|
||||
auto A = getKeyframeAt(f, 0.f);
|
||||
_frameInstances[f] = { true, it->second, A, A };
|
||||
|
||||
_frameInstances.insert( { f, { it->second, A, A } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -77,9 +78,7 @@ void Animator::setFrameVisibility(ModelFrame *frame, bool visible)
|
||||
_frameInstances.insert({
|
||||
frame,
|
||||
{
|
||||
visible,
|
||||
nullptr,
|
||||
{}, {}
|
||||
visible
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -105,8 +104,6 @@ void Animator::setFrameOrientation(ModelFrame *frame, const glm::quat &orientati
|
||||
frame,
|
||||
{
|
||||
true,
|
||||
nullptr,
|
||||
{}, {},
|
||||
orientation
|
||||
}
|
||||
});
|
||||
@ -122,14 +119,25 @@ glm::quat Animator::getFrameOrientation(ModelFrame *frame) const
|
||||
return glm::toQuat(frame->getDefaultRotation());
|
||||
}
|
||||
|
||||
Animator::FrameInstanceData *Animator::getFrameInstance(ModelFrame *frame)
|
||||
{
|
||||
auto fit = _frameInstances.find(frame);
|
||||
if( fit != _frameInstances.end() ) {
|
||||
return &fit->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Animator::tick(float dt)
|
||||
{
|
||||
if( model == nullptr || _animations.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( playing ) {
|
||||
lastServerTime = serverTime;
|
||||
serverTime += dt;
|
||||
}
|
||||
|
||||
for( auto& p : _frameInstances ) {
|
||||
p.second.second = p.second.first;
|
||||
@ -203,21 +211,22 @@ AnimationKeyframe Animator::getKeyframeAt(ModelFrame *frame, float time) const
|
||||
return it->second->getInterpolatedKeyframe(time);
|
||||
}
|
||||
}
|
||||
return { glm::toQuat(frame->getDefaultRotation()), frame->getDefaultTranslation(), glm::vec3(1.f), 0.f };
|
||||
return { glm::toQuat(frame->getDefaultRotation()), frame->getDefaultTranslation(), glm::vec3(1.f), 0.f, 0 };
|
||||
}
|
||||
|
||||
glm::mat4 Animator::getFrameMatrix(ModelFrame *frame, float alpha, bool ignoreRoot) const
|
||||
{
|
||||
auto it = _frameInstances.find( frame );
|
||||
if( it != _frameInstances.end() && it->second.bone ) {
|
||||
const AnimationKeyframe& S = it->second.first;
|
||||
const AnimationKeyframe& F = it->second.second;
|
||||
const AnimationKeyframe& F = it->second.first;
|
||||
const AnimationKeyframe& S = it->second.second;
|
||||
|
||||
AnimationKeyframe kf {
|
||||
glm::slerp(F.rotation, S.rotation, alpha),
|
||||
glm::mix(F.position, S.position, alpha),
|
||||
glm::mix(F.scale, S.scale, alpha),
|
||||
glm::mix(F.starttime, S.starttime, alpha)
|
||||
glm::mix(F.starttime, S.starttime, alpha),
|
||||
S.id
|
||||
};
|
||||
|
||||
glm::mat4 m;
|
||||
@ -257,5 +266,7 @@ float Animator::getAnimationTime(float alpha) const
|
||||
|
||||
void Animator::setAnimationTime(float time)
|
||||
{
|
||||
lastServerTime = serverTime;
|
||||
serverTime = time;
|
||||
|
||||
}
|
||||
|
@ -408,9 +408,6 @@ void GameWorld::doWeaponScan(const WeaponScan &scan)
|
||||
di.hitpoints = scan.damage;
|
||||
go->takeDamage(di);
|
||||
}
|
||||
|
||||
// Make the renderer draw a tracer.
|
||||
renderer.addTracer(scan.center, hitEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,16 +41,27 @@ AnimationKeyframe AnimationBone::getInterpolatedKeyframe(float time)
|
||||
|
||||
if( findKeyframes(time, this, f1, f2, alpha) ) {
|
||||
return {
|
||||
glm::normalize(glm::lerp(f1.rotation, f2.rotation, alpha)),
|
||||
glm::normalize(glm::slerp(f1.rotation, f2.rotation, alpha)),
|
||||
glm::mix(f1.position, f2.position, alpha),
|
||||
glm::mix(f1.scale, f2.scale, alpha),
|
||||
time
|
||||
time,
|
||||
std::max(f1.id, f2.id)
|
||||
};
|
||||
}
|
||||
|
||||
return frames.back();
|
||||
}
|
||||
|
||||
AnimationKeyframe AnimationBone::getKeyframe(float time)
|
||||
{
|
||||
for(size_t f = 0; f < frames.size(); ++f ) {
|
||||
if( time >= frames[f].starttime ) {
|
||||
return frames[f];
|
||||
}
|
||||
}
|
||||
return frames.back();
|
||||
}
|
||||
|
||||
bool LoaderIFP::loadFromMemory(char *data)
|
||||
{
|
||||
size_t data_offs = 0;
|
||||
@ -97,7 +108,8 @@ bool LoaderIFP::loadFromMemory(char *data)
|
||||
q,
|
||||
glm::vec3(0.f, 0.f, 0.f),
|
||||
glm::vec3(1.f, 1.f, 1.f),
|
||||
time
|
||||
time,
|
||||
d
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -111,7 +123,8 @@ bool LoaderIFP::loadFromMemory(char *data)
|
||||
q,
|
||||
p,
|
||||
glm::vec3(1.f, 1.f, 1.f),
|
||||
time
|
||||
time,
|
||||
d
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -126,7 +139,8 @@ bool LoaderIFP::loadFromMemory(char *data)
|
||||
q,
|
||||
p,
|
||||
s,
|
||||
time
|
||||
time,
|
||||
d
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,24 @@ DrawBuffer waterLQDraw;
|
||||
GeometryBuffer waterHQBuffer;
|
||||
DrawBuffer waterHQDraw;
|
||||
|
||||
/// @TODO collapse all of these into "VertPNC" etc.
|
||||
struct ParticleVert {
|
||||
static const AttributeList vertex_attributes() {
|
||||
return {
|
||||
{ATRS_Position, 2, sizeof(ParticleVert), 0ul},
|
||||
{ATRS_TexCoord, 2, sizeof(ParticleVert), 2ul * sizeof(float)},
|
||||
{ATRS_Colour, 3, sizeof(ParticleVert), 4ul * sizeof(float)}
|
||||
};
|
||||
}
|
||||
|
||||
float x, y;
|
||||
float u, v;
|
||||
float r, g, b;
|
||||
};
|
||||
|
||||
GeometryBuffer particleGeom;
|
||||
DrawBuffer particleDraw;
|
||||
|
||||
GLuint compileShader(GLenum type, const char *source)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
@ -103,6 +121,16 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, uboScene);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboObject);
|
||||
|
||||
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);
|
||||
|
||||
skyProgram = compileProgram(GameShaders::Sky::VertexShader,
|
||||
GameShaders::Sky::FragmentShader);
|
||||
|
||||
@ -189,6 +217,15 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
||||
glGenBuffers(1, &debugVBO);
|
||||
glGenTextures(1, &debugTex);
|
||||
glGenVertexArrays(1, &debugVAO);
|
||||
|
||||
particleGeom.uploadVertices<ParticleVert>(
|
||||
{
|
||||
{ 0.5f, 0.5f, 1.f, 1.f, 1.f, 1.f, 1.f},
|
||||
{-0.5f, 0.5f, 0.f, 1.f, 1.f, 1.f, 1.f},
|
||||
{ 0.5f,-0.5f, 1.f, 0.f, 1.f, 1.f, 1.f},
|
||||
{-0.5f,-0.5f, 0.f, 0.f, 1.f, 1.f, 1.f}
|
||||
});
|
||||
particleDraw.addGeometry(&particleGeom);
|
||||
}
|
||||
|
||||
float mix(uint8_t a, uint8_t b, float num)
|
||||
@ -367,32 +404,7 @@ void GameRenderer::renderWorld(float alpha)
|
||||
|
||||
glDrawElements(GL_TRIANGLES, skydomeSegments * skydomeRows * 6, GL_UNSIGNED_SHORT, NULL);
|
||||
|
||||
// Draw bullets like this for now
|
||||
if( _tracers.size() > 0 ) {
|
||||
glUseProgram(worldProgram);
|
||||
glBindVertexArray( vao );
|
||||
glBindBuffer(GL_ARRAY_BUFFER, debugVBO);
|
||||
glBindTexture(GL_TEXTURE_2D, debugTex);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _tracers.size() * 2, _tracers.data(), GL_STREAM_DRAW);
|
||||
GLint posAttrib = glGetAttribLocation(worldProgram, "position");
|
||||
glEnableVertexAttribArray(posAttrib);
|
||||
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
uploadUBO<ObjectUniformData>(
|
||||
uboObject, {
|
||||
glm::mat4(),
|
||||
glm::vec4(1.f),
|
||||
1.f, 1.f
|
||||
});
|
||||
|
||||
float img3[] = {1.f, 1.f, 0.f};
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, GL_RGB, 1, 1,
|
||||
0, GL_RGB, GL_FLOAT, img3
|
||||
);
|
||||
glDrawArrays(GL_LINES, 0, _tracers.size());
|
||||
|
||||
_tracers.clear();
|
||||
}
|
||||
renderParticles();
|
||||
|
||||
glUseProgram(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
@ -617,6 +629,71 @@ void GameRenderer::renderGeometry(Model* model, size_t g, const glm::mat4& model
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderParticles()
|
||||
{
|
||||
_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() );
|
||||
|
||||
auto cpos = camera.worldPos;
|
||||
auto cfwd = glm::normalize(glm::inverse(glm::mat3(camera.frustum.view)) * 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 );
|
||||
});
|
||||
|
||||
for(FXParticle& part : _particles) {
|
||||
glBindTexture(GL_TEXTURE_2D, part.texture);
|
||||
auto& p = part._currentPosition;
|
||||
|
||||
glm::mat4 m(1.f);
|
||||
|
||||
// Figure the direction to the camera center.
|
||||
auto amp = cpos - p;
|
||||
glm::vec3 ptc = part.up;
|
||||
|
||||
if( part.orientation == FXParticle::UpCamera ) {
|
||||
ptc = glm::normalize(amp - (glm::dot(amp, cfwd))*cfwd);
|
||||
}
|
||||
|
||||
glm::vec3 f = glm::normalize(part.direction);
|
||||
glm::vec3 s = glm::cross(f, glm::normalize(ptc));
|
||||
glm::vec3 u = glm::cross(s, f);
|
||||
m[0][0] = s.x;
|
||||
m[1][0] = s.y;
|
||||
m[2][0] = s.z;
|
||||
m[0][1] =-f.x;
|
||||
m[1][1] =-f.y;
|
||||
m[2][1] =-f.z;
|
||||
m[0][2] = u.x;
|
||||
m[1][2] = u.y;
|
||||
m[2][2] = u.z;
|
||||
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(part.size, 1.f));
|
||||
uploadUBO<ObjectUniformData>(
|
||||
uboObject, {
|
||||
m,
|
||||
glm::vec4(1.f),
|
||||
1.f, 1.f
|
||||
});
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix, GameObject* object, bool queueTransparent)
|
||||
{
|
||||
auto localmatrix = matrix;
|
||||
@ -806,3 +883,8 @@ void GameRenderer::renderPaths()
|
||||
carlines.clear();
|
||||
glBindVertexArray( 0 );
|
||||
}
|
||||
|
||||
void GameRenderer::addParticle(const FXParticle &particle)
|
||||
{
|
||||
_particles.push_back(particle);
|
||||
}
|
||||
|
@ -134,4 +134,40 @@ void main()
|
||||
* (vec4(0.5) + dynamic * 0.5) * c, 1.f);
|
||||
})";
|
||||
|
||||
const char* Particle::FragmentShader = R"(
|
||||
#version 130
|
||||
#extension GL_ARB_uniform_buffer_object : enable
|
||||
in vec3 Normal;
|
||||
in vec2 TexCoords;
|
||||
in vec4 Colour;
|
||||
uniform sampler2D texture;
|
||||
|
||||
layout(std140) uniform SceneData {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
vec4 ambient;
|
||||
vec4 dynamic;
|
||||
float fogStart;
|
||||
float fogEnd;
|
||||
};
|
||||
|
||||
layout(std140) uniform ObjectData {
|
||||
mat4 model;
|
||||
vec4 colour;
|
||||
float diffusefac;
|
||||
float ambientfac;
|
||||
};
|
||||
|
||||
#define ALPHA_DISCARD_THRESHOLD 0.01
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 c = texture2D(texture, TexCoords);
|
||||
c.a = clamp(0, length(c.rgb) * 2, 1);
|
||||
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 );
|
||||
gl_FragColor = mix(ambient, colour * (vec4(0.5) + Colour * 0.5)
|
||||
* (vec4(0.5) + dynamic * 0.5) * c, 1.f);
|
||||
})";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user