mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-03 00:59:47 +02:00
Threaded model and texture loading
This commit is contained in:
parent
f24ceeb42f
commit
61931145fe
@ -52,6 +52,9 @@ public:
|
||||
virtual void complete() {}
|
||||
};
|
||||
|
||||
// TODO: refactor everything to remove this.
|
||||
class GameWorld;
|
||||
|
||||
class WorkContext
|
||||
{
|
||||
std::queue<WorkJob*> _workQueue;
|
||||
@ -62,10 +65,12 @@ class WorkContext
|
||||
std::mutex _inMutex;
|
||||
std::mutex _outMutex;
|
||||
|
||||
GameWorld* _world;
|
||||
|
||||
public:
|
||||
|
||||
WorkContext()
|
||||
: _worker(this) { }
|
||||
WorkContext(GameWorld* world = nullptr)
|
||||
: _worker(this), _world(world) { }
|
||||
|
||||
void queueJob( WorkJob* job )
|
||||
{
|
||||
@ -79,6 +84,15 @@ public:
|
||||
const std::queue<WorkJob*> getWorkQueue() const { return _workQueue; }
|
||||
const std::queue<WorkJob*> getCompleteQueue() const { return _completeQueue; }
|
||||
|
||||
bool isEmpty() {
|
||||
std::lock_guard<std::mutex> guardIn( _inMutex );
|
||||
std::lock_guard<std::mutex> guardOu( _outMutex );
|
||||
|
||||
return (getWorkQueue().size() + getCompleteQueue().size()) == 0;
|
||||
}
|
||||
|
||||
GameWorld* getWorld() const { return _world; }
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,16 @@ class Animation;
|
||||
|
||||
typedef std::map<std::string, Animation*> AnimationSet;
|
||||
|
||||
class Model;
|
||||
|
||||
// TODO: Make generic.
|
||||
struct ModelHandle {
|
||||
Model* model;
|
||||
std::string name;
|
||||
|
||||
ModelHandle(const std::string& name) : model( nullptr ), name(name) {}
|
||||
};
|
||||
|
||||
namespace GTATypes
|
||||
{
|
||||
|
||||
@ -46,7 +56,6 @@ struct WaterRect
|
||||
float xLeft, yBottom;
|
||||
float xRight, yTop;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -55,7 +55,7 @@ public:
|
||||
struct GTAFile
|
||||
{
|
||||
bool archived; /// Is the file inside an IMG or on the filesystem?
|
||||
std::string path; /// Path to the file containing the file
|
||||
std::string path; /// Path to the file containing the file.
|
||||
};
|
||||
|
||||
/**
|
||||
@ -113,12 +113,12 @@ public:
|
||||
/**
|
||||
* Attempts to load a TXD, or does nothing if it has already been loaded
|
||||
*/
|
||||
void loadTXD(const std::string& name);
|
||||
|
||||
void loadTXD(const std::string& name, bool async = false);
|
||||
|
||||
/**
|
||||
* Attempts to load a DFF or does nothing if is already loaded
|
||||
*/
|
||||
void loadDFF(const std::string& name);
|
||||
void loadDFF(const std::string& name, bool async = false);
|
||||
|
||||
/**
|
||||
* Loads an IFP file containing animations
|
||||
@ -129,11 +129,19 @@ public:
|
||||
* Loads data from an object definition dat.
|
||||
*/
|
||||
void loadDynamicObjects(const std::string& name);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a pointer to the named file if it is available, the memory must be freed.
|
||||
* Returns a pointer to the named file if it is available,
|
||||
* the memory must be freed by the caller.
|
||||
* @param name the filename in the archive
|
||||
* @return pointer to the data, NULL if it is not available
|
||||
* @return pointer to the data, NULL if it is not available
|
||||
*/
|
||||
char* openFile(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief loadFile Marks a file as open, and opens it.
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
char* loadFile(const std::string& name);
|
||||
|
||||
@ -157,9 +165,9 @@ public:
|
||||
std::map<std::string, std::string> ideLocations;
|
||||
|
||||
/**
|
||||
* Maps file names to their locations
|
||||
* Maps file names to data about the file.
|
||||
*/
|
||||
std::map<std::string, GTAFile> fileLocations;
|
||||
std::map<std::string, GTAFile> _knownFiles;
|
||||
|
||||
/**
|
||||
* Map of loaded archives
|
||||
@ -190,11 +198,11 @@ public:
|
||||
* Weather Loader
|
||||
*/
|
||||
WeatherLoader weatherLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Loaded models
|
||||
*/
|
||||
std::map<std::string, Model*> models;
|
||||
std::map<std::string, ModelHandle*> models;
|
||||
|
||||
/**
|
||||
* Loaded Textures and their atlas entries.
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <memory>
|
||||
|
||||
class GTAAIController;
|
||||
class Model;
|
||||
class ModelFrame;
|
||||
class Animator;
|
||||
|
||||
@ -23,7 +22,7 @@ struct GameObject
|
||||
glm::vec3 position;
|
||||
glm::quat rotation;
|
||||
|
||||
Model* model; /// Cached pointer to Object's Model.
|
||||
ModelHandle* model; /// Cached pointer to Object's Model.
|
||||
|
||||
GameWorld* engine;
|
||||
|
||||
@ -34,7 +33,7 @@ struct GameObject
|
||||
*/
|
||||
float mHealth;
|
||||
|
||||
GameObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, Model* model)
|
||||
GameObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model)
|
||||
: position(pos), rotation(rot), model(model), engine(engine), animator(nullptr), mHealth(0.f)
|
||||
{}
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <ai/GTAAINode.hpp>
|
||||
#include <ai/AIGraph.hpp>
|
||||
|
||||
class WorkContext;
|
||||
|
||||
class GameObject;
|
||||
class GTACharacter;
|
||||
class GTAInstance;
|
||||
@ -31,6 +33,8 @@ class GameWorld
|
||||
public:
|
||||
|
||||
GameWorld(const std::string& gamepath);
|
||||
|
||||
~GameWorld();
|
||||
|
||||
/**
|
||||
* Loads the game data
|
||||
@ -191,7 +195,11 @@ public:
|
||||
btBroadphaseInterface* broadphase;
|
||||
btSequentialImpulseConstraintSolver* solver;
|
||||
btDiscreteDynamicsWorld* dynamicsWorld;
|
||||
|
||||
|
||||
/**
|
||||
* Work related
|
||||
*/
|
||||
WorkContext* _work;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -27,8 +27,6 @@ public:
|
||||
|
||||
#include <functional>
|
||||
|
||||
class LoaderIMG;
|
||||
|
||||
class LoadModelJob : public WorkJob
|
||||
{
|
||||
public:
|
||||
@ -36,13 +34,12 @@ public:
|
||||
|
||||
private:
|
||||
GameData* _gameData;
|
||||
LoaderIMG* _archive;
|
||||
std::string _file;
|
||||
ModelCallback _callback;
|
||||
char* _data;
|
||||
public:
|
||||
|
||||
LoadModelJob(WorkContext* context, GameData* gd, LoaderIMG& archive, const std::string& file, ModelCallback cb);
|
||||
LoadModelJob(WorkContext* context, GameData* gd, const std::string& file, ModelCallback cb);
|
||||
|
||||
void work();
|
||||
|
||||
|
@ -19,4 +19,23 @@ public:
|
||||
bool loadFromMemory(char *data, GameData* gameData);
|
||||
};
|
||||
|
||||
#include <WorkContext.hpp>
|
||||
#include <functional>
|
||||
|
||||
// TODO: refactor this interface to be more like ModelLoader so they can be rolled into one.
|
||||
class LoadTextureArchiveJob : public WorkJob
|
||||
{
|
||||
private:
|
||||
GameData* _gameData;
|
||||
std::string _file;
|
||||
char* _data;
|
||||
public:
|
||||
|
||||
LoadTextureArchiveJob(WorkContext* context, GameData* gd, const std::string& file);
|
||||
|
||||
void work();
|
||||
|
||||
void complete();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
* @param model
|
||||
* @param ped PEDS_t struct to use.
|
||||
*/
|
||||
GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, Model* model, std::shared_ptr<CharacterData> data);
|
||||
GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle *model, std::shared_ptr<CharacterData> data);
|
||||
|
||||
~GTACharacter();
|
||||
|
||||
|
@ -17,7 +17,7 @@ struct GTAInstance : public GameObject
|
||||
GameWorld* engine,
|
||||
const glm::vec3& pos,
|
||||
const glm::quat& rot,
|
||||
Model* model,
|
||||
ModelHandle* model,
|
||||
const glm::vec3& scale,
|
||||
std::shared_ptr<ObjectData> obj,
|
||||
std::shared_ptr<GTAInstance> lod
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
GTAVehicle(GameWorld* engine,
|
||||
const glm::vec3& pos,
|
||||
const glm::quat& rot,
|
||||
Model* model,
|
||||
ModelHandle* model,
|
||||
VehicleDataHandle data,
|
||||
VehicleInfoHandle info,
|
||||
const glm::vec3& prim,
|
||||
|
@ -82,9 +82,9 @@ void GameData::load()
|
||||
parseDAT(datpath+"/data/default.dat");
|
||||
parseDAT(datpath+"/data/gta3.dat");
|
||||
|
||||
fileLocations.insert({"wheels.DFF", {false, datpath+"/models/Generic/wheels.DFF"}});
|
||||
fileLocations.insert({"loplyguy.dff", {false, datpath+"/models/Generic/loplyguy.dff"}});
|
||||
fileLocations.insert({"particle.txd", {false, datpath+"/models/particle.txd"}});
|
||||
_knownFiles.insert({"wheels.DFF", {false, datpath+"/models/Generic/wheels.DFF"}});
|
||||
_knownFiles.insert({"loplyguy.dff", {false, datpath+"/models/Generic/loplyguy.dff"}});
|
||||
_knownFiles.insert({"particle.txd", {false, datpath+"/models/particle.txd"}});
|
||||
loadDFF("wheels.DFF");
|
||||
loadTXD("particle.txd");
|
||||
|
||||
@ -147,7 +147,7 @@ void GameData::parseDAT(const std::string& path)
|
||||
}
|
||||
texpath = findPathRealCase(datpath, texpath);
|
||||
std::string texname = texpath.substr(texpath.find_last_of("/")+1);
|
||||
fileLocations.insert({ texname, { false, texpath }});
|
||||
_knownFiles.insert({ texname, { false, texpath }});
|
||||
loadTXD(texname);
|
||||
}
|
||||
}
|
||||
@ -201,12 +201,12 @@ void GameData::loadIMG(const std::string& name)
|
||||
else
|
||||
{
|
||||
// Enter the asset twice..
|
||||
fileLocations.insert({ filename, { true, archivePath }});
|
||||
_knownFiles.insert({ filename, { true, archivePath }});
|
||||
for(size_t t = 0; t < filename.size(); ++t)
|
||||
{
|
||||
filename[t] = tolower(filename[t]);
|
||||
}
|
||||
fileLocations.insert({ filename, { true, archivePath }});
|
||||
_knownFiles.insert({ filename, { true, archivePath }});
|
||||
}
|
||||
}
|
||||
archives.insert({archivePath, imgLoader});
|
||||
@ -388,32 +388,54 @@ void GameData::loadWater(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::loadTXD(const std::string& name)
|
||||
void GameData::loadTXD(const std::string& name, bool async)
|
||||
{
|
||||
if( loadedFiles.find(name) != loadedFiles.end()) {
|
||||
if( loadedFiles.find(name) != loadedFiles.end() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* file = loadFile(name);
|
||||
if(file) {
|
||||
textureLoader.loadFromMemory(file, this);
|
||||
delete[] file;
|
||||
|
||||
loadedFiles[name] = true;
|
||||
|
||||
auto j = new LoadTextureArchiveJob(this->engine->_work, this, name);
|
||||
|
||||
if( async ) {
|
||||
this->engine->_work->queueJob( j );
|
||||
}
|
||||
else {
|
||||
j->work();
|
||||
j->complete();
|
||||
delete j;
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::loadDFF(const std::string& name)
|
||||
void GameData::loadDFF(const std::string& name, bool async)
|
||||
{
|
||||
if( loadedFiles.find(name) != loadedFiles.end()) {
|
||||
auto realname = name.substr(0, name.size() - 4);
|
||||
if( models.find(realname) != models.end() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *file = loadFile(name);
|
||||
if(file)
|
||||
{
|
||||
LoaderDFF dffLoader;
|
||||
models[name.substr(0, name.size() - 4)] = dffLoader.loadFromMemory(file, this);
|
||||
delete[] file;
|
||||
|
||||
// Before starting the job make sure the file isn't loaded again.
|
||||
loadedFiles.insert({name, true});
|
||||
|
||||
models[realname] = new ModelHandle(realname);
|
||||
|
||||
auto j = new LoadModelJob(this->engine->_work, this, name,
|
||||
[&, realname]( Model* model ) {
|
||||
models[realname]->model = model;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if( async ) {
|
||||
this->engine->_work->queueJob( j );
|
||||
}
|
||||
else {
|
||||
j->work();
|
||||
j->complete();
|
||||
delete j;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameData::loadIFP(const std::string &name)
|
||||
@ -471,15 +493,10 @@ void GameData::loadDynamicObjects(const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
char* GameData::loadFile(const std::string& name)
|
||||
char* GameData::openFile(const std::string& name)
|
||||
{
|
||||
if( loadedFiles.find(name) != loadedFiles.end()) {
|
||||
std::cerr << "File " << name << " already loaded!" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto i = fileLocations.find(name);
|
||||
if(i != fileLocations.end())
|
||||
auto i = _knownFiles.find(name);
|
||||
if(i != _knownFiles.end())
|
||||
{
|
||||
if(i->second.archived)
|
||||
{
|
||||
@ -487,7 +504,6 @@ char* GameData::loadFile(const std::string& name)
|
||||
auto ai = archives.find(i->second.path);
|
||||
if(ai != archives.end())
|
||||
{
|
||||
loadedFiles[name] = true;
|
||||
return ai->second.loadToMemory(name);
|
||||
}
|
||||
else
|
||||
@ -509,8 +525,6 @@ char* GameData::loadFile(const std::string& name)
|
||||
char *data = new char[length];
|
||||
dfile.read(data, length);
|
||||
|
||||
loadedFiles[name] = true;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -525,6 +539,18 @@ char* GameData::loadFile(const std::string& name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* GameData::loadFile(const std::string &name)
|
||||
{
|
||||
auto it = loadedFiles.find(name);
|
||||
if( it != loadedFiles.end() ) {
|
||||
std::cerr << "File " << name << " already loaded?" << std::endl;
|
||||
}
|
||||
|
||||
loadedFiles[name] = true;
|
||||
|
||||
return openFile(name);
|
||||
}
|
||||
|
||||
TextureAtlas* GameData::getAtlas(size_t i)
|
||||
{
|
||||
if( i < atlases.size() ) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <ai/GTADefaultAIController.hpp>
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
#include <render/Model.hpp>
|
||||
#include <WorkContext.hpp>
|
||||
|
||||
// 3 isn't enough to cause a factory.
|
||||
#include <objects/GTACharacter.hpp>
|
||||
@ -11,11 +12,18 @@
|
||||
#include <objects/GTAVehicle.hpp>
|
||||
|
||||
GameWorld::GameWorld(const std::string& path)
|
||||
: gameTime(0.f), gameData(path), renderer(this), randomEngine(rand())
|
||||
: gameTime(0.f), gameData(path), renderer(this), randomEngine(rand()),
|
||||
_work( new WorkContext( this ) )
|
||||
{
|
||||
gameData.engine = this;
|
||||
}
|
||||
|
||||
GameWorld::~GameWorld()
|
||||
{
|
||||
delete _work;
|
||||
// TODO: delete other things.
|
||||
}
|
||||
|
||||
bool GameWorld::load()
|
||||
{
|
||||
collisionConfig = new btDefaultCollisionConfiguration;
|
||||
@ -174,10 +182,10 @@ GTAInstance *GameWorld::createInstance(const uint16_t id, const glm::vec3& pos,
|
||||
if( oi != objectTypes.end()) {
|
||||
// Make sure the DFF and TXD are loaded
|
||||
if(! oi->second->modelName.empty()) {
|
||||
gameData.loadDFF(oi->second->modelName + ".dff");
|
||||
gameData.loadDFF(oi->second->modelName + ".dff", true);
|
||||
}
|
||||
if(! oi->second->textureName.empty()) {
|
||||
gameData.loadTXD(oi->second->textureName + ".txd");
|
||||
gameData.loadTXD(oi->second->textureName + ".txd", true);
|
||||
}
|
||||
|
||||
auto instance = std::shared_ptr<GTAInstance>(new GTAInstance(
|
||||
@ -236,7 +244,8 @@ GTAVehicle *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos, co
|
||||
}
|
||||
}
|
||||
|
||||
Model* model = gameData.models[vti->second->modelName];
|
||||
ModelHandle* m = gameData.models[vti->second->modelName];
|
||||
auto model = m->model;
|
||||
auto info = gameData.vehicleInfo.find(vti->second->handlingID);
|
||||
if(model && info != gameData.vehicleInfo.end()) {
|
||||
if( info->second->wheels.size() == 0 && info->second->seats.size() == 0 ) {
|
||||
@ -258,7 +267,7 @@ GTAVehicle *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos, co
|
||||
}
|
||||
}
|
||||
|
||||
vehicleInstances.push_back(new GTAVehicle{ this, pos, rot, model, vti->second, info->second, prim, sec });
|
||||
vehicleInstances.push_back(new GTAVehicle{ this, pos, rot, m, vti->second, info->second, prim, sec });
|
||||
return vehicleInstances.back();
|
||||
}
|
||||
return nullptr;
|
||||
@ -280,10 +289,10 @@ GTACharacter* GameWorld::createPedestrian(const uint16_t id, const glm::vec3 &po
|
||||
gameData.loadTXD(pt->textureName + ".txd");
|
||||
}
|
||||
|
||||
Model* model = gameData.models[pt->modelName];
|
||||
ModelHandle* m = gameData.models[pt->modelName];
|
||||
|
||||
if(model != nullptr) {
|
||||
auto ped = new GTACharacter( this, pos, rot, model, pt );
|
||||
if(m != nullptr) {
|
||||
auto ped = new GTACharacter( this, pos, rot, m, pt );
|
||||
pedestrians.push_back(ped);
|
||||
new GTADefaultAIController(ped);
|
||||
return ped;
|
||||
|
@ -312,25 +312,28 @@ RW::BSSectionHeader LoaderDFF::readHeader(char *data, size_t &dataI)
|
||||
return readStructure<RW::BSSectionHeader>(data, dataI);
|
||||
}
|
||||
|
||||
#include <loaders/LoaderIMG.hpp>
|
||||
|
||||
LoadModelJob::LoadModelJob(WorkContext *context, GameData* gd, LoaderIMG &archive, const std::string &file, ModelCallback cb)
|
||||
: WorkJob(context), _gameData(gd), _archive(&archive), _file(file), _callback(cb), _data(nullptr)
|
||||
LoadModelJob::LoadModelJob(WorkContext *context, GameData* gd, const std::string &file, ModelCallback cb)
|
||||
: WorkJob(context), _gameData(gd), _file(file), _callback(cb), _data(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void LoadModelJob::work()
|
||||
{
|
||||
_data = _archive->loadToMemory(_file);
|
||||
_data = _gameData->openFile(_file);
|
||||
}
|
||||
|
||||
void LoadModelJob::complete()
|
||||
{
|
||||
// TODO allow some of the loading to process in a seperate thread.
|
||||
LoaderDFF loader;
|
||||
Model* m = nullptr;
|
||||
// TODO error status
|
||||
if( _data ) {
|
||||
|
||||
Model* m = loader.loadFromMemory(_data, _gameData);
|
||||
// TODO allow some of the loading to process in a seperate thread.
|
||||
LoaderDFF loader;
|
||||
|
||||
m = loader.loadFromMemory(_data, _gameData);
|
||||
}
|
||||
|
||||
_callback(m);
|
||||
|
||||
|
@ -217,3 +217,26 @@ bool TextureLoader::loadFromMemory(char *data, GameData *gameData)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
LoadTextureArchiveJob::LoadTextureArchiveJob(WorkContext *context, GameData *gd, const std::string &file)
|
||||
: WorkJob(context), _gameData(gd), _file(file), _data(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void LoadTextureArchiveJob::work()
|
||||
{
|
||||
_data = _gameData->openFile(_file);
|
||||
}
|
||||
|
||||
void LoadTextureArchiveJob::complete()
|
||||
{
|
||||
// TODO error status
|
||||
if(_data) {
|
||||
TextureLoader loader;
|
||||
loader.loadFromMemory(_data, _gameData);
|
||||
}
|
||||
|
||||
delete[] _data;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
// TODO: make this not hardcoded
|
||||
static glm::vec3 enter_offset(0.81756252f, 0.34800607f, -0.486281008f);
|
||||
|
||||
GTACharacter::GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, Model* model, std::shared_ptr<CharacterData> data)
|
||||
GTACharacter::GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model, std::shared_ptr<CharacterData> data)
|
||||
: GameObject(engine, pos, rot, model),
|
||||
currentVehicle(nullptr), currentSeat(0),
|
||||
_hasTargetPosition(false),
|
||||
@ -35,7 +35,7 @@ GTACharacter::GTACharacter(GameWorld* engine, const glm::vec3& pos, const glm::q
|
||||
|
||||
if(model) {
|
||||
animator = new Animator();
|
||||
animator->setModel(model);
|
||||
animator->setModel(model->model);
|
||||
|
||||
createActor();
|
||||
enterAction(Idle);
|
||||
|
@ -2,11 +2,10 @@
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <data/CollisionModel.hpp>
|
||||
|
||||
GTAInstance::GTAInstance(
|
||||
GameWorld* engine,
|
||||
GTAInstance::GTAInstance(GameWorld* engine,
|
||||
const glm::vec3& pos,
|
||||
const glm::quat& rot,
|
||||
Model* model,
|
||||
ModelHandle *model,
|
||||
const glm::vec3& scale,
|
||||
std::shared_ptr<ObjectData> obj,
|
||||
std::shared_ptr<GTAInstance> lod
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <data/CollisionModel.hpp>
|
||||
#include <render/Model.hpp>
|
||||
|
||||
GTAVehicle::GTAVehicle(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, Model* model, VehicleDataHandle data, VehicleInfoHandle info, const glm::vec3& prim, const glm::vec3& sec)
|
||||
GTAVehicle::GTAVehicle(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model, VehicleDataHandle data, VehicleInfoHandle info, const glm::vec3& prim, const glm::vec3& sec)
|
||||
: GameObject(engine, pos, rot, model),
|
||||
steerAngle(0.f), throttle(0.f), brake(0.f), handbrake(false),
|
||||
damageFlags(0), vehicle(data), info(info), colourPrimary(prim),
|
||||
|
@ -268,9 +268,9 @@ void GTARenderer::renderWorld(float alpha)
|
||||
matrixModel = glm::translate(matrixModel, charac->getPosition());
|
||||
matrixModel = matrixModel * glm::mat4_cast(charac->getRotation());
|
||||
|
||||
if(!charac->model) continue;
|
||||
if(!charac->model->model) continue;
|
||||
|
||||
renderModel(charac->model, matrixModel, charac, charac->animator);
|
||||
renderModel(charac->model->model, matrixModel, charac, charac->animator);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < engine->objectInstances.size(); ++i) {
|
||||
@ -284,24 +284,24 @@ void GTARenderer::renderWorld(float alpha)
|
||||
}
|
||||
}
|
||||
|
||||
if(!inst.model)
|
||||
{
|
||||
std::cout << "model " << inst.model << " not loaded (" << engine->gameData.models.size() << " models loaded)" << std::endl;
|
||||
}
|
||||
|
||||
glm::mat4 matrixModel;
|
||||
matrixModel = glm::translate(matrixModel, inst.position);
|
||||
matrixModel = glm::scale(matrixModel, inst.scale);
|
||||
matrixModel = matrixModel * glm::mat4_cast(inst.rotation);
|
||||
if(!inst.model->model)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float mindist = 100000.f;
|
||||
for (size_t g = 0; g < inst.model->geometries.size(); g++)
|
||||
{
|
||||
RW::BSGeometryBounds& bounds = inst.model->geometries[g]->geometryBounds;
|
||||
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - camera.worldPos) - bounds.radius);
|
||||
}
|
||||
|
||||
if ( mindist > inst.object->drawDistance[0] ) {
|
||||
glm::mat4 matrixModel;
|
||||
matrixModel = glm::translate(matrixModel, inst.position);
|
||||
matrixModel = glm::scale(matrixModel, inst.scale);
|
||||
matrixModel = matrixModel * glm::mat4_cast(inst.rotation);
|
||||
|
||||
float mindist = 100000.f;
|
||||
for (size_t g = 0; g < inst.model->model->geometries.size(); g++)
|
||||
{
|
||||
RW::BSGeometryBounds& bounds = inst.model->model->geometries[g]->geometryBounds;
|
||||
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - camera.worldPos) - bounds.radius);
|
||||
}
|
||||
|
||||
if ( mindist > inst.object->drawDistance[0] ) {
|
||||
if ( !inst.LODinstance ) {
|
||||
culled++;
|
||||
continue;
|
||||
@ -311,13 +311,13 @@ void GTARenderer::renderWorld(float alpha)
|
||||
culled++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
renderModel(inst.LODinstance->model, matrixModel);
|
||||
else if (inst.LODinstance->model->model) {
|
||||
renderModel(inst.LODinstance->model->model, matrixModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (! inst.object->LOD ) {
|
||||
renderModel(inst.model, matrixModel);
|
||||
renderModel(inst.model->model, matrixModel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,13 +333,13 @@ void GTARenderer::renderWorld(float alpha)
|
||||
matrixModel = glm::translate(matrixModel, inst->getPosition());
|
||||
matrixModel = matrixModel * glm::mat4_cast(inst->getRotation());
|
||||
|
||||
renderModel(inst->model, matrixModel, inst);
|
||||
renderModel(inst->model->model, matrixModel, inst);
|
||||
|
||||
// Draw wheels n' stuff
|
||||
for( size_t w = 0; w < inst->info->wheels.size(); ++w) {
|
||||
auto woi = engine->objectTypes.find(inst->vehicle->wheelModelID);
|
||||
if(woi != engine->objectTypes.end()) {
|
||||
Model* wheelModel = engine->gameData.models["wheels"];
|
||||
Model* wheelModel = engine->gameData.models["wheels"]->model;
|
||||
if( wheelModel) {
|
||||
// Tell bullet to update the matrix for this wheel.
|
||||
inst->physVehicle->updateWheelTransform(w, false);
|
||||
|
@ -1,5 +1,6 @@
|
||||
add_executable(rwgame
|
||||
main.cpp
|
||||
loadingstate.cpp
|
||||
ingamestate.cpp
|
||||
pausestate.cpp
|
||||
menustate.cpp
|
||||
|
@ -119,6 +119,12 @@ struct StateManager
|
||||
states.push_back(state);
|
||||
state->enter();
|
||||
}
|
||||
|
||||
void exec(State* state)
|
||||
{
|
||||
exit();
|
||||
enter(state);
|
||||
}
|
||||
|
||||
void tick(float dt)
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ void IngameState::tick(float dt)
|
||||
float viewDistance = 2.f;
|
||||
if( _playerCharacter->getCurrentVehicle() ) {
|
||||
auto model = _playerCharacter->getCurrentVehicle()->model;
|
||||
for(auto& g : model->geometries) {
|
||||
for(auto& g : model->model->geometries) {
|
||||
viewDistance = std::max(
|
||||
(glm::length(g->geometryBounds.center) + g->geometryBounds.radius) * 1.5f,
|
||||
viewDistance);
|
||||
|
55
rwgame/loadingstate.cpp
Normal file
55
rwgame/loadingstate.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "loadingstate.hpp"
|
||||
#include "menustate.hpp"
|
||||
#include "game.hpp"
|
||||
|
||||
LoadingState::LoadingState()
|
||||
{
|
||||
}
|
||||
|
||||
void LoadingState::enter()
|
||||
{
|
||||
// Load all of the files waiting to be loaded.
|
||||
auto world = getWorld();
|
||||
|
||||
// Loade all of the IDEs.
|
||||
for(std::map<std::string, std::string>::iterator it = world->gameData.ideLocations.begin();
|
||||
it != world->gameData.ideLocations.end();
|
||||
++it) {
|
||||
world->defineItems(it->second);
|
||||
}
|
||||
|
||||
// Load IPLs
|
||||
for(std::map<std::string, std::string>::iterator it = world->gameData.iplLocations.begin();
|
||||
it != world->gameData.iplLocations.end();
|
||||
++it) {
|
||||
world->loadZone(it->second);
|
||||
world->placeItems(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadingState::exit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void LoadingState::tick(float dt)
|
||||
{
|
||||
// Check to see if the GameWorld has run out of jobs
|
||||
// (i.e. it's time to open the main menu)
|
||||
if( getWorld()->_work->isEmpty() ) {
|
||||
StateManager::get().exec(new MenuState);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadingState::handleEvent(const sf::Event &e)
|
||||
{
|
||||
State::handleEvent(e);
|
||||
}
|
||||
|
||||
void LoadingState::draw(sf::RenderWindow &w)
|
||||
{
|
||||
// Display some manner of loading screen.
|
||||
sf::Text loadingText("Loading...", getFont(), 28);
|
||||
loadingText.setPosition({30.f, 20.f});
|
||||
w.draw(loadingText);
|
||||
}
|
21
rwgame/loadingstate.hpp
Normal file
21
rwgame/loadingstate.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef LOADINGSTATE_HHP
|
||||
#define LOADINGSTATE_HPP
|
||||
|
||||
#include "State.hpp"
|
||||
|
||||
class LoadingState : public State
|
||||
{
|
||||
public:
|
||||
LoadingState();
|
||||
|
||||
virtual void enter();
|
||||
virtual void exit();
|
||||
|
||||
virtual void tick(float dt);
|
||||
|
||||
virtual void draw(sf::RenderWindow &w);
|
||||
|
||||
virtual void handleEvent(const sf::Event& event);
|
||||
};
|
||||
|
||||
#endif // MENUSTATE_HPP
|
@ -13,7 +13,7 @@
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "menustate.hpp"
|
||||
#include "loadingstate.hpp"
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
#include <memory>
|
||||
@ -39,8 +39,8 @@ int debugMode = 0;
|
||||
|
||||
sf::Font font;
|
||||
|
||||
glm::vec3 viewPosition;
|
||||
glm::vec2 viewAngles;
|
||||
glm::vec3 viewPosition { -200.f, -100.f, 45.f };
|
||||
glm::vec2 viewAngles { -0.90f, 0.2f };
|
||||
|
||||
void setViewParameters(const glm::vec3 ¢er, const glm::vec2 &angles)
|
||||
{
|
||||
@ -293,23 +293,6 @@ void init(std::string gtapath, bool loadWorld)
|
||||
// Set time to noon.
|
||||
gta->gameTime = 12.f * 60.f;
|
||||
|
||||
// Loade all of the IDEs.
|
||||
for(std::map<std::string, std::string>::iterator it = gta->gameData.ideLocations.begin();
|
||||
it != gta->gameData.ideLocations.end();
|
||||
++it) {
|
||||
gta->defineItems(it->second);
|
||||
}
|
||||
|
||||
if(loadWorld) {
|
||||
// Load IPLs
|
||||
for(std::map<std::string, std::string>::iterator it = gta->gameData.iplLocations.begin();
|
||||
it != gta->gameData.iplLocations.end();
|
||||
++it) {
|
||||
gta->loadZone(it->second);
|
||||
gta->placeItems(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
debugDrawer = new DebugDraw;
|
||||
debugDrawer->setShaderProgram(gta->renderer.worldProgram);
|
||||
debugDrawer->setDebugMode(btIDebugDraw::DBG_DrawWireframe);
|
||||
@ -361,6 +344,8 @@ void update(float dt)
|
||||
|
||||
void render(float alpha)
|
||||
{
|
||||
gta->_work->update();
|
||||
|
||||
// Update aspect ratio..
|
||||
gta->renderer.camera.frustum.aspectRatio = window.getSize().x / (float) window.getSize().y;
|
||||
|
||||
@ -491,9 +476,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
sf::Clock clock;
|
||||
|
||||
MenuState* menuState = new MenuState();
|
||||
|
||||
StateManager::get().enter(menuState);
|
||||
StateManager::get().enter(new LoadingState);
|
||||
|
||||
float accum = 0.f;
|
||||
float ts = 1.f / 60.f;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "DFFFramesTreeModel.hpp"
|
||||
#include <render/Model.hpp>
|
||||
|
||||
DFFFramesTreeModel::DFFFramesTreeModel(Model* m, QObject* parent)
|
||||
DFFFramesTreeModel::DFFFramesTreeModel(ModelHandle *m, QObject* parent)
|
||||
: QAbstractItemModel(parent), model(m)
|
||||
{
|
||||
|
||||
@ -29,7 +29,7 @@ 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->model->frames[model->model->rootFrameIdx]);
|
||||
}
|
||||
ModelFrame* f = static_cast<ModelFrame*>(parent.internalPointer());
|
||||
ModelFrame* p = f->getChildren()[row];
|
||||
|
@ -2,14 +2,14 @@
|
||||
#ifndef _DFFFRAMESTREEMODEL_HPP_
|
||||
#define _DFFFRAMESTREEMODEL_HPP_
|
||||
#include <QAbstractItemModel>
|
||||
#include <engine/GTATypes.hpp>
|
||||
|
||||
class Model;
|
||||
class DFFFramesTreeModel : public QAbstractItemModel
|
||||
{
|
||||
Model* model;
|
||||
ModelHandle* model;
|
||||
public:
|
||||
|
||||
explicit DFFFramesTreeModel(Model* m, QObject* parent = 0);
|
||||
explicit DFFFramesTreeModel(ModelHandle* m, QObject* parent = 0);
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
|
||||
@ -24,4 +24,4 @@ public:
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@ ModelFramesWidget::ModelFramesWidget(QWidget* parent, Qt::WindowFlags flags)
|
||||
setWidget(tree);
|
||||
}
|
||||
|
||||
void ModelFramesWidget::setModel(Model* model)
|
||||
void ModelFramesWidget::setModel(ModelHandle *model)
|
||||
{
|
||||
if(framemodel) {
|
||||
delete framemodel;
|
||||
|
@ -4,20 +4,20 @@
|
||||
#include <QDockWidget>
|
||||
#include <QTreeView>
|
||||
#include "DFFFramesTreeModel.hpp"
|
||||
#include <engine/GTATypes.hpp>
|
||||
|
||||
class Model;
|
||||
class ModelFramesWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Model* gmodel;
|
||||
ModelHandle* gmodel;
|
||||
DFFFramesTreeModel* framemodel;
|
||||
QTreeView* tree;
|
||||
|
||||
public:
|
||||
ModelFramesWidget(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
|
||||
void setModel(Model* model);
|
||||
void setModel(ModelHandle* model);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -71,7 +71,7 @@ void ViewerWidget::paintGL()
|
||||
glUniformMatrix4fv(r.uniView, 1, GL_FALSE, glm::value_ptr(view));
|
||||
glUniformMatrix4fv(r.uniProj, 1, GL_FALSE, glm::value_ptr(proj));
|
||||
|
||||
gworld->renderer.renderModel(cmodel, m, dummyObject);
|
||||
gworld->renderer.renderModel(cmodel->model, m, dummyObject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ void ViewerWidget::showDFF(const QString& file)
|
||||
dummyObject = new GameObject(gworld, glm::vec3(), glm::quat(), cmodel);
|
||||
float radius = 0.f;
|
||||
for(auto& g
|
||||
: cmodel->geometries) {
|
||||
: cmodel->model->geometries) {
|
||||
radius = std::max(
|
||||
radius,
|
||||
glm::length(g->geometryBounds.center)+g->geometryBounds.radius);
|
||||
@ -130,13 +130,13 @@ void ViewerWidget::showAnimation(Animation *anim)
|
||||
if(dummyObject) {
|
||||
if(dummyObject->animator == nullptr) {
|
||||
dummyObject->animator = new Animator;
|
||||
dummyObject->animator->setModel(dummyObject->model);
|
||||
dummyObject->animator->setModel(dummyObject->model->model);
|
||||
}
|
||||
dummyObject->animator->setAnimation(canimation);
|
||||
}
|
||||
}
|
||||
|
||||
Model* ViewerWidget::currentModel() const
|
||||
ModelHandle* ViewerWidget::currentModel() const
|
||||
{
|
||||
return cmodel;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class ViewerWidget : public QGLWidget
|
||||
|
||||
GameObject* dummyObject;
|
||||
|
||||
Model* cmodel;
|
||||
ModelHandle* cmodel;
|
||||
Animation* canimation;
|
||||
|
||||
float viewDistance;
|
||||
@ -44,7 +44,7 @@ public:
|
||||
|
||||
FileMode fileMode() const;
|
||||
|
||||
Model* currentModel() const;
|
||||
ModelHandle *currentModel() const;
|
||||
|
||||
GameWorld* world();
|
||||
|
||||
|
@ -12,7 +12,7 @@ BOOST_AUTO_TEST_CASE(test_matrix)
|
||||
Animator animator;
|
||||
|
||||
Global::get().e->gameData.loadDFF("player.dff");
|
||||
Model* test_model = Global::get().e->gameData.models["player"];
|
||||
ModelHandle* test_model = Global::get().e->gameData.models["player"];
|
||||
|
||||
BOOST_REQUIRE( test_model );
|
||||
|
||||
@ -34,16 +34,16 @@ BOOST_AUTO_TEST_CASE(test_matrix)
|
||||
};
|
||||
|
||||
animator.setAnimation(&animation);
|
||||
animator.setModel( test_model );
|
||||
animator.setModel( test_model->model );
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrixAt( test_model->frames[0], 0.0f );
|
||||
auto intp_matrix = animator.getFrameMatrixAt( test_model->model->frames[0], 0.0f );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 0.f, 0.f)), 0.0f );
|
||||
}
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrixAt( test_model->frames[0], 0.5f );
|
||||
auto intp_matrix = animator.getFrameMatrixAt( test_model->model->frames[0], 0.5f );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( 0.0f, glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 0.5f, 0.0f)) );
|
||||
}
|
||||
@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(test_interpolate)
|
||||
Animator animator;
|
||||
|
||||
Global::get().e->gameData.loadDFF("player.dff");
|
||||
Model* test_model = Global::get().e->gameData.models["player"];
|
||||
ModelHandle* test_model = Global::get().e->gameData.models["player"];
|
||||
|
||||
BOOST_REQUIRE( test_model );
|
||||
|
||||
@ -82,10 +82,10 @@ BOOST_AUTO_TEST_CASE(test_interpolate)
|
||||
};
|
||||
|
||||
animator.setAnimation(&animation);
|
||||
animator.setModel( test_model );
|
||||
animator.setModel( test_model->model );
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->frames[0] );
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->model->frames[0] );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 0.f, 0.f)), 0.0f );
|
||||
}
|
||||
@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(test_interpolate)
|
||||
animator.tick( 1.f );
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->frames[0], 0.5f );
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->model->frames[0], 0.5f );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( 0.0f, glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 0.5f, 0.0f)) );
|
||||
}
|
||||
@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(test_interpolate)
|
||||
animator.tick( 1.f );
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->frames[0], 0.5f );
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->model->frames[0], 0.5f );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( 0.0f, glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 1.5f, 0.0f)) );
|
||||
}
|
||||
@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(test_interpolate)
|
||||
animator.tick( 1.f );
|
||||
|
||||
{
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->frames[0], 0.5f );
|
||||
auto intp_matrix = animator.getFrameMatrix( test_model->model->frames[0], 0.5f );
|
||||
auto intp_col = intp_matrix[3];
|
||||
BOOST_CHECK_EQUAL( 0.0f, glm::distance(glm::vec3(intp_col), glm::vec3(0.f, 0.5f, 0.0f)) );
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ BOOST_AUTO_TEST_CASE(test_activities)
|
||||
{
|
||||
GTAVehicle* vehicle = Global::get().e->createVehicle(90u, glm::vec3(10.f, 0.f, 0.f), glm::quat());
|
||||
BOOST_REQUIRE(vehicle != nullptr);
|
||||
BOOST_REQUIRE(vehicle->model != nullptr);
|
||||
|
||||
auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f});
|
||||
|
||||
|
@ -50,6 +50,10 @@ public:
|
||||
}
|
||||
|
||||
e->dynamicsWorld->setGravity(btVector3(0.f, 0.f, 0.f));
|
||||
|
||||
while( ! e->_work->isEmpty() ) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
~Global() {
|
||||
|
@ -7,12 +7,8 @@ BOOST_AUTO_TEST_SUITE(LoaderDFFTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_open_dff)
|
||||
{
|
||||
LoaderIMG archive;
|
||||
|
||||
BOOST_REQUIRE( archive.load(Global::getGamePath() + "/models/gta3") );
|
||||
|
||||
{
|
||||
auto d = archive.loadToMemory("landstal.dff");
|
||||
auto d = Global::get().e->gameData.openFile("landstal.dff");
|
||||
|
||||
LoaderDFF loader;
|
||||
|
||||
@ -29,16 +25,12 @@ BOOST_AUTO_TEST_CASE(test_open_dff)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_modeljob)
|
||||
{
|
||||
LoaderIMG archive;
|
||||
|
||||
BOOST_REQUIRE( archive.load(Global::getGamePath() + "/models/gta3") );
|
||||
|
||||
{
|
||||
WorkContext ctx;
|
||||
|
||||
Model* m = nullptr;
|
||||
bool done = false;
|
||||
LoadModelJob* lmj = new LoadModelJob(&ctx, &Global::get().e->gameData, archive, "landstal.dff",
|
||||
LoadModelJob* lmj = new LoadModelJob(&ctx, &Global::get().e->gameData, "landstal.dff",
|
||||
[&](Model* model) { m = model; done = true; });
|
||||
|
||||
ctx.queueJob(lmj);
|
||||
|
@ -31,8 +31,8 @@ BOOST_AUTO_TEST_CASE(vehicle_frame_flags)
|
||||
BOOST_REQUIRE(vehicle != nullptr);
|
||||
BOOST_REQUIRE(vehicle->model != nullptr);
|
||||
|
||||
auto bonnet_ok = vehicle->model->findFrame("bonnet_hi_ok");
|
||||
auto bonnet_dam = vehicle->model->findFrame("bonnet_hi_dam");
|
||||
auto bonnet_ok = vehicle->model->model->findFrame("bonnet_hi_ok");
|
||||
auto bonnet_dam = vehicle->model->model->findFrame("bonnet_hi_dam");
|
||||
|
||||
BOOST_REQUIRE(bonnet_ok != nullptr);
|
||||
BOOST_REQUIRE(bonnet_dam != nullptr);
|
||||
|
Loading…
Reference in New Issue
Block a user