mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 11:52:40 +01:00
Animations working again
This commit is contained in:
parent
bd96b0b190
commit
52d90a9b26
@ -11,6 +11,7 @@
|
||||
class Animation;
|
||||
class AnimationBone;
|
||||
class Model;
|
||||
class ModelFrame;
|
||||
|
||||
/**
|
||||
* @brief The Animator class handles updating frame matricies for animations.
|
||||
@ -46,6 +47,14 @@ public:
|
||||
|
||||
void setModel(Model* model);
|
||||
|
||||
/**
|
||||
* @brief getFrameMatrix returns the matrix for frame at the current time
|
||||
* @param t
|
||||
* @param frame
|
||||
* @return
|
||||
*/
|
||||
glm::mat4 getFrameMatrix(ModelFrame* frame, float alpha) const;
|
||||
|
||||
/**
|
||||
* @brief tick Update animation paramters for server-side data.
|
||||
* @param dt
|
||||
@ -70,17 +79,12 @@ public:
|
||||
*/
|
||||
glm::quat getRootRotation() const;
|
||||
|
||||
/**
|
||||
* @brief getFrameMatrix returns the matrix for a given frame index.
|
||||
* @param frame
|
||||
* @return
|
||||
*/
|
||||
glm::mat4 getFrameMatrix(size_t frame) const;
|
||||
|
||||
/**
|
||||
* Returns true if the animation has finished playing.
|
||||
*/
|
||||
bool isCompleted() const;
|
||||
|
||||
float getAnimationTime(float alpha = 0.f) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -43,10 +43,11 @@ struct GameObject
|
||||
{
|
||||
Instance,
|
||||
Character,
|
||||
Vehicle
|
||||
Vehicle,
|
||||
Unknown
|
||||
};
|
||||
|
||||
virtual Type type() = 0;
|
||||
virtual Type type() { return Unknown; }
|
||||
|
||||
virtual void setPosition(const glm::vec3& pos);
|
||||
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
|
||||
void reset();
|
||||
void setTransform(const glm::mat4& m);
|
||||
const glm::mat4& getTransform() const { return matrix; }
|
||||
|
||||
void setName(const std::string& fname)
|
||||
{ name = fname; }
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <loaders/LoaderDFF.hpp>
|
||||
#include <loaders/LoaderIFP.hpp>
|
||||
#include <render/Model.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Animator::Animator()
|
||||
: animation(nullptr), model(nullptr), time(0.f), serverTime(0.f), lastServerTime(0.f), repeat(true)
|
||||
@ -53,11 +54,6 @@ void Animator::tick(float dt)
|
||||
|
||||
}
|
||||
|
||||
void Animator::render(float dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
glm::vec3 Animator::getRootTranslation() const
|
||||
{
|
||||
if( model && model->rootFrameIdx != -1 ) {
|
||||
@ -71,12 +67,40 @@ glm::quat Animator::getRootRotation() const
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
glm::mat4 Animator::getFrameMatrix(size_t frame) const
|
||||
glm::mat4 Animator::getFrameMatrix(ModelFrame* frame, float alpha) const
|
||||
{
|
||||
return glm::mat4();
|
||||
auto it = animation->bones.find(frame->getName());
|
||||
if(it != animation->bones.end()) {
|
||||
auto kf = it->second->getInterpolatedKeyframe(getAnimationTime(alpha));
|
||||
glm::mat4 m;
|
||||
if(it->second->type == AnimationBone::R00) {
|
||||
m = glm::translate(m, frame->getDefaultTranslation());
|
||||
m = m * glm::mat4_cast(kf.rotation);
|
||||
}
|
||||
else if(it->second->type == AnimationBone::RT0) {
|
||||
m = glm::mat4_cast(kf.rotation);
|
||||
m = glm::translate(m, kf.position);
|
||||
}
|
||||
else {
|
||||
m = glm::mat4_cast(kf.rotation);
|
||||
m = glm::translate(m, kf.position);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
else {
|
||||
return frame->getTransform();
|
||||
}
|
||||
}
|
||||
|
||||
bool Animator::isCompleted() const
|
||||
{
|
||||
return serverTime >= animation->duration;
|
||||
}
|
||||
}
|
||||
|
||||
float Animator::getAnimationTime(float alpha) const
|
||||
{
|
||||
if(repeat) {
|
||||
return fmod(serverTime + alpha, this->animation->duration);
|
||||
}
|
||||
return serverTime + alpha;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ bool findKeyframes(float t, AnimationBone* bone, AnimationKeyframe& f1, Animatio
|
||||
alpha = 1.f;
|
||||
}
|
||||
else {
|
||||
alpha = (t - f1.starttime) / tdiff;
|
||||
alpha = glm::clamp((t - f1.starttime) / tdiff, 0.f, 1.f);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -39,10 +39,9 @@ AnimationKeyframe AnimationBone::getInterpolatedKeyframe(float time)
|
||||
AnimationKeyframe f1, f2;
|
||||
float alpha;
|
||||
|
||||
if( findKeyframes(time, this, f1, f2, alpha) ) {
|
||||
if( findKeyframes(time, this, f1, f2, alpha) ) {
|
||||
return {
|
||||
(1.f - abs(glm::dot( f1.rotation, f2.rotation )) < 0.001f) ? f1.rotation
|
||||
: glm::normalize(glm::mix(f1.rotation, f2.rotation, alpha)),
|
||||
glm::normalize(glm::lerp(f1.rotation, f2.rotation, alpha)),
|
||||
glm::mix(f1.position, f2.position, alpha),
|
||||
glm::mix(f1.scale, f2.scale, alpha),
|
||||
time
|
||||
|
@ -444,7 +444,15 @@ void GTARenderer::renderGeometry(Model* model, size_t g, const glm::mat4& modelM
|
||||
|
||||
bool GTARenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix, GameObject* object, bool queueTransparent)
|
||||
{
|
||||
auto localmatrix = matrix * f->getMatrix();
|
||||
auto localmatrix = matrix;
|
||||
|
||||
if(object && object->animator) {
|
||||
localmatrix *= object->animator->getFrameMatrix(f, 0.f);
|
||||
}
|
||||
else {
|
||||
localmatrix *= f->getTransform();
|
||||
}
|
||||
|
||||
bool vis = object == nullptr || object->isFrameVisible(f);
|
||||
for(size_t g : f->getGeometries()) {
|
||||
if(!vis ) continue;
|
||||
@ -464,7 +472,7 @@ bool GTARenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
}
|
||||
|
||||
for(ModelFrame* c : f->getChildren()) {
|
||||
renderFrame(m, c, matrix, object, queueTransparent);
|
||||
renderFrame(m, c, localmatrix, object, queueTransparent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
35
rwviewer/AnimationListModel.cpp
Normal file
35
rwviewer/AnimationListModel.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "AnimationListModel.hpp"
|
||||
|
||||
QVariant AnimationListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if(role == Qt::DisplayRole) {
|
||||
if(index.row() < animations.size()) {
|
||||
auto& f = animations.at(index.row());
|
||||
if(index.column() == 0) {
|
||||
return QString(f.first.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
QVariant AnimationListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
||||
if(section == 0) {
|
||||
return "Name";
|
||||
}
|
||||
}
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
int AnimationListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return animations.size();
|
||||
}
|
||||
|
||||
int AnimationListModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
32
rwviewer/AnimationListModel.hpp
Normal file
32
rwviewer/AnimationListModel.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#ifndef _ANIMATIONLISTMODEL_HPP_
|
||||
#define _ANIMATIONLISTMODEL_HPP_
|
||||
#include <QAbstractItemModel>
|
||||
#include <loaders/LoaderIFP.hpp>
|
||||
|
||||
typedef std::vector<std::pair<std::string, Animation*>> AnimationList;
|
||||
|
||||
class AnimationListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
AnimationList animations;
|
||||
|
||||
public:
|
||||
|
||||
AnimationListModel(const AnimationList& anims, QObject* parent = 0)
|
||||
: QAbstractListModel(parent), animations(anims)
|
||||
{}
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
|
||||
const AnimationList& getAnimations() const { return animations; }
|
||||
};
|
||||
|
||||
#endif
|
48
rwviewer/AnimationListWidget.cpp
Normal file
48
rwviewer/AnimationListWidget.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "AnimationListWidget.hpp"
|
||||
#include <QVBoxLayout>
|
||||
|
||||
AnimationListWidget::AnimationListWidget(QWidget* parent, Qt::WindowFlags flags)
|
||||
: QDockWidget(parent, flags), filter(nullptr), model(nullptr)
|
||||
{
|
||||
setWindowTitle("Animations");
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
QWidget* intermediate = new QWidget();
|
||||
|
||||
searchbox = new QLineEdit();
|
||||
searchbox->setPlaceholderText("Search");
|
||||
|
||||
table = new QListView();
|
||||
layout->addWidget(searchbox);
|
||||
layout->addWidget(table);
|
||||
intermediate->setLayout(layout);
|
||||
setWidget(intermediate);
|
||||
|
||||
filter = new QSortFilterProxyModel;
|
||||
table->setModel(filter);
|
||||
connect(table->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(selectedIndexChanged(QModelIndex)));
|
||||
connect(searchbox, SIGNAL(textChanged(QString)), SLOT(setFilter(QString)));
|
||||
}
|
||||
|
||||
void AnimationListWidget::setAnimations(const AnimationList& archive)
|
||||
{
|
||||
auto m = new AnimationListModel(archive);
|
||||
filter->setSourceModel(m);
|
||||
if(model) {
|
||||
delete model;
|
||||
}
|
||||
model = m;
|
||||
}
|
||||
|
||||
void AnimationListWidget::selectedIndexChanged(const QModelIndex& current)
|
||||
{
|
||||
auto mts = filter->mapToSource(current);
|
||||
if(mts.row() < model->getAnimations().size()) {
|
||||
auto& f = model->getAnimations().at(mts.row());
|
||||
emit selectedAnimationChanged(f.second);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationListWidget::setFilter(const QString &f)
|
||||
{
|
||||
filter->setFilterRegExp(QRegExp(f, Qt::CaseInsensitive));
|
||||
}
|
35
rwviewer/AnimationListWidget.hpp
Normal file
35
rwviewer/AnimationListWidget.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#ifndef _ANIMATIONLISTWIDGET_HPP_
|
||||
#define _ANIMATIONLISTWIDGET_HPP_
|
||||
#include <QDockWidget>
|
||||
#include <QListView>
|
||||
#include <QLineEdit>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include "AnimationListModel.hpp"
|
||||
|
||||
class AnimationListWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QSortFilterProxyModel* filter;
|
||||
AnimationListModel* model;
|
||||
QListView* table;
|
||||
QLineEdit* searchbox;
|
||||
|
||||
public:
|
||||
|
||||
AnimationListWidget(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
|
||||
void setAnimations(const AnimationList& anims);
|
||||
|
||||
signals:
|
||||
|
||||
void selectedAnimationChanged(Animation* anim);
|
||||
|
||||
public slots:
|
||||
void selectedIndexChanged(const QModelIndex& current);
|
||||
|
||||
void setFilter(const QString& f);
|
||||
};
|
||||
|
||||
#endif
|
@ -10,7 +10,9 @@ add_executable(rwviewer
|
||||
IMGArchiveModel.cpp
|
||||
ArchiveContentsWidget.cpp
|
||||
DFFFramesTreeModel.cpp
|
||||
ModelFramesWidget.cpp)
|
||||
ModelFramesWidget.cpp
|
||||
AnimationListModel.cpp
|
||||
AnimationListWidget.cpp)
|
||||
|
||||
include_directories("${CMAKE_SOURCE_DIR}/rwengine/include" /usr/include/bullet)
|
||||
|
||||
|
@ -2,10 +2,13 @@
|
||||
#include <render/Model.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <QMouseEvent>
|
||||
#include <engine/GameObject.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
|
||||
ViewerWidget::ViewerWidget(QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f)
|
||||
: QGLWidget(parent, shareWidget, f), gworld(nullptr), cmodel(nullptr),
|
||||
viewDistance(1.f), dragging(false), fm(ViewerWidget::UNK)
|
||||
: QGLWidget(parent, shareWidget, f), gworld(nullptr), dummyObject(nullptr),
|
||||
cmodel(nullptr), canimation(nullptr), viewDistance(1.f), dragging(false),
|
||||
fm(ViewerWidget::UNK)
|
||||
{
|
||||
}
|
||||
|
||||
@ -41,6 +44,10 @@ void ViewerWidget::paintGL()
|
||||
r.camera.frustum.near = 0.1f;
|
||||
r.camera.frustum.fov = 60.f;
|
||||
r.camera.frustum.aspectRatio = width()/(height()*1.f);
|
||||
|
||||
if(dummyObject && dummyObject->animator) {
|
||||
dummyObject->animator->tick(1.f/60.f);
|
||||
}
|
||||
|
||||
if(cmodel) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@ -64,7 +71,7 @@ void ViewerWidget::paintGL()
|
||||
glUniformMatrix4fv(r.uniView, 1, GL_FALSE, glm::value_ptr(view));
|
||||
glUniformMatrix4fv(r.uniProj, 1, GL_FALSE, glm::value_ptr(proj));
|
||||
|
||||
gworld->renderer.renderModel(cmodel, m);
|
||||
gworld->renderer.renderModel(cmodel, m, dummyObject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +103,9 @@ void ViewerWidget::showDFF(const QString& file)
|
||||
auto mit = gworld->gameData.models.find(basename.toStdString());
|
||||
if(mit != gworld->gameData.models.end()) {
|
||||
// TODO better error handling
|
||||
if(dummyObject) delete dummyObject;
|
||||
cmodel = mit->second;
|
||||
dummyObject = new GameObject(gworld, glm::vec3(), glm::quat(), cmodel);
|
||||
float radius = 0.f;
|
||||
for(auto& g
|
||||
: cmodel->geometries) {
|
||||
@ -115,6 +124,18 @@ void ViewerWidget::showTXD(const QString& file)
|
||||
fm = ViewerWidget::TXD;
|
||||
}
|
||||
|
||||
void ViewerWidget::showAnimation(Animation *anim)
|
||||
{
|
||||
canimation = anim;
|
||||
if(dummyObject) {
|
||||
if(dummyObject->animator == nullptr) {
|
||||
dummyObject->animator = new Animator;
|
||||
dummyObject->animator->setModel(dummyObject->model);
|
||||
}
|
||||
dummyObject->animator->setAnimation(canimation);
|
||||
}
|
||||
}
|
||||
|
||||
Model* ViewerWidget::currentModel() const
|
||||
{
|
||||
return cmodel;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <QGLWidget>
|
||||
#include <QTimer>
|
||||
#include <loaders/LoaderIFP.hpp>
|
||||
|
||||
class Model;
|
||||
class ViewerWidget : public QGLWidget
|
||||
@ -15,8 +16,11 @@ class ViewerWidget : public QGLWidget
|
||||
|
||||
QTimer timer;
|
||||
GameWorld* gworld;
|
||||
|
||||
GameObject* dummyObject;
|
||||
|
||||
Model* cmodel;
|
||||
Animation* canimation;
|
||||
|
||||
float viewDistance;
|
||||
glm::vec2 viewAngles;
|
||||
@ -50,6 +54,8 @@ public slots:
|
||||
void showDFF(const QString& file);
|
||||
void showTXD(const QString& file);
|
||||
|
||||
void showAnimation(Animation* anim);
|
||||
|
||||
signals:
|
||||
|
||||
void fileOpened(const QString& file);
|
||||
@ -65,4 +71,4 @@ private:
|
||||
FileMode fm;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -3,10 +3,12 @@
|
||||
#include "ViewerWidget.hpp"
|
||||
#include "ArchiveContentsWidget.hpp"
|
||||
#include "ModelFramesWidget.hpp"
|
||||
#include "AnimationListWidget.hpp"
|
||||
#include <QMenuBar>
|
||||
#include <QFileDialog>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <fstream>
|
||||
|
||||
static int MaxRecentArchives = 5;
|
||||
|
||||
@ -24,6 +26,10 @@ ViewerWindow::ViewerWindow(QWidget* parent, Qt::WindowFlags flags): QMainWindow(
|
||||
frameswidget = new ModelFramesWidget;
|
||||
frameswidget->setObjectName("frameswidget");
|
||||
this->addDockWidget(Qt::RightDockWidgetArea, frameswidget);
|
||||
|
||||
animationswidget = new AnimationListWidget;
|
||||
animationswidget->setObjectName("animationswidget");
|
||||
this->addDockWidget(Qt::RightDockWidgetArea, animationswidget);
|
||||
|
||||
QMenuBar* mb = this->menuBar();
|
||||
QMenu* file = mb->addMenu("&File");
|
||||
@ -38,8 +44,12 @@ ViewerWindow::ViewerWindow(QWidget* parent, Qt::WindowFlags flags): QMainWindow(
|
||||
auto ex = file->addAction("E&xit");
|
||||
ex->setShortcut(QKeySequence::Quit);
|
||||
connect(ex, SIGNAL(triggered()), QApplication::instance(), SLOT(closeAllWindows()));
|
||||
|
||||
QMenu* anim = mb->addMenu("&Animation");
|
||||
anim->addAction("Load &Animations", this, SLOT(openAnimations()));
|
||||
|
||||
connect(archivewidget, SIGNAL(selectedFileChanged(QString)), viewer, SLOT(showFile(QString)));
|
||||
connect(animationswidget, SIGNAL(selectedAnimationChanged(Animation*)), viewer, SLOT(showAnimation(Animation*)));
|
||||
connect(viewer, SIGNAL(fileOpened(QString)), SLOT(openFileChanged(QString)));
|
||||
|
||||
updateRecentArchives();
|
||||
@ -97,6 +107,35 @@ void ViewerWindow::openArchive()
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWindow::openAnimations()
|
||||
{
|
||||
QFileDialog dialog(this, "Open Animations", QDir::homePath(), "IFP Animations (*.ifp)");
|
||||
if(dialog.exec()) {
|
||||
std::ifstream dfile(dialog.selectedFiles().at(0).toStdString().c_str());
|
||||
AnimationList anims;
|
||||
|
||||
if(dfile.is_open())
|
||||
{
|
||||
dfile.seekg(0, std::ios_base::end);
|
||||
size_t length = dfile.tellg();
|
||||
dfile.seekg(0);
|
||||
char *file = new char[length];
|
||||
dfile.read(file, length);
|
||||
|
||||
LoaderIFP loader;
|
||||
if( loader.loadFromMemory(file) ) {
|
||||
for(auto& f : loader.animations) {
|
||||
anims.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] file;
|
||||
}
|
||||
|
||||
animationswidget->setAnimations(anims);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWindow::openFileChanged(const QString& name)
|
||||
{
|
||||
setWindowTitle(name);
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
class ModelFramesWidget;
|
||||
class ArchiveContentsWidget;
|
||||
class AnimationListWidget;
|
||||
class ViewerWidget;
|
||||
|
||||
class ViewerWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -13,6 +15,7 @@ class ViewerWindow : public QMainWindow
|
||||
ViewerWidget* viewer;
|
||||
ArchiveContentsWidget* archivewidget;
|
||||
ModelFramesWidget* frameswidget;
|
||||
AnimationListWidget* animationswidget;
|
||||
public:
|
||||
|
||||
ViewerWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
@ -27,6 +30,8 @@ public slots:
|
||||
|
||||
void openArchive();
|
||||
|
||||
void openAnimations();
|
||||
|
||||
private slots:
|
||||
|
||||
void openFileChanged(const QString& name);
|
||||
@ -39,4 +44,4 @@ private:
|
||||
void updateRecentArchives();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user