mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-15 15:02:34 +02:00
Merge pull request #262 from danhedron/dff-improvement
Overhaul Clump, object and rendering Implementation
This commit is contained in:
commit
1df0ab7c17
@ -41,8 +41,6 @@ set(RWENGINE_SOURCES
|
||||
src/data/PathData.hpp
|
||||
src/data/PedData.cpp
|
||||
src/data/PedData.hpp
|
||||
src/data/Skeleton.cpp
|
||||
src/data/Skeleton.hpp
|
||||
src/data/WeaponData.hpp
|
||||
src/data/ZoneData.hpp
|
||||
src/dynamics/CollisionInstance.cpp
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <ai/CharacterController.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
@ -1 +1,25 @@
|
||||
#include "data/ModelData.hpp"
|
||||
|
||||
void SimpleModelInfo::setupBigBuilding(const ModelInfoTable& models) {
|
||||
if (loddistances_[0] > 300.f && atomics_[2] == nullptr) {
|
||||
isbigbuilding_ = true;
|
||||
findRelatedModel(models);
|
||||
if (related_) {
|
||||
loddistances_[2] = related_->getLargestLodDistance();
|
||||
} else {
|
||||
loddistances_[2] = 100.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleModelInfo::findRelatedModel(const ModelInfoTable& models) {
|
||||
for (const auto& model : models) {
|
||||
if (model.second.get() == this) continue;
|
||||
const auto& othername = model.second->name;
|
||||
if (othername.size() <= 3 || name.size() <= 3) continue;
|
||||
if (othername.substr(3) == name.substr(3)) {
|
||||
related_ = static_cast<SimpleModelInfo*>(model.second.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <data/CollisionModel.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/PathData.hpp>
|
||||
#include <rw/defines.hpp>
|
||||
#ifdef RW_WINDOWS
|
||||
@ -108,6 +108,9 @@ private:
|
||||
std::unique_ptr<CollisionModel> collision;
|
||||
};
|
||||
|
||||
using ModelInfoTable =
|
||||
std::unordered_map<ModelID, std::unique_ptr<BaseModelInfo>>;
|
||||
|
||||
/**
|
||||
* Model data for simple types
|
||||
*
|
||||
@ -121,8 +124,6 @@ public:
|
||||
int timeOn = 0;
|
||||
int timeOff = 24;
|
||||
int flags;
|
||||
/// @todo Remove this?
|
||||
bool LOD = false;
|
||||
/// Information loaded from PATH sections
|
||||
/// @todo remove this from here too :)
|
||||
std::vector<PathData> paths;
|
||||
@ -133,18 +134,19 @@ public:
|
||||
}
|
||||
|
||||
/// @todo change with librw
|
||||
void setAtomic(Model* model, int n, ModelFrame* atomic) {
|
||||
void setAtomic(Clump* model, int n, AtomicPtr atomic) {
|
||||
model_ = model;
|
||||
/// @todo disassociated the Atomic from Clump
|
||||
atomics_[n] = atomic;
|
||||
}
|
||||
|
||||
/// @todo remove this
|
||||
Model* getModel() const {
|
||||
Clump* getModel() const {
|
||||
return model_;
|
||||
}
|
||||
|
||||
ModelFrame* getAtomic(int n) const {
|
||||
return atomics_[n];
|
||||
Atomic* getAtomic(int n) const {
|
||||
return atomics_[n].get();
|
||||
}
|
||||
|
||||
void setLodDistance(int n, float d) {
|
||||
@ -157,6 +159,15 @@ public:
|
||||
return loddistances_[n];
|
||||
}
|
||||
|
||||
Atomic* getDistanceAtomic(float d) {
|
||||
for (auto i = 0; i < getNumAtomics(); ++i) {
|
||||
if (d < loddistances_[i]) {
|
||||
return atomics_[i].get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setNumAtomics(int num) {
|
||||
numatomics_ = num;
|
||||
}
|
||||
@ -203,12 +214,49 @@ public:
|
||||
NO_ZBUFFER_WRITE = 1 << 6,
|
||||
};
|
||||
|
||||
// Set up data for big building objects
|
||||
void setupBigBuilding(const ModelInfoTable& models);
|
||||
bool isBigBuilding() const {
|
||||
return isbigbuilding_;
|
||||
}
|
||||
|
||||
void findRelatedModel(const ModelInfoTable& models);
|
||||
|
||||
float getLargestLodDistance() const {
|
||||
return furthest_ != 0 ? loddistances_[furthest_ - 1]
|
||||
: loddistances_[numatomics_ - 1];
|
||||
}
|
||||
|
||||
float getNearLodDistance() const {
|
||||
return loddistances_[2];
|
||||
}
|
||||
|
||||
void determineFurthest() {
|
||||
furthest_ = 0;
|
||||
if (numatomics_ == 2) {
|
||||
furthest_ = loddistances_[0] >= loddistances_[1] ? 1 : 0;
|
||||
}
|
||||
if (numatomics_ == 3) {
|
||||
furthest_ = loddistances_[0] >= loddistances_[1]
|
||||
? 1
|
||||
: loddistances_[1] >= loddistances_[2] ? 2 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleModelInfo* related() const {
|
||||
return related_;
|
||||
}
|
||||
|
||||
private:
|
||||
Model* model_ = nullptr;
|
||||
ModelFrame* atomics_[3] = {};
|
||||
Clump* model_ = nullptr;
|
||||
std::array<AtomicPtr, 3> atomics_;
|
||||
float loddistances_[3] = {};
|
||||
uint8_t numatomics_ = 0;
|
||||
uint8_t alpha_ = 0; /// @todo ask aap why
|
||||
bool isbigbuilding_ = 0;
|
||||
uint8_t furthest_ = 0;
|
||||
|
||||
SimpleModelInfo* related_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -231,11 +279,11 @@ public:
|
||||
ClumpModelInfo(ModelDataType type) : BaseModelInfo(type) {
|
||||
}
|
||||
|
||||
void setModel(Model* model) {
|
||||
void setModel(Clump* model) {
|
||||
model_ = model;
|
||||
}
|
||||
|
||||
Model* getModel() const {
|
||||
Clump* getModel() const {
|
||||
return model_;
|
||||
}
|
||||
|
||||
@ -249,7 +297,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Model* model_ = nullptr;
|
||||
Clump* model_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,100 +0,0 @@
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Skeleton::FrameTransform Skeleton::IdentityTransform = {glm::vec3(0.f),
|
||||
glm::quat()};
|
||||
Skeleton::FrameData Skeleton::IdentityData = {
|
||||
Skeleton::IdentityTransform, Skeleton::IdentityTransform, true};
|
||||
|
||||
Skeleton::Skeleton() {
|
||||
}
|
||||
|
||||
void Skeleton::setAllData(const Skeleton::FramesData& data) {
|
||||
framedata = data;
|
||||
}
|
||||
|
||||
const Skeleton::FrameData& Skeleton::getData(unsigned int frameIdx) const {
|
||||
auto fdit = framedata.find(frameIdx);
|
||||
if (fdit == framedata.end()) {
|
||||
return Skeleton::IdentityData;
|
||||
}
|
||||
|
||||
return fdit->second;
|
||||
}
|
||||
|
||||
void Skeleton::setData(unsigned int frameIdx, const Skeleton::FrameData& data) {
|
||||
framedata[frameIdx] = data;
|
||||
}
|
||||
|
||||
void Skeleton::setEnabled(ModelFrame* frame, bool enabled) {
|
||||
auto fdit = framedata.find(frame->getIndex());
|
||||
if (fdit != framedata.end()) {
|
||||
fdit->second.enabled = enabled;
|
||||
} else {
|
||||
FrameTransform tf{frame->getDefaultTranslation(),
|
||||
glm::quat_cast(frame->getDefaultRotation())};
|
||||
framedata.insert({frame->getIndex(), {tf, tf, enabled}});
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::setEnabled(unsigned int frameIdx, bool enabled) {
|
||||
auto fdit = framedata.find(frameIdx);
|
||||
if (fdit == framedata.end()) {
|
||||
framedata.insert({frameIdx,
|
||||
{Skeleton::IdentityTransform,
|
||||
Skeleton::IdentityTransform, enabled}});
|
||||
} else {
|
||||
fdit->second.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
const Skeleton::FrameTransform& Skeleton::getInterpolated(
|
||||
unsigned int frameIdx) const {
|
||||
auto itit = interpolateddata.find(frameIdx);
|
||||
if (itit == interpolateddata.end()) {
|
||||
return Skeleton::IdentityTransform;
|
||||
}
|
||||
|
||||
return itit->second;
|
||||
}
|
||||
|
||||
void Skeleton::interpolate(float alpha) {
|
||||
interpolateddata.clear();
|
||||
|
||||
for (auto i = framedata.begin(); i != framedata.end(); ++i) {
|
||||
auto& t2 = i->second.a.translation;
|
||||
auto& t1 = i->second.b.translation;
|
||||
|
||||
auto& r2 = i->second.a.rotation;
|
||||
auto& r1 = i->second.b.rotation;
|
||||
|
||||
interpolateddata.insert(
|
||||
{i->first, {glm::mix(t1, t2, alpha), glm::slerp(r1, r2, alpha)}});
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 Skeleton::getMatrix(unsigned int frameIdx) const {
|
||||
const FrameTransform& ft = getInterpolated(frameIdx);
|
||||
|
||||
glm::mat4 m;
|
||||
|
||||
m = glm::translate(m, ft.translation);
|
||||
m = m * glm::mat4_cast(ft.rotation);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
glm::mat4 Skeleton::getMatrix(ModelFrame* frame) const {
|
||||
auto itit = interpolateddata.find(frame->getIndex());
|
||||
if (itit != interpolateddata.end()) {
|
||||
glm::mat4 m;
|
||||
|
||||
m = glm::translate(m, itit->second.translation);
|
||||
m = m * glm::mat4_cast(itit->second.rotation);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
return frame->getTransform();
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#ifndef _SKELETON_HPP_
|
||||
#define _SKELETON_HPP_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class ModelFrame;
|
||||
/**
|
||||
* Data class for additional frame transformation and meta data.
|
||||
*
|
||||
* Provides interfaces to modify and query the visibility of model frames,
|
||||
* as well as their transformation. Modified by Animator to animate models.
|
||||
*/
|
||||
class Skeleton {
|
||||
public:
|
||||
struct FrameTransform {
|
||||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
};
|
||||
|
||||
static FrameTransform IdentityTransform;
|
||||
|
||||
struct FrameData {
|
||||
FrameTransform a;
|
||||
FrameTransform b;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static FrameData IdentityData;
|
||||
|
||||
typedef std::map<unsigned int, FrameData> FramesData;
|
||||
typedef std::map<unsigned int, FrameTransform> TransformData;
|
||||
|
||||
Skeleton();
|
||||
|
||||
void setAllData(const FramesData& data);
|
||||
|
||||
const FrameData& getData(unsigned int frameIdx) const;
|
||||
|
||||
void setData(unsigned int frameIdx, const FrameData& data);
|
||||
void setEnabled(ModelFrame* frame, bool enabled);
|
||||
|
||||
void setEnabled(unsigned int frameIdx, bool enabled);
|
||||
|
||||
const FrameTransform& getInterpolated(unsigned int frameIdx) const;
|
||||
|
||||
glm::mat4 getMatrix(unsigned int frameIdx) const;
|
||||
glm::mat4 getMatrix(ModelFrame* frame) const;
|
||||
|
||||
void interpolate(float alpha);
|
||||
|
||||
private:
|
||||
FramesData framedata;
|
||||
TransformData interpolateddata;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,11 +1,10 @@
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <loaders/LoaderDFF.hpp>
|
||||
#include <queue>
|
||||
|
||||
Animator::Animator(Model* model, Skeleton* skeleton)
|
||||
: model(model), skeleton(skeleton) {
|
||||
Animator::Animator(Clump* model) : model(model) {
|
||||
}
|
||||
|
||||
void Animator::tick(float dt) {
|
||||
@ -18,21 +17,21 @@ void Animator::tick(float dt) {
|
||||
glm::quat rotation;
|
||||
};
|
||||
|
||||
#if 0
|
||||
// Blend all active animations together
|
||||
std::map<unsigned int, BoneTransform> blendFrames;
|
||||
std::map<ModelFrame*, BoneTransform> blendFrames;
|
||||
#endif
|
||||
|
||||
for (AnimationState& state : animations) {
|
||||
RW_CHECK(state.animation != nullptr,
|
||||
"AnimationState with no animation");
|
||||
if (state.animation == nullptr) continue;
|
||||
|
||||
if (state.boneInstances.size() == 0) {
|
||||
for (unsigned int f = 0; f < model->frames.size(); ++f) {
|
||||
auto bit =
|
||||
state.animation->bones.find(model->frames[f]->getName());
|
||||
if (bit != state.animation->bones.end()) {
|
||||
state.boneInstances.insert({bit->second, {f}});
|
||||
if (state.boneInstances.empty()) {
|
||||
for (const auto& bone : state.animation->bones) {
|
||||
auto frame = model->findFrame(bone.first);
|
||||
if (!frame) {
|
||||
continue;
|
||||
}
|
||||
state.boneInstances.insert({bone.second, frame});
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,23 +71,20 @@ void Animator::tick(float dt) {
|
||||
blendFrames[b.second.frameIndex] = xform;
|
||||
}
|
||||
#else
|
||||
blendFrames[b.second.frameIndex] = xform;
|
||||
b.second->setTranslation(b.second->getDefaultTranslation() +
|
||||
xform.translation);
|
||||
b.second->setRotation(glm::mat3_cast(xform.rotation));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (auto& p : blendFrames) {
|
||||
auto& data = skeleton->getData(p.first);
|
||||
Skeleton::FrameData fd;
|
||||
fd.b = data.a;
|
||||
fd.enabled = data.enabled;
|
||||
|
||||
fd.a.translation = model->frames[p.first]->getDefaultTranslation() +
|
||||
p.second.translation;
|
||||
fd.a.rotation = p.second.rotation;
|
||||
|
||||
skeleton->setData(p.first, fd);
|
||||
p.first->setTranslation(p.first->getDefaultTranslation() +
|
||||
p.second.translation);
|
||||
p.first->setRotation(glm::mat3_cast(p.second.rotation));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Animator::isCompleted(unsigned int slot) const {
|
||||
|
@ -8,11 +8,9 @@
|
||||
#include <map>
|
||||
#include <rw/defines.hpp>
|
||||
|
||||
class Model;
|
||||
class Clump;
|
||||
class ModelFrame;
|
||||
|
||||
class Skeleton;
|
||||
|
||||
/**
|
||||
* @brief calculates animation frame matrices, as well as procedural frame
|
||||
* animation.
|
||||
@ -24,13 +22,6 @@ class Skeleton;
|
||||
* The Animator will blend all active animations together.
|
||||
*/
|
||||
class Animator {
|
||||
/**
|
||||
* @brief Stores data required to animate a model frame
|
||||
*/
|
||||
struct BoneInstanceData {
|
||||
unsigned int frameIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The AnimationState struct stores information about playing
|
||||
* animations
|
||||
@ -43,18 +34,13 @@ class Animator {
|
||||
float speed;
|
||||
/// Automatically restart
|
||||
bool repeat;
|
||||
std::map<AnimationBone*, BoneInstanceData> boneInstances;
|
||||
std::map<AnimationBone*, ModelFrame*> boneInstances;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief model The model being animated.
|
||||
*/
|
||||
Model* model;
|
||||
|
||||
/**
|
||||
* @brief Skeleton instance.
|
||||
*/
|
||||
Skeleton* skeleton;
|
||||
Clump* model;
|
||||
|
||||
/**
|
||||
* @brief Currently playing animations
|
||||
@ -62,7 +48,7 @@ class Animator {
|
||||
std::vector<AnimationState> animations;
|
||||
|
||||
public:
|
||||
Animator(Model* model, Skeleton* skeleton);
|
||||
Animator(Clump* model);
|
||||
|
||||
Animation* getAnimation(unsigned int slot) {
|
||||
if (slot < animations.size()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/ModelData.hpp>
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
@ -115,6 +115,13 @@ void GameData::loadLevelFile(const std::string& path) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& model : modelinfo) {
|
||||
if (model.second->type() == ModelDataType::SimpleInfo) {
|
||||
auto simple = static_cast<SimpleModelInfo*>(model.second.get());
|
||||
simple->setupBigBuilding(modelinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::loadIDE(const std::string& path) {
|
||||
@ -358,12 +365,12 @@ TextureArchive GameData::loadTextureArchive(const std::string& name) {
|
||||
void GameData::getNameAndLod(std::string& name, int& lod) {
|
||||
auto lodpos = name.rfind("_l");
|
||||
if (lodpos != std::string::npos) {
|
||||
lod = std::atoi(name.substr(lodpos + 1).c_str());
|
||||
lod = std::atoi(name.substr(lodpos + 2).c_str());
|
||||
name = name.substr(0, lodpos);
|
||||
}
|
||||
}
|
||||
|
||||
Model* GameData::loadClump(const std::string& name) {
|
||||
Clump* GameData::loadClump(const std::string& name) {
|
||||
auto file = index.openFile(name);
|
||||
if (!file) {
|
||||
logger->error("Data", "Failed to load model " + name);
|
||||
@ -390,9 +397,9 @@ void GameData::loadModelFile(const std::string& name) {
|
||||
}
|
||||
|
||||
// Associate the frames with models.
|
||||
for (auto& frame : m->frames) {
|
||||
for (const auto& atomic : m->getAtomics()) {
|
||||
/// @todo this is useful elsewhere, please move elsewhere
|
||||
std::string name = frame->getName();
|
||||
std::string name = atomic->getFrame()->getName();
|
||||
int lod = 0;
|
||||
getNameAndLod(name, lod);
|
||||
for (auto& model : modelinfo) {
|
||||
@ -402,7 +409,9 @@ void GameData::loadModelFile(const std::string& name) {
|
||||
}
|
||||
if (boost::iequals(info->name, name)) {
|
||||
auto simple = static_cast<SimpleModelInfo*>(info);
|
||||
simple->setAtomic(m, lod, frame);
|
||||
simple->setAtomic(m, lod, atomic);
|
||||
auto identity = std::make_shared<ModelFrame>();
|
||||
atomic->setFrame(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -458,11 +467,13 @@ void GameData::loadModel(ModelID model) {
|
||||
if (isSimple) {
|
||||
auto simple = static_cast<SimpleModelInfo*>(info);
|
||||
// Associate atomics
|
||||
for (auto& frame : m->frames) {
|
||||
auto name = frame->getName();
|
||||
for (auto& atomic : m->getAtomics()) {
|
||||
auto name = atomic->getFrame()->getName();
|
||||
int lod = 0;
|
||||
getNameAndLod(name, lod);
|
||||
simple->setAtomic(m, lod, frame);
|
||||
simple->setAtomic(m, lod, atomic);
|
||||
auto identity = std::make_shared<ModelFrame>();
|
||||
atomic->setFrame(identity);
|
||||
}
|
||||
} else {
|
||||
// Associate clumps
|
||||
|
@ -133,7 +133,7 @@ public:
|
||||
/**
|
||||
* Loads an archived model and returns it directly
|
||||
*/
|
||||
Model* loadClump(const std::string& name);
|
||||
Clump* loadClump(const std::string& name);
|
||||
|
||||
/**
|
||||
* Loads a DFF and associates its atomics with models.
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
#include <ai/DefaultAIController.hpp>
|
||||
#include <ai/TrafficDirector.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <loaders/LoaderIDE.hpp>
|
||||
#include <loaders/LoaderIPL.hpp>
|
||||
@ -124,20 +124,6 @@ bool GameWorld::placeItems(const std::string& name) {
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to Associate LODs.
|
||||
for (auto& p : instancePool.objects) {
|
||||
auto object = p.second;
|
||||
InstanceObject* instance = static_cast<InstanceObject*>(object);
|
||||
auto modelinfo = instance->getModelInfo<SimpleModelInfo>();
|
||||
if (!modelinfo->LOD && modelinfo->name.length() > 3) {
|
||||
auto lodInstit =
|
||||
modelInstances.find("LOD" + modelinfo->name.substr(3));
|
||||
if (lodInstit != modelInstances.end()) {
|
||||
instance->LODinstance = lodInstit->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
logger->error("Data", "Failed to load IPL " + path);
|
||||
@ -170,8 +156,8 @@ InstanceObject* GameWorld::createInstance(const uint16_t id,
|
||||
"World", "Instance with missing model: " + std::to_string(id));
|
||||
}
|
||||
|
||||
auto instance = new InstanceObject(
|
||||
this, pos, rot, glm::vec3(1.f, 1.f, 1.f), oi, nullptr, dydata);
|
||||
auto instance =
|
||||
new InstanceObject(this, pos, rot, glm::vec3(1.f), oi, dydata);
|
||||
|
||||
instancePool.insert(instance);
|
||||
allObjects.push_back(instance);
|
||||
@ -280,18 +266,23 @@ VehicleObject* GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos,
|
||||
|
||||
auto model = vti->getModel();
|
||||
auto info = data->vehicleInfo.find(vti->handling_);
|
||||
if (model && info != data->vehicleInfo.end()) {
|
||||
if (info->second->wheels.size() == 0 &&
|
||||
info->second->seats.size() == 0) {
|
||||
for (const ModelFrame* f : model->frames) {
|
||||
const std::string& name = f->getName();
|
||||
if (model && info != data->vehicleInfo.end() &&
|
||||
info->second->wheels.size() == 0 && info->second->seats.size() == 0) {
|
||||
auto root = model->getFrame();
|
||||
for (const auto& frame : root->getChildren()) {
|
||||
const std::string& name = frame->getName();
|
||||
|
||||
if (name.size() > 5 && name.substr(0, 5) == "wheel") {
|
||||
auto frameTrans = f->getMatrix();
|
||||
info->second->wheels.push_back({glm::vec3(frameTrans[3])});
|
||||
}
|
||||
if (name == "ped_frontseat") {
|
||||
auto p = f->getDefaultTranslation();
|
||||
if (name.size() > 5 && name.substr(0, 5) == "wheel") {
|
||||
const auto& frameTrans = frame->getWorldTransform();
|
||||
info->second->wheels.push_back({glm::vec3(frameTrans[3])});
|
||||
}
|
||||
|
||||
if (name == "chassis_dummy") {
|
||||
// These are nested within chassis_dummy
|
||||
auto frontseat = frame->findDescendant("ped_frontseat");
|
||||
auto backseat = frame->findDescendant("ped_backseat");
|
||||
if (frontseat) {
|
||||
auto p = frontseat->getDefaultTranslation();
|
||||
// Left seat
|
||||
p.x = -p.x;
|
||||
info->second->seats.front.push_back({p});
|
||||
@ -299,10 +290,10 @@ VehicleObject* GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos,
|
||||
p.x = -p.x;
|
||||
info->second->seats.front.push_back({p});
|
||||
}
|
||||
if (name == "ped_backseat") {
|
||||
if (backseat) {
|
||||
// @todo how does this work for the barracks, ambulance
|
||||
// or coach?
|
||||
auto p = f->getDefaultTranslation();
|
||||
auto p = backseat->getDefaultTranslation();
|
||||
// Left seat
|
||||
p.x = -p.x;
|
||||
info->second->seats.back.push_back({p});
|
||||
|
@ -1,23 +1,14 @@
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <items/Weapon.hpp>
|
||||
#include <objects/ProjectileObject.hpp>
|
||||
|
||||
void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) {
|
||||
auto handFrame = owner->getModel()->findFrame("srhand");
|
||||
glm::mat4 handMatrix;
|
||||
if (handFrame) {
|
||||
while (handFrame->getParent()) {
|
||||
handMatrix =
|
||||
owner->skeleton->getMatrix(handFrame->getIndex()) * handMatrix;
|
||||
handFrame = handFrame->getParent();
|
||||
}
|
||||
}
|
||||
auto handFrame = owner->getClump()->findFrame("srhand");
|
||||
glm::mat4 handMatrix = handFrame->getWorldTransform();
|
||||
|
||||
const auto& raydirection = owner->getLookDirection();
|
||||
const auto rayend = owner->getPosition() + raydirection * weapon->hitRange;
|
||||
auto handPos = glm::vec3(handMatrix[3]);
|
||||
auto fireOrigin = owner->getPosition() + owner->getRotation() * handPos;
|
||||
auto fireOrigin = glm::vec3(handMatrix[3]);
|
||||
float dmg = weapon->damage;
|
||||
|
||||
owner->engine->doWeaponScan({dmg, fireOrigin, rayend, weapon});
|
||||
|
@ -82,6 +82,8 @@ bool LoaderIDE::load(const std::string &filename, const PedStatsList &stats) {
|
||||
objs->setLodDistance(i, atof(buff.c_str()));
|
||||
}
|
||||
|
||||
objs->determineFurthest();
|
||||
|
||||
getline(strstream, buff, ',');
|
||||
objs->flags = atoi(buff.c_str());
|
||||
|
||||
@ -96,12 +98,6 @@ bool LoaderIDE::load(const std::string &filename, const PedStatsList &stats) {
|
||||
objs->timeOff = 24;
|
||||
}
|
||||
|
||||
/// @todo Figure out how LOD stuff is intended to work
|
||||
if (objs->name.find("LOD", 0, 3) != std::string::npos &&
|
||||
objs->name != "LODistancoast01") {
|
||||
objs->LOD = true;
|
||||
}
|
||||
|
||||
objects.emplace(objs->id(), std::move(objs));
|
||||
break;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <ai/CharacterController.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
@ -71,10 +70,10 @@ CharacterObject::CharacterObject(GameWorld* engine, const glm::vec3& pos,
|
||||
animations.ko_shot_front = engine->data->animations["ko_shot_front"];
|
||||
|
||||
auto info = getModelInfo<PedModelInfo>();
|
||||
setClump(ClumpPtr(info->getModel()->clone()));
|
||||
if (info->getModel()) {
|
||||
setModel(info->getModel());
|
||||
skeleton = new Skeleton;
|
||||
animator = new Animator(getModel(), skeleton);
|
||||
animator = new Animator(getClump().get());
|
||||
|
||||
createActor();
|
||||
}
|
||||
@ -108,9 +107,8 @@ void CharacterObject::createActor(const glm::vec2& size) {
|
||||
physCharacter =
|
||||
new btKinematicCharacterController(physObject, physShape, 0.30f, 2);
|
||||
#else
|
||||
physCharacter =
|
||||
new btKinematicCharacterController(physObject, physShape, 0.30f,
|
||||
btVector3(0.f, 0.f, 1.f));
|
||||
physCharacter = new btKinematicCharacterController(
|
||||
physObject, physShape, 0.30f, btVector3(0.f, 0.f, 1.f));
|
||||
#endif
|
||||
physCharacter->setFallSpeed(20.f);
|
||||
physCharacter->setUseGhostSweepTest(true);
|
||||
@ -220,42 +218,42 @@ glm::vec3 CharacterObject::updateMovementAnimation(float dt) {
|
||||
}
|
||||
|
||||
// If we have to, interrogate the movement animation
|
||||
if (movementAnimation != animations.idle) {
|
||||
if (!getModel()->frames[0]->getChildren().empty()) {
|
||||
ModelFrame* root = getModel()->frames[0]->getChildren()[0];
|
||||
auto it = movementAnimation->bones.find(root->getName());
|
||||
if (it != movementAnimation->bones.end()) {
|
||||
AnimationBone* rootBone = it->second;
|
||||
float step = dt;
|
||||
const float duration =
|
||||
animator->getAnimation(AnimIndexMovement)->duration;
|
||||
float animTime = fmod(
|
||||
animator->getAnimationTime(AnimIndexMovement), duration);
|
||||
|
||||
// Handle any remaining transformation before the end of the
|
||||
// keyframes
|
||||
if ((animTime + step) > duration) {
|
||||
glm::vec3 a =
|
||||
rootBone->getInterpolatedKeyframe(animTime).position;
|
||||
glm::vec3 b =
|
||||
rootBone->getInterpolatedKeyframe(duration).position;
|
||||
glm::vec3 d = (b - a);
|
||||
animTranslate.y += d.y;
|
||||
step -= (duration - animTime);
|
||||
animTime = 0.f;
|
||||
}
|
||||
const auto& modelroot = getClump()->getFrame();
|
||||
if (movementAnimation != animations.idle &&
|
||||
!modelroot->getChildren().empty()) {
|
||||
const auto& root = modelroot->getChildren()[0];
|
||||
auto it = movementAnimation->bones.find(root->getName());
|
||||
if (it != movementAnimation->bones.end()) {
|
||||
AnimationBone* rootBone = it->second;
|
||||
float step = dt;
|
||||
const float duration =
|
||||
animator->getAnimation(AnimIndexMovement)->duration;
|
||||
float animTime =
|
||||
fmod(animator->getAnimationTime(AnimIndexMovement), duration);
|
||||
|
||||
// Handle any remaining transformation before the end of the
|
||||
// keyframes
|
||||
if ((animTime + step) > duration) {
|
||||
glm::vec3 a =
|
||||
rootBone->getInterpolatedKeyframe(animTime).position;
|
||||
glm::vec3 b =
|
||||
rootBone->getInterpolatedKeyframe(animTime + step).position;
|
||||
rootBone->getInterpolatedKeyframe(duration).position;
|
||||
glm::vec3 d = (b - a);
|
||||
animTranslate.y += d.y;
|
||||
|
||||
Skeleton::FrameData fd = skeleton->getData(root->getIndex());
|
||||
fd.a.translation.y = 0.f;
|
||||
skeleton->setData(root->getIndex(), fd);
|
||||
step -= (duration - animTime);
|
||||
animTime = 0.f;
|
||||
}
|
||||
|
||||
glm::vec3 a = rootBone->getInterpolatedKeyframe(animTime).position;
|
||||
glm::vec3 b =
|
||||
rootBone->getInterpolatedKeyframe(animTime + step).position;
|
||||
glm::vec3 d = (b - a);
|
||||
animTranslate.y += d.y;
|
||||
|
||||
// Kludge: Drop y component of root bone
|
||||
auto t = glm::vec3(root->getTransform()[3]);
|
||||
t.y = 0.f;
|
||||
root->setTranslation(t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,10 +274,10 @@ void CharacterObject::tick(float dt) {
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterObject::setRotation(const glm::quat &orientation)
|
||||
{
|
||||
void CharacterObject::setRotation(const glm::quat& orientation) {
|
||||
m_look.x = glm::roll(orientation);
|
||||
rotation = orientation;
|
||||
getClump()->getFrame()->setRotation(glm::mat3_cast(rotation));
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
@ -293,15 +291,13 @@ void CharacterObject::changeCharacterModel(const std::string& name) {
|
||||
engine->data->loadTXD(modelName + ".txd");
|
||||
auto newmodel = engine->data->loadClump(modelName + ".dff");
|
||||
|
||||
if (skeleton) {
|
||||
if (animator) {
|
||||
delete animator;
|
||||
delete skeleton;
|
||||
}
|
||||
|
||||
setModel(newmodel);
|
||||
|
||||
skeleton = new Skeleton;
|
||||
animator = new Animator(getModel(), skeleton);
|
||||
animator = new Animator(getClump().get());
|
||||
}
|
||||
|
||||
void CharacterObject::updateCharacter(float dt) {
|
||||
@ -332,6 +328,7 @@ void CharacterObject::updateCharacter(float dt) {
|
||||
yaw += std::atan2(movement.z, movement.x);
|
||||
}
|
||||
rotation = glm::quat(glm::vec3(0.f, 0.f, yaw));
|
||||
getClump()->getFrame()->setRotation(glm::mat3_cast(rotation));
|
||||
}
|
||||
|
||||
walkDir = rotation * walkDir;
|
||||
@ -352,6 +349,7 @@ void CharacterObject::updateCharacter(float dt) {
|
||||
auto Pos =
|
||||
physCharacter->getGhostObject()->getWorldTransform().getOrigin();
|
||||
position = glm::vec3(Pos.x(), Pos.y(), Pos.z());
|
||||
getClump()->getFrame()->setTranslation(position);
|
||||
|
||||
// Handle above waist height water.
|
||||
auto wi = engine->data->getWaterIndexAt(getPosition());
|
||||
@ -407,6 +405,7 @@ void CharacterObject::setPosition(const glm::vec3& pos) {
|
||||
physCharacter->warp(bpos);
|
||||
}
|
||||
position = pos;
|
||||
getClump()->getFrame()->setTranslation(pos);
|
||||
}
|
||||
|
||||
bool CharacterObject::isAlive() const {
|
||||
@ -611,8 +610,7 @@ void CharacterObject::useItem(bool active, bool primary) {
|
||||
if (!currentState.primaryActive && active) {
|
||||
// If we've just started, activate
|
||||
controller->setNextActivity(new Activities::UseItem(item));
|
||||
}
|
||||
else if (currentState.primaryActive && !active) {
|
||||
} else if (currentState.primaryActive && !active) {
|
||||
// UseItem will cancel itself upon !primaryActive
|
||||
}
|
||||
currentState.primaryActive = active;
|
||||
|
@ -98,7 +98,7 @@ struct AnimationGroup {
|
||||
* @brief The CharacterObject struct
|
||||
* Implements Character object behaviours.
|
||||
*/
|
||||
class CharacterObject : public GameObject {
|
||||
class CharacterObject : public GameObject, public ClumpObject {
|
||||
private:
|
||||
CharacterState currentState;
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
|
||||
CutsceneObject::CutsceneObject(GameWorld *engine, const glm::vec3 &pos,
|
||||
const glm::quat &rot, Model *model,
|
||||
const glm::quat &rot, Clump *model,
|
||||
BaseModelInfo *modelinfo)
|
||||
: GameObject(engine, pos, rot, modelinfo)
|
||||
, _parent(nullptr)
|
||||
@ -14,8 +13,8 @@ CutsceneObject::CutsceneObject(GameWorld *engine, const glm::vec3 &pos,
|
||||
else {
|
||||
setModel(getModelInfo<ClumpModelInfo>()->getModel());
|
||||
}
|
||||
skeleton = new Skeleton;
|
||||
animator = new Animator(getModel(), skeleton);
|
||||
setClump(ClumpPtr(getModel()->clone()));
|
||||
animator = new Animator(getClump().get());
|
||||
}
|
||||
|
||||
CutsceneObject::~CutsceneObject() {
|
||||
|
@ -6,13 +6,13 @@
|
||||
/**
|
||||
* @brief Object type used for cutscene animations.
|
||||
*/
|
||||
class CutsceneObject : public GameObject {
|
||||
class CutsceneObject : public GameObject, public ClumpObject {
|
||||
GameObject* _parent;
|
||||
ModelFrame* _bone;
|
||||
|
||||
public:
|
||||
CutsceneObject(GameWorld* engine, const glm::vec3& pos,
|
||||
const glm::quat& rot, Model* model,
|
||||
const glm::quat& rot, Clump* model,
|
||||
BaseModelInfo* modelinfo);
|
||||
~CutsceneObject();
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <loaders/LoaderDFF.hpp>
|
||||
@ -9,9 +8,6 @@ GameObject::~GameObject() {
|
||||
if (animator) {
|
||||
delete animator;
|
||||
}
|
||||
if (skeleton) {
|
||||
delete skeleton;
|
||||
}
|
||||
|
||||
if (modelinfo_) {
|
||||
modelinfo_->removeReference();
|
||||
|
@ -2,7 +2,7 @@
|
||||
#ifndef _GAMEOBJECT_HPP_
|
||||
#define _GAMEOBJECT_HPP_
|
||||
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <loaders/LoaderIDE.hpp>
|
||||
@ -11,9 +11,7 @@
|
||||
#include <objects/ObjectTypes.hpp>
|
||||
#include <rw/types.hpp>
|
||||
|
||||
class Skeleton;
|
||||
class CharacterController;
|
||||
class ModelFrame;
|
||||
class Animator;
|
||||
class GameWorld;
|
||||
|
||||
@ -33,7 +31,7 @@ class GameObject {
|
||||
/**
|
||||
* Model used for rendering
|
||||
*/
|
||||
Model* model_;
|
||||
Clump* model_;
|
||||
|
||||
protected:
|
||||
void changeModelInfo(BaseModelInfo* next) {
|
||||
@ -47,7 +45,6 @@ public:
|
||||
GameWorld* engine;
|
||||
|
||||
Animator* animator; /// Object's animator.
|
||||
Skeleton* skeleton;
|
||||
|
||||
bool inWater;
|
||||
|
||||
@ -72,7 +69,6 @@ public:
|
||||
, rotation(rot)
|
||||
, engine(engine)
|
||||
, animator(nullptr)
|
||||
, skeleton(nullptr)
|
||||
, inWater(false)
|
||||
, _lastHeight(std::numeric_limits<float>::max())
|
||||
, visible(true)
|
||||
@ -106,14 +102,14 @@ public:
|
||||
/**
|
||||
* @return The model used in rendering
|
||||
*/
|
||||
Model* getModel() const {
|
||||
Clump* getModel() const {
|
||||
return model_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the current model, used for re-dressing chars
|
||||
*/
|
||||
void setModel(Model* model) {
|
||||
void setModel(Clump* model) {
|
||||
model_ = model;
|
||||
}
|
||||
|
||||
@ -235,7 +231,7 @@ public:
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
void updateTransform(const glm::vec3& pos, const glm::quat& rot) {
|
||||
virtual void updateTransform(const glm::vec3& pos, const glm::quat& rot) {
|
||||
_lastPosition = position;
|
||||
_lastRotation = rotation;
|
||||
position = pos;
|
||||
@ -246,4 +242,18 @@ private:
|
||||
ObjectLifetime lifetime;
|
||||
};
|
||||
|
||||
class ClumpObject {
|
||||
ClumpPtr clump_;
|
||||
|
||||
protected:
|
||||
void setClump(ClumpPtr ptr) {
|
||||
clump_ = ptr;
|
||||
}
|
||||
|
||||
public:
|
||||
const ClumpPtr& getClump() const {
|
||||
return clump_;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __GAMEOBJECTS_HPP__
|
||||
|
@ -7,17 +7,23 @@
|
||||
|
||||
InstanceObject::InstanceObject(GameWorld* engine, const glm::vec3& pos,
|
||||
const glm::quat& rot, const glm::vec3& scale,
|
||||
BaseModelInfo* modelinfo, InstanceObject* lod,
|
||||
BaseModelInfo* modelinfo,
|
||||
std::shared_ptr<DynamicObjectData> dyn)
|
||||
: GameObject(engine, pos, rot, modelinfo)
|
||||
, health(100.f)
|
||||
, scale(scale)
|
||||
, body(nullptr)
|
||||
, LODinstance(lod)
|
||||
, dynamics(dyn)
|
||||
, _enablePhysics(false) {
|
||||
if (modelinfo) {
|
||||
changeModel(modelinfo);
|
||||
setPosition(pos);
|
||||
setRotation(rot);
|
||||
|
||||
// Disable the main island LOD
|
||||
if (modelinfo->name.find("IslandLOD") != std::string::npos) {
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
/// @todo store path information properly
|
||||
if (modelinfo->type() == ModelDataType::SimpleInfo) {
|
||||
@ -133,6 +139,17 @@ void InstanceObject::changeModel(BaseModelInfo* incoming) {
|
||||
setModel(getModelInfo<SimpleModelInfo>()->getModel());
|
||||
auto collision = getModelInfo<SimpleModelInfo>()->getCollision();
|
||||
|
||||
auto modelatomic = getModelInfo<SimpleModelInfo>()->getAtomic(0);
|
||||
if (modelatomic) {
|
||||
auto previousatomic = atomic_;
|
||||
atomic_ = modelatomic->clone();
|
||||
if (previousatomic) {
|
||||
atomic_->setFrame(previousatomic->getFrame());
|
||||
} else {
|
||||
atomic_->setFrame(std::make_shared<ModelFrame>());
|
||||
}
|
||||
}
|
||||
|
||||
if (collision) {
|
||||
body.reset(new CollisionInstance);
|
||||
body->createPhysicsBody(this, collision, dynamics.get());
|
||||
@ -141,11 +158,25 @@ void InstanceObject::changeModel(BaseModelInfo* incoming) {
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceObject::setPosition(const glm::vec3& pos) {
|
||||
if (body) {
|
||||
auto& wtr = body->getBulletBody()->getWorldTransform();
|
||||
wtr.setOrigin(btVector3(pos.x, pos.y, pos.z));
|
||||
}
|
||||
if (atomic_) {
|
||||
atomic_->getFrame()->setTranslation(pos);
|
||||
}
|
||||
GameObject::setPosition(pos);
|
||||
}
|
||||
|
||||
void InstanceObject::setRotation(const glm::quat& r) {
|
||||
if (body) {
|
||||
auto& wtr = body->getBulletBody()->getWorldTransform();
|
||||
wtr.setRotation(btQuaternion(r.x, r.y, r.z, r.w));
|
||||
}
|
||||
if (atomic_) {
|
||||
atomic_->getFrame()->setRotation(glm::mat3_cast(r));
|
||||
}
|
||||
GameObject::setRotation(r);
|
||||
}
|
||||
|
||||
@ -181,3 +212,11 @@ void InstanceObject::setSolid(bool solid) {
|
||||
}
|
||||
body->getBulletBody()->setCollisionFlags(flags);
|
||||
}
|
||||
|
||||
void InstanceObject::updateTransform(const glm::vec3& pos,
|
||||
const glm::quat& rot) {
|
||||
position = pos;
|
||||
rotation = rot;
|
||||
getAtomic()->getFrame()->setRotation(glm::mat3_cast(rot));
|
||||
getAtomic()->getFrame()->setTranslation(pos);
|
||||
}
|
||||
|
@ -14,16 +14,20 @@ class InstanceObject : public GameObject {
|
||||
float health;
|
||||
bool visible = true;
|
||||
|
||||
/**
|
||||
* The Atomic instance for this object
|
||||
*/
|
||||
AtomicPtr atomic_;
|
||||
|
||||
public:
|
||||
glm::vec3 scale;
|
||||
std::unique_ptr<CollisionInstance> body;
|
||||
InstanceObject* LODinstance;
|
||||
std::shared_ptr<DynamicObjectData> dynamics;
|
||||
bool _enablePhysics;
|
||||
|
||||
InstanceObject(GameWorld* engine, const glm::vec3& pos,
|
||||
const glm::quat& rot, const glm::vec3& scale,
|
||||
BaseModelInfo* modelinfo, InstanceObject* lod,
|
||||
BaseModelInfo* modelinfo,
|
||||
std::shared_ptr<DynamicObjectData> dyn);
|
||||
~InstanceObject();
|
||||
|
||||
@ -31,10 +35,15 @@ public:
|
||||
return Instance;
|
||||
}
|
||||
|
||||
const AtomicPtr& getAtomic() const {
|
||||
return atomic_;
|
||||
}
|
||||
|
||||
void tick(float dt);
|
||||
|
||||
void changeModel(BaseModelInfo* incoming);
|
||||
|
||||
virtual void setPosition(const glm::vec3& pos);
|
||||
virtual void setRotation(const glm::quat& r);
|
||||
|
||||
virtual bool takeDamage(const DamageInfo& damage);
|
||||
@ -51,6 +60,8 @@ public:
|
||||
float getHealth() const {
|
||||
return health;
|
||||
}
|
||||
|
||||
void updateTransform(const glm::vec3& pos, const glm::quat& rot) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <BulletDynamics/Vehicle/btRaycastVehicle.h>
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/CollisionModel.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <dynamics/CollisionInstance.hpp>
|
||||
#include <dynamics/RaycastCallbacks.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
@ -70,11 +69,7 @@ public:
|
||||
const auto& rot = tform.getRotation();
|
||||
auto r2 = inv * glm::quat(rot.w(), rot.x(), rot.y(), rot.z());
|
||||
|
||||
auto skeleton = m_object->skeleton;
|
||||
auto& prev = skeleton->getData(m_part->dummy->getIndex()).a;
|
||||
auto next = prev;
|
||||
next.rotation = r2;
|
||||
skeleton->setData(m_part->dummy->getIndex(), {next, prev, true});
|
||||
m_part->dummy->setRotation(glm::mat3_cast(r2));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -148,23 +143,28 @@ VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos,
|
||||
: 1.f - info->handling.tractionBias);
|
||||
}
|
||||
|
||||
// Hide all LOD and damage frames.
|
||||
skeleton = new Skeleton;
|
||||
|
||||
setModel(getVehicle()->getModel());
|
||||
setClump(ClumpPtr(getModelInfo<VehicleModelInfo>()->getModel()->clone()));
|
||||
|
||||
for (ModelFrame* frame : getModel()->frames) {
|
||||
auto& name = frame->getName();
|
||||
bool isDam = name.find("_dam") != name.npos;
|
||||
bool isLod = name.find("lo") != name.npos;
|
||||
bool isDum = name.find("_dummy") != name.npos;
|
||||
/*bool isOk = name.find("_ok") != name.npos;*/
|
||||
if (isDam || isLod || isDum) {
|
||||
skeleton->setEnabled(frame, false);
|
||||
// Locate the Atomics for the chassis_hi and chassis_vlo frames
|
||||
for (const auto& atomic : getClump()->getAtomics()) {
|
||||
auto frame = atomic->getFrame().get();
|
||||
if (frame->getName() == "chassis_vlo") {
|
||||
chassislow_ = atomic.get();
|
||||
}
|
||||
if (frame->getName() == "chassis_hi") {
|
||||
chassishigh_ = atomic.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Create meta-data for dummy parts
|
||||
auto chassisframe = getClump()->findFrame("chassis_dummy");
|
||||
RW_CHECK(chassisframe, "Vehicle has no chassis_dummy");
|
||||
for (auto& frame : chassisframe->getChildren()) {
|
||||
auto& name = frame->getName();
|
||||
bool isDum = name.find("_dummy") != name.npos;
|
||||
if (isDum) {
|
||||
registerPart(frame);
|
||||
registerPart(frame.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,6 +184,7 @@ VehicleObject::~VehicleObject() {
|
||||
|
||||
void VehicleObject::setPosition(const glm::vec3& pos) {
|
||||
GameObject::setPosition(pos);
|
||||
getClump()->getFrame()->setTranslation(pos);
|
||||
if (collision->getBulletBody()) {
|
||||
auto bodyOrigin = btVector3(position.x, position.y, position.z);
|
||||
for (auto& part : dynamicParts) {
|
||||
@ -201,6 +202,7 @@ void VehicleObject::setPosition(const glm::vec3& pos) {
|
||||
}
|
||||
|
||||
void VehicleObject::setRotation(const glm::quat& orientation) {
|
||||
getClump()->getFrame()->setRotation(glm::mat3_cast(orientation));
|
||||
if (collision->getBulletBody()) {
|
||||
auto t = collision->getBulletBody()->getWorldTransform();
|
||||
t.setRotation(btQuaternion(orientation.x, orientation.y, orientation.z,
|
||||
@ -210,6 +212,14 @@ void VehicleObject::setRotation(const glm::quat& orientation) {
|
||||
GameObject::setRotation(orientation);
|
||||
}
|
||||
|
||||
void VehicleObject::updateTransform(const glm::vec3& pos,
|
||||
const glm::quat& rot) {
|
||||
position = pos;
|
||||
rotation = rot;
|
||||
getClump()->getFrame()->setRotation(glm::mat3_cast(rot));
|
||||
getClump()->getFrame()->setTranslation(pos);
|
||||
}
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
void VehicleObject::tick(float dt) {
|
||||
@ -550,18 +560,14 @@ bool VehicleObject::takeDamage(const GameObject::DamageInfo& dmg) {
|
||||
|
||||
if (p->normal == nullptr) continue;
|
||||
|
||||
if (skeleton->getData(p->normal->getIndex()).enabled) {
|
||||
auto& geom =
|
||||
getModel()->geometries[p->normal->getGeometries()[0]];
|
||||
auto pp =
|
||||
p->normal->getMatrix() * glm::vec4(0.f, 0.f, 0.f, 1.f);
|
||||
float td = glm::distance(
|
||||
glm::vec3(pp) + geom->geometryBounds.center, dpoint);
|
||||
if (td < geom->geometryBounds.radius * 1.2f) {
|
||||
setPartState(p, DAM);
|
||||
}
|
||||
/// @todo determine when doors etc. should un-latch
|
||||
/// @todo correct logic
|
||||
float damageradius = 0.1f;
|
||||
auto center = glm::vec3(p->dummy->getWorldTransform()[3]);
|
||||
float td = glm::distance(center, dpoint);
|
||||
if (td < damageradius * 1.2f) {
|
||||
setPartState(p, DAM);
|
||||
}
|
||||
/// @todo determine when doors etc. should un-latch
|
||||
}
|
||||
}
|
||||
|
||||
@ -571,11 +577,11 @@ bool VehicleObject::takeDamage(const GameObject::DamageInfo& dmg) {
|
||||
void VehicleObject::setPartState(VehicleObject::Part* part,
|
||||
VehicleObject::FrameState state) {
|
||||
if (state == VehicleObject::OK) {
|
||||
if (part->normal) skeleton->setEnabled(part->normal, true);
|
||||
if (part->damaged) skeleton->setEnabled(part->damaged, false);
|
||||
if (part->normal) part->normal->setFlag(Atomic::ATOMIC_RENDER, true);
|
||||
if (part->damaged) part->damaged->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
} else if (state == VehicleObject::DAM) {
|
||||
if (part->normal) skeleton->setEnabled(part->normal, false);
|
||||
if (part->damaged) skeleton->setEnabled(part->damaged, true);
|
||||
if (part->normal) part->normal->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
if (part->damaged) part->damaged->setFlag(Atomic::ATOMIC_RENDER, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,10 +612,7 @@ void VehicleObject::setPartLocked(VehicleObject::Part* part, bool locked) {
|
||||
destroyObjectHinge(part);
|
||||
|
||||
// Restore default bone transform
|
||||
auto dt = part->dummy->getDefaultTranslation();
|
||||
auto dr = glm::quat_cast(part->dummy->getDefaultRotation());
|
||||
Skeleton::FrameTransform tf{dt, dr};
|
||||
skeleton->setData(part->dummy->getIndex(), {tf, tf, true});
|
||||
part->dummy->reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,26 +641,31 @@ VehicleObject::Part* VehicleObject::getPart(const std::string& name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModelFrame* findStateFrame(ModelFrame* f, const std::string& state) {
|
||||
auto it = std::find_if(
|
||||
f->getChildren().begin(), f->getChildren().end(), [&](ModelFrame* c) {
|
||||
return c->getName().find(state) != std::string::npos;
|
||||
});
|
||||
if (it != f->getChildren().end()) {
|
||||
return *it;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VehicleObject::registerPart(ModelFrame* mf) {
|
||||
auto normal = findStateFrame(mf, "_ok");
|
||||
auto damage = findStateFrame(mf, "_dam");
|
||||
auto dummynameend = mf->getName().find("_dummy");
|
||||
RW_CHECK(dummynameend != std::string::npos,
|
||||
"Can't create part from non-dummy");
|
||||
auto dummyname = mf->getName().substr(0, dummynameend);
|
||||
auto normalframe = mf->findDescendant(dummyname + "_hi_ok");
|
||||
auto damageframe = mf->findDescendant(dummyname + "_hi_dam");
|
||||
|
||||
if (normal == nullptr && damage == nullptr) {
|
||||
if (normalframe == nullptr && damageframe == nullptr) {
|
||||
// Not actually a useful part, just a dummy.
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the Atomics for the part
|
||||
Atomic *normal = nullptr, *damage = nullptr;
|
||||
for (const auto& atomic : getClump()->getAtomics()) {
|
||||
if (atomic->getFrame().get() == normalframe) {
|
||||
normal = atomic.get();
|
||||
}
|
||||
if (atomic->getFrame().get() == damageframe) {
|
||||
damage = atomic.get();
|
||||
damage->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
}
|
||||
}
|
||||
|
||||
dynamicParts.insert(
|
||||
{mf->getName(),
|
||||
{mf, normal, damage, nullptr, nullptr, false, 0.f, 0.f, 0.f}});
|
||||
@ -672,20 +680,12 @@ void VehicleObject::createObjectHinge(Part* part) {
|
||||
|
||||
auto& fn = part->dummy->getName();
|
||||
|
||||
ModelFrame* okframe = part->normal;
|
||||
|
||||
if (okframe->getGeometries().size() == 0) return;
|
||||
|
||||
auto& geom = getModel()->geometries[okframe->getGeometries()[0]];
|
||||
auto gbounds = geom->geometryBounds;
|
||||
|
||||
if (fn.find("door") != fn.npos) {
|
||||
hingeAxis = {0.f, 0.f, 1.f};
|
||||
// hingePosition = {0.f, 0.2f, 0.f};
|
||||
boxSize = {0.15f, 0.5f, 0.6f};
|
||||
// boxOffset = {0.f,-0.2f, gbounds.center.z/2.f};
|
||||
auto tf = gbounds.center;
|
||||
boxOffset = btVector3(tf.x, tf.y, tf.z);
|
||||
boxOffset = btVector3(0.f, -0.25f, 0.f);
|
||||
hingePosition = -boxOffset;
|
||||
|
||||
if (sign < 0.f) {
|
||||
|
@ -16,13 +16,15 @@ class btTransform;
|
||||
* @class VehicleObject
|
||||
* Implements Vehicle behaviours.
|
||||
*/
|
||||
class VehicleObject : public GameObject {
|
||||
class VehicleObject : public GameObject, public ClumpObject {
|
||||
private:
|
||||
float steerAngle;
|
||||
float throttle;
|
||||
float brake;
|
||||
bool handbrake;
|
||||
|
||||
Atomic* chassishigh_ = nullptr;
|
||||
Atomic* chassislow_ = nullptr;
|
||||
public:
|
||||
VehicleInfoHandle info;
|
||||
glm::u8vec3 colourPrimary;
|
||||
@ -36,8 +38,8 @@ public:
|
||||
|
||||
struct Part {
|
||||
ModelFrame* dummy;
|
||||
ModelFrame* normal;
|
||||
ModelFrame* damaged;
|
||||
Atomic* normal;
|
||||
Atomic* damaged;
|
||||
btRigidBody* body;
|
||||
btHingeConstraint* constraint;
|
||||
bool moveToAngle;
|
||||
@ -58,10 +60,15 @@ public:
|
||||
|
||||
void setRotation(const glm::quat& orientation);
|
||||
|
||||
void updateTransform(const glm::vec3& pos, const glm::quat& rot) override;
|
||||
|
||||
VehicleModelInfo* getVehicle() const {
|
||||
return getModelInfo<VehicleModelInfo>();
|
||||
}
|
||||
|
||||
Atomic* getHighLOD() const { return chassishigh_; }
|
||||
Atomic* getLowLOD() const { return chassislow_; }
|
||||
|
||||
Type type() {
|
||||
return Vehicle;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <LinearMath/btIDebugDraw.h>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <render/GameRenderer.hpp>
|
||||
|
||||
class DebugDraw : public btIDebugDraw {
|
||||
@ -28,7 +28,7 @@ public:
|
||||
protected:
|
||||
int debugMode;
|
||||
|
||||
std::vector<Model::GeometryVertex> lines;
|
||||
std::vector<GeometryVertex> lines;
|
||||
size_t maxlines;
|
||||
GeometryBuffer *lineBuff;
|
||||
DrawBuffer *dbuff;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <engine/GameState.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
@ -14,7 +14,6 @@
|
||||
#include <data/ModelData.hpp>
|
||||
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
#include <render/ObjectRenderer.hpp>
|
||||
|
||||
@ -318,8 +317,7 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera& camera,
|
||||
m, glm::vec3(i.radius +
|
||||
0.15f * glm::sin(_renderWorld->getGameTime() * 5.f)));
|
||||
|
||||
objectRenderer.renderFrame(sphereModel, sphereModel->frames[0], m,
|
||||
nullptr, 1.f, renderList);
|
||||
objectRenderer.renderClump(sphereModel, m, nullptr, renderList);
|
||||
}
|
||||
|
||||
// Render arrows above anything that isn't radar only (or hidden)
|
||||
@ -346,8 +344,7 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera& camera,
|
||||
glm::vec3(0.f, 0.f, 2.5f + glm::sin(a) * 0.5f));
|
||||
model = glm::rotate(model, a, glm::vec3(0.f, 0.f, 1.f));
|
||||
model = glm::scale(model, glm::vec3(1.5f, 1.5f, 1.5f));
|
||||
objectRenderer.renderFrame(arrowModel, arrowModel->frames[0], model,
|
||||
nullptr, 1.f, renderList);
|
||||
objectRenderer.renderClump(arrowModel, model, nullptr, renderList);
|
||||
}
|
||||
|
||||
RW_PROFILE_END();
|
||||
@ -475,57 +472,6 @@ void GameRenderer::renderPostProcess() {
|
||||
renderer->drawArrays(glm::mat4(), &ssRectDraw, wdp);
|
||||
}
|
||||
|
||||
void GameRenderer::renderGeometry(Model* model, size_t g,
|
||||
const glm::mat4& modelMatrix, float opacity,
|
||||
GameObject* object) {
|
||||
for (size_t sg = 0; sg < model->geometries[g]->subgeom.size(); ++sg) {
|
||||
Model::SubGeometry& subgeom = model->geometries[g]->subgeom[sg];
|
||||
|
||||
Renderer::DrawParameters dp;
|
||||
|
||||
dp.colour = {255, 255, 255, 255};
|
||||
dp.count = subgeom.numIndices;
|
||||
dp.start = subgeom.start;
|
||||
dp.textures = {0};
|
||||
|
||||
if (model->geometries[g]->materials.size() > subgeom.material) {
|
||||
Model::Material& mat =
|
||||
model->geometries[g]->materials[subgeom.material];
|
||||
|
||||
if (mat.textures.size() > 0) {
|
||||
auto tex = mat.textures[0].texture;
|
||||
if (tex) {
|
||||
dp.textures = {tex->getName()};
|
||||
}
|
||||
}
|
||||
|
||||
if ((model->geometries[g]->flags &
|
||||
RW::BSGeometry::ModuleMaterialColor) ==
|
||||
RW::BSGeometry::ModuleMaterialColor) {
|
||||
dp.colour = mat.colour;
|
||||
|
||||
if (object && object->type() == GameObject::Vehicle) {
|
||||
auto vehicle = static_cast<VehicleObject*>(object);
|
||||
if (dp.colour.r == 60 && dp.colour.g == 255 &&
|
||||
dp.colour.b == 0) {
|
||||
dp.colour = glm::u8vec4(vehicle->colourPrimary, 255);
|
||||
} else if (dp.colour.r == 255 && dp.colour.g == 0 &&
|
||||
dp.colour.b == 175) {
|
||||
dp.colour = glm::u8vec4(vehicle->colourSecondary, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dp.colour.a *= opacity;
|
||||
|
||||
dp.diffuse = mat.diffuseIntensity;
|
||||
dp.ambient = mat.ambientIntensity;
|
||||
}
|
||||
|
||||
renderer->draw(modelMatrix, &model->geometries[g]->dbuff, dp);
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderEffects(GameWorld* world) {
|
||||
renderer->useProgram(particleProg);
|
||||
|
||||
@ -656,49 +602,6 @@ void GameRenderer::drawColour(const glm::vec4& colour, glm::vec4 extents) {
|
||||
renderer->invalidate();
|
||||
}
|
||||
|
||||
bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
GameObject* object, float opacity,
|
||||
bool queueTransparent) {
|
||||
auto localmatrix = matrix;
|
||||
bool vis = true;
|
||||
|
||||
if (object && object->skeleton) {
|
||||
// Skeleton is loaded with the correct matrix via Animator.
|
||||
localmatrix *= object->skeleton->getMatrix(f);
|
||||
|
||||
vis = object->skeleton->getData(f->getIndex()).enabled;
|
||||
} else {
|
||||
localmatrix *= f->getTransform();
|
||||
}
|
||||
|
||||
if (vis) {
|
||||
for (size_t g : f->getGeometries()) {
|
||||
if (!object || !object->animator) {
|
||||
RW::BSGeometryBounds& bounds = m->geometries[g]->geometryBounds;
|
||||
|
||||
glm::vec3 boundpos = bounds.center + glm::vec3(localmatrix[3]);
|
||||
if (!_camera.frustum.intersects(boundpos, bounds.radius)) {
|
||||
culled++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
renderGeometry(m, g, localmatrix, opacity, object);
|
||||
}
|
||||
}
|
||||
|
||||
for (ModelFrame* c : f->getChildren()) {
|
||||
renderFrame(m, c, localmatrix, object, queueTransparent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameRenderer::renderModel(Model* model, const glm::mat4& modelMatrix,
|
||||
GameObject* object) {
|
||||
renderFrame(model, model->frames[model->rootFrameIdx], modelMatrix, object,
|
||||
1.f);
|
||||
}
|
||||
|
||||
void GameRenderer::renderPaths() {
|
||||
/*glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, debugTex);
|
||||
|
@ -16,7 +16,7 @@ class Logger;
|
||||
#include "TextRenderer.hpp"
|
||||
#include "WaterRenderer.hpp"
|
||||
|
||||
class Model;
|
||||
class Clump;
|
||||
class ModelFrame;
|
||||
class GameWorld;
|
||||
class GameObject;
|
||||
@ -51,30 +51,6 @@ class GameRenderer {
|
||||
/** The low-level drawing interface to use */
|
||||
Renderer* renderer;
|
||||
|
||||
/** Stores data for deferring transparent objects */
|
||||
struct RQueueEntry {
|
||||
Model* model;
|
||||
size_t g;
|
||||
size_t sg;
|
||||
glm::mat4 matrix;
|
||||
Renderer::DrawParameters dp;
|
||||
GameObject* object;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief renders a model's frame.
|
||||
* @param m
|
||||
* @param f
|
||||
* @param matrix
|
||||
* @param object
|
||||
* @param queueTransparent abort the draw if the frame contains transparent
|
||||
* materials
|
||||
* @return True if the frame was drawn, false if it should be queued
|
||||
*/
|
||||
bool renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
GameObject* object, float opacity,
|
||||
bool queueTransparent = true);
|
||||
|
||||
// Temporary variables used during rendering
|
||||
float _renderAlpha;
|
||||
GameWorld* _renderWorld;
|
||||
@ -155,15 +131,6 @@ public:
|
||||
void drawTexture(TextureData* texture, glm::vec4 extents);
|
||||
void drawColour(const glm::vec4& colour, glm::vec4 extents);
|
||||
|
||||
/**
|
||||
* Renders a model (who'd have thought)
|
||||
*/
|
||||
void renderModel(Model*, const glm::mat4& modelMatrix,
|
||||
GameObject* = nullptr);
|
||||
|
||||
void renderGeometry(Model*, size_t geom, const glm::mat4& modelMatrix,
|
||||
float opacity, GameObject* = nullptr);
|
||||
|
||||
/** method for rendering AI debug information */
|
||||
void renderPaths();
|
||||
|
||||
@ -207,15 +174,15 @@ public:
|
||||
*
|
||||
* GameRenderer will take ownership of the Model* pointer
|
||||
*/
|
||||
void setSpecialModel(SpecialModel usage, Model* model) {
|
||||
void setSpecialModel(SpecialModel usage, Clump* model) {
|
||||
specialmodels_[usage].reset(model);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Hard-coded models to use for each of the special models
|
||||
std::unique_ptr<Model>
|
||||
std::unique_ptr<Clump>
|
||||
specialmodels_[SpecialModel::SpecialModelCount];
|
||||
Model* getSpecialModel(SpecialModel usage) const {
|
||||
Clump* getSpecialModel(SpecialModel usage) const {
|
||||
return specialmodels_[usage].get();
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <engine/GameState.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
@ -17,12 +16,15 @@
|
||||
#include <rw_mingw.hpp>
|
||||
#endif
|
||||
|
||||
constexpr float kDrawDistanceFactor = 1.0f;
|
||||
constexpr float kDrawDistanceFactor = 1.5f;
|
||||
constexpr float kWorldDrawDistanceFactor = kDrawDistanceFactor;
|
||||
#if 0 // There's no distance based culling for these types of objects yet
|
||||
constexpr float kVehicleDrawDistanceFactor = kDrawDistanceFactor;
|
||||
#if 0 // There's no distance based culling for these types of objects yet
|
||||
constexpr float kPedestrianDrawDistanceFactor = kDrawDistanceFactor;
|
||||
#endif
|
||||
constexpr float kMagicLODDistance = 330.f;
|
||||
constexpr float kVehicleLODDistance = 70.f;
|
||||
constexpr float kVehicleDrawDistance = 280.f;
|
||||
|
||||
RenderKey createKey(bool transparent, float normalizedDepth,
|
||||
Renderer::Textures& textures) {
|
||||
@ -33,12 +35,10 @@ RenderKey createKey(bool transparent, float normalizedDepth,
|
||||
uint8_t(0xFF & (textures.size() > 0 ? textures[0] : 0)) << 0;
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderGeometry(Model* model, size_t g,
|
||||
const glm::mat4& modelMatrix, float opacity,
|
||||
void ObjectRenderer::renderGeometry(Geometry* geom,
|
||||
const glm::mat4& modelMatrix,
|
||||
GameObject* object, RenderList& outList) {
|
||||
for (size_t sg = 0; sg < model->geometries[g]->subgeom.size(); ++sg) {
|
||||
Model::SubGeometry& subgeom = model->geometries[g]->subgeom[sg];
|
||||
|
||||
for (SubGeometry& subgeom : geom->subgeom) {
|
||||
bool isTransparent = false;
|
||||
|
||||
Renderer::DrawParameters dp;
|
||||
@ -55,9 +55,8 @@ void ObjectRenderer::renderGeometry(Model* model, size_t g,
|
||||
!(modelinfo->flags & SimpleModelInfo::NO_ZBUFFER_WRITE);
|
||||
}
|
||||
|
||||
if (model->geometries[g]->materials.size() > subgeom.material) {
|
||||
Model::Material& mat =
|
||||
model->geometries[g]->materials[subgeom.material];
|
||||
if (geom->materials.size() > subgeom.material) {
|
||||
Geometry::Material& mat = geom->materials[subgeom.material];
|
||||
|
||||
if (mat.textures.size() > 0) {
|
||||
auto tex = mat.textures[0].texture;
|
||||
@ -69,8 +68,7 @@ void ObjectRenderer::renderGeometry(Model* model, size_t g,
|
||||
}
|
||||
}
|
||||
|
||||
if ((model->geometries[g]->flags &
|
||||
RW::BSGeometry::ModuleMaterialColor) ==
|
||||
if ((geom->flags & RW::BSGeometry::ModuleMaterialColor) ==
|
||||
RW::BSGeometry::ModuleMaterialColor) {
|
||||
dp.colour = mat.colour;
|
||||
|
||||
@ -86,7 +84,7 @@ void ObjectRenderer::renderGeometry(Model* model, size_t g,
|
||||
}
|
||||
}
|
||||
|
||||
dp.visibility = opacity;
|
||||
dp.visibility = 1.f;
|
||||
|
||||
if (dp.colour.a < 255) {
|
||||
isTransparent = true;
|
||||
@ -104,49 +102,48 @@ void ObjectRenderer::renderGeometry(Model* model, size_t g,
|
||||
(m_camera.frustum.far - m_camera.frustum.near);
|
||||
outList.emplace_back(
|
||||
createKey(isTransparent, depth * depth, dp.textures), modelMatrix,
|
||||
&model->geometries[g]->dbuff, dp);
|
||||
&geom->dbuff, dp);
|
||||
}
|
||||
}
|
||||
bool ObjectRenderer::renderFrame(Model* m, ModelFrame* f,
|
||||
const glm::mat4& matrix, GameObject* object,
|
||||
float opacity, RenderList& outList) {
|
||||
auto localmatrix = matrix;
|
||||
bool vis = true;
|
||||
|
||||
if (object && object->skeleton) {
|
||||
// Skeleton is loaded with the correct matrix via Animator.
|
||||
localmatrix *= object->skeleton->getMatrix(f);
|
||||
void ObjectRenderer::renderAtomic(Atomic* atomic,
|
||||
const glm::mat4& worldtransform,
|
||||
GameObject* object, RenderList& render) {
|
||||
RW_CHECK(atomic->getGeometry(), "Can't render an atomic without geometry");
|
||||
RW_CHECK(atomic->getFrame(), "Can't render an atomic without a frame");
|
||||
|
||||
vis = object->skeleton->getData(f->getIndex()).enabled;
|
||||
} else {
|
||||
localmatrix *= f->getTransform();
|
||||
const auto& geometry = atomic->getGeometry();
|
||||
const auto& frame = atomic->getFrame();
|
||||
|
||||
RW::BSGeometryBounds& bounds = geometry->geometryBounds;
|
||||
|
||||
auto transform = worldtransform * frame->getWorldTransform();
|
||||
|
||||
glm::vec3 boundpos = bounds.center + glm::vec3(transform[3]);
|
||||
if (!m_camera.frustum.intersects(boundpos, bounds.radius)) {
|
||||
culled++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vis) {
|
||||
for (size_t g : f->getGeometries()) {
|
||||
if (!object || !object->animator) {
|
||||
RW::BSGeometryBounds& bounds = m->geometries[g]->geometryBounds;
|
||||
renderGeometry(geometry.get(), transform, object, render);
|
||||
}
|
||||
|
||||
glm::vec3 boundpos = bounds.center + glm::vec3(localmatrix[3]);
|
||||
if (!m_camera.frustum.intersects(boundpos, bounds.radius)) {
|
||||
culled++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
renderGeometry(m, g, localmatrix, opacity, object, outList);
|
||||
void ObjectRenderer::renderClump(Clump* model, const glm::mat4& worldtransform,
|
||||
GameObject* object, RenderList& render) {
|
||||
for (const auto& atomic : model->getAtomics()) {
|
||||
const auto flags = atomic->getFlags();
|
||||
if ((flags & Atomic::ATOMIC_RENDER) == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (ModelFrame* c : f->getChildren()) {
|
||||
renderFrame(m, c, localmatrix, object, opacity, outList);
|
||||
renderAtomic(atomic.get(), worldtransform, object, render);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderInstance(InstanceObject* instance,
|
||||
RenderList& outList) {
|
||||
if (!instance->getModel()) {
|
||||
const auto& atomic = instance->getAtomic();
|
||||
if (!atomic) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -169,127 +166,61 @@ void ObjectRenderer::renderInstance(InstanceObject* instance,
|
||||
return;
|
||||
}
|
||||
|
||||
auto matrixModel = instance->getTimeAdjustedTransform(m_renderAlpha);
|
||||
float mindist = glm::length(instance->getPosition() - m_camera.position) /
|
||||
kDrawDistanceFactor;
|
||||
|
||||
float mindist = glm::length(instance->getPosition() - m_camera.position) -
|
||||
instance->getModel()->getBoundingRadius();
|
||||
mindist *= 1.f / kDrawDistanceFactor;
|
||||
if (mindist > modelinfo->getLargestLodDistance()) {
|
||||
culled++;
|
||||
return;
|
||||
}
|
||||
|
||||
Model* model = nullptr;
|
||||
ModelFrame* frame = nullptr;
|
||||
|
||||
// These are used to gracefully fade out things that are just out of view
|
||||
// distance.
|
||||
Model* fadingModel = nullptr;
|
||||
ModelFrame* fadingFrame = nullptr;
|
||||
auto fadingMatrix = matrixModel;
|
||||
float opacity = 0.f;
|
||||
constexpr float fadeRange = 50.f;
|
||||
|
||||
/// @todo replace this block with the correct logic
|
||||
if (modelinfo->getNumAtomics() == 1) {
|
||||
// Is closest point greater than the *object* draw distance
|
||||
float objectRange = modelinfo->getLodDistance(0);
|
||||
float overlap = (mindist - objectRange);
|
||||
if (mindist > objectRange) {
|
||||
// Check for LOD instances
|
||||
if (instance->LODinstance) {
|
||||
// Is the closest point greater than the *LOD* draw distance
|
||||
auto lodmodelinfo =
|
||||
instance->LODinstance->getModelInfo<SimpleModelInfo>();
|
||||
float LODrange = lodmodelinfo->getLodDistance(0);
|
||||
if (mindist <= LODrange && instance->LODinstance->getModel()) {
|
||||
// The model matrix needs to be for the LOD instead
|
||||
matrixModel =
|
||||
instance->LODinstance->getTimeAdjustedTransform(
|
||||
m_renderAlpha);
|
||||
// If the object is only just out of range, keep
|
||||
// rendering it and screen-door the LOD.
|
||||
if (overlap < fadeRange) {
|
||||
model = instance->LODinstance->getModel();
|
||||
fadingModel = instance->getModel();
|
||||
opacity = 1.f - (overlap / fadeRange);
|
||||
} else {
|
||||
model = instance->LODinstance->getModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
// We don't have a LOD object, so fade out gracefully.
|
||||
else if (overlap < fadeRange) {
|
||||
fadingModel = instance->getModel();
|
||||
opacity = 1.f - (overlap / fadeRange);
|
||||
}
|
||||
}
|
||||
// Otherwise, if we aren't marked as a LOD model, we can render
|
||||
else if (!modelinfo->LOD) {
|
||||
model = instance->getModel();
|
||||
}
|
||||
} else {
|
||||
auto root = instance->getModel()->frames[0];
|
||||
auto objectModel = instance->getModel();
|
||||
fadingFrame = nullptr;
|
||||
fadingModel = nullptr;
|
||||
|
||||
matrixModel *= root->getTransform();
|
||||
|
||||
for (int i = 0; i < modelinfo->getNumAtomics() - 1; ++i) {
|
||||
auto ind = (modelinfo->getNumAtomics() - 1) - i;
|
||||
float lodDistance = modelinfo->getLodDistance(i);
|
||||
if (mindist > lodDistance) {
|
||||
fadingFrame = root->getChildren()[ind];
|
||||
fadingModel = objectModel;
|
||||
opacity = 1.f - ((mindist - lodDistance) / fadeRange);
|
||||
} else {
|
||||
model = objectModel;
|
||||
frame = root->getChildren()[ind];
|
||||
}
|
||||
if (modelinfo->isBigBuilding() &&
|
||||
mindist < modelinfo->getNearLodDistance() &&
|
||||
mindist < kMagicLODDistance) {
|
||||
auto related = modelinfo->related();
|
||||
if (!related || related->isLoaded()) {
|
||||
culled++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (model) {
|
||||
frame = frame ? frame : model->frames[0];
|
||||
renderFrame(model, frame,
|
||||
matrixModel * glm::inverse(frame->getTransform()), instance,
|
||||
1.f, outList);
|
||||
Atomic* distanceatomic =
|
||||
modelinfo->getDistanceAtomic(mindist / kDrawDistanceFactor);
|
||||
if (!distanceatomic) {
|
||||
return;
|
||||
}
|
||||
if (fadingModel) {
|
||||
if (opacity >= 0.01f) {
|
||||
fadingFrame = fadingFrame ? fadingFrame : fadingModel->frames[0];
|
||||
renderFrame(
|
||||
fadingModel, fadingFrame,
|
||||
fadingMatrix * glm::inverse(fadingFrame->getTransform()),
|
||||
instance, opacity, outList);
|
||||
}
|
||||
|
||||
if (atomic->getGeometry() != distanceatomic->getGeometry()) {
|
||||
atomic->setGeometry(distanceatomic->getGeometry());
|
||||
}
|
||||
|
||||
// Render the atomic the instance thinks it should be
|
||||
renderAtomic(atomic.get(), glm::mat4(), instance, outList);
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderCharacter(CharacterObject* pedestrian,
|
||||
RenderList& outList) {
|
||||
glm::mat4 matrixModel;
|
||||
const auto& clump = pedestrian->getClump();
|
||||
|
||||
if (pedestrian->getCurrentVehicle()) {
|
||||
auto vehicle = pedestrian->getCurrentVehicle();
|
||||
const auto& vehicleclump = vehicle->getClump();
|
||||
auto seat = pedestrian->getCurrentSeat();
|
||||
matrixModel = vehicle->getTimeAdjustedTransform(m_renderAlpha);
|
||||
auto matrixModel = vehicleclump->getFrame()->getWorldTransform();
|
||||
if (pedestrian->isEnteringOrExitingVehicle()) {
|
||||
matrixModel = glm::translate(matrixModel,
|
||||
vehicle->getSeatEntryPosition(seat));
|
||||
clump->getFrame()->setTransform(matrixModel);
|
||||
} else {
|
||||
if (seat < vehicle->info->seats.size()) {
|
||||
matrixModel = glm::translate(matrixModel,
|
||||
vehicle->info->seats[seat].offset);
|
||||
clump->getFrame()->setTransform(matrixModel);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
matrixModel = pedestrian->getTimeAdjustedTransform(m_renderAlpha);
|
||||
}
|
||||
|
||||
if (!pedestrian->getModel()) return;
|
||||
|
||||
auto root = pedestrian->getModel()->frames[0];
|
||||
|
||||
renderFrame(pedestrian->getModel(), root->getChildren()[0], matrixModel,
|
||||
pedestrian, 1.f, outList);
|
||||
renderClump(pedestrian->getClump().get(), glm::mat4(), nullptr, outList);
|
||||
|
||||
auto item = pedestrian->getActiveItem();
|
||||
const auto& weapon = pedestrian->engine->data->weaponData[item];
|
||||
@ -298,81 +229,73 @@ void ObjectRenderer::renderCharacter(CharacterObject* pedestrian,
|
||||
return; // No model for this item
|
||||
}
|
||||
|
||||
auto handFrame = pedestrian->getModel()->findFrame("srhand");
|
||||
glm::mat4 localMatrix;
|
||||
auto handFrame = pedestrian->getClump()->findFrame("srhand");
|
||||
if (handFrame) {
|
||||
while (handFrame->getParent()) {
|
||||
localMatrix =
|
||||
pedestrian->skeleton->getMatrix(handFrame->getIndex()) *
|
||||
localMatrix;
|
||||
handFrame = handFrame->getParent();
|
||||
}
|
||||
auto simple =
|
||||
m_world->data->findModelInfo<SimpleModelInfo>(weapon->modelID);
|
||||
auto itematomic = simple->getAtomic(0);
|
||||
renderAtomic(itematomic, handFrame->getWorldTransform(), nullptr,
|
||||
outList);
|
||||
}
|
||||
|
||||
// Assume items are all simple
|
||||
auto simple =
|
||||
m_world->data->findModelInfo<SimpleModelInfo>(weapon->modelID);
|
||||
auto geometry = simple->getAtomic(0)->getGeometries().at(0);
|
||||
renderGeometry(simple->getModel(), geometry, matrixModel * localMatrix, 1.f,
|
||||
nullptr, outList);
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderVehicle(VehicleObject* vehicle,
|
||||
RenderList& outList) {
|
||||
RW_CHECK(vehicle->getModel(), "Vehicle model is null");
|
||||
|
||||
if (!vehicle->getModel()) {
|
||||
const auto& clump = vehicle->getClump();
|
||||
RW_CHECK(clump, "Vehicle clump is null");
|
||||
if (!clump) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::mat4 matrixModel = vehicle->getTimeAdjustedTransform(m_renderAlpha);
|
||||
float mindist = glm::length(vehicle->getPosition() - m_camera.position) / kVehicleDrawDistanceFactor;
|
||||
if (mindist < kVehicleLODDistance) {
|
||||
// Swich visibility to the high LOD
|
||||
vehicle->getHighLOD()->setFlag(Atomic::ATOMIC_RENDER, true);
|
||||
vehicle->getLowLOD()->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
} else if (mindist < kVehicleDrawDistance) {
|
||||
// Switch to low
|
||||
vehicle->getHighLOD()->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
vehicle->getLowLOD()->setFlag(Atomic::ATOMIC_RENDER, true);
|
||||
} else {
|
||||
culled++;
|
||||
return;
|
||||
}
|
||||
|
||||
renderFrame(vehicle->getModel(), vehicle->getModel()->frames[0],
|
||||
matrixModel, vehicle, 1.f, outList);
|
||||
renderClump(clump.get(), glm::mat4(), vehicle, outList);
|
||||
|
||||
auto modelinfo = vehicle->getVehicle();
|
||||
|
||||
// Draw wheels n' stuff
|
||||
auto woi =
|
||||
m_world->data->findModelInfo<SimpleModelInfo>(modelinfo->wheelmodel_);
|
||||
if (!woi || !woi->isLoaded()) {
|
||||
if (!woi || !woi->isLoaded() || !woi->getDistanceAtomic(mindist)) {
|
||||
return;
|
||||
}
|
||||
auto wheelgeom = woi->getAtomic(0)->getGeometries().at(0);
|
||||
|
||||
auto wheelatomic = woi->getDistanceAtomic(mindist);
|
||||
for (size_t w = 0; w < vehicle->info->wheels.size(); ++w) {
|
||||
auto& wi = vehicle->physVehicle->getWheelInfo(w);
|
||||
// Construct our own matrix so we can use the local transform
|
||||
vehicle->physVehicle->updateWheelTransform(w, false);
|
||||
/// @todo migrate this into Vehicle physics tick so we can
|
||||
/// interpolate old -> new
|
||||
|
||||
glm::mat4 wheelM(matrixModel);
|
||||
|
||||
auto up = -wi.m_wheelDirectionCS;
|
||||
auto right = wi.m_wheelAxleCS;
|
||||
auto fwd = up.cross(right);
|
||||
btQuaternion steerQ(up, wi.m_steering);
|
||||
btQuaternion rollQ(right, -wi.m_rotation);
|
||||
|
||||
btMatrix3x3 basis(right[0], fwd[0], up[0], right[1], fwd[1], up[1],
|
||||
right[2], fwd[2], up[2]);
|
||||
|
||||
btTransform t;
|
||||
t.setBasis(btMatrix3x3(steerQ) * btMatrix3x3(rollQ) * basis);
|
||||
t.setOrigin(wi.m_chassisConnectionPointCS +
|
||||
wi.m_wheelDirectionCS *
|
||||
wi.m_raycastInfo.m_suspensionLength);
|
||||
|
||||
btTransform t(
|
||||
btMatrix3x3(steerQ) * btMatrix3x3(rollQ) * basis,
|
||||
wi.m_chassisConnectionPointCS +
|
||||
wi.m_wheelDirectionCS * wi.m_raycastInfo.m_suspensionLength);
|
||||
glm::mat4 wheelM;
|
||||
t.getOpenGLMatrix(glm::value_ptr(wheelM));
|
||||
wheelM = matrixModel * wheelM;
|
||||
|
||||
wheelM = clump->getFrame()->getWorldTransform() * wheelM;
|
||||
wheelM = glm::scale(wheelM, glm::vec3(modelinfo->wheelscale_));
|
||||
if (wi.m_chassisConnectionPointCS.x() < 0.f) {
|
||||
wheelM = glm::scale(wheelM, glm::vec3(-1.f, 1.f, 1.f));
|
||||
}
|
||||
|
||||
renderGeometry(woi->getModel(), wheelgeom, wheelM, 1.f, nullptr,
|
||||
outList);
|
||||
renderAtomic(wheelatomic, wheelM, nullptr, outList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,59 +308,29 @@ void ObjectRenderer::renderPickup(PickupObject* pickup, RenderList& outList) {
|
||||
|
||||
auto odata = pickup->getModelInfo<SimpleModelInfo>();
|
||||
|
||||
auto model = odata->getModel();
|
||||
auto itemModel = odata->getAtomic(0);
|
||||
auto geom = 0;
|
||||
if (!itemModel->getGeometries().empty()) {
|
||||
geom = itemModel->getGeometries()[0];
|
||||
} else if (!itemModel->getChildren().empty()) {
|
||||
geom = itemModel->getChildren()[0]->getGeometries()[0];
|
||||
}
|
||||
auto atomic = odata->getAtomic(0);
|
||||
|
||||
renderGeometry(model, geom, modelMatrix, 1.f, pickup, outList);
|
||||
renderAtomic(atomic, modelMatrix, nullptr, outList);
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderCutsceneObject(CutsceneObject* cutscene,
|
||||
RenderList& outList) {
|
||||
if (!m_world->state->currentCutscene) return;
|
||||
const auto& clump = cutscene->getClump();
|
||||
|
||||
if (!cutscene->getModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::mat4 matrixModel;
|
||||
auto cutsceneOffset = m_world->state->currentCutscene->meta.sceneOffset +
|
||||
glm::vec3(0.f, 0.f, 1.f);
|
||||
glm::mat4 cutscenespace;
|
||||
|
||||
cutscenespace = glm::translate(cutscenespace, cutsceneOffset);
|
||||
if (cutscene->getParentActor()) {
|
||||
matrixModel = glm::translate(matrixModel, cutsceneOffset);
|
||||
// matrixModel =
|
||||
// cutscene->getParentActor()->getTimeAdjustedTransform(_renderAlpha);
|
||||
// matrixModel = glm::translate(matrixModel, glm::vec3(0.f, 0.f, 1.f));
|
||||
glm::mat4 localMatrix;
|
||||
auto boneframe = cutscene->getParentFrame();
|
||||
while (boneframe) {
|
||||
localMatrix = cutscene->getParentActor()->skeleton->getMatrix(
|
||||
boneframe->getIndex()) *
|
||||
localMatrix;
|
||||
boneframe = boneframe->getParent();
|
||||
}
|
||||
matrixModel = matrixModel * localMatrix;
|
||||
} else {
|
||||
matrixModel = glm::translate(matrixModel, cutsceneOffset);
|
||||
auto parent = cutscene->getParentFrame();
|
||||
cutscenespace *= parent->getWorldTransform();
|
||||
cutscenespace =
|
||||
glm::rotate(cutscenespace, glm::half_pi<float>(), {0.f, 1.f, 0.f});
|
||||
}
|
||||
|
||||
auto model = cutscene->getModel();
|
||||
if (cutscene->getParentActor()) {
|
||||
glm::mat4 align;
|
||||
/// @todo figure out where this 90 degree offset is coming from.
|
||||
align = glm::rotate(align, glm::half_pi<float>(), {0.f, 1.f, 0.f});
|
||||
renderFrame(model, model->frames[0], matrixModel * align, cutscene, 1.f,
|
||||
outList);
|
||||
} else {
|
||||
renderFrame(model, model->frames[0], matrixModel, cutscene, 1.f,
|
||||
outList);
|
||||
}
|
||||
renderClump(clump.get(), cutscenespace, nullptr, outList);
|
||||
}
|
||||
|
||||
void ObjectRenderer::renderProjectile(ProjectileObject* projectile,
|
||||
@ -447,18 +340,11 @@ void ObjectRenderer::renderProjectile(ProjectileObject* projectile,
|
||||
auto odata = m_world->data->findModelInfo<SimpleModelInfo>(
|
||||
projectile->getProjectileInfo().weapon->modelID);
|
||||
|
||||
auto model = odata->getModel();
|
||||
auto modelframe = odata->getAtomic(0);
|
||||
auto geom = modelframe->getGeometries().at(0);
|
||||
|
||||
renderGeometry(model, geom, modelMatrix, 1.f, projectile, outList);
|
||||
auto atomic = odata->getAtomic(0);
|
||||
renderAtomic(atomic, modelMatrix, nullptr, outList);
|
||||
}
|
||||
|
||||
void ObjectRenderer::buildRenderList(GameObject* object, RenderList& outList) {
|
||||
if (object->skeleton) {
|
||||
object->skeleton->interpolate(m_renderAlpha);
|
||||
}
|
||||
|
||||
// Right now specialized on each object type
|
||||
switch (object->type()) {
|
||||
case GameObject::Instance:
|
||||
|
@ -37,11 +37,25 @@ public:
|
||||
size_t culled;
|
||||
void buildRenderList(GameObject* object, RenderList& outList);
|
||||
|
||||
bool renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
GameObject* object, float opacity, RenderList& outList);
|
||||
void renderGeometry(Geometry* geom, const glm::mat4& modelMatrix,
|
||||
GameObject* object, RenderList& outList);
|
||||
|
||||
void renderGeometry(Model* model, size_t g, const glm::mat4& modelMatrix,
|
||||
float opacity, GameObject* object, RenderList& outList);
|
||||
/**
|
||||
* @brief renderAtomic renders an atomic with the given worldtransform
|
||||
* @param model
|
||||
* @param atomic
|
||||
* @param worldtransform
|
||||
* @param object
|
||||
*/
|
||||
void renderAtomic(Atomic* atomic, const glm::mat4& worldtransform, GameObject* object, RenderList& render);
|
||||
|
||||
/**
|
||||
* @brief renderClump Renders all visible atomics in the clump
|
||||
* @param model
|
||||
* @param worldtransform
|
||||
* @param render
|
||||
*/
|
||||
void renderClump(Clump* model, const glm::mat4& worldtransform, GameObject* object, RenderList& render);
|
||||
|
||||
private:
|
||||
GameWorld* m_world;
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <engine/GameState.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <core/Logger.hpp>
|
||||
#include <ai/PlayerController.hpp>
|
||||
#include <data/CutsceneData.hpp>
|
||||
@ -8455,11 +8454,15 @@ void opcode_02f4(const ScriptArguments& args, const ScriptObject object0, const
|
||||
RW_UNUSED(model);
|
||||
RW_UNUSED(object1);
|
||||
auto id = args[1].integer;
|
||||
auto actor = args.getObject<CutsceneObject>(0);
|
||||
auto actor = static_cast<CutsceneObject*>(args.getObject<CutsceneObject>(0));
|
||||
CutsceneObject* object = args.getWorld()->createCutsceneObject(id, args.getWorld()->state->currentCutscene->meta.sceneOffset );
|
||||
|
||||
auto headframe = actor->getModel()->findFrame("shead");
|
||||
actor->skeleton->setEnabled(headframe, false);
|
||||
auto headframe = actor->getClump()->findFrame("shead");
|
||||
for (const auto& atomic : actor->getClump()->getAtomics()) {
|
||||
if (atomic->getFrame().get() == headframe) {
|
||||
atomic->setFlag(Atomic::ATOMIC_RENDER, false);
|
||||
}
|
||||
}
|
||||
object->setParentActor(actor, headframe);
|
||||
|
||||
*args[2].globalInteger = object->getGameObjectID();
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <ai/PlayerController.hpp>
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <dynamics/CollisionInstance.hpp>
|
||||
#include <dynamics/RaycastCallbacks.hpp>
|
||||
|
@ -23,8 +23,8 @@ SET(RWLIB_SOURCES
|
||||
"source/platform/FileIndex.hpp"
|
||||
"source/platform/FileIndex.cpp"
|
||||
|
||||
"source/data/Model.hpp"
|
||||
"source/data/Model.cpp"
|
||||
"source/data/Clump.hpp"
|
||||
"source/data/Clump.cpp"
|
||||
|
||||
"source/loaders/LoaderIMG.hpp"
|
||||
"source/loaders/LoaderIMG.cpp"
|
||||
|
145
rwlib/source/data/Clump.cpp
Normal file
145
rwlib/source/data/Clump.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "data/Clump.hpp"
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Geometry::Geometry() : flags(0) {
|
||||
}
|
||||
|
||||
Geometry::~Geometry() {
|
||||
}
|
||||
|
||||
ModelFrame::ModelFrame(unsigned int index, glm::mat3 dR, glm::vec3 dT)
|
||||
: index(index)
|
||||
, defaultRotation(dR)
|
||||
, defaultTranslation(dT)
|
||||
, parent_(nullptr) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void ModelFrame::reset() {
|
||||
matrix = glm::translate(glm::mat4(), defaultTranslation) *
|
||||
glm::mat4(defaultRotation);
|
||||
updateHierarchyTransform();
|
||||
}
|
||||
|
||||
void ModelFrame::updateHierarchyTransform() {
|
||||
// Update our own transformation
|
||||
if (parent_) {
|
||||
worldtransform_ = parent_->getWorldTransform() * matrix;
|
||||
} else {
|
||||
worldtransform_ = matrix;
|
||||
}
|
||||
for (const auto& child : children_) {
|
||||
child->updateHierarchyTransform();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelFrame::addChild(ModelFramePtr child) {
|
||||
// Make sure the child is an orphan
|
||||
if (child->getParent()) {
|
||||
auto& other_children = child->getParent()->children_;
|
||||
other_children.erase(
|
||||
std::remove(other_children.begin(), other_children.end(), child),
|
||||
other_children.end());
|
||||
}
|
||||
child->parent_ = this;
|
||||
children_.push_back(child);
|
||||
}
|
||||
|
||||
ModelFrame* ModelFrame::findDescendant(const std::string& name) const {
|
||||
for (const auto& frame : children_) {
|
||||
if (frame->getName() == name) {
|
||||
return frame.get();
|
||||
}
|
||||
|
||||
auto result = frame->findDescendant(name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModelFramePtr ModelFrame::cloneHierarchy() const {
|
||||
auto self = std::make_shared<ModelFrame>(getIndex(), getDefaultRotation(),
|
||||
getDefaultTranslation());
|
||||
self->setName(getName());
|
||||
for (const auto& child : getChildren()) {
|
||||
auto childclone = child->cloneHierarchy();
|
||||
self->addChild(childclone);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
AtomicPtr Atomic::clone() const {
|
||||
auto newatomic = std::make_shared<Atomic>();
|
||||
newatomic->setGeometry(getGeometry());
|
||||
newatomic->setFrame(getFrame());
|
||||
newatomic->setFlags(getFlags());
|
||||
return newatomic;
|
||||
}
|
||||
|
||||
ModelFrame* Clump::findFrame(const std::string& name) const {
|
||||
if (rootframe_->getName() == name) {
|
||||
return rootframe_.get();
|
||||
}
|
||||
|
||||
return rootframe_->findDescendant(name);
|
||||
}
|
||||
|
||||
Clump::~Clump() {
|
||||
}
|
||||
|
||||
void Clump::recalculateMetrics() {
|
||||
boundingRadius = std::numeric_limits<float>::min();
|
||||
for (const auto& atomic : atomics_) {
|
||||
const auto& geometry = atomic->getGeometry();
|
||||
if (!geometry) {
|
||||
continue;
|
||||
}
|
||||
const auto& bounds = geometry->geometryBounds;
|
||||
boundingRadius = std::max(boundingRadius,
|
||||
glm::length(bounds.center) + bounds.radius);
|
||||
}
|
||||
}
|
||||
|
||||
Clump* Clump::clone() const {
|
||||
// Clone frame hierarchy
|
||||
auto newroot = rootframe_->cloneHierarchy();
|
||||
auto clump = new Clump;
|
||||
clump->setFrame(newroot);
|
||||
|
||||
// This isn't the most optimal implementation, but this code is likely
|
||||
// to be replaced soon.
|
||||
auto find_new_frame = [&](const ModelFramePtr& old) -> ModelFramePtr {
|
||||
std::queue<ModelFramePtr> open;
|
||||
open.push(newroot);
|
||||
while (!open.empty()) {
|
||||
auto frame = open.front();
|
||||
open.pop();
|
||||
if (frame->getIndex() == old->getIndex()) {
|
||||
return frame;
|
||||
}
|
||||
for (const auto& child : frame->getChildren()) {
|
||||
open.push(child);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// Generate new atomics
|
||||
for (const auto& atomic : getAtomics()) {
|
||||
auto newatomic = atomic->clone();
|
||||
// Replace the original frame with the cloned frame
|
||||
if (atomic->getFrame()) {
|
||||
newatomic->setFrame(find_new_frame(atomic->getFrame()));
|
||||
}
|
||||
clump->addAtomic(newatomic);
|
||||
}
|
||||
|
||||
return clump;
|
||||
}
|
283
rwlib/source/data/Clump.hpp
Normal file
283
rwlib/source/data/Clump.hpp
Normal file
@ -0,0 +1,283 @@
|
||||
#pragma once
|
||||
#ifndef _MODEL_HPP_
|
||||
#define _MODEL_HPP_
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <gl/DrawBuffer.hpp>
|
||||
#include <gl/GeometryBuffer.hpp>
|
||||
#include <gl/TextureData.hpp>
|
||||
#include <loaders/RWBinaryStream.hpp>
|
||||
|
||||
// Forward Declerations
|
||||
class ModelFrame;
|
||||
struct Geometry;
|
||||
class Atomic;
|
||||
class Clump;
|
||||
|
||||
// Pointer types
|
||||
using ModelFramePtr = std::shared_ptr<ModelFrame>;
|
||||
using GeometryPtr = std::shared_ptr<Geometry>;
|
||||
using AtomicPtr = std::shared_ptr<Atomic>;
|
||||
using AtomicList = std::vector<AtomicPtr>;
|
||||
using ClumpPtr = std::shared_ptr<Clump>;
|
||||
|
||||
/**
|
||||
* ModelFrame stores transformation hierarchy
|
||||
*/
|
||||
class ModelFrame {
|
||||
unsigned int index;
|
||||
glm::mat3 defaultRotation;
|
||||
glm::vec3 defaultTranslation;
|
||||
glm::mat4 matrix;
|
||||
glm::mat4 worldtransform_;
|
||||
ModelFrame* parent_;
|
||||
std::string name;
|
||||
std::vector<ModelFramePtr> children_;
|
||||
|
||||
public:
|
||||
ModelFrame(unsigned int index = 0, glm::mat3 dR = glm::mat3(),
|
||||
glm::vec3 dT = glm::vec3());
|
||||
|
||||
void reset();
|
||||
|
||||
void setTransform(const glm::mat4& m) {
|
||||
matrix = m;
|
||||
updateHierarchyTransform();
|
||||
}
|
||||
|
||||
const glm::mat4& getTransform() const {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void setName(const std::string& fname) {
|
||||
name = fname;
|
||||
}
|
||||
|
||||
unsigned int getIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
glm::vec3 getDefaultTranslation() const {
|
||||
return defaultTranslation;
|
||||
}
|
||||
|
||||
glm::mat3 getDefaultRotation() const {
|
||||
return defaultRotation;
|
||||
}
|
||||
|
||||
void setTranslation(const glm::vec3& t) {
|
||||
matrix[3] = glm::vec4(t, matrix[3][3]);
|
||||
updateHierarchyTransform();
|
||||
}
|
||||
|
||||
void setRotation(const glm::mat3& r) {
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
matrix[i] = glm::vec4(r[i], matrix[i][3]);
|
||||
}
|
||||
updateHierarchyTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cached matrix
|
||||
*/
|
||||
void updateHierarchyTransform();
|
||||
|
||||
/**
|
||||
* @return the cached world transformation for this Frame
|
||||
*/
|
||||
const glm::mat4& getWorldTransform() const {
|
||||
return worldtransform_;
|
||||
}
|
||||
|
||||
ModelFrame* getParent() const {
|
||||
return parent_;
|
||||
}
|
||||
|
||||
void addChild(ModelFramePtr child);
|
||||
|
||||
const std::vector<ModelFramePtr>& getChildren() const {
|
||||
return children_;
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
ModelFrame* findDescendant(const std::string& name) const;
|
||||
|
||||
ModelFramePtr cloneHierarchy() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subgeometry
|
||||
*/
|
||||
|
||||
struct SubGeometry {
|
||||
GLuint start = 0;
|
||||
size_t material = 0;
|
||||
std::vector<uint32_t> indices;
|
||||
size_t numIndices = 0;
|
||||
};
|
||||
|
||||
struct GeometryVertex {
|
||||
glm::vec3 position; /* 0 */
|
||||
glm::vec3 normal; /* 24 */
|
||||
glm::vec2 texcoord; /* 48 */
|
||||
glm::u8vec4 colour; /* 64 */
|
||||
|
||||
/** @see GeometryBuffer */
|
||||
static const AttributeList vertex_attributes() {
|
||||
return {{ATRS_Position, 3, sizeof(GeometryVertex), 0ul},
|
||||
{ATRS_Normal, 3, sizeof(GeometryVertex), sizeof(float) * 3},
|
||||
{ATRS_TexCoord, 2, sizeof(GeometryVertex), sizeof(float) * 6},
|
||||
{ATRS_Colour, 4, sizeof(GeometryVertex), sizeof(float) * 8,
|
||||
GL_UNSIGNED_BYTE}};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Geometry
|
||||
*/
|
||||
struct Geometry {
|
||||
enum FaceType { Triangles = 0, TriangleStrip = 1 };
|
||||
|
||||
struct Texture {
|
||||
std::string name;
|
||||
std::string alphaName;
|
||||
TextureData::Handle texture;
|
||||
};
|
||||
|
||||
enum { MTF_PrimaryColour = 1 << 0, MTF_SecondaryColour = 1 << 1 };
|
||||
|
||||
struct Material {
|
||||
std::vector<Texture> textures;
|
||||
glm::u8vec4 colour;
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
float diffuseIntensity;
|
||||
float ambientIntensity;
|
||||
};
|
||||
|
||||
DrawBuffer dbuff;
|
||||
GeometryBuffer gbuff;
|
||||
|
||||
GLuint EBO;
|
||||
|
||||
RW::BSGeometryBounds geometryBounds;
|
||||
|
||||
uint32_t clumpNum;
|
||||
|
||||
FaceType facetype;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
std::vector<Material> materials;
|
||||
std::vector<SubGeometry> subgeom;
|
||||
|
||||
Geometry();
|
||||
~Geometry();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The Atomic struct
|
||||
*/
|
||||
class Atomic {
|
||||
ModelFramePtr frame_;
|
||||
GeometryPtr geometry_;
|
||||
uint32_t flags_;
|
||||
|
||||
public:
|
||||
enum {
|
||||
// If this is set, the atomic will be rendered
|
||||
ATOMIC_RENDER = 0x04
|
||||
};
|
||||
|
||||
void setFrame(ModelFramePtr frame) {
|
||||
frame_ = frame;
|
||||
}
|
||||
|
||||
const ModelFramePtr& getFrame() const {
|
||||
return frame_;
|
||||
}
|
||||
|
||||
void setGeometry(GeometryPtr geom) {
|
||||
geometry_ = geom;
|
||||
}
|
||||
|
||||
const GeometryPtr& getGeometry() const {
|
||||
return geometry_;
|
||||
}
|
||||
|
||||
void setFlags(uint32_t flags) {
|
||||
flags_ = flags;
|
||||
}
|
||||
|
||||
uint32_t getFlags() const {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void setFlag(uint32_t flag, bool set) {
|
||||
if (set) {
|
||||
flags_ |= flag;
|
||||
}
|
||||
else {
|
||||
flags_ &= ~flag;
|
||||
}
|
||||
}
|
||||
|
||||
AtomicPtr clone() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* A clump is a collection of Frames and Atomics
|
||||
*/
|
||||
class Clump {
|
||||
public:
|
||||
/**
|
||||
* @brief findFrame Locates frame with name anywhere in the hierarchy
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
ModelFrame* findFrame(const std::string& name) const;
|
||||
|
||||
~Clump();
|
||||
|
||||
void recalculateMetrics();
|
||||
|
||||
float getBoundingRadius() const {
|
||||
return boundingRadius;
|
||||
}
|
||||
|
||||
void addAtomic(AtomicPtr& atomic) {
|
||||
atomics_.push_back(atomic);
|
||||
}
|
||||
|
||||
const AtomicList& getAtomics() const {
|
||||
return atomics_;
|
||||
}
|
||||
|
||||
void setFrame(ModelFramePtr& root) {
|
||||
rootframe_ = root;
|
||||
}
|
||||
|
||||
const ModelFramePtr& getFrame() const {
|
||||
return rootframe_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A Copy of the frames and atomics in this clump
|
||||
*/
|
||||
Clump* clone() const;
|
||||
|
||||
private:
|
||||
float boundingRadius;
|
||||
AtomicList atomics_;
|
||||
ModelFramePtr rootframe_;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,46 +0,0 @@
|
||||
#include "data/Model.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Model::Geometry::Geometry() : flags(0) {
|
||||
}
|
||||
|
||||
Model::Geometry::~Geometry() {
|
||||
}
|
||||
|
||||
ModelFrame::ModelFrame(unsigned int index, ModelFrame* parent, glm::mat3 dR,
|
||||
glm::vec3 dT)
|
||||
: index(index)
|
||||
, defaultRotation(dR)
|
||||
, defaultTranslation(dT)
|
||||
, parentFrame(parent) {
|
||||
if (parent != nullptr) {
|
||||
parent->childs.push_back(this);
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
void ModelFrame::reset() {
|
||||
matrix = glm::translate(glm::mat4(), defaultTranslation) *
|
||||
glm::mat4(defaultRotation);
|
||||
}
|
||||
|
||||
void ModelFrame::addGeometry(size_t idx) {
|
||||
geometries.push_back(idx);
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
for (auto mf : frames) {
|
||||
delete mf;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::recalculateMetrics() {
|
||||
boundingRadius = std::numeric_limits<float>::min();
|
||||
for (size_t g = 0; g < geometries.size(); g++) {
|
||||
RW::BSGeometryBounds& bounds = geometries[g]->geometryBounds;
|
||||
boundingRadius = std::max(boundingRadius,
|
||||
glm::length(bounds.center) + bounds.radius);
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef _MODEL_HPP_
|
||||
#define _MODEL_HPP_
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <gl/DrawBuffer.hpp>
|
||||
#include <gl/GeometryBuffer.hpp>
|
||||
#include <gl/TextureData.hpp>
|
||||
#include <loaders/RWBinaryStream.hpp>
|
||||
|
||||
/**
|
||||
* ModelFrame stores the hierarchy of a model's geometry as well as default
|
||||
* transformations.
|
||||
*/
|
||||
class ModelFrame {
|
||||
unsigned int index;
|
||||
glm::mat3 defaultRotation;
|
||||
glm::vec3 defaultTranslation;
|
||||
glm::mat4 matrix;
|
||||
ModelFrame* parentFrame;
|
||||
std::string name;
|
||||
std::vector<size_t> geometries;
|
||||
std::vector<ModelFrame*> childs;
|
||||
|
||||
public:
|
||||
ModelFrame(unsigned int index, ModelFrame* parent, glm::mat3 dR,
|
||||
glm::vec3 dT);
|
||||
|
||||
void reset();
|
||||
void setTransform(const glm::mat4& m);
|
||||
const glm::mat4& getTransform() const {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void setName(const std::string& fname) {
|
||||
name = fname;
|
||||
}
|
||||
|
||||
void addGeometry(size_t idx);
|
||||
|
||||
unsigned int getIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
glm::vec3 getDefaultTranslation() const {
|
||||
return defaultTranslation;
|
||||
}
|
||||
|
||||
glm::mat3 getDefaultRotation() const {
|
||||
return defaultRotation;
|
||||
}
|
||||
|
||||
glm::mat4 getMatrix() const {
|
||||
return (parentFrame ? parentFrame->getMatrix() : glm::mat4()) * matrix;
|
||||
}
|
||||
|
||||
ModelFrame* getParent() const {
|
||||
return parentFrame;
|
||||
}
|
||||
|
||||
const std::vector<ModelFrame*>& getChildren() const {
|
||||
return childs;
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
const std::vector<size_t>& getGeometries() const {
|
||||
return geometries;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Model stores all the data contained within a DFF, as well as data required
|
||||
* to render them.
|
||||
*/
|
||||
class Model {
|
||||
public:
|
||||
enum FaceType { Triangles = 0, TriangleStrip = 1 };
|
||||
|
||||
std::uint32_t numAtomics;
|
||||
|
||||
struct Texture {
|
||||
std::string name;
|
||||
std::string alphaName;
|
||||
TextureData::Handle texture;
|
||||
};
|
||||
|
||||
enum { MTF_PrimaryColour = 1 << 0, MTF_SecondaryColour = 1 << 1 };
|
||||
|
||||
struct Material {
|
||||
std::vector<Texture> textures;
|
||||
glm::u8vec4 colour;
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
float diffuseIntensity;
|
||||
float ambientIntensity;
|
||||
};
|
||||
|
||||
struct SubGeometry {
|
||||
GLuint start = 0;
|
||||
size_t material;
|
||||
std::vector<uint32_t> indices;
|
||||
size_t numIndices;
|
||||
};
|
||||
|
||||
struct GeometryVertex {
|
||||
glm::vec3 position; /* 0 */
|
||||
glm::vec3 normal; /* 24 */
|
||||
glm::vec2 texcoord; /* 48 */
|
||||
glm::u8vec4 colour; /* 64 */
|
||||
|
||||
/** @see GeometryBuffer */
|
||||
static const AttributeList vertex_attributes() {
|
||||
return {
|
||||
{ATRS_Position, 3, sizeof(GeometryVertex), 0ul},
|
||||
{ATRS_Normal, 3, sizeof(GeometryVertex), sizeof(float) * 3},
|
||||
{ATRS_TexCoord, 2, sizeof(GeometryVertex), sizeof(float) * 6},
|
||||
{ATRS_Colour, 4, sizeof(GeometryVertex), sizeof(float) * 8,
|
||||
GL_UNSIGNED_BYTE}};
|
||||
}
|
||||
};
|
||||
|
||||
struct Geometry {
|
||||
DrawBuffer dbuff;
|
||||
GeometryBuffer gbuff;
|
||||
|
||||
GLuint EBO;
|
||||
|
||||
RW::BSGeometryBounds geometryBounds;
|
||||
|
||||
uint32_t clumpNum;
|
||||
|
||||
FaceType facetype;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
std::vector<Material> materials;
|
||||
std::vector<SubGeometry> subgeom;
|
||||
|
||||
Geometry();
|
||||
~Geometry();
|
||||
};
|
||||
|
||||
struct Atomic {
|
||||
uint32_t frame;
|
||||
uint32_t geometry;
|
||||
};
|
||||
|
||||
std::vector<ModelFrame*> frames;
|
||||
/** @TODO clean up this mess a little */
|
||||
std::vector<std::shared_ptr<Geometry>> geometries;
|
||||
std::vector<Atomic> atomics;
|
||||
|
||||
int32_t rootFrameIdx;
|
||||
|
||||
ModelFrame* findFrame(const std::string& name) const {
|
||||
auto fit =
|
||||
std::find_if(frames.begin(), frames.end(),
|
||||
[&](ModelFrame* f) { return f->getName() == name; });
|
||||
return fit != frames.end() ? *fit : nullptr;
|
||||
}
|
||||
|
||||
~Model();
|
||||
|
||||
void recalculateMetrics();
|
||||
|
||||
float getBoundingRadius() const {
|
||||
return boundingRadius;
|
||||
}
|
||||
|
||||
private:
|
||||
float boundingRadius;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,5 +1,6 @@
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <loaders/LoaderDFF.hpp>
|
||||
#include <rw/defines.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@ -40,7 +41,7 @@ struct RWBSFrame {
|
||||
uint32_t matrixflags; // Not used
|
||||
};
|
||||
|
||||
void LoaderDFF::readFrameList(Model *model, const RWBStream &stream) {
|
||||
LoaderDFF::FrameList LoaderDFF::readFrameList(const RWBStream &stream) {
|
||||
auto listStream = stream.getInnerStream();
|
||||
|
||||
auto listStructID = listStream.getNextChunk();
|
||||
@ -53,21 +54,22 @@ void LoaderDFF::readFrameList(Model *model, const RWBStream &stream) {
|
||||
unsigned int numFrames = *(std::uint32_t *)headerPtr;
|
||||
headerPtr += sizeof(std::uint32_t);
|
||||
|
||||
model->frames.reserve(numFrames);
|
||||
FrameList framelist;
|
||||
framelist.reserve(numFrames);
|
||||
|
||||
for (size_t f = 0; f < numFrames; ++f) {
|
||||
auto data = (RWBSFrame *)headerPtr;
|
||||
headerPtr += sizeof(RWBSFrame);
|
||||
auto frame =
|
||||
std::make_shared<ModelFrame>(f, data->rotation, data->position);
|
||||
|
||||
ModelFrame *parent = nullptr;
|
||||
if (data->index != -1) {
|
||||
parent = model->frames[data->index];
|
||||
} else {
|
||||
model->rootFrameIdx = f;
|
||||
RW_CHECK(data->index < int(framelist.size()),
|
||||
"Frame parent out of bounds");
|
||||
if (data->index != -1 && data->index < int(framelist.size())) {
|
||||
framelist[data->index]->addChild(frame);
|
||||
}
|
||||
|
||||
auto frame = new ModelFrame(f, parent, data->rotation, data->position);
|
||||
model->frames.push_back(frame);
|
||||
framelist.push_back(frame);
|
||||
}
|
||||
|
||||
size_t namedFrames = 0;
|
||||
@ -87,8 +89,8 @@ void LoaderDFF::readFrameList(Model *model, const RWBStream &stream) {
|
||||
std::transform(fname.begin(), fname.end(),
|
||||
fname.begin(), ::tolower);
|
||||
|
||||
if (namedFrames < model->frames.size()) {
|
||||
model->frames[namedFrames++]->setName(fname);
|
||||
if (namedFrames < framelist.size()) {
|
||||
framelist[namedFrames++]->setName(fname);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
@ -100,9 +102,11 @@ void LoaderDFF::readFrameList(Model *model, const RWBStream &stream) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return framelist;
|
||||
}
|
||||
|
||||
void LoaderDFF::readGeometryList(Model *model, const RWBStream &stream) {
|
||||
LoaderDFF::GeometryList LoaderDFF::readGeometryList(const RWBStream &stream) {
|
||||
auto listStream = stream.getInnerStream();
|
||||
|
||||
auto listStructID = listStream.getNextChunk();
|
||||
@ -115,21 +119,24 @@ void LoaderDFF::readGeometryList(Model *model, const RWBStream &stream) {
|
||||
unsigned int numGeometries = *(std::uint32_t *)headerPtr;
|
||||
headerPtr += sizeof(std::uint32_t);
|
||||
|
||||
model->geometries.reserve(numGeometries);
|
||||
std::vector<GeometryPtr> geometrylist;
|
||||
geometrylist.reserve(numGeometries);
|
||||
|
||||
for (auto chunkID = listStream.getNextChunk(); chunkID != 0;
|
||||
chunkID = listStream.getNextChunk()) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_GEOMETRY:
|
||||
readGeometry(model, listStream);
|
||||
break;
|
||||
case CHUNK_GEOMETRY: {
|
||||
geometrylist.push_back(readGeometry(listStream));
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return geometrylist;
|
||||
}
|
||||
|
||||
void LoaderDFF::readGeometry(Model *model, const RWBStream &stream) {
|
||||
GeometryPtr LoaderDFF::readGeometry(const RWBStream &stream) {
|
||||
auto geomStream = stream.getInnerStream();
|
||||
|
||||
auto geomStructID = geomStream.getNextChunk();
|
||||
@ -137,7 +144,7 @@ void LoaderDFF::readGeometry(Model *model, const RWBStream &stream) {
|
||||
throw DFFLoaderException("Geometry missing struct chunk");
|
||||
}
|
||||
|
||||
std::shared_ptr<Model::Geometry> geom(new Model::Geometry);
|
||||
auto geom = std::make_shared<Geometry>();
|
||||
|
||||
char *headerPtr = geomStream.getCursor();
|
||||
|
||||
@ -156,7 +163,7 @@ void LoaderDFF::readGeometry(Model *model, const RWBStream &stream) {
|
||||
/*unsigned int numFrames = *(std::uint32_t*)headerPtr;*/
|
||||
headerPtr += sizeof(std::uint32_t);
|
||||
|
||||
std::vector<Model::GeometryVertex> verts;
|
||||
std::vector<GeometryVertex> verts;
|
||||
verts.resize(numVerts);
|
||||
|
||||
if (geomStream.getChunkVersion() < 0x1003FFFF) {
|
||||
@ -216,26 +223,24 @@ void LoaderDFF::readGeometry(Model *model, const RWBStream &stream) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add the geometry to the model now so that it can be accessed.
|
||||
model->geometries.push_back(geom);
|
||||
|
||||
// Process the geometry child sections
|
||||
for (auto chunkID = geomStream.getNextChunk(); chunkID != 0;
|
||||
chunkID = geomStream.getNextChunk()) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_MATERIALLIST:
|
||||
readMaterialList(model, geomStream);
|
||||
readMaterialList(geom, geomStream);
|
||||
break;
|
||||
case CHUNK_EXTENSION:
|
||||
readGeometryExtension(model, geomStream);
|
||||
readGeometryExtension(geom, geomStream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
geom->dbuff.setFaceType(
|
||||
geom->facetype == Model::Triangles ? GL_TRIANGLES : GL_TRIANGLE_STRIP);
|
||||
geom->dbuff.setFaceType(geom->facetype == Geometry::Triangles
|
||||
? GL_TRIANGLES
|
||||
: GL_TRIANGLE_STRIP);
|
||||
geom->gbuff.uploadVertices(verts);
|
||||
geom->dbuff.addGeometry(&geom->gbuff);
|
||||
|
||||
@ -244,16 +249,18 @@ void LoaderDFF::readGeometry(Model *model, const RWBStream &stream) {
|
||||
|
||||
size_t icount = std::accumulate(
|
||||
geom->subgeom.begin(), geom->subgeom.end(), 0u,
|
||||
[](size_t a, const Model::SubGeometry &b) { return a + b.numIndices; });
|
||||
[](size_t a, const SubGeometry &b) { return a + b.numIndices; });
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * icount, 0,
|
||||
GL_STATIC_DRAW);
|
||||
for (auto &sg : geom->subgeom) {
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, sg.start * sizeof(uint32_t),
|
||||
sizeof(uint32_t) * sg.numIndices, sg.indices.data());
|
||||
}
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
void LoaderDFF::readMaterialList(Model *model, const RWBStream &stream) {
|
||||
void LoaderDFF::readMaterialList(GeometryPtr &geom, const RWBStream &stream) {
|
||||
auto listStream = stream.getInnerStream();
|
||||
|
||||
auto listStructID = listStream.getNextChunk();
|
||||
@ -263,13 +270,13 @@ void LoaderDFF::readMaterialList(Model *model, const RWBStream &stream) {
|
||||
|
||||
unsigned int numMaterials = *(std::uint32_t *)listStream.getCursor();
|
||||
|
||||
model->geometries.back()->materials.reserve(numMaterials);
|
||||
geom->materials.reserve(numMaterials);
|
||||
|
||||
RWBStream::ChunkID chunkID;
|
||||
while ((chunkID = listStream.getNextChunk())) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_MATERIAL:
|
||||
readMaterial(model, listStream);
|
||||
readMaterial(geom, listStream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -277,7 +284,7 @@ void LoaderDFF::readMaterialList(Model *model, const RWBStream &stream) {
|
||||
}
|
||||
}
|
||||
|
||||
void LoaderDFF::readMaterial(Model *model, const RWBStream &stream) {
|
||||
void LoaderDFF::readMaterial(GeometryPtr &geom, const RWBStream &stream) {
|
||||
auto materialStream = stream.getInnerStream();
|
||||
|
||||
auto matStructID = materialStream.getNextChunk();
|
||||
@ -287,7 +294,7 @@ void LoaderDFF::readMaterial(Model *model, const RWBStream &stream) {
|
||||
|
||||
char *matData = materialStream.getCursor();
|
||||
|
||||
Model::Material material;
|
||||
Geometry::Material material;
|
||||
|
||||
// Unkown
|
||||
matData += sizeof(std::uint32_t);
|
||||
@ -306,21 +313,22 @@ void LoaderDFF::readMaterial(Model *model, const RWBStream &stream) {
|
||||
matData += sizeof(float);
|
||||
material.flags = 0;
|
||||
|
||||
model->geometries.back()->materials.push_back(material);
|
||||
|
||||
RWBStream::ChunkID chunkID;
|
||||
while ((chunkID = materialStream.getNextChunk())) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_TEXTURE:
|
||||
readTexture(model, materialStream);
|
||||
readTexture(material, materialStream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
geom->materials.push_back(material);
|
||||
}
|
||||
|
||||
void LoaderDFF::readTexture(Model *model, const RWBStream &stream) {
|
||||
void LoaderDFF::readTexture(Geometry::Material &material,
|
||||
const RWBStream &stream) {
|
||||
auto texStream = stream.getInnerStream();
|
||||
|
||||
auto texStructID = texStream.getNextChunk();
|
||||
@ -343,18 +351,18 @@ void LoaderDFF::readTexture(Model *model, const RWBStream &stream) {
|
||||
|
||||
TextureData::Handle textureinst =
|
||||
texturelookup ? texturelookup(name, alpha) : nullptr;
|
||||
model->geometries.back()->materials.back().textures.push_back(
|
||||
{name, alpha, textureinst});
|
||||
material.textures.push_back({name, alpha, textureinst});
|
||||
}
|
||||
|
||||
void LoaderDFF::readGeometryExtension(Model *model, const RWBStream &stream) {
|
||||
void LoaderDFF::readGeometryExtension(GeometryPtr &geom,
|
||||
const RWBStream &stream) {
|
||||
auto extStream = stream.getInnerStream();
|
||||
|
||||
RWBStream::ChunkID chunkID;
|
||||
while ((chunkID = extStream.getNextChunk())) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_BINMESHPLG:
|
||||
readBinMeshPLG(model, extStream);
|
||||
readBinMeshPLG(geom, extStream);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -362,11 +370,10 @@ void LoaderDFF::readGeometryExtension(Model *model, const RWBStream &stream) {
|
||||
}
|
||||
}
|
||||
|
||||
void LoaderDFF::readBinMeshPLG(Model *model, const RWBStream &stream) {
|
||||
void LoaderDFF::readBinMeshPLG(GeometryPtr &geom, const RWBStream &stream) {
|
||||
auto data = stream.getCursor();
|
||||
|
||||
model->geometries.back()->facetype =
|
||||
static_cast<Model::FaceType>(*(std::uint32_t *)data);
|
||||
geom->facetype = static_cast<Geometry::FaceType>(*(std::uint32_t *)data);
|
||||
data += sizeof(std::uint32_t);
|
||||
|
||||
unsigned int numSplits = *(std::uint32_t *)data;
|
||||
@ -375,12 +382,12 @@ void LoaderDFF::readBinMeshPLG(Model *model, const RWBStream &stream) {
|
||||
// Number of triangles.
|
||||
data += sizeof(std::uint32_t);
|
||||
|
||||
model->geometries.back()->subgeom.reserve(numSplits);
|
||||
geom->subgeom.reserve(numSplits);
|
||||
|
||||
size_t start = 0;
|
||||
|
||||
for (size_t s = 0; s < numSplits; ++s) {
|
||||
Model::SubGeometry sg;
|
||||
SubGeometry sg;
|
||||
sg.numIndices = *(std::uint32_t *)data;
|
||||
data += sizeof(std::uint32_t);
|
||||
sg.material = *(std::uint32_t *)data;
|
||||
@ -393,11 +400,13 @@ void LoaderDFF::readBinMeshPLG(Model *model, const RWBStream &stream) {
|
||||
sizeof(std::uint32_t) * sg.numIndices);
|
||||
data += sizeof(std::uint32_t) * sg.numIndices;
|
||||
|
||||
model->geometries.back()->subgeom.push_back(sg);
|
||||
geom->subgeom.push_back(sg);
|
||||
}
|
||||
}
|
||||
|
||||
void LoaderDFF::readAtomic(Model *model, const RWBStream &stream) {
|
||||
AtomicPtr LoaderDFF::readAtomic(FrameList &framelist,
|
||||
GeometryList &geometrylist,
|
||||
const RWBStream &stream) {
|
||||
auto atomicStream = stream.getInnerStream();
|
||||
|
||||
auto atomicStructID = atomicStream.getNextChunk();
|
||||
@ -405,19 +414,33 @@ void LoaderDFF::readAtomic(Model *model, const RWBStream &stream) {
|
||||
throw DFFLoaderException("Atomic missing struct chunk");
|
||||
}
|
||||
|
||||
Model::Atomic atom;
|
||||
auto data = atomicStream.getCursor();
|
||||
atom.frame = *(std::uint32_t *)data;
|
||||
auto frame = *(std::uint32_t *)data;
|
||||
data += sizeof(std::uint32_t);
|
||||
atom.geometry = *(std::uint32_t *)data;
|
||||
model->frames[atom.frame]->addGeometry(atom.geometry);
|
||||
model->atomics.push_back(atom);
|
||||
auto geometry = *(std::uint32_t *)data;
|
||||
data += sizeof(std::uint32_t);
|
||||
auto flags = *(std::uint32_t *) data;
|
||||
|
||||
/// @todo are any atomic extensions important?
|
||||
// Verify the atomic's particulars
|
||||
RW_CHECK(frame < framelist.size(), "atomic frame " << frame
|
||||
<< " out of bounds");
|
||||
RW_CHECK(geometry < geometrylist.size(),
|
||||
"atomic geometry " << geometry << " out of bounds");
|
||||
|
||||
auto atomic = std::make_shared<Atomic>();
|
||||
if (geometry < geometrylist.size()) {
|
||||
atomic->setGeometry(geometrylist[geometry]);
|
||||
}
|
||||
if (frame < framelist.size()) {
|
||||
atomic->setFrame(framelist[frame]);
|
||||
}
|
||||
atomic->setFlags(flags);
|
||||
|
||||
return atomic;
|
||||
}
|
||||
|
||||
Model *LoaderDFF::loadFromMemory(FileHandle file) {
|
||||
auto model = new Model;
|
||||
Clump *LoaderDFF::loadFromMemory(FileHandle file) {
|
||||
auto model = new Clump;
|
||||
|
||||
RWBStream rootStream(file->data, file->length);
|
||||
|
||||
@ -434,26 +457,40 @@ Model *LoaderDFF::loadFromMemory(FileHandle file) {
|
||||
}
|
||||
|
||||
// There is only one value in the struct section.
|
||||
model->numAtomics = *(std::uint32_t *)rootStream.getCursor();
|
||||
auto numAtomics = *(std::uint32_t *)rootStream.getCursor();
|
||||
RW_UNUSED(numAtomics);
|
||||
|
||||
GeometryList geometrylist;
|
||||
FrameList framelist;
|
||||
|
||||
// Process everything inside the clump stream.
|
||||
RWBStream::ChunkID chunkID;
|
||||
while ((chunkID = modelStream.getNextChunk())) {
|
||||
switch (chunkID) {
|
||||
case CHUNK_FRAMELIST:
|
||||
readFrameList(model, modelStream);
|
||||
framelist = readFrameList(modelStream);
|
||||
break;
|
||||
case CHUNK_GEOMETRYLIST:
|
||||
readGeometryList(model, modelStream);
|
||||
break;
|
||||
case CHUNK_ATOMIC:
|
||||
readAtomic(model, modelStream);
|
||||
geometrylist = readGeometryList(modelStream);
|
||||
break;
|
||||
case CHUNK_ATOMIC: {
|
||||
auto atomic = readAtomic(framelist, geometrylist, modelStream);
|
||||
RW_CHECK(atomic, "Failed to read atomic");
|
||||
if (!atomic) {
|
||||
// Abort reading the rest of the clump
|
||||
return nullptr;
|
||||
}
|
||||
model->addAtomic(atomic);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!framelist.empty()) {
|
||||
model->setFrame(framelist[0]);
|
||||
}
|
||||
|
||||
// Ensure the model has cached metrics
|
||||
model->recalculateMetrics();
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#ifndef _LOADERDFF_HPP_
|
||||
#define _LOADERDFF_HPP_
|
||||
|
||||
#include <data/Clump.hpp>
|
||||
#include <gl/TextureData.hpp>
|
||||
#include <loaders/RWBinaryStream.hpp>
|
||||
#include <platform/FileHandle.hpp>
|
||||
@ -9,8 +10,6 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class Model;
|
||||
|
||||
class DFFLoaderException {
|
||||
std::string _message;
|
||||
|
||||
@ -24,40 +23,39 @@ public:
|
||||
};
|
||||
|
||||
class LoaderDFF {
|
||||
/**
|
||||
* @brief loads a Frame List chunk from stream into model.
|
||||
* @param model
|
||||
* @param stream
|
||||
*/
|
||||
void readFrameList(Model* model, const RWBStream& stream);
|
||||
|
||||
void readGeometryList(Model* model, const RWBStream& stream);
|
||||
|
||||
void readGeometry(Model* model, const RWBStream& stream);
|
||||
|
||||
void readMaterialList(Model* model, const RWBStream& stream);
|
||||
|
||||
void readMaterial(Model* model, const RWBStream& stream);
|
||||
|
||||
void readTexture(Model* model, const RWBStream& stream);
|
||||
|
||||
void readGeometryExtension(Model* model, const RWBStream& stream);
|
||||
|
||||
void readBinMeshPLG(Model* model, const RWBStream& stream);
|
||||
|
||||
void readAtomic(Model* model, const RWBStream& stream);
|
||||
|
||||
public:
|
||||
using TextureLookupCallback = std::function<TextureData::Handle(
|
||||
const std::string&, const std::string&)>;
|
||||
using GeometryList = std::vector<GeometryPtr>;
|
||||
using FrameList = std::vector<ModelFramePtr>;
|
||||
|
||||
Model* loadFromMemory(FileHandle file);
|
||||
Clump* loadFromMemory(FileHandle file);
|
||||
|
||||
void setTextureLookupCallback(TextureLookupCallback tlc) {
|
||||
texturelookup = tlc;
|
||||
}
|
||||
|
||||
private:
|
||||
TextureLookupCallback texturelookup;
|
||||
|
||||
FrameList readFrameList(const RWBStream& stream);
|
||||
|
||||
GeometryList readGeometryList(const RWBStream& stream);
|
||||
|
||||
GeometryPtr readGeometry(const RWBStream& stream);
|
||||
|
||||
void readMaterialList(GeometryPtr& geom, const RWBStream& stream);
|
||||
|
||||
void readMaterial(GeometryPtr& geom, const RWBStream& stream);
|
||||
|
||||
void readTexture(Geometry::Material& material, const RWBStream& stream);
|
||||
|
||||
void readGeometryExtension(GeometryPtr& geom, const RWBStream& stream);
|
||||
|
||||
void readBinMeshPLG(GeometryPtr& geom, const RWBStream& stream);
|
||||
|
||||
AtomicPtr readAtomic(FrameList& framelist, GeometryList& geometrylist,
|
||||
const RWBStream& stream);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -2,17 +2,16 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMouseEvent>
|
||||
#include <algorithm>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <objects/GameObject.hpp>
|
||||
#include <render/GameRenderer.hpp>
|
||||
#include <render/OpenGLRenderer.hpp>
|
||||
|
||||
#include <objects/CharacterObject.hpp>
|
||||
#include <objects/GameObject.hpp>
|
||||
#include <objects/InstanceObject.hpp>
|
||||
#include <objects/VehicleObject.hpp>
|
||||
#include <render/GameRenderer.hpp>
|
||||
#include <render/ObjectRenderer.hpp>
|
||||
#include <render/OpenGLRenderer.hpp>
|
||||
|
||||
ViewerWidget::ViewerWidget(QGLFormat g, QWidget* parent,
|
||||
const QGLWidget* shareWidget, Qt::WindowFlags f)
|
||||
@ -81,9 +80,8 @@ void ViewerWidget::paintGL() {
|
||||
|
||||
r.setViewport(width(), height());
|
||||
|
||||
if (dummyObject && dummyObject->animator && dummyObject->skeleton) {
|
||||
if (dummyObject && dummyObject->animator) {
|
||||
dummyObject->animator->tick(1.f / 60.f);
|
||||
dummyObject->skeleton->interpolate(1.f);
|
||||
}
|
||||
|
||||
r.getRenderer()->invalidate();
|
||||
@ -103,7 +101,7 @@ void ViewerWidget::paintGL() {
|
||||
vc.frustum.fov = viewFov;
|
||||
vc.frustum.aspectRatio = width() / (height() * 1.f);
|
||||
|
||||
Model* model = activeModel;
|
||||
Clump* model = activeModel;
|
||||
if (model != _lastModel) {
|
||||
_lastModel = model;
|
||||
emit modelChanged(_lastModel);
|
||||
@ -113,10 +111,15 @@ void ViewerWidget::paintGL() {
|
||||
cos(viewAngles.x) * cos(viewAngles.y), sin(viewAngles.y));
|
||||
|
||||
if (model) {
|
||||
model->getFrame()->updateHierarchyTransform();
|
||||
|
||||
// Ensure camera is still accurate
|
||||
vc.position = eye * viewDistance;
|
||||
glm::mat4 proj = vc.frustum.projection();
|
||||
glm::mat4 view =
|
||||
glm::lookAt(eye * viewDistance, glm::vec3(0.f, 0.f, 0.f),
|
||||
glm::vec3(0.f, 0.f, 1.f));
|
||||
glm::mat4 view = glm::lookAt(vc.position, glm::vec3(0.f, 0.f, 0.f),
|
||||
glm::vec3(0.f, 0.f, 1.f));
|
||||
vc.rotation = -glm::quat_cast(view);
|
||||
vc.frustum.update(proj * view);
|
||||
|
||||
r.getRenderer()->setSceneParameters(
|
||||
{proj, view, glm::vec4(0.15f), glm::vec4(0.7f), glm::vec4(1.f),
|
||||
@ -126,9 +129,12 @@ void ViewerWidget::paintGL() {
|
||||
|
||||
r.setupRender();
|
||||
|
||||
r.renderModel(model, m, dummyObject);
|
||||
ObjectRenderer renderer(world(), vc, 1.f, 0);
|
||||
RenderList renders;
|
||||
renderer.renderClump(model, glm::mat4(), nullptr, renders);
|
||||
r.getRenderer()->drawBatched(renders);
|
||||
|
||||
drawFrameWidget(model->frames[model->rootFrameIdx]);
|
||||
drawFrameWidget(model->getFrame().get());
|
||||
r.renderPostProcess();
|
||||
} else if (world()->allObjects.size() > 0) {
|
||||
vc.frustum.fov = glm::radians(90.f);
|
||||
@ -143,26 +149,24 @@ void ViewerWidget::paintGL() {
|
||||
|
||||
void ViewerWidget::drawFrameWidget(ModelFrame* f, const glm::mat4& m) {
|
||||
auto thisM = m * f->getTransform();
|
||||
if (f->getGeometries().size() == 0) {
|
||||
Renderer::DrawParameters dp;
|
||||
dp.count = _frameWidgetGeom->getCount();
|
||||
dp.start = 0;
|
||||
dp.ambient = 1.f;
|
||||
dp.diffuse = 1.f;
|
||||
if (f == selectedFrame) {
|
||||
dp.colour = {255, 255, 0, 255};
|
||||
// Sorry!
|
||||
glLineWidth(10.f);
|
||||
} else {
|
||||
dp.colour = {255, 255, 255, 255};
|
||||
glLineWidth(1.f);
|
||||
}
|
||||
dp.textures = {whiteTex};
|
||||
renderer->getRenderer()->drawArrays(thisM, _frameWidgetDraw, dp);
|
||||
Renderer::DrawParameters dp;
|
||||
dp.count = _frameWidgetGeom->getCount();
|
||||
dp.start = 0;
|
||||
dp.ambient = 1.f;
|
||||
dp.diffuse = 1.f;
|
||||
if (f == selectedFrame) {
|
||||
dp.colour = {255, 255, 0, 255};
|
||||
// Sorry!
|
||||
glLineWidth(10.f);
|
||||
} else {
|
||||
dp.colour = {255, 255, 255, 255};
|
||||
glLineWidth(1.f);
|
||||
}
|
||||
dp.textures = {whiteTex};
|
||||
renderer->getRenderer()->drawArrays(thisM, _frameWidgetDraw, dp);
|
||||
|
||||
for (auto c : f->getChildren()) {
|
||||
drawFrameWidget(c, thisM);
|
||||
drawFrameWidget(c.get(), thisM);
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +201,7 @@ void ViewerWidget::showObject(qint16 item) {
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::showModel(Model* model) {
|
||||
void ViewerWidget::showModel(Clump* model) {
|
||||
if (dummyObject) gworld->destroyObject(dummyObject);
|
||||
dummyObject = nullptr;
|
||||
activeModel = model;
|
||||
@ -259,7 +263,7 @@ void ViewerWidget::keyReleaseEvent(QKeyEvent* e) {
|
||||
if (e->key() == Qt::Key_Shift) moveFast = false;
|
||||
}
|
||||
|
||||
Model* ViewerWidget::currentModel() const {
|
||||
Clump* ViewerWidget::currentModel() const {
|
||||
return activeModel;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#ifndef _VIEWERWIDGET_HPP_
|
||||
#define _VIEWERWIDGET_HPP_
|
||||
#include <QTimer>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <gl/DrawBuffer.hpp>
|
||||
@ -16,7 +16,7 @@
|
||||
#include <QGLWidget>
|
||||
|
||||
class GameRenderer;
|
||||
class Model;
|
||||
class Clump;
|
||||
class ViewerWidget : public QGLWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@ -27,12 +27,12 @@ class ViewerWidget : public QGLWidget {
|
||||
QTimer timer;
|
||||
GameWorld* gworld;
|
||||
|
||||
Model* activeModel;
|
||||
Clump* activeModel;
|
||||
ModelFrame* selectedFrame;
|
||||
GameObject* dummyObject;
|
||||
quint16 currentObjectID;
|
||||
|
||||
Model* _lastModel;
|
||||
Clump* _lastModel;
|
||||
Animation* canimation;
|
||||
|
||||
float viewDistance;
|
||||
@ -60,7 +60,7 @@ public:
|
||||
|
||||
virtual void paintGL();
|
||||
|
||||
Model* currentModel() const;
|
||||
Clump* currentModel() const;
|
||||
GameObject* currentObject() const;
|
||||
|
||||
GameWorld* world();
|
||||
@ -68,7 +68,7 @@ public:
|
||||
public slots:
|
||||
|
||||
void showObject(qint16 item);
|
||||
void showModel(Model* model);
|
||||
void showModel(Clump* model);
|
||||
void selectFrame(ModelFrame* frame);
|
||||
|
||||
void exportModel();
|
||||
@ -81,7 +81,7 @@ signals:
|
||||
|
||||
void fileOpened(const QString& file);
|
||||
|
||||
void modelChanged(Model* model);
|
||||
void modelChanged(Clump* model);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent*) override;
|
||||
|
@ -1,10 +1,9 @@
|
||||
#include "DFFFramesTreeModel.hpp"
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
|
||||
DFFFramesTreeModel::DFFFramesTreeModel(Model* m, Skeleton* skel,
|
||||
DFFFramesTreeModel::DFFFramesTreeModel(Clump* m,
|
||||
QObject* parent)
|
||||
: QAbstractItemModel(parent), model(m), skeleton(skel) {
|
||||
: QAbstractItemModel(parent), model(m) {
|
||||
}
|
||||
|
||||
int DFFFramesTreeModel::columnCount(const QModelIndex& parent) const {
|
||||
@ -27,10 +26,10 @@ int DFFFramesTreeModel::rowCount(const QModelIndex& parent) const {
|
||||
QModelIndex DFFFramesTreeModel::index(int row, int column,
|
||||
const QModelIndex& parent) const {
|
||||
if (parent.row() == -1 && parent.column() == -1) {
|
||||
return createIndex(row, column, model->frames[model->rootFrameIdx]);
|
||||
return createIndex(row, column, model->getFrame().get());
|
||||
}
|
||||
ModelFrame* f = static_cast<ModelFrame*>(parent.internalPointer());
|
||||
ModelFrame* p = f->getChildren()[row];
|
||||
ModelFrame* p = f->getChildren()[row].get();
|
||||
return createIndex(row, column, p);
|
||||
}
|
||||
|
||||
@ -40,7 +39,7 @@ QModelIndex DFFFramesTreeModel::parent(const QModelIndex& child) const {
|
||||
auto cp = c->getParent();
|
||||
if (cp->getParent()) {
|
||||
for (size_t i = 0; i < cp->getParent()->getChildren().size(); ++i) {
|
||||
if (cp->getParent()->getChildren()[i] == c->getParent()) {
|
||||
if (cp->getParent()->getChildren()[i].get() == c->getParent()) {
|
||||
return createIndex(i, 0, c->getParent());
|
||||
}
|
||||
}
|
||||
@ -59,10 +58,7 @@ QVariant DFFFramesTreeModel::data(const QModelIndex& index, int role) const {
|
||||
}
|
||||
} else if (role == Qt::CheckStateRole) {
|
||||
if (index.column() == 0) {
|
||||
if (skeleton) {
|
||||
return skeleton->getData(f->getIndex()).enabled ? Qt::Checked
|
||||
: Qt::Unchecked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
@ -77,11 +73,10 @@ bool DFFFramesTreeModel::setData(const QModelIndex& index,
|
||||
ModelFrame* f = static_cast<ModelFrame*>(index.internalPointer());
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
if (index.column() == 0 && skeleton) {
|
||||
if (index.column() == 0) {
|
||||
if ((Qt::CheckState)value.toInt() == Qt::Checked) {
|
||||
skeleton->setEnabled(f, true);
|
||||
RW_UNIMPLEMENTED("Hiding Frames");
|
||||
} else {
|
||||
skeleton->setEnabled(f, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -97,7 +92,7 @@ Qt::ItemFlags DFFFramesTreeModel::flags(const QModelIndex& index) const {
|
||||
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
|
||||
if (index.column() == 0 && skeleton) {
|
||||
if (index.column() == 0) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
|
@ -4,15 +4,13 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <rw/types.hpp>
|
||||
|
||||
class Model;
|
||||
class Skeleton;
|
||||
class Clump;
|
||||
|
||||
class DFFFramesTreeModel : public QAbstractItemModel {
|
||||
Model* model;
|
||||
Skeleton* skeleton;
|
||||
Clump* model;
|
||||
|
||||
public:
|
||||
explicit DFFFramesTreeModel(Model* m, Skeleton* skel, QObject* parent = 0);
|
||||
explicit DFFFramesTreeModel(Clump* m, QObject* parent = 0);
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "ModelViewer.hpp"
|
||||
#include <QDebug>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <fstream>
|
||||
#include <objects/GameObject.hpp>
|
||||
@ -9,7 +8,7 @@
|
||||
|
||||
ModelViewer::ModelViewer(ViewerWidget* viewer, QWidget* parent,
|
||||
Qt::WindowFlags f)
|
||||
: ViewerInterface(parent, f), viewing(nullptr), skeleton(nullptr) {
|
||||
: ViewerInterface(parent, f), viewing(nullptr) {
|
||||
mainSplit = new QSplitter;
|
||||
mainLayout = new QVBoxLayout;
|
||||
|
||||
@ -40,21 +39,16 @@ void ModelViewer::setViewerWidget(ViewerWidget* widget) {
|
||||
showModel(viewing);
|
||||
}
|
||||
|
||||
void ModelViewer::showModel(Model* model) {
|
||||
void ModelViewer::showModel(Clump* model) {
|
||||
viewing = model;
|
||||
if (skeleton) {
|
||||
delete skeleton;
|
||||
}
|
||||
skeleton = new Skeleton();
|
||||
viewerWidget->showModel(model);
|
||||
frames->setModel(model, nullptr);
|
||||
frames->setModel(model);
|
||||
}
|
||||
|
||||
void ModelViewer::showObject(uint16_t object) {
|
||||
viewerWidget->showObject(object);
|
||||
viewing = viewerWidget->currentModel();
|
||||
skeleton = viewerWidget->currentObject()->skeleton;
|
||||
frames->setModel(viewing, skeleton);
|
||||
frames->setModel(viewing);
|
||||
}
|
||||
|
||||
void ModelViewer::loadAnimations(const QString& file) {
|
||||
|
@ -14,16 +14,14 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class ViewerWidget;
|
||||
class Model;
|
||||
class Skeleton;
|
||||
class Clump;
|
||||
class ModelFramesWidget;
|
||||
class Animation;
|
||||
|
||||
class ModelViewer : public ViewerInterface {
|
||||
Q_OBJECT
|
||||
|
||||
Model* viewing;
|
||||
Skeleton* skeleton;
|
||||
Clump* viewing;
|
||||
|
||||
QSplitter* mainSplit;
|
||||
QVBoxLayout* mainLayout;
|
||||
@ -45,7 +43,7 @@ public slots:
|
||||
/**
|
||||
* Display a raw model
|
||||
*/
|
||||
void showModel(Model* model);
|
||||
void showModel(Clump* model);
|
||||
|
||||
/**
|
||||
* Display a game object's model
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <QTableView>
|
||||
|
||||
class ViewerWidget;
|
||||
class Model;
|
||||
class Clump;
|
||||
|
||||
class ObjectViewer : public ViewerInterface {
|
||||
Q_OBJECT
|
||||
@ -40,7 +40,7 @@ protected:
|
||||
|
||||
signals:
|
||||
|
||||
void modelChanged(Model* model);
|
||||
void modelChanged(Clump* model);
|
||||
|
||||
void showObjectModel(uint16_t object);
|
||||
|
||||
|
@ -1,28 +1,15 @@
|
||||
#include "ModelFramesWidget.hpp"
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
void ModelFramesWidget::updateInfoBox(Model* model, ModelFrame* f) {
|
||||
void ModelFramesWidget::updateInfoBox(Clump* model, ModelFrame* f) {
|
||||
if (f == nullptr) {
|
||||
_frameLabel->setText("");
|
||||
} else {
|
||||
auto labText = QString("Name: %1\nTranslation: %2\nTextures:%3")
|
||||
auto labText = QString("Name: %1\nTranslation: %2")
|
||||
.arg(QString::fromStdString(f->getName()))
|
||||
.arg(QString::fromStdString(
|
||||
glm::to_string(f->getDefaultTranslation())));
|
||||
QString geomString;
|
||||
for (size_t gi : f->getGeometries()) {
|
||||
auto& g = model->geometries[gi];
|
||||
// for(Model::SubGeometry& sg : g->subgeom)
|
||||
for (Model::Material& m : g->materials) {
|
||||
for (Model::Texture& t : m.textures) {
|
||||
geomString += QString("\n %1 (%2)")
|
||||
.arg(t.name.c_str())
|
||||
.arg(t.alphaName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
labText = labText.arg(geomString);
|
||||
_frameLabel->setText(labText);
|
||||
}
|
||||
}
|
||||
@ -46,7 +33,7 @@ ModelFramesWidget::ModelFramesWidget(QWidget* parent, Qt::WindowFlags flags)
|
||||
setLayout(_layout);
|
||||
}
|
||||
|
||||
void ModelFramesWidget::setModel(Model* model, Skeleton* skeleton) {
|
||||
void ModelFramesWidget::setModel(Clump* model) {
|
||||
if (framemodel) {
|
||||
delete framemodel;
|
||||
framemodel = nullptr;
|
||||
@ -54,7 +41,7 @@ void ModelFramesWidget::setModel(Model* model, Skeleton* skeleton) {
|
||||
}
|
||||
gmodel = model;
|
||||
if (model != nullptr) {
|
||||
framemodel = new DFFFramesTreeModel(model, skeleton, this);
|
||||
framemodel = new DFFFramesTreeModel(model, this);
|
||||
tree->setModel(framemodel);
|
||||
tree->setDisabled(false);
|
||||
connect(tree->selectionModel(),
|
||||
|
@ -13,7 +13,7 @@ class ModelFrame;
|
||||
class ModelFramesWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
Model* gmodel;
|
||||
Clump* gmodel;
|
||||
DFFFramesTreeModel* framemodel;
|
||||
QTreeView* tree;
|
||||
QVBoxLayout* _layout;
|
||||
@ -21,7 +21,7 @@ class ModelFramesWidget : public QWidget {
|
||||
|
||||
private slots:
|
||||
|
||||
void updateInfoBox(Model* model, ModelFrame* f);
|
||||
void updateInfoBox(Clump* model, ModelFrame* f);
|
||||
|
||||
void selectedModelChanged(const QModelIndex&, const QModelIndex&);
|
||||
|
||||
@ -30,7 +30,7 @@ public:
|
||||
|
||||
public slots:
|
||||
|
||||
void setModel(Model* model, Skeleton* skeleton);
|
||||
void setModel(Clump* model);
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -38,7 +38,6 @@ set(TEST_SOURCES
|
||||
"test_rwbstream.cpp"
|
||||
"test_SaveGame.cpp"
|
||||
"test_scriptmachine.cpp"
|
||||
"test_skeleton.cpp"
|
||||
"test_state.cpp"
|
||||
"test_text.cpp"
|
||||
"test_trafficdirector.cpp"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include "test_globals.hpp"
|
||||
@ -10,14 +9,13 @@ BOOST_AUTO_TEST_SUITE(AnimationTests)
|
||||
#if RW_TEST_WITH_DATA
|
||||
BOOST_AUTO_TEST_CASE(test_matrix) {
|
||||
{
|
||||
Skeleton skeleton;
|
||||
Animation animation;
|
||||
|
||||
/** Models are currently needed to relate animation bones <=> model
|
||||
* frame #s. */
|
||||
auto test_model = Global::get().d->loadClump("player.dff");
|
||||
|
||||
Animator animator(test_model, &skeleton);
|
||||
Animator animator(test_model);
|
||||
|
||||
animation.duration = 1.f;
|
||||
animation.bones["player"] = new AnimationBone{
|
||||
@ -35,17 +33,15 @@ BOOST_AUTO_TEST_CASE(test_matrix) {
|
||||
|
||||
animator.tick(0.0f);
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).a.translation ==
|
||||
glm::vec3(0.f, 0.f, 0.f));
|
||||
BOOST_CHECK(skeleton.getData(0).b.translation ==
|
||||
const auto& root = test_model->findFrame("player");
|
||||
|
||||
BOOST_CHECK(glm::vec3(root->getTransform()[3]) ==
|
||||
glm::vec3(0.f, 0.f, 0.f));
|
||||
|
||||
animator.tick(1.0f);
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).a.translation ==
|
||||
BOOST_CHECK(glm::vec3(root->getTransform()[3]) ==
|
||||
glm::vec3(0.f, 1.f, 0.f));
|
||||
BOOST_CHECK(skeleton.getData(0).b.translation ==
|
||||
glm::vec3(0.f, 0.f, 0.f));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include "test_globals.hpp"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(LoaderDFFTests)
|
||||
@ -11,26 +11,17 @@ BOOST_AUTO_TEST_CASE(test_load_dff) {
|
||||
|
||||
LoaderDFF loader;
|
||||
|
||||
Model* m = loader.loadFromMemory(d);
|
||||
Clump* m = loader.loadFromMemory(d);
|
||||
|
||||
BOOST_REQUIRE(m != nullptr);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(m->frames.size(), 40);
|
||||
BOOST_REQUIRE(m->getFrame());
|
||||
|
||||
BOOST_REQUIRE_EQUAL(m->geometries.size(), 16);
|
||||
BOOST_REQUIRE(!m->getAtomics().empty());
|
||||
const auto& atomic = m->getAtomics()[0];
|
||||
|
||||
BOOST_REQUIRE_EQUAL(m->geometries[0]->subgeom.size(), 5);
|
||||
|
||||
for (auto& g : m->geometries) {
|
||||
BOOST_CHECK_GT(g->geometryBounds.radius, 0.f);
|
||||
}
|
||||
|
||||
BOOST_REQUIRE(m->atomics.size() > 0);
|
||||
|
||||
for (Model::Atomic& a : m->atomics) {
|
||||
BOOST_CHECK(a.frame < m->frames.size());
|
||||
BOOST_CHECK(a.geometry < m->geometries.size());
|
||||
}
|
||||
BOOST_REQUIRE(atomic->getGeometry());
|
||||
BOOST_REQUIRE(atomic->getFrame());
|
||||
|
||||
delete m;
|
||||
}
|
||||
@ -38,4 +29,44 @@ BOOST_AUTO_TEST_CASE(test_load_dff) {
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_clump_clone) {
|
||||
{
|
||||
auto frame1 = std::make_shared<ModelFrame>(0);
|
||||
frame1->setName("Frame1");
|
||||
auto frame2 = std::make_shared<ModelFrame>(1);
|
||||
frame2->setName("Frame2");
|
||||
|
||||
frame1->addChild(frame2);
|
||||
|
||||
auto geometry = std::make_shared<Geometry>();
|
||||
|
||||
auto atomic = std::make_shared<Atomic>();
|
||||
atomic->setFrame(frame2);
|
||||
atomic->setGeometry(geometry);
|
||||
|
||||
auto clump = std::make_shared<Clump>();
|
||||
clump->addAtomic(atomic);
|
||||
clump->setFrame(frame1);
|
||||
|
||||
// Now clone and verify that:
|
||||
// The hierarchy has the same data but different objects
|
||||
// The atomics have the same data but different objects
|
||||
// Suspected correct behaviour:
|
||||
// The geometry is the same for each atomic
|
||||
|
||||
auto newclump = std::shared_ptr<Clump>(clump->clone());
|
||||
BOOST_REQUIRE(newclump);
|
||||
|
||||
BOOST_CHECK_NE(newclump, clump);
|
||||
|
||||
BOOST_REQUIRE(newclump->getFrame());
|
||||
BOOST_CHECK_NE(newclump->getFrame(), clump->getFrame());
|
||||
BOOST_CHECK_EQUAL(newclump->getFrame()->getChildren().size(), 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(newclump->getAtomics().size(), 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(frame1->getName(), newclump->getFrame()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -1,63 +0,0 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(SkeletonTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_methods) {
|
||||
Skeleton::FrameTransform t1{glm::vec3(0.f, 0.f, 0.f),
|
||||
glm::quat(0.f, 0.f, 0.f, 0.f)};
|
||||
Skeleton::FrameTransform t2{glm::vec3(1.f, 0.f, 0.f),
|
||||
glm::quat(0.f, 0.f, 1.f, 0.f)};
|
||||
|
||||
Skeleton skeleton;
|
||||
|
||||
skeleton.setAllData({{0, {t1, t2, true}}});
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).a.translation == t1.translation);
|
||||
BOOST_CHECK(skeleton.getData(0).a.rotation == t1.rotation);
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).b.translation == t2.translation);
|
||||
BOOST_CHECK(skeleton.getData(0).b.rotation == t2.rotation);
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).enabled);
|
||||
|
||||
skeleton.setData(0, {t2, t1, false});
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).a.translation == t2.translation);
|
||||
BOOST_CHECK(skeleton.getData(0).a.rotation == t2.rotation);
|
||||
|
||||
BOOST_CHECK(skeleton.getData(0).b.translation == t1.translation);
|
||||
BOOST_CHECK(skeleton.getData(0).b.rotation == t1.rotation);
|
||||
|
||||
BOOST_CHECK(!skeleton.getData(0).enabled);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_interpolate) {
|
||||
Skeleton::FrameTransform t1{glm::vec3(0.f, 0.f, 0.f),
|
||||
glm::quat(0.f, 0.f, 0.f, 0.f)};
|
||||
Skeleton::FrameTransform t2{glm::vec3(1.f, 0.f, 0.f),
|
||||
glm::quat(0.f, 0.f, 1.f, 0.f)};
|
||||
|
||||
Skeleton skeleton;
|
||||
|
||||
skeleton.setAllData({{0, {t2, t1, true}}});
|
||||
|
||||
/** Without calling Skeleton::interpolate(alpha) the result is identity */
|
||||
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).translation ==
|
||||
Skeleton::IdentityTransform.translation);
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).rotation ==
|
||||
Skeleton::IdentityTransform.rotation);
|
||||
|
||||
skeleton.interpolate(0.f);
|
||||
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).translation == t1.translation);
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).rotation == t1.rotation);
|
||||
|
||||
skeleton.interpolate(1.f);
|
||||
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).translation == t2.translation);
|
||||
BOOST_CHECK(skeleton.getInterpolated(0).rotation == t2.rotation);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -1,6 +1,5 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <data/Model.hpp>
|
||||
#include <data/Skeleton.hpp>
|
||||
#include <data/Clump.hpp>
|
||||
#include <objects/VehicleObject.hpp>
|
||||
#include "test_globals.hpp"
|
||||
|
||||
@ -41,8 +40,11 @@ BOOST_AUTO_TEST_CASE(vehicle_parts) {
|
||||
BOOST_REQUIRE(part->normal != nullptr);
|
||||
BOOST_REQUIRE(part->damaged != nullptr);
|
||||
|
||||
BOOST_CHECK_EQUAL(part->normal->getName(), "bonnet_hi_ok");
|
||||
BOOST_CHECK_EQUAL(part->damaged->getName(), "bonnet_hi_dam");
|
||||
BOOST_REQUIRE(part->normal->getFrame());
|
||||
BOOST_REQUIRE(part->damaged->getFrame());
|
||||
|
||||
BOOST_CHECK_EQUAL(part->normal->getFrame()->getName(), "bonnet_hi_ok");
|
||||
BOOST_CHECK_EQUAL(part->damaged->getFrame()->getName(), "bonnet_hi_dam");
|
||||
|
||||
Global::get().e->destroyObject(vehicle);
|
||||
}
|
||||
@ -55,17 +57,16 @@ BOOST_AUTO_TEST_CASE(vehicle_part_vis) {
|
||||
BOOST_REQUIRE(vehicle->getModel() != nullptr);
|
||||
|
||||
VehicleObject::Part* bonnetpart = vehicle->getPart("bonnet_dummy");
|
||||
auto skel = vehicle->skeleton;
|
||||
|
||||
vehicle->setPartState(bonnetpart, VehicleObject::DAM);
|
||||
|
||||
BOOST_CHECK(!skel->getData(bonnetpart->normal->getIndex()).enabled);
|
||||
BOOST_CHECK(skel->getData(bonnetpart->damaged->getIndex()).enabled);
|
||||
BOOST_CHECK((bonnetpart->normal->getFlags() & Atomic::ATOMIC_RENDER) == 0);
|
||||
BOOST_CHECK((bonnetpart->damaged->getFlags() & Atomic::ATOMIC_RENDER) != 0);
|
||||
|
||||
vehicle->setPartState(bonnetpart, VehicleObject::OK);
|
||||
|
||||
BOOST_CHECK(skel->getData(bonnetpart->normal->getIndex()).enabled);
|
||||
BOOST_CHECK(!skel->getData(bonnetpart->damaged->getIndex()).enabled);
|
||||
BOOST_CHECK((bonnetpart->normal->getFlags() & Atomic::ATOMIC_RENDER) != 0);
|
||||
BOOST_CHECK((bonnetpart->damaged->getFlags() & Atomic::ATOMIC_RENDER) == 0);
|
||||
|
||||
Global::get().e->destroyObject(vehicle);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user