1
0
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:
Daniel Evans 2014-07-09 02:06:59 +01:00
parent b8860722fd
commit 67fbc5afa3
11 changed files with 457 additions and 177 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,11 @@ struct WorldObject {
static const char* FragmentShader;
};
struct Particle {
/* Shares vertex with WorldObject */
static const char* FragmentShader;
};
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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