Commit d6dfaefe authored by Martin Turski's avatar Martin Turski

- added world class for entity registration and later streaming

- added support for GTA3 and GTAVC in the IDE and IPL parsing
- added LOD management to ModelManager
- added LOD instances support for SA IPL files
parent 6edc4c53
Pipeline #41 skipped
#pragma once
#include "Streaming.h"
#include "ModelInfo.h"
// Entity class for having actual objects in the world of things.
......@@ -9,7 +10,10 @@ namespace krt
struct Entity
{
Entity( void );
friend struct World;
friend class Game;
Entity( Game *ourGame );
~Entity();
void SetModelIndex( streaming::ident_t modelID );
......@@ -17,16 +21,52 @@ struct Entity
bool CreateRWObject( void );
void DeleteRWObject( void );
void SetMatrix( const rw::Matrix& mat );
void SetModelling( const rw::Matrix& mat );
const rw::Matrix& GetModelling( void ) const;
const rw::Matrix& GetMatrix( void ) const;
//todo.
bool GetWorldBoundingSphere( rw::Sphere& sphere ) const;
void LinkToWorld( World *theWorld );
World* GetWorld( void );
void SetLODEntity( Entity *lodInst );
Entity* GetLODEntity( void );
bool IsLowerLODOf( Entity *inst ) const;
bool IsHigherLODOf( Entity *inst ) const;
ModelManager::ModelResource* GetModelInfo( void ) const;
inline Game* GetGame( void ) const { return this->ourGame; }
private:
Game *ourGame;
NestedListEntry <Entity> gameNode;
streaming::ident_t modelID;
int interiorId;
// Some entity flags given by IPL.
bool isUnderwater;
bool isTunnelObject;
bool isTunnelTransition;
bool isUnimportantToStreamer;
rw::Matrix matrix;
bool hasLocalMatrix;
rw::Object *rwObject;
Entity *higherQualityEntity;
Entity *lowerQualityEntity;
// Node of the world entity list.
NestedListEntry <Entity> worldNode;
World *onWorld;
};
}
\ No newline at end of file
......@@ -9,6 +9,7 @@
#include "Streaming.h"
#include "TexDict.h"
#include "ModelInfo.h"
#include "World.h"
#include "Console.Commands.h"
......@@ -17,6 +18,7 @@ namespace krt
class Game
{
friend struct Entity;
public:
Game( void );
~Game( void );
......@@ -29,6 +31,8 @@ public:
inline TextureManager& GetTextureManager( void ) { return this->texManager; }
inline ModelManager& GetModelManager( void ) { return this->modelManager; }
inline World* GetWorld( void ) { return &theWorld; }
void LoadIMG( std::string relPath );
void LoadIDEFile( std::string relPath );
......@@ -43,6 +47,10 @@ private:
TextureManager texManager;
ModelManager modelManager;
World theWorld;
NestedList <Entity> activeEntities;
};
extern Game *theGame;
......
......@@ -26,6 +26,9 @@ struct ModelManager : public streaming::StreamingTypeInterface
{
inline streaming::ident_t GetID( void ) const { return this->id; }
inline eModelType GetType( void ) const { return this->modelType; }
inline float GetLODDistance( void ) const { return this->lodDistance; }
inline ModelResource* GetLODModel( void ) { return this->lod_model; }
rw::Object* CloneModel( void );
void ReleaseModel( rw::Object *rwobj );
......@@ -35,7 +38,7 @@ struct ModelManager : public streaming::StreamingTypeInterface
static void NativeReleaseModel( rw::Object *rwobj );
inline ModelResource( vfs::DevicePtr device, std::string pathToRes ) : vfsResLoc( device, pathToRes )
inline ModelResource( vfs::DevicePtr device, std::string pathToRes ) : vfsResLoc( device, std::move( pathToRes ) )
{
return;
}
......@@ -52,6 +55,8 @@ struct ModelManager : public streaming::StreamingTypeInterface
float lodDistance;
int flags;
ModelResource *lod_model; // model of a lower quality model than this one.
eModelType modelType;
DeviceResourceLocation vfsResLoc;
......@@ -86,6 +91,9 @@ private:
std::vector <ModelResource*> models;
std::map <std::string, ModelResource*> modelByName;
std::map <std::string, ModelResource*> basifierLookup;
std::map <std::string, ModelResource*> lodifierLookup;
};
}
\ No newline at end of file
#pragma once
// World class for managing all alive Entities.
#include "Entity.h"
namespace krt
{
struct World
{
friend struct Entity;
World( void );
~World( void );
private:
NestedList <Entity> entityList;
};
};
\ No newline at end of file
......@@ -6,16 +6,62 @@
namespace krt
{
Entity::Entity( void )
Entity::Entity( Game *ourGame )
{
this->ourGame = ourGame;
LIST_INSERT( ourGame->activeEntities.root, this->gameNode );
this->modelID = -1;
this->interiorId = 0;
this->matrix.setIdentity();
this->hasLocalMatrix = false;
// Initialize the flags.
this->isUnderwater = false;
this->isTunnelObject = false;
this->isTunnelTransition = false;
this->isUnimportantToStreamer = false;
// Initialize LOD things.
this->higherQualityEntity = NULL;
this->lowerQualityEntity = NULL;
this->rwObject = NULL;
this->onWorld = NULL; // we are not part of a world.
}
Entity::~Entity( void )
{
// Make sure we released our RW object.
this->DeleteRWObject();
// Remove us from any world.
this->LinkToWorld( NULL );
// Check whether we are linked as LOD or have a LOD.
// Unlink those relationships.
{
if ( Entity *highLOD = this->higherQualityEntity )
{
highLOD->lowerQualityEntity = NULL;
this->higherQualityEntity = NULL;
}
if ( Entity *lowLOD = this->lowerQualityEntity )
{
lowLOD->higherQualityEntity = NULL;
this->lowerQualityEntity = NULL;
}
}
// Remove us from the game.
LIST_REMOVE( this->gameNode );
}
void Entity::SetModelIndex( streaming::ident_t modelID )
......@@ -27,6 +73,26 @@ void Entity::SetModelIndex( streaming::ident_t modelID )
this->modelID = modelID;
}
static rw::Frame* RwObjectGetFrame( rw::Object *rwObj )
{
rw::uint8 objType = rwObj->type;
if ( objType == rw::Atomic::ID )
{
rw::Atomic *atomic = (rw::Atomic*)rwObj;
return atomic->getFrame();
}
else if ( objType == rw::Clump::ID )
{
rw::Clump *clump = (rw::Clump*)rwObj;
return clump->getFrame();
}
return NULL;
}
bool Entity::CreateRWObject( void )
{
streaming::ident_t modelID = this->modelID;
......@@ -51,8 +117,53 @@ bool Entity::CreateRWObject( void )
if ( !rwobj )
return false;
// Make sure our RW object has a frame.
// This is because we will render it.
{
rw::uint8 objType = rwobj->type;
if ( objType == rw::Atomic::ID )
{
rw::Atomic *atomic = (rw::Atomic*)rwobj;
if ( atomic->getFrame() == NULL )
{
rw::Frame *parentFrame = rw::Frame::create();
atomic->setFrame( parentFrame );
}
}
else if ( objType == rw::Clump::ID )
{
rw::Clump *clump = (rw::Clump*)rwobj;
if ( clump->getFrame() == NULL )
{
rw::Frame *parentFrame = rw::Frame::create();
clump->setFrame( parentFrame );
}
}
}
this->rwObject = rwobj;
// If we have a local matrix, then set it into our frame.
{
if ( this->hasLocalMatrix )
{
rw::Frame *parentFrame = RwObjectGetFrame( rwobj );
if ( parentFrame )
{
parentFrame->matrix = this->matrix;
parentFrame->updateObjects();
this->hasLocalMatrix = false;
}
}
}
return true;
}
......@@ -70,19 +181,231 @@ void Entity::DeleteRWObject( void )
if ( !modelEntry )
return;
// Store our matrix.
{
rw::Frame *parentFrame = RwObjectGetFrame( rwobj );
if ( parentFrame )
{
this->matrix = parentFrame->matrix;
this->hasLocalMatrix = true;
}
}
modelEntry->ReleaseModel( rwobj );
this->rwObject = NULL;
}
void Entity::SetMatrix( const rw::Matrix& mat )
void Entity::SetModelling( const rw::Matrix& mat )
{
rw::Object *rwobj = this->rwObject;
if ( rwobj == NULL )
{
this->matrix = mat;
this->hasLocalMatrix = false;
}
else
{
rw::Frame *parentFrame = RwObjectGetFrame( rwobj );
assert( parentFrame != NULL );
if ( parentFrame )
{
parentFrame->matrix = mat;
parentFrame->updateObjects(); // that's kinda how RW works; you say that you updated the struct.
}
}
}
const rw::Matrix& Entity::GetModelling( void ) const
{
this->matrix = mat;
rw::Object *rwobj = this->rwObject;
if ( rwobj )
{
rw::Frame *parentFrame = RwObjectGetFrame( rwobj );
if ( parentFrame )
{
return parentFrame->matrix;
}
}
return this->matrix;
}
const rw::Matrix& Entity::GetMatrix( void ) const
{
rw::Object *rwobj = this->rwObject;
if ( rwobj )
{
rw::Frame *parentFrame = RwObjectGetFrame( rwobj );
if ( parentFrame )
{
return *parentFrame->getLTM();
}
}
return this->matrix;
}
static bool RpClumpCalculateBoundingSphere( rw::Clump *clump, rw::Sphere& sphereOut )
{
rw::Sphere tmpSphere;
bool hasSphere = false;
rw::clumpForAllAtomics( clump,
[&]( rw::Atomic *atomic )
{
rw::Sphere *atomicSphere = atomic->getWorldBoundingSphere();
if ( atomicSphere )
{
if ( !hasSphere )
{
tmpSphere = *atomicSphere;
hasSphere = true;
}
else
{
// Create a new sphere that encloses both spheres.
rw::V3d vecHalfDist = rw::scale( rw::sub( atomicSphere->center, tmpSphere.center ), 0.5f );
// Adjust the radius.
float vecHalfDistScalar = rw::length( vecHalfDist );
tmpSphere.center = rw::add( tmpSphere.center, vecHalfDist );
tmpSphere.radius = std::max( tmpSphere.radius, atomicSphere->radius ) + vecHalfDistScalar;
}
}
});
if ( !hasSphere )
return false;
sphereOut = tmpSphere;
return true;
}
bool Entity::GetWorldBoundingSphere( rw::Sphere& sphereOut ) const
{
rw::Object *rwObj = this->rwObject;
if ( !rwObj == NULL )
return false;
if ( rwObj->type == rw::Atomic::ID )
{
rw::Atomic *atomic = (rw::Atomic*)rwObj;
rw::Sphere *atomicSphere = atomic->getWorldBoundingSphere();
if ( atomicSphere )
{
sphereOut = *atomicSphere;
return true;
}
}
else if ( rwObj->type == rw::Clump::ID )
{
rw::Clump *clump = (rw::Clump*)rwObj;
return RpClumpCalculateBoundingSphere( clump, sphereOut );
}
return false;
}
void Entity::LinkToWorld( World *theWorld )
{
if ( World *prevWorld = this->onWorld )
{
LIST_REMOVE( this->worldNode );
this->onWorld = NULL;
}
if ( theWorld )
{
LIST_INSERT( theWorld->entityList.root, worldNode );
this->onWorld = theWorld;
}
}
World* Entity::GetWorld( void )
{
return onWorld;
}
void Entity::SetLODEntity( Entity *lodInst )
{
if ( Entity *prevLOD = this->lowerQualityEntity )
{
prevLOD->higherQualityEntity = NULL;
this->lowerQualityEntity = NULL;
}
if ( lodInst )
{
if ( Entity *prevHighLOD = lodInst->higherQualityEntity )
{
prevHighLOD->lowerQualityEntity = NULL;
}
this->lowerQualityEntity = lodInst;
lodInst->higherQualityEntity = this;
}
}
Entity* Entity::GetLODEntity( void )
{
return this->lowerQualityEntity;
}
bool Entity::IsLowerLODOf( Entity *inst ) const
{
Entity *entity = this->lowerQualityEntity;
while ( entity )
{
if ( entity == inst )
return true;
entity = entity->lowerQualityEntity;
}
return false;
}
bool Entity::IsHigherLODOf( Entity *inst ) const
{
Entity *entity = this->higherQualityEntity;
while ( entity )
{
if ( entity == inst )
return true;
entity = entity->higherQualityEntity;
}
return false;
}
ModelManager::ModelResource* Entity::GetModelInfo( void ) const
{
return theGame->GetModelManager().GetModelByID( this->modelID );
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -34,6 +34,8 @@ ModelManager::~ModelManager( void )
this->models.clear();
this->modelByName.clear();
this->basifierLookup.clear();
this->lodifierLookup.clear();
streaming.UnregisterResourceType( MODEL_ID_BASE );
}
......@@ -55,6 +57,17 @@ void ModelManager::RegisterAtomicModel(
}
}
// Check whether we already have a model that goes by that name.
{
auto findIter = this->modelByName.find( name );
if ( findIter != this->modelByName.end() )
{
// The name is already taken, so bail.
return;
}
}
// Get the device this model is bound to.
std::string pathPrefix = theGame->GetDevicePathPrefix();
......@@ -68,6 +81,13 @@ void ModelManager::RegisterAtomicModel(
return;
}
// Check whether this resource even exists.
if ( resDevice->GetLength( devPath ) == -1 )
{
// Does not exist, I think.
return;
}
ModelResource *modelEntry = new ModelResource( resDevice, std::move( devPath ) );
modelEntry->manager = this;
......@@ -78,6 +98,7 @@ void ModelManager::RegisterAtomicModel(
modelEntry->flags = flags;
modelEntry->modelPtr = NULL;
modelEntry->modelType = eModelType::ATOMIC;
modelEntry->lod_model = NULL;
modelEntry->lockModelLoading = SRWLOCK_INIT;
......@@ -114,6 +135,63 @@ void ModelManager::RegisterAtomicModel(
modelEntry->texDictID = texDictID;
}
// Check whether we are a LOD model.
bool isLODModel = false;
if ( name.size() >= 4 )
{
// We are only interresting for LOD matching if our name is longer than three characters.
std::string lod_id = name.substr( 3 );
if ( strncmp( name.c_str(), "LOD", 3 ) == 0 )
{
isLODModel = true;
}
// If we are a LOD model, we have to check whether there already is a model that would want this as LOD instance.
// Otherwise we have to check whether there already is a LOD model that would map to us.
if ( isLODModel )
{
// DO NOTE that we lookup for the last model that registered itself as base here.
// There can be multiple that map to the same base id in the IDE files, and we have to be
// careful about that if we ever want to support unregistering of models.
auto findBaseModel = this->basifierLookup.find( lod_id );
if ( findBaseModel != this->basifierLookup.end() )
{
// We found a base model to map to!
ModelResource *baseModelEntry = findBaseModel->second;
baseModelEntry->lod_model = modelEntry;
}
}
else
{
// Check whether a LOD model already exists that should map to us.
auto findLODModel = this->lodifierLookup.find( lod_id );
if ( findLODModel != this->lodifierLookup.end() )
{
// Good catch. There is a LOD model already that should be mapped to us.
ModelResource *lodModelEntry = findLODModel->second;
modelEntry->lod_model = lodModelEntry;
}
}
// Register us for lookup.
if ( isLODModel )
{
// Register us in the LOD lookup map.
this->lodifierLookup.insert( std::make_pair( std::move( lod_id ), modelEntry ) );
}
else
{
// We are not a LOD model, so lets register us as base model.
this->basifierLookup.insert( std::make_pair( std::move( lod_id ), modelEntry ) );
}
}
// Store us. :)
this->modelByName.insert( std::make_pair( name, modelEntry ) );
......@@ -122,6 +200,8 @@ void ModelManager::RegisterAtomicModel(
// Success!
}
// TODO: maybe allow unregistering of models.
ModelManager::ModelResource* ModelManager::GetModelByID( streaming::ident_t id )
{
if ( id < 0 || id >= MAX_MODELS )
......@@ -245,9 +325,6 @@ void ModelManager::LoadResource( streaming::ident_t localID, const void *dataBuf
if ( txdID != -1 )
{
// TODO: this does not work because this thing is a global variable.
// It breaks multi-threaded loading, which is a real shame.
// Solution: wait till tomorrow so that aap has built in some texture lookup function!
texManager.SetCurrentTXD( txdID );
}
}
......
......@@ -55,7 +55,7 @@ TextureManager::~TextureManager( void )
delete texDict;
}
// Clear addressable lists.
// Clear addressible lists.
this->texDictMap.clear();
this->texDictList.clear();
......
#include "StdInc.h"
#include "World.h"
namespace krt
{
World::World( void )
{
// A world can store a lot of entities.
LIST_CLEAR( this->entityList.root );
}
World::~World( void )
{
// Unlink all entities from the world.
{
LIST_FOREACH_BEGIN( Entity, this->entityList.root, worldNode )
item->onWorld = NULL;
LIST_FOREACH_END
LIST_CLEAR( this->entityList.root );
}
}
// TODO: add more cool world stuff.
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment