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;
|
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)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,6 +107,7 @@ 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;
|
||||||
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
})";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user