1
0
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:
Daniel Evans 2014-07-30 13:44:25 +01:00
parent 688ee493b4
commit 02838ce3b4
18 changed files with 523 additions and 151 deletions

View File

@ -137,4 +137,11 @@ struct DynamicObjectData
bool cameraAvoid;
};
struct CutsceneObjectData
{
uint16_t ID;
std::string modelName;
std::string textureName;
};
#endif

View File

@ -64,6 +64,7 @@ public:
Character,
Vehicle,
Pickup,
Cutscene,
Unknown
};

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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