mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-23 02:42:39 +01:00
Implement cutscene objects and animations (sans skinning)
+ Add CutsceneObject for non-interactive animating objects + Implement HIER section to load cutscene object data
This commit is contained in:
parent
688ee493b4
commit
02838ce3b4
@ -137,4 +137,11 @@ struct DynamicObjectData
|
||||
bool cameraAvoid;
|
||||
};
|
||||
|
||||
struct CutsceneObjectData
|
||||
{
|
||||
uint16_t ID;
|
||||
std::string modelName;
|
||||
std::string textureName;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -64,6 +64,7 @@ public:
|
||||
Character,
|
||||
Vehicle,
|
||||
Pickup,
|
||||
Cutscene,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define _GAMESTATE_HPP_
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class PlayerController;
|
||||
class CutsceneData;
|
||||
@ -45,6 +46,10 @@ struct GameState
|
||||
float osTextStart;
|
||||
float osTextTime;
|
||||
|
||||
/// Stores the "special" character and cutscene model indices.
|
||||
std::map<unsigned short, std::string> specialCharacters;
|
||||
std::map<unsigned short, std::string> specialModels;
|
||||
|
||||
GameState() :
|
||||
maxProgress(1),
|
||||
numMissions(0),
|
||||
|
@ -99,15 +99,20 @@ public:
|
||||
* Creates an instance
|
||||
*/
|
||||
InstanceObject *createInstance(const uint16_t id, const glm::vec3& pos, const glm::quat& rot = glm::quat());
|
||||
|
||||
/**
|
||||
* @brief Creates an InstanceObject for use in the current Cutscene.
|
||||
*/
|
||||
CutsceneObject *createCutsceneObject(const uint16_t id, const glm::vec3& pos, const glm::quat& rot = glm::quat());
|
||||
|
||||
/**
|
||||
* Creates a vehicle
|
||||
*/
|
||||
VehicleObject *createVehicle(const uint16_t id, const glm::vec3& pos, const glm::quat& rot = glm::quat());
|
||||
|
||||
/**
|
||||
* Creates a pedestrian.
|
||||
*/
|
||||
/**
|
||||
* Creates a pedestrian.
|
||||
*/
|
||||
CharacterObject* createPedestrian(const uint16_t id, const glm::vec3& pos, const glm::quat& rot = glm::quat());
|
||||
|
||||
/**
|
||||
@ -167,6 +172,11 @@ public:
|
||||
*/
|
||||
std::map<uint16_t, std::vector<std::shared_ptr<PathData>>> objectNodes;
|
||||
|
||||
/**
|
||||
* Stores cutscene object model "names"
|
||||
*/
|
||||
std::map<uint16_t, std::shared_ptr<CutsceneObjectData>> cutsceneObjectTypes;
|
||||
|
||||
/**
|
||||
* Vehicle type definitions
|
||||
* @todo move this non-instance data to GameData
|
||||
@ -235,6 +245,14 @@ public:
|
||||
* @param name
|
||||
*/
|
||||
void loadCutscene(const std::string& name);
|
||||
|
||||
void clearCutscene();
|
||||
|
||||
/**
|
||||
* @brief loads a model into a special character slot.
|
||||
*/
|
||||
void loadSpecialCharacter(const unsigned short index, const std::string& name);
|
||||
void loadSpecialModel(const unsigned short index, const std::string& name);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -32,6 +32,7 @@ public:
|
||||
std::vector<std::shared_ptr<VehicleData>> CARSs;
|
||||
std::vector<std::shared_ptr<CharacterData>> PEDSs;
|
||||
std::vector<std::shared_ptr<PathData>> PATHs;
|
||||
std::vector<std::shared_ptr<CutsceneObjectData>> HIERs;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -105,6 +105,11 @@ public:
|
||||
|
||||
void tick(float dt);
|
||||
|
||||
/**
|
||||
* @brief Loads the model and texture for a character skin.
|
||||
*/
|
||||
void changeCharacterModel(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief updateCharacter updates internall bullet Character.
|
||||
*/
|
||||
|
38
rwengine/include/objects/CutsceneObject.hpp
Normal file
38
rwengine/include/objects/CutsceneObject.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#ifndef _CUTSCENEOBJECT_HPP_
|
||||
#define _CUTSCENEOBJECT_HPP_
|
||||
#include <engine/GameObject.hpp>
|
||||
|
||||
/**
|
||||
* @brief Object type used for cutscene animations.
|
||||
*/
|
||||
class CutsceneObject : public GameObject
|
||||
{
|
||||
GameObject* _parent;
|
||||
ModelFrame* _bone;
|
||||
|
||||
public:
|
||||
|
||||
CutsceneObject(GameWorld* engine,
|
||||
const glm::vec3& pos,
|
||||
ModelHandle* model);
|
||||
~CutsceneObject();
|
||||
|
||||
Type type() { return Cutscene; }
|
||||
|
||||
void tick(float dt);
|
||||
|
||||
void setParentActor(GameObject* parent, ModelFrame* bone);
|
||||
|
||||
GameObject* getParentActor() const
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
|
||||
ModelFrame* getParentFrame() const
|
||||
{
|
||||
return _bone;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -17,6 +17,7 @@ class CharacterObject;
|
||||
class VehicleObject;
|
||||
class InstanceObject;
|
||||
class PickupObject;
|
||||
class CutsceneObject;
|
||||
|
||||
class Animator;
|
||||
class InventoryItem;
|
||||
@ -192,6 +193,8 @@ public:
|
||||
*/
|
||||
void renderPickup(PickupObject* pickup);
|
||||
|
||||
void renderCutsceneObject(CutsceneObject *cutscene);
|
||||
|
||||
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);
|
||||
|
||||
void renderItem(InventoryItem* item, const glm::mat4& modelMatrix);
|
||||
|
@ -54,12 +54,15 @@ public:
|
||||
bool intersects(glm::vec3 center, float radius)
|
||||
{
|
||||
float d;
|
||||
bool result = true;
|
||||
|
||||
for(size_t i = 0; i < 6; ++i)
|
||||
{
|
||||
d = glm::dot(planes[i].normal, center) + planes[i].distance;
|
||||
if( d < -radius ) return false;
|
||||
if( d < -radius ) result = false;
|
||||
}
|
||||
return true;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -91,6 +91,7 @@ void GameData::load()
|
||||
_knownFiles.insert({"weapons.dff", {false, datpath+"/models/Generic/weapons.dff"}});
|
||||
_knownFiles.insert({"particle.txd", {false, datpath+"/models/particle.txd"}});
|
||||
_knownFiles.insert({"english.gxt", {false, datpath+"/TEXT/english.gxt"}});
|
||||
_knownFiles.insert({"ped.ifp", {false, datpath+"/anim/ped.ifp"}});
|
||||
|
||||
loadDFF("wheels.DFF");
|
||||
loadDFF("weapons.dff");
|
||||
@ -103,7 +104,7 @@ void GameData::load()
|
||||
loadWater(datpath+"/data/water.dat");
|
||||
loadWeaponDAT(datpath+"/data/weapon.dat");
|
||||
|
||||
loadIFP(datpath+"/anim/ped.ifp");
|
||||
loadIFP("ped.ifp");
|
||||
|
||||
}
|
||||
|
||||
@ -479,23 +480,16 @@ void GameData::loadDFF(const std::string& name, bool async)
|
||||
|
||||
void GameData::loadIFP(const std::string &name)
|
||||
{
|
||||
std::ifstream dfile(name.c_str());
|
||||
auto f = openFile2(name);
|
||||
|
||||
if(dfile.is_open())
|
||||
{
|
||||
dfile.seekg(0, std::ios_base::end);
|
||||
size_t length = dfile.tellg();
|
||||
dfile.seekg(0);
|
||||
char *file = new char[length];
|
||||
dfile.read(file, length);
|
||||
if(f)
|
||||
{
|
||||
LoaderIFP loader;
|
||||
if( loader.loadFromMemory(f->data) ) {
|
||||
animations.insert(loader.animations.begin(), loader.animations.end());
|
||||
}
|
||||
|
||||
LoaderIFP loader;
|
||||
if( loader.loadFromMemory(file) ) {
|
||||
animations.insert(loader.animations.begin(), loader.animations.end());
|
||||
}
|
||||
|
||||
delete[] file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::loadDynamicObjects(const std::string& name)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <objects/CharacterObject.hpp>
|
||||
#include <objects/InstanceObject.hpp>
|
||||
#include <objects/VehicleObject.hpp>
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <loaders/LoaderCutsceneDAT.hpp>
|
||||
@ -152,6 +153,13 @@ bool GameWorld::defineItems(const std::string& name)
|
||||
});
|
||||
}
|
||||
|
||||
for( size_t v = 0; v < idel.HIERs.size(); ++v) {
|
||||
cutsceneObjectTypes.insert({
|
||||
idel.HIERs[v]->ID,
|
||||
idel.HIERs[v]
|
||||
});
|
||||
}
|
||||
|
||||
// Load AI information.
|
||||
for( size_t a = 0; a < idel.PATHs.size(); ++a ) {
|
||||
auto pathit = objectNodes.find(idel.PATHs[a]->ID);
|
||||
@ -260,14 +268,22 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
|
||||
{
|
||||
auto oi = objectTypes.find(id);
|
||||
if( oi != objectTypes.end()) {
|
||||
// Make sure the DFF and TXD are loaded
|
||||
|
||||
std::string modelname = oi->second->modelName;
|
||||
std::string texturename = oi->second->textureName;
|
||||
|
||||
// Ensure the relevant data is loaded.
|
||||
if(! oi->second->modelName.empty()) {
|
||||
gameData.loadDFF(oi->second->modelName + ".dff", true);
|
||||
if( modelname != "null" ) {
|
||||
gameData.loadDFF(modelname + ".dff", true);
|
||||
}
|
||||
}
|
||||
if(! oi->second->textureName.empty()) {
|
||||
gameData.loadTXD(oi->second->textureName + ".txd", true);
|
||||
if(! texturename.empty()) {
|
||||
gameData.loadTXD(texturename + ".txd", true);
|
||||
}
|
||||
|
||||
ModelHandle* m = gameData.models[modelname];
|
||||
|
||||
// Check for dynamic data.
|
||||
auto dyit = gameData.dynamicObjectData.find(oi->second->modelName);
|
||||
std::shared_ptr<DynamicObjectData> dydata;
|
||||
@ -275,7 +291,7 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
|
||||
dydata = dyit->second;
|
||||
}
|
||||
|
||||
if( oi->second->modelName.empty() ) {
|
||||
if( modelname.empty() ) {
|
||||
logWarning("Instance with missing model: " + std::to_string(id));
|
||||
}
|
||||
|
||||
@ -283,7 +299,7 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
|
||||
this,
|
||||
pos,
|
||||
rot,
|
||||
gameData.models[oi->second->modelName],
|
||||
m,
|
||||
glm::vec3(1.f, 1.f, 1.f),
|
||||
oi->second, nullptr, dydata
|
||||
);
|
||||
@ -301,6 +317,73 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#include <ai/PlayerController.hpp>
|
||||
CutsceneObject *GameWorld::createCutsceneObject(const uint16_t id, const glm::vec3 &pos, const glm::quat &rot)
|
||||
{
|
||||
std::string modelname;
|
||||
std::string texturename;
|
||||
|
||||
/// @todo merge all object defintion types so we don't have to deal with this.
|
||||
auto ci = cutsceneObjectTypes.find(id);
|
||||
if( ci != cutsceneObjectTypes.end()) {
|
||||
modelname = state.specialModels[id];
|
||||
texturename = state.specialModels[id];
|
||||
}
|
||||
else {
|
||||
auto ii = objectTypes.find(id);
|
||||
if( ii != objectTypes.end() ) {
|
||||
modelname = ii->second->modelName;
|
||||
texturename = ii->second->textureName;
|
||||
}
|
||||
else {
|
||||
auto pi = pedestrianTypes.find(id);
|
||||
if( pi != pedestrianTypes.end() ) {
|
||||
modelname = pi->second->modelName;
|
||||
texturename = pi->second->textureName;
|
||||
|
||||
static std::string specialPrefix("special");
|
||||
if(! modelname.compare(0, specialPrefix.size(), specialPrefix) ) {
|
||||
auto sid = modelname.substr(specialPrefix.size());
|
||||
unsigned short specialID = std::atoi(sid.c_str());
|
||||
modelname = state.specialCharacters[specialID];
|
||||
texturename = state.specialCharacters[specialID];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( id == 0 ) {
|
||||
modelname = state.player->getCharacter()->model->name;
|
||||
}
|
||||
|
||||
// Ensure the relevant data is loaded.
|
||||
if( modelname.empty() ) {
|
||||
std::cerr << "Couldn't find model for id " << id << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( modelname != "null" ) {
|
||||
gameData.loadDFF(modelname + ".dff", false);
|
||||
}
|
||||
|
||||
if(! texturename.empty()) {
|
||||
gameData.loadTXD(texturename + ".txd", true);
|
||||
}
|
||||
|
||||
|
||||
ModelHandle* m = gameData.models[modelname];
|
||||
|
||||
auto instance = new CutsceneObject(
|
||||
this,
|
||||
pos,
|
||||
m);
|
||||
|
||||
objects.insert(instance);
|
||||
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
VehicleObject *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos, const glm::quat& rot)
|
||||
{
|
||||
auto vti = vehicleTypes.find(id);
|
||||
@ -367,21 +450,34 @@ VehicleObject *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos,
|
||||
|
||||
CharacterObject* GameWorld::createPedestrian(const uint16_t id, const glm::vec3 &pos, const glm::quat& rot)
|
||||
{
|
||||
auto pti = pedestrianTypes.find(id);
|
||||
if( pti != pedestrianTypes.end() ) {
|
||||
auto& pt = pti->second;
|
||||
auto pti = pedestrianTypes.find(id);
|
||||
if( pti != pedestrianTypes.end() ) {
|
||||
auto& pt = pti->second;
|
||||
|
||||
// Ensure the relevant data is loaded.
|
||||
if(! pt->modelName.empty()) {
|
||||
if( pt->modelName != "null" ) {
|
||||
gameData.loadDFF(pt->modelName + ".dff");
|
||||
std::string modelname = pt->modelName;
|
||||
std::string texturename = pt->textureName;
|
||||
|
||||
// Ensure the relevant data is loaded.
|
||||
if(! pt->modelName.empty()) {
|
||||
// Some model names have special meanings.
|
||||
/// @todo Should CharacterObjects handle this?
|
||||
static std::string specialPrefix("special");
|
||||
if(! modelname.compare(0, specialPrefix.size(), specialPrefix) ) {
|
||||
auto sid = modelname.substr(specialPrefix.size());
|
||||
unsigned short specialID = std::atoi(sid.c_str());
|
||||
modelname = state.specialCharacters[specialID];
|
||||
texturename = state.specialCharacters[specialID];
|
||||
}
|
||||
}
|
||||
if(! pt->textureName.empty()) {
|
||||
gameData.loadTXD(pt->textureName + ".txd");
|
||||
}
|
||||
|
||||
ModelHandle* m = gameData.models[pt->modelName];
|
||||
if( modelname != "null" ) {
|
||||
gameData.loadDFF(modelname + ".dff");
|
||||
}
|
||||
}
|
||||
if(! texturename.empty()) {
|
||||
gameData.loadTXD(texturename + ".txd");
|
||||
}
|
||||
|
||||
ModelHandle* m = gameData.models[modelname];
|
||||
|
||||
if(m && m->model) {
|
||||
auto ped = new CharacterObject( this, pos, rot, m, pt );
|
||||
@ -389,8 +485,8 @@ CharacterObject* GameWorld::createPedestrian(const uint16_t id, const glm::vec3
|
||||
new DefaultAIController(ped);
|
||||
return ped;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GameWorld::destroyObject(GameObject* object)
|
||||
@ -561,6 +657,7 @@ void GameWorld::loadCutscene(const std::string &name)
|
||||
{
|
||||
std::string lowerName(name);
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
|
||||
auto datfile = gameData.openFile2(lowerName + ".dat");
|
||||
|
||||
CutsceneData* cutscene = new CutsceneData;
|
||||
@ -570,6 +667,8 @@ void GameWorld::loadCutscene(const std::string &name)
|
||||
loaderdat.load(cutscene->tracks, datfile);
|
||||
}
|
||||
|
||||
gameData.loadIFP(lowerName + ".ifp");
|
||||
|
||||
if( state.currentCutscene ) {
|
||||
delete state.currentCutscene;
|
||||
}
|
||||
@ -577,3 +676,37 @@ void GameWorld::loadCutscene(const std::string &name)
|
||||
std::cout << "Loaded cutscene: " << name << std::endl;
|
||||
}
|
||||
|
||||
void GameWorld::clearCutscene()
|
||||
{
|
||||
/// @todo replace with the queued deletion from the projectile branch
|
||||
for(auto i = objects.begin(); i != objects.end();) {
|
||||
if( (*i)->type() == GameObject::Cutscene ) {
|
||||
delete (*i);
|
||||
i = objects.erase(i);
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
delete state.currentCutscene;
|
||||
state.currentCutscene = nullptr;
|
||||
state.cutsceneStartTime = -1.f;
|
||||
}
|
||||
|
||||
void GameWorld::loadSpecialCharacter(const unsigned short index, const std::string &name)
|
||||
{
|
||||
std::string lowerName(name);
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
/// @todo a bit more smarter than this
|
||||
state.specialCharacters[index] = lowerName;
|
||||
}
|
||||
|
||||
void GameWorld::loadSpecialModel(const unsigned short index, const std::string &name)
|
||||
{
|
||||
std::string lowerName(name);
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
/// @todo a bit more smarter than this
|
||||
state.specialModels[index] = lowerName;
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,20 @@ bool LoaderIDE::load(const std::string &filename)
|
||||
|
||||
break;
|
||||
}
|
||||
case HIER: {
|
||||
std::shared_ptr<CutsceneObjectData> cut(new CutsceneObjectData);
|
||||
|
||||
std::string id;
|
||||
|
||||
getline(strstream, id, ',');
|
||||
getline(strstream, cut->modelName, ',');
|
||||
getline(strstream, cut->textureName, ',');
|
||||
|
||||
cut->ID = atoi(id.c_str());
|
||||
|
||||
HIERs.push_back(cut);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ LoaderIMGFile &LoaderIMG::getAssetInfo(const std::string& assetname)
|
||||
{
|
||||
for(size_t i = 0; i < m_assets.size(); ++i)
|
||||
{
|
||||
if(strcmp(m_assets[i].name, assetname.c_str()) == 0)
|
||||
if(strcasecmp(m_assets[i].name, assetname.c_str()) == 0)
|
||||
{
|
||||
return m_assets[i];
|
||||
}
|
||||
|
@ -184,6 +184,22 @@ void CharacterObject::tick(float dt)
|
||||
}
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
void CharacterObject::changeCharacterModel(const std::string &name)
|
||||
{
|
||||
auto modelName = std::string(name);
|
||||
std::transform(modelName.begin(), modelName.end(), modelName.begin(), ::tolower);
|
||||
|
||||
engine->gameData.loadDFF(modelName + ".dff");
|
||||
engine->gameData.loadTXD(modelName + ".txd");
|
||||
|
||||
auto& models = engine->gameData.models;
|
||||
auto mfind = models.find(modelName);
|
||||
if( mfind != models.end() ) {
|
||||
model = mfind->second;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterObject::updateCharacter(float dt)
|
||||
{
|
||||
if(physCharacter) {
|
||||
|
24
rwengine/src/objects/CutsceneObject.cpp
Normal file
24
rwengine/src/objects/CutsceneObject.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
|
||||
CutsceneObject::CutsceneObject(GameWorld *engine, const glm::vec3 &pos, ModelHandle *model)
|
||||
: GameObject(engine, pos, {}, model), _parent(nullptr), _bone(nullptr)
|
||||
{
|
||||
animator = new Animator;
|
||||
}
|
||||
|
||||
CutsceneObject::~CutsceneObject()
|
||||
{
|
||||
delete animator;
|
||||
}
|
||||
|
||||
void CutsceneObject::tick(float dt)
|
||||
{
|
||||
animator->tick(dt);
|
||||
}
|
||||
|
||||
void CutsceneObject::setParentActor(GameObject *parent, ModelFrame *bone)
|
||||
{
|
||||
_parent = parent;
|
||||
_bone = bone;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include <objects/InstanceObject.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <data/CollisionModel.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
|
||||
InstanceObject::InstanceObject(GameWorld* engine,
|
||||
const glm::vec3& pos,
|
||||
@ -13,98 +14,101 @@ InstanceObject::InstanceObject(GameWorld* engine,
|
||||
: GameObject(engine, pos, rot, model), scale(scale), object(obj),
|
||||
LODinstance(lod), dynamics(dyn), _collisionHeight(0.f), _enablePhysics(false)
|
||||
{
|
||||
auto phyit = engine->gameData.collisions.find(obj->modelName);
|
||||
if( phyit != engine->gameData.collisions.end()) {
|
||||
btCompoundShape* cmpShape = new btCompoundShape;
|
||||
btDefaultMotionState* msta = new btDefaultMotionState;
|
||||
msta->setWorldTransform(btTransform(
|
||||
btQuaternion(
|
||||
rot.x, rot.y, rot.z, -rot.w
|
||||
).inverse(),
|
||||
btVector3(
|
||||
pos.x, pos.y, pos.z
|
||||
)
|
||||
));
|
||||
btRigidBody::btRigidBodyConstructionInfo info(0.f, msta, cmpShape);
|
||||
CollisionModel& physInst = *phyit->second.get();
|
||||
if( obj ) {
|
||||
auto phyit = engine->gameData.collisions.find(obj->modelName);
|
||||
if( phyit != engine->gameData.collisions.end()) {
|
||||
btCompoundShape* cmpShape = new btCompoundShape;
|
||||
btDefaultMotionState* msta = new btDefaultMotionState;
|
||||
msta->setWorldTransform(btTransform(
|
||||
btQuaternion(
|
||||
rot.x, rot.y, rot.z, -rot.w
|
||||
).inverse(),
|
||||
btVector3(
|
||||
pos.x, pos.y, pos.z
|
||||
)
|
||||
));
|
||||
btRigidBody::btRigidBodyConstructionInfo info(0.f, msta, cmpShape);
|
||||
CollisionModel& physInst = *phyit->second.get();
|
||||
|
||||
float colMin = std::numeric_limits<float>::max(),
|
||||
colMax = std::numeric_limits<float>::lowest();
|
||||
float colMin = std::numeric_limits<float>::max(),
|
||||
colMax = std::numeric_limits<float>::lowest();
|
||||
|
||||
// Boxes
|
||||
for( size_t i = 0; i < physInst.boxes.size(); ++i ) {
|
||||
auto& box = physInst.boxes[i];
|
||||
auto size = (box.max - box.min) / 2.f;
|
||||
auto mid = (box.min + box.max) / 2.f;
|
||||
btCollisionShape* bshape = new btBoxShape( btVector3(size.x, size.y, size.z) );
|
||||
btTransform t; t.setIdentity();
|
||||
t.setOrigin(btVector3(mid.x, mid.y, mid.z));
|
||||
cmpShape->addChildShape(t, bshape);
|
||||
// Boxes
|
||||
for( size_t i = 0; i < physInst.boxes.size(); ++i ) {
|
||||
auto& box = physInst.boxes[i];
|
||||
auto size = (box.max - box.min) / 2.f;
|
||||
auto mid = (box.min + box.max) / 2.f;
|
||||
btCollisionShape* bshape = new btBoxShape( btVector3(size.x, size.y, size.z) );
|
||||
btTransform t; t.setIdentity();
|
||||
t.setOrigin(btVector3(mid.x, mid.y, mid.z));
|
||||
cmpShape->addChildShape(t, bshape);
|
||||
|
||||
colMin = std::min(colMin, mid.z - size.z);
|
||||
colMax = std::max(colMax, mid.z + size.z);
|
||||
}
|
||||
|
||||
// Spheres
|
||||
for( size_t i = 0; i < physInst.spheres.size(); ++i ) {
|
||||
auto& sphere = physInst.spheres[i];
|
||||
btCollisionShape* sshape = new btSphereShape(sphere.radius);
|
||||
btTransform t; t.setIdentity();
|
||||
t.setOrigin(btVector3(sphere.center.x, sphere.center.y, sphere.center.z));
|
||||
cmpShape->addChildShape(t, sshape);
|
||||
|
||||
colMin = std::min(colMin, sphere.center.z - sphere.radius);
|
||||
colMax = std::max(colMax, sphere.center.z + sphere.radius);
|
||||
}
|
||||
|
||||
if( physInst.vertices.size() > 0 && physInst.indices.size() >= 3 ) {
|
||||
btTriangleIndexVertexArray* vertarray = new btTriangleIndexVertexArray(
|
||||
physInst.indices.size()/3,
|
||||
(int*) physInst.indices.data(),
|
||||
sizeof(uint32_t)*3,
|
||||
physInst.vertices.size(),
|
||||
&(physInst.vertices[0].x),
|
||||
sizeof(glm::vec3));
|
||||
btBvhTriangleMeshShape* trishape = new btBvhTriangleMeshShape(vertarray, false);
|
||||
trishape->setMargin(0.05f);
|
||||
btTransform t; t.setIdentity();
|
||||
cmpShape->addChildShape(t, trishape);
|
||||
}
|
||||
|
||||
_collisionHeight = colMax - colMin;
|
||||
|
||||
if( dynamics ) {
|
||||
if( dynamics->uprootForce > 0.f ) {
|
||||
info.m_mass = 0.f;
|
||||
colMin = std::min(colMin, mid.z - size.z);
|
||||
colMax = std::max(colMax, mid.z + size.z);
|
||||
}
|
||||
else {
|
||||
btVector3 inert;
|
||||
cmpShape->calculateLocalInertia(dynamics->mass, inert);
|
||||
info.m_mass = dynamics->mass;
|
||||
info.m_localInertia = inert;
|
||||
|
||||
// Spheres
|
||||
for( size_t i = 0; i < physInst.spheres.size(); ++i ) {
|
||||
auto& sphere = physInst.spheres[i];
|
||||
btCollisionShape* sshape = new btSphereShape(sphere.radius);
|
||||
btTransform t; t.setIdentity();
|
||||
t.setOrigin(btVector3(sphere.center.x, sphere.center.y, sphere.center.z));
|
||||
cmpShape->addChildShape(t, sshape);
|
||||
|
||||
colMin = std::min(colMin, sphere.center.z - sphere.radius);
|
||||
colMax = std::max(colMax, sphere.center.z + sphere.radius);
|
||||
}
|
||||
|
||||
if( physInst.vertices.size() > 0 && physInst.indices.size() >= 3 ) {
|
||||
btTriangleIndexVertexArray* vertarray = new btTriangleIndexVertexArray(
|
||||
physInst.indices.size()/3,
|
||||
(int*) physInst.indices.data(),
|
||||
sizeof(uint32_t)*3,
|
||||
physInst.vertices.size(),
|
||||
&(physInst.vertices[0].x),
|
||||
sizeof(glm::vec3));
|
||||
btBvhTriangleMeshShape* trishape = new btBvhTriangleMeshShape(vertarray, false);
|
||||
trishape->setMargin(0.05f);
|
||||
btTransform t; t.setIdentity();
|
||||
cmpShape->addChildShape(t, trishape);
|
||||
}
|
||||
|
||||
_collisionHeight = colMax - colMin;
|
||||
|
||||
if( dynamics ) {
|
||||
if( dynamics->uprootForce > 0.f ) {
|
||||
info.m_mass = 0.f;
|
||||
}
|
||||
else {
|
||||
btVector3 inert;
|
||||
cmpShape->calculateLocalInertia(dynamics->mass, inert);
|
||||
info.m_mass = dynamics->mass;
|
||||
info.m_localInertia = inert;
|
||||
}
|
||||
}
|
||||
|
||||
body = new btRigidBody(info);
|
||||
body->setUserPointer(this);
|
||||
engine->dynamicsWorld->addRigidBody(body);
|
||||
|
||||
if( dynamics && dynamics->uprootForce > 0.f ) {
|
||||
body->setCollisionFlags(body->getCollisionFlags()
|
||||
| btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
|
||||
}
|
||||
|
||||
body->setActivationState(ISLAND_SLEEPING);
|
||||
}
|
||||
|
||||
auto pathit = engine->objectNodes.find(obj->ID);
|
||||
if( pathit != engine->objectNodes.end() ) {
|
||||
auto& pathlist = pathit->second;
|
||||
for( size_t p = 0; p < pathlist.size(); ++p ) {
|
||||
auto& path = pathlist[p];
|
||||
engine->aigraph.createPathNodes(position, rot, *path);
|
||||
}
|
||||
}
|
||||
|
||||
body = new btRigidBody(info);
|
||||
body->setUserPointer(this);
|
||||
engine->dynamicsWorld->addRigidBody(body);
|
||||
|
||||
if( dynamics && dynamics->uprootForce > 0.f ) {
|
||||
body->setCollisionFlags(body->getCollisionFlags()
|
||||
| btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
|
||||
}
|
||||
|
||||
body->setActivationState(ISLAND_SLEEPING);
|
||||
}
|
||||
|
||||
auto pathit = engine->objectNodes.find(obj->ID);
|
||||
if( pathit != engine->objectNodes.end() ) {
|
||||
auto& pathlist = pathit->second;
|
||||
for( size_t p = 0; p < pathlist.size(); ++p ) {
|
||||
auto& path = pathlist[p];
|
||||
engine->aigraph.createPathNodes(position, rot, *path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceObject::tick(float dt)
|
||||
@ -180,6 +184,8 @@ void InstanceObject::tick(float dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( animator ) animator->tick(dt);
|
||||
}
|
||||
|
||||
void InstanceObject::setRotation(const glm::quat &r)
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include <data/ObjectData.hpp>
|
||||
#include <items/InventoryItem.hpp>
|
||||
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
|
||||
#include <render/GameShaders.hpp>
|
||||
|
||||
#include <deque>
|
||||
@ -345,6 +348,9 @@ void GameRenderer::renderWorld(float alpha)
|
||||
case GameObject::Pickup:
|
||||
renderPickup(static_cast<PickupObject*>(object));
|
||||
break;
|
||||
case GameObject::Cutscene:
|
||||
renderCutsceneObject(static_cast<CutsceneObject*>(object));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@ -583,7 +589,7 @@ void GameRenderer::renderVehicle(VehicleObject *vehicle)
|
||||
|
||||
void GameRenderer::renderInstance(InstanceObject *instance)
|
||||
{
|
||||
if(instance->object->timeOn != instance->object->timeOff) {
|
||||
if(instance->object && instance->object->timeOn != instance->object->timeOff) {
|
||||
// Update rendering flags.
|
||||
if(engine->getHour() < instance->object->timeOn
|
||||
&& engine->getHour() > instance->object->timeOff) {
|
||||
@ -600,11 +606,15 @@ void GameRenderer::renderInstance(InstanceObject *instance)
|
||||
if( instance->body ) {
|
||||
instance->body->getWorldTransform().getOpenGLMatrix(glm::value_ptr(matrixModel));
|
||||
}
|
||||
else {
|
||||
else if(instance->object) {
|
||||
matrixModel = glm::translate(matrixModel, instance->position);
|
||||
matrixModel = glm::scale(matrixModel, instance->scale);
|
||||
matrixModel = matrixModel * glm::mat4_cast(instance->rotation);
|
||||
}
|
||||
else {
|
||||
/// @todo clean up this
|
||||
matrixModel = glm::translate(matrixModel, engine->state.currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
|
||||
}
|
||||
|
||||
float mindist = 100000.f;
|
||||
for (size_t g = 0; g < instance->model->model->geometries.size(); g++)
|
||||
@ -613,7 +623,11 @@ void GameRenderer::renderInstance(InstanceObject *instance)
|
||||
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - camera.worldPos) - bounds.radius);
|
||||
}
|
||||
|
||||
if( instance->object->numClumps == 1 ) {
|
||||
/// @todo not sure if this is the best way to handle cutscene objects
|
||||
if(! instance->object ) {
|
||||
renderModel(instance->model->model, matrixModel, instance);
|
||||
}
|
||||
else if( instance->object->numClumps == 1 ) {
|
||||
if( mindist > instance->object->drawDistance[0] ) {
|
||||
// Check for LOD instances
|
||||
if ( instance->LODinstance ) {
|
||||
@ -675,6 +689,49 @@ void GameRenderer::renderPickup(PickupObject *pickup)
|
||||
std::cerr << "weapons.dff not loaded (" << pickup->getModelID() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
void GameRenderer::renderCutsceneObject(CutsceneObject *cutscene)
|
||||
{
|
||||
if(!cutscene->model->model)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glm::mat4 matrixModel;
|
||||
|
||||
if( cutscene->getParentActor() ) {
|
||||
matrixModel = glm::translate(matrixModel, engine->state.currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
|
||||
//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()->animator->getFrameMatrix(boneframe, _renderAlpha, false) * localMatrix;
|
||||
boneframe = boneframe->getParent();
|
||||
}
|
||||
matrixModel = matrixModel * localMatrix;
|
||||
}
|
||||
else {
|
||||
matrixModel = glm::translate(matrixModel, engine->state.currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
|
||||
}
|
||||
|
||||
float mindist = 100000.f;
|
||||
for (size_t g = 0; g < cutscene->model->model->geometries.size(); g++)
|
||||
{
|
||||
RW::BSGeometryBounds& bounds = cutscene->model->model->geometries[g]->geometryBounds;
|
||||
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - camera.worldPos) - bounds.radius);
|
||||
}
|
||||
|
||||
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});
|
||||
renderModel(cutscene->model->model, matrixModel * align, cutscene);
|
||||
}
|
||||
else {
|
||||
renderModel(cutscene->model->model, matrixModel, cutscene);
|
||||
}
|
||||
}
|
||||
|
||||
void GameRenderer::renderWheel(Model* model, const glm::mat4 &matrix, const std::string& name)
|
||||
{
|
||||
@ -815,7 +872,7 @@ bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
auto localmatrix = matrix;
|
||||
|
||||
if(object && object->animator) {
|
||||
localmatrix *= object->animator->getFrameMatrix(f, _renderAlpha, object->isAnimationFixed());
|
||||
localmatrix *= object->animator->getFrameMatrix(f, _renderAlpha, false); //object->isAnimationFixed());
|
||||
}
|
||||
else {
|
||||
localmatrix *= f->getTransform();
|
||||
@ -828,7 +885,8 @@ bool GameRenderer::renderFrame(Model* m, ModelFrame* f, const glm::mat4& matrix,
|
||||
if(!vis ) continue;
|
||||
|
||||
RW::BSGeometryBounds& bounds = m->geometries[g]->geometryBounds;
|
||||
if(! camera.frustum.intersects(bounds.center + glm::vec3(matrix[3]), bounds.radius)) {
|
||||
/// @todo fix culling animating objects?
|
||||
if( (!object || !object->animator) && ! camera.frustum.intersects(bounds.center + glm::vec3(matrix[3]), bounds.radius)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,18 @@
|
||||
#include <objects/VehicleObject.hpp>
|
||||
#include <objects/CharacterObject.hpp>
|
||||
|
||||
#include <render/Model.hpp>
|
||||
#include <engine/Animator.hpp>
|
||||
#include <ai/PlayerController.hpp>
|
||||
#include <ai/DefaultAIController.hpp>
|
||||
|
||||
#include <data/CutsceneData.hpp>
|
||||
#include <objects/CutsceneObject.hpp>
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#define OPC(code, name, params, func) { code, { name, params, [](ScriptMachine* m, SCMThread* t, SCMParams* p) func } },
|
||||
#define OPC_COND(code, name, params, func) { code, { name, params, [](ScriptMachine* m, SCMThread* t, SCMParams* p) { t->conditionResult = ([=]() {func})(); } } },
|
||||
@ -205,15 +210,12 @@ SCMMicrocodeTable ops_game = {
|
||||
glm::vec3 position(p->at(2).real, p->at(3).real, p->at(4).real);
|
||||
|
||||
if( type == 21 ) {
|
||||
std::cout << "Special Characters Unimplemented, forcing Model 2" << std::endl;
|
||||
id = 2;
|
||||
|
||||
}
|
||||
if( position.z < -99.f ) {
|
||||
position = m->getWorld()->getGroundAtPosition(position);
|
||||
}
|
||||
|
||||
std::cout << position.x << " " << position.y << " " << position.z << std::endl;
|
||||
|
||||
auto character = m->getWorld()->createPedestrian(id, position + spawnMagic);
|
||||
auto controller = new DefaultAIController(character);
|
||||
|
||||
@ -452,7 +454,9 @@ SCMMicrocodeTable ops_game = {
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0236, "Set Gang Car", 2 )
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0237, "Set Gang Weapons", 3 )
|
||||
|
||||
OPC_UNIMPLEMENTED_MSG( 0x023C, "Load Special Character", 2)
|
||||
OPC( 0x023C, "Load Special Character", 2, {
|
||||
m->getWorld()->loadSpecialCharacter(p->at(0).integer, p->at(1).string);
|
||||
})
|
||||
OPC_COND( 0x023D, "Is Special Character Loaded", 1, {
|
||||
auto chfind = m->getWorld()->pedestrianTypes.find(p->at(0).integer);
|
||||
if( chfind != m->getWorld()->pedestrianTypes.end() ) {
|
||||
@ -510,7 +514,6 @@ SCMMicrocodeTable ops_game = {
|
||||
|
||||
auto inst = m->getWorld()->createInstance(object->ID, position);
|
||||
|
||||
/// @todo this will fall over due to 64-bit pointers.
|
||||
*p->at(4).handle = inst;
|
||||
})
|
||||
|
||||
@ -528,8 +531,29 @@ SCMMicrocodeTable ops_game = {
|
||||
m->getWorld()->loadCutscene(p->at(0).string);
|
||||
m->getWorld()->state.cutsceneStartTime = -1.f;
|
||||
})
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02E5, "Create Cutscene Object", 2)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02E6, "Set Cutscene Animation", 2)
|
||||
OPC( 0x02E5, "Create Cutscene Object", 2, {
|
||||
auto id = p->at(0).integer;
|
||||
|
||||
GameObject* object = object = m->getWorld()->createCutsceneObject(id, m->getWorld()->state.currentCutscene->meta.sceneOffset );
|
||||
*p->at(1).handle = object;
|
||||
|
||||
if( object == nullptr ) {
|
||||
std::cerr << "Could not create cutscene object " << id << std::endl;
|
||||
}
|
||||
})
|
||||
OPC( 0x02E6, "Set Cutscene Animation", 2, {
|
||||
GameObject* object = static_cast<GameObject*>(*p->at(0).handle);
|
||||
std::string animName = p->at(1).string;
|
||||
std::transform(animName.begin(), animName.end(), animName.begin(), ::tolower);
|
||||
Animation* anim = m->getWorld()->gameData.animations[animName];
|
||||
if( anim ) {
|
||||
object->animator->setModel(object->model->model);
|
||||
object->animator->setAnimation(anim, false);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to find cutscene animation: " << animName << std::endl;
|
||||
}
|
||||
})
|
||||
OPC( 0x02E7, "Start Cutscene", 0, {
|
||||
m->getWorld()->state.cutsceneStartTime = m->getWorld()->gameTime;
|
||||
})
|
||||
@ -542,16 +566,10 @@ SCMMicrocodeTable ops_game = {
|
||||
float time = m->getWorld()->gameTime - m->getWorld()->state.cutsceneStartTime;
|
||||
return time > m->getWorld()->state.currentCutscene->tracks.duration;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
OPC( 0x02EA, "Clear Cutscene", 0, {
|
||||
/** @todo Implement cutscenes */
|
||||
if( m->getWorld()->state.currentCutscene ) {
|
||||
delete m->getWorld()->state.currentCutscene;
|
||||
m->getWorld()->state.currentCutscene = nullptr;
|
||||
}
|
||||
m->getWorld()->state.cutsceneStartTime = -1.f;
|
||||
m->getWorld()->clearCutscene();
|
||||
})
|
||||
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02EC, "Create Hidden Package", 3 )
|
||||
@ -560,10 +578,34 @@ SCMMicrocodeTable ops_game = {
|
||||
m->getWorld()->state.numHiddenPackages = p->at(0).integer;
|
||||
})
|
||||
|
||||
/// @todo parse HIER section for this opcode
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02F3, "Load Special Model", 2)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02F4, "Create Cutscene Actor Head", 3)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02F5, "Set Cutscene Head Animation", 2)
|
||||
OPC( 0x02F3, "Load Special Model", 2, {
|
||||
m->getWorld()->loadSpecialModel(p->at(0).integer, p->at(1).string);
|
||||
})
|
||||
OPC( 0x02F4, "Create Cutscene Actor Head", 3, {
|
||||
auto id = p->at(1).integer;
|
||||
auto actor = static_cast<GameObject*>(*p->at(0).handle);
|
||||
CutsceneObject* object = m->getWorld()->createCutsceneObject(id, m->getWorld()->state.currentCutscene->meta.sceneOffset );
|
||||
|
||||
auto headframe = actor->model->model->findFrame("shead");
|
||||
actor->animator->setFrameVisibility(headframe, false);
|
||||
object->setParentActor(actor, headframe);
|
||||
|
||||
*p->at(2).handle = object;
|
||||
})
|
||||
OPC( 0x02F5, "Set Cutscene Head Animation", 2,
|
||||
{
|
||||
GameObject* object = static_cast<GameObject*>(*p->at(0).handle);
|
||||
std::string animName = p->at(1).string;
|
||||
std::transform(animName.begin(), animName.end(), animName.begin(), ::tolower);
|
||||
Animation* anim = m->getWorld()->gameData.animations[animName];
|
||||
if( anim ) {
|
||||
object->animator->setModel(object->model->model);
|
||||
object->animator->setAnimation(anim, false);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Failed to find cutscene animation: " << animName << std::endl;
|
||||
}
|
||||
})
|
||||
|
||||
OPC_UNIMPLEMENTED_MSG( 0x02FB, "Create Crusher Crane", 10)
|
||||
|
||||
@ -594,7 +636,11 @@ SCMMicrocodeTable ops_game = {
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0348, "Set Text Size Proportional", 1)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0349, "Set Text Font", 1)
|
||||
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0352, "Set Character Model", 2)
|
||||
OPC( 0x0352, "Set Character Model", 2,
|
||||
{
|
||||
auto controller = static_cast<CharacterController*>(*p->at(0).handle);
|
||||
controller->getCharacter()->changeCharacterModel(p->at(1).string);
|
||||
})
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0353, "Refresh Actor Model", 1)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0354, "Start Chase Scene", 1)
|
||||
OPC_UNIMPLEMENTED_MSG( 0x0355, "Stop Chase Scene", 0)
|
||||
|
Loading…
Reference in New Issue
Block a user