1
0
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:
Daniel Evans 2017-02-09 00:07:38 +00:00 committed by GitHub
commit 1df0ab7c17
51 changed files with 1187 additions and 1297 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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