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; glm::vec3 end;
WeaponData* weapon;
// Constructor for a RADIUS hitscan // Constructor for a RADIUS hitscan
WeaponScan( float damage, const glm::vec3& center, float radius ) WeaponScan( float damage, const glm::vec3& center, float radius, WeaponData* weapon = nullptr )
: type(RADIUS), damage(damage), center(center), radius(radius) : type(RADIUS), damage(damage), center(center), radius(radius), weapon(weapon)
{} {}
// Constructor for a ray hitscan // Constructor for a ray hitscan
WeaponScan( float damage, const glm::vec3& start, const glm::vec3& end) WeaponScan( float damage, const glm::vec3& start, const glm::vec3& end, WeaponData* weapon = nullptr )
: type(HITSCAN), damage(damage), center(start), end(end) : type(HITSCAN), damage(damage), center(start), end(end), weapon(weapon)
{} {}
}; };

View File

@ -35,6 +35,14 @@ class Animator
AnimationKeyframe second; AnimationKeyframe second;
// Used if bone is null and entry exists. // Used if bone is null and entry exists.
glm::quat orientation; 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; std::map<ModelFrame*, FrameInstanceData> _frameInstances;
@ -48,6 +56,7 @@ class Animator
float serverTime; float serverTime;
float lastServerTime; float lastServerTime;
bool playing;
bool repeat; bool repeat;
void reset(); void reset();
@ -82,6 +91,8 @@ public:
void setFrameOrientation(ModelFrame* frame, const glm::quat& orientation); void setFrameOrientation(ModelFrame* frame, const glm::quat& orientation);
glm::quat getFrameOrientation(ModelFrame* frame) const; glm::quat getFrameOrientation(ModelFrame* frame) const;
FrameInstanceData* getFrameInstance(ModelFrame* frame);
/** /**
* @brief getFrameMatrix returns the matrix for frame at the given time * @brief getFrameMatrix returns the matrix for frame at the given time
* @param t * @param t
@ -124,6 +135,8 @@ public:
float getAnimationTime(float alpha = 0.f) const; float getAnimationTime(float alpha = 0.f) const;
void setAnimationTime(float time); void setAnimationTime(float time);
void setPlaying( bool play ) { playing = play; }
}; };
#endif #endif

View File

@ -14,6 +14,7 @@ struct AnimationKeyframe
glm::vec3 position; glm::vec3 position;
glm::vec3 scale; glm::vec3 scale;
float starttime; float starttime;
size_t id;
}; };
struct AnimationBone struct AnimationBone
@ -33,6 +34,7 @@ struct AnimationBone
std::vector<AnimationKeyframe> frames; std::vector<AnimationKeyframe> frames;
AnimationKeyframe getInterpolatedKeyframe(float time); AnimationKeyframe getInterpolatedKeyframe(float time);
AnimationKeyframe getKeyframe(float time);
}; };
struct Animation struct Animation

View File

@ -51,7 +51,42 @@ class GameRenderer
float _renderAlpha; 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: public:
@ -72,10 +107,11 @@ public:
GLuint skyProgram; GLuint skyProgram;
GLuint waterProgram, waterMVP, waterHeight, waterTexture, waterSize, waterTime, waterPosition, waterWave; GLuint waterProgram, waterMVP, waterHeight, waterTexture, waterSize, waterTime, waterPosition, waterWave;
GLint skyUniView, skyUniProj, skyUniTop, skyUniBottom; GLint skyUniView, skyUniProj, skyUniTop, skyUniBottom;
GLuint particleProgram;
/// Internal VAO to avoid clobbering global state. /// Internal VAO to avoid clobbering global state.
GLuint vao, debugVAO; GLuint vao, debugVAO;
GLuint skydomeVBO, skydomeIBO, debugVBO; GLuint skydomeVBO, skydomeIBO, debugVBO;
GLuint debugTex; GLuint debugTex;
@ -96,20 +132,20 @@ public:
void renderGeometry(Model*, size_t geom, const glm::mat4& modelMatrix, GameObject* = nullptr); void renderGeometry(Model*, size_t geom, const glm::mat4& modelMatrix, GameObject* = nullptr);
void renderParticles();
/** /**
* Renders a model (who'd have thought) * Renders a model (who'd have thought)
*/ */
void renderModel(Model*, const glm::mat4& modelMatrix, GameObject* = nullptr, Animator* animator = nullptr); void renderModel(Model*, const glm::mat4& modelMatrix, GameObject* = nullptr, Animator* animator = nullptr);
/** /**
* Debug method renders all AI paths * Debug method renders all AI paths
*/ */
void renderPaths(); void renderPaths();
void addTracer(const glm::vec3& from, const glm::vec3& to) { void addParticle(const FXParticle& particle);
_tracers.push_back({from, to});
}
static GLuint currentUBO; static GLuint currentUBO;
template<class T> void uploadUBO(GLuint buffer, const T& data) template<class T> void uploadUBO(GLuint buffer, const T& data)

View File

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

View File

@ -172,6 +172,7 @@ bool Activities::ExitVehicle::update(CharacterObject *character, CharacterContro
} }
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <render/Model.hpp>
bool Activities::ShootWeapon::update(CharacterObject *character, CharacterController *controller) bool Activities::ShootWeapon::update(CharacterObject *character, CharacterController *controller)
{ {
auto& wepdata = _item->getWeaponData(); auto& wepdata = _item->getWeaponData();
@ -182,17 +183,98 @@ bool Activities::ShootWeapon::update(CharacterObject *character, CharacterContro
auto shootanim = character->engine->gameData.animations[wepdata->animation1]; auto shootanim = character->engine->gameData.animations[wepdata->animation1];
if( shootanim ) { if( shootanim ) {
character->animator->setAnimation(shootanim, false); 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; _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() + auto farTarget = character->getPosition() +
character->getRotation() * glm::vec3(0.f, wepdata->hitRange, 0.f); 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() + 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; _fired = true;
} }

View File

@ -4,7 +4,7 @@
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
Animator::Animator() 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; if( it == getAnimation()->bones.end() ) continue;
auto A = getKeyframeAt(f, 0.f); 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({ _frameInstances.insert({
frame, frame,
{ {
visible, visible
nullptr,
{}, {}
} }
}); });
} }
@ -105,8 +104,6 @@ void Animator::setFrameOrientation(ModelFrame *frame, const glm::quat &orientati
frame, frame,
{ {
true, true,
nullptr,
{}, {},
orientation orientation
} }
}); });
@ -122,14 +119,25 @@ glm::quat Animator::getFrameOrientation(ModelFrame *frame) const
return glm::toQuat(frame->getDefaultRotation()); 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) void Animator::tick(float dt)
{ {
if( model == nullptr || _animations.empty() ) { if( model == nullptr || _animations.empty() ) {
return; return;
} }
lastServerTime = serverTime; if( playing ) {
serverTime += dt; lastServerTime = serverTime;
serverTime += dt;
}
for( auto& p : _frameInstances ) { for( auto& p : _frameInstances ) {
p.second.second = p.second.first; p.second.second = p.second.first;
@ -203,21 +211,22 @@ AnimationKeyframe Animator::getKeyframeAt(ModelFrame *frame, float time) const
return it->second->getInterpolatedKeyframe(time); 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 glm::mat4 Animator::getFrameMatrix(ModelFrame *frame, float alpha, bool ignoreRoot) const
{ {
auto it = _frameInstances.find( frame ); auto it = _frameInstances.find( frame );
if( it != _frameInstances.end() && it->second.bone ) { if( it != _frameInstances.end() && it->second.bone ) {
const AnimationKeyframe& S = it->second.first; const AnimationKeyframe& F = it->second.first;
const AnimationKeyframe& F = it->second.second; const AnimationKeyframe& S = it->second.second;
AnimationKeyframe kf { AnimationKeyframe kf {
glm::slerp(F.rotation, S.rotation, alpha), glm::slerp(F.rotation, S.rotation, alpha),
glm::mix(F.position, S.position, alpha), glm::mix(F.position, S.position, alpha),
glm::mix(F.scale, S.scale, 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; glm::mat4 m;
@ -257,5 +266,7 @@ float Animator::getAnimationTime(float alpha) const
void Animator::setAnimationTime(float time) void Animator::setAnimationTime(float time)
{ {
lastServerTime = serverTime;
serverTime = time; serverTime = time;
} }

View File

@ -408,9 +408,6 @@ void GameWorld::doWeaponScan(const WeaponScan &scan)
di.hitpoints = scan.damage; di.hitpoints = scan.damage;
go->takeDamage(di); go->takeDamage(di);
} }
// Make the renderer draw a tracer.
renderer.addTracer(scan.center, hitEnd);
} }
} }

View File

@ -4,165 +4,179 @@
bool findKeyframes(float t, AnimationBone* bone, AnimationKeyframe& f1, AnimationKeyframe& f2, float& alpha) bool findKeyframes(float t, AnimationBone* bone, AnimationKeyframe& f1, AnimationKeyframe& f2, float& alpha)
{ {
for(size_t f = 0; f < bone->frames.size(); ++f ) { for(size_t f = 0; f < bone->frames.size(); ++f ) {
if( t <= bone->frames[f].starttime ) { if( t <= bone->frames[f].starttime ) {
f2 = bone->frames[f]; f2 = bone->frames[f];
if( f == 0 ) { if( f == 0 ) {
if( bone->frames.size() != 1 ) { if( bone->frames.size() != 1 ) {
f1 = bone->frames.back(); f1 = bone->frames.back();
} }
else { else {
f1 = f2; f1 = f2;
} }
} }
else { else {
f1 = bone->frames[f-1]; f1 = bone->frames[f-1];
} }
float tdiff = (f2.starttime - f1.starttime); float tdiff = (f2.starttime - f1.starttime);
if( tdiff == 0.f ) { if( tdiff == 0.f ) {
alpha = 1.f; alpha = 1.f;
} }
else { else {
alpha = glm::clamp((t - f1.starttime) / tdiff, 0.f, 1.f); alpha = glm::clamp((t - f1.starttime) / tdiff, 0.f, 1.f);
} }
return true; return true;
} }
} }
return false; return false;
} }
AnimationKeyframe AnimationBone::getInterpolatedKeyframe(float time) AnimationKeyframe AnimationBone::getInterpolatedKeyframe(float time)
{ {
AnimationKeyframe f1, f2; AnimationKeyframe f1, f2;
float alpha; float alpha;
if( findKeyframes(time, this, f1, f2, alpha) ) { if( findKeyframes(time, this, f1, f2, alpha) ) {
return { 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.position, f2.position, alpha),
glm::mix(f1.scale, f2.scale, alpha), glm::mix(f1.scale, f2.scale, alpha),
time time,
}; std::max(f1.id, f2.id)
} };
}
return frames.back(); 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) bool LoaderIFP::loadFromMemory(char *data)
{ {
size_t data_offs = 0; size_t data_offs = 0;
size_t* dataI = &data_offs; size_t* dataI = &data_offs;
ANPK* fileRoot = read<ANPK>(data, dataI); ANPK* fileRoot = read<ANPK>(data, dataI);
std::string listname = readString(data, dataI); std::string listname = readString(data, dataI);
for( int a = 0; a < fileRoot->info.entries; ++a ) { for( int a = 0; a < fileRoot->info.entries; ++a ) {
// something about a name? // something about a name?
/*NAME* n =*/ read<NAME>(data, dataI); /*NAME* n =*/ read<NAME>(data, dataI);
std::string animname = readString(data, dataI); std::string animname = readString(data, dataI);
Animation* animation = new Animation; Animation* animation = new Animation;
animation->duration = 0.f; animation->duration = 0.f;
animation->name = animname; animation->name = animname;
size_t animstart = data_offs + 8; size_t animstart = data_offs + 8;
DGAN* animroot = read<DGAN>(data, dataI); DGAN* animroot = read<DGAN>(data, dataI);
std::string infoname = readString(data, dataI); std::string infoname = readString(data, dataI);
for( int c = 0; c < animroot->info.entries; ++c ) { for( int c = 0; c < animroot->info.entries; ++c ) {
size_t start = data_offs; size_t start = data_offs;
CPAN* cpan = read<CPAN>(data, dataI); CPAN* cpan = read<CPAN>(data, dataI);
ANIM* frames = read<ANIM>(data, dataI); ANIM* frames = read<ANIM>(data, dataI);
AnimationBone* bonedata = new AnimationBone; AnimationBone* bonedata = new AnimationBone;
bonedata->name = frames->name; bonedata->name = frames->name;
bonedata->frames.reserve(frames->frames); bonedata->frames.reserve(frames->frames);
data_offs += ((8+frames->base.size) - sizeof(ANIM)); data_offs += ((8+frames->base.size) - sizeof(ANIM));
KFRM* frame = read<KFRM>(data, dataI); KFRM* frame = read<KFRM>(data, dataI);
std::string type(frame->base.magic, 4); std::string type(frame->base.magic, 4);
float time = 0.f; float time = 0.f;
if(type == "KR00") { if(type == "KR00") {
bonedata->type = AnimationBone::R00; bonedata->type = AnimationBone::R00;
for( int d = 0; d < frames->frames; ++d ) {
glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI));
time = *read<float>(data,dataI);
bonedata->frames.push_back({
q,
glm::vec3(0.f, 0.f, 0.f),
glm::vec3(1.f, 1.f, 1.f),
time
});
}
}
else if(type == "KRT0") {
bonedata->type = AnimationBone::RT0;
for( int d = 0; d < frames->frames; ++d ) {
glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI));
glm::vec3 p = *read<glm::vec3>(data, dataI);
time = *read<float>(data,dataI);
bonedata->frames.push_back({
q,
p,
glm::vec3(1.f, 1.f, 1.f),
time
});
}
}
else if(type == "KRTS") {
bonedata->type = AnimationBone::RTS;
for( int d = 0; d < frames->frames; ++d ) { for( int d = 0; d < frames->frames; ++d ) {
glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI)); glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI));
glm::vec3 p = *read<glm::vec3>(data, dataI); time = *read<float>(data,dataI);
glm::vec3 s = *read<glm::vec3>(data, dataI); bonedata->frames.push_back({
time = *read<float>(data,dataI); q,
bonedata->frames.push_back({ glm::vec3(0.f, 0.f, 0.f),
q, glm::vec3(1.f, 1.f, 1.f),
p, time,
s, d
time });
}); }
} }
} else if(type == "KRT0") {
bonedata->type = AnimationBone::RT0;
for( int d = 0; d < frames->frames; ++d ) {
glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI));
glm::vec3 p = *read<glm::vec3>(data, dataI);
time = *read<float>(data,dataI);
bonedata->frames.push_back({
q,
p,
glm::vec3(1.f, 1.f, 1.f),
time,
d
});
}
}
else if(type == "KRTS") {
bonedata->type = AnimationBone::RTS;
for( int d = 0; d < frames->frames; ++d ) {
glm::quat q = glm::conjugate(*read<glm::quat>(data, dataI));
glm::vec3 p = *read<glm::vec3>(data, dataI);
glm::vec3 s = *read<glm::vec3>(data, dataI);
time = *read<float>(data,dataI);
bonedata->frames.push_back({
q,
p,
s,
time,
d
});
}
}
bonedata->duration = time; bonedata->duration = time;
animation->duration = std::max(bonedata->duration, animation->duration); animation->duration = std::max(bonedata->duration, animation->duration);
data_offs = start + sizeof(CPAN) + cpan->base.size; data_offs = start + sizeof(CPAN) + cpan->base.size;
std::string framename(frames->name); std::string framename(frames->name);
std::transform(framename.begin(), framename.end(), framename.begin(), ::tolower ); std::transform(framename.begin(), framename.end(), framename.begin(), ::tolower );
animation->bones.insert({ animation->bones.insert({
framename, framename,
bonedata bonedata
}); });
} }
data_offs = animstart + animroot->base.size; data_offs = animstart + animroot->base.size;
std::transform(animname.begin(), animname.end(), animname.begin(), ::tolower ); std::transform(animname.begin(), animname.end(), animname.begin(), ::tolower );
animations.insert({ animname, animation }); animations.insert({ animname, animation });
} }
return true; return true;
} }
std::string LoaderIFP::readString(char *data, size_t *ofs) std::string LoaderIFP::readString(char *data, size_t *ofs)
{ {
size_t b = *ofs; size_t b = *ofs;
for(size_t o = *ofs; (o = *ofs);) { for(size_t o = *ofs; (o = *ofs);) {
*ofs += 4; *ofs += 4;
if(data[o+0] == 0) break; if(data[o+0] == 0) break;
if(data[o+1] == 0) break; if(data[o+1] == 0) break;
if(data[o+2] == 0) break; if(data[o+2] == 0) break;
if(data[o+3] == 0) break; if(data[o+3] == 0) break;
} }
return std::string(data+b); return std::string(data+b);
} }

View File

@ -47,6 +47,24 @@ DrawBuffer waterLQDraw;
GeometryBuffer waterHQBuffer; GeometryBuffer waterHQBuffer;
DrawBuffer waterHQDraw; 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 compileShader(GLenum type, const char *source)
{ {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
@ -103,6 +121,16 @@ GameRenderer::GameRenderer(GameWorld* engine)
glBindBufferBase(GL_UNIFORM_BUFFER, 1, uboScene); glBindBufferBase(GL_UNIFORM_BUFFER, 1, uboScene);
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboObject); 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, skyProgram = compileProgram(GameShaders::Sky::VertexShader,
GameShaders::Sky::FragmentShader); GameShaders::Sky::FragmentShader);
@ -189,6 +217,15 @@ GameRenderer::GameRenderer(GameWorld* engine)
glGenBuffers(1, &debugVBO); glGenBuffers(1, &debugVBO);
glGenTextures(1, &debugTex); glGenTextures(1, &debugTex);
glGenVertexArrays(1, &debugVAO); 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) 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); glDrawElements(GL_TRIANGLES, skydomeSegments * skydomeRows * 6, GL_UNSIGNED_SHORT, NULL);
// Draw bullets like this for now renderParticles();
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();
}
glUseProgram(0); glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -614,7 +626,72 @@ void GameRenderer::renderGeometry(Model* model, size_t g, const glm::mat4& model
{model, g, sg, modelMatrix, object} {model, g, sg, modelMatrix, object}
); );
} }
} }
}
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) bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix, GameObject* object, bool queueTransparent)
@ -804,5 +881,10 @@ void GameRenderer::renderPaths()
pedlines.clear(); pedlines.clear();
carlines.clear(); carlines.clear();
glBindVertexArray( 0 ); 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); * (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);
})";
} }