mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 11:52:40 +01:00
Initial chase scene implementation, opening cutscene chase works
This commit is contained in:
parent
14c33024b7
commit
46621093f9
57
rwengine/include/data/Chase.hpp
Normal file
57
rwengine/include/data/Chase.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef _CHASE_HPP_
|
||||
#define _CHASE_HPP_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class GameObject;
|
||||
|
||||
struct ChaseKeyframe
|
||||
{
|
||||
glm::vec3 velocity;
|
||||
int steeringAngle;
|
||||
int acceleratorPower;
|
||||
int brakePower;
|
||||
bool handbrake;
|
||||
glm::vec3 position;
|
||||
glm::quat rotation;
|
||||
|
||||
static bool load(const std::string& filePath, std::vector<ChaseKeyframe>& frames);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ChaseCoordinator class handles loading and playing a chase
|
||||
*
|
||||
* It reads in ChaseKeyframes for a set of objects, and replays them
|
||||
* over time.
|
||||
*/
|
||||
class ChaseCoordinator
|
||||
{
|
||||
public:
|
||||
|
||||
ChaseCoordinator()
|
||||
: chaseTime(-1.f)
|
||||
{ }
|
||||
|
||||
bool addChaseVehicle(GameObject* vehicle, int index, const std::string& pathFile);
|
||||
GameObject* getChaseVehicle(int index);
|
||||
void removeChaseVehicle(int index);
|
||||
|
||||
void start();
|
||||
void update(float dt);
|
||||
|
||||
void cleanup();
|
||||
private:
|
||||
float chaseTime;
|
||||
struct ChaseObject {
|
||||
std::vector<ChaseKeyframe> keyframes;
|
||||
GameObject* object;
|
||||
};
|
||||
|
||||
std::map<int, ChaseObject> chaseVehicles;
|
||||
};
|
||||
|
||||
#endif
|
@ -27,6 +27,8 @@ class VehicleObject;
|
||||
class InventoryItem;
|
||||
struct WeaponScan;
|
||||
|
||||
#include <data/Chase.hpp>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
@ -167,6 +169,11 @@ public:
|
||||
*/
|
||||
SoundManager sound;
|
||||
|
||||
/**
|
||||
* Chase state
|
||||
*/
|
||||
ChaseCoordinator chase;
|
||||
|
||||
/**
|
||||
* Each object type is allocated from a pool. This object helps manage
|
||||
* the individual pools.
|
||||
|
127
rwengine/src/data/Chase.cpp
Normal file
127
rwengine/src/data/Chase.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "data/Chase.hpp"
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
#include <rw/defines.hpp>
|
||||
#include <objects/GameObject.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
||||
#define KEYFRAMES_PER_SECOND 30
|
||||
|
||||
bool ChaseKeyframe::load(const std::string &filePath, std::vector<ChaseKeyframe> &frames)
|
||||
{
|
||||
std::ifstream chaseFile(filePath, std::ios_base::binary);
|
||||
RW_CHECK(chaseFile.is_open(), "Failed to open chase file");
|
||||
if (!chaseFile.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
chaseFile.seekg(0, std::ios_base::end);
|
||||
size_t fileLength = chaseFile.tellg();
|
||||
chaseFile.seekg(0);
|
||||
|
||||
struct ChaseEntryRecord {
|
||||
int16_t velocity[3];
|
||||
int8_t right[3];
|
||||
int8_t up[3];
|
||||
uint8_t steering;
|
||||
uint8_t driving;
|
||||
uint8_t braking;
|
||||
uint8_t handbrake;
|
||||
glm::vec3 position;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ChaseEntryRecord) == 28, "ChaseEntryRecord is not 28 bytes");
|
||||
RW_CHECK(fileLength % sizeof(ChaseEntryRecord) == 0, "File is not a mulitple of 28 byte");
|
||||
|
||||
size_t recordCount = fileLength / sizeof(ChaseEntryRecord);
|
||||
|
||||
for (size_t i = 0; i < recordCount; ++i)
|
||||
{
|
||||
ChaseEntryRecord rec;
|
||||
chaseFile.read((char*)&rec, sizeof(ChaseEntryRecord));
|
||||
glm::vec3 velocity {
|
||||
rec.velocity[0]/16383.5f,
|
||||
rec.velocity[1]/16383.5f,
|
||||
rec.velocity[2]/16383.5f,
|
||||
};
|
||||
glm::vec3 right{
|
||||
rec.right[0]/127.5f,
|
||||
rec.right[1]/127.5f,
|
||||
rec.right[2]/127.5f,
|
||||
};
|
||||
glm::vec3 up{
|
||||
rec.up[0]/127.5f,
|
||||
rec.up[1]/127.5f,
|
||||
rec.up[2]/127.5f,
|
||||
};
|
||||
glm::mat3 rotation(right, up, glm::cross(right, up));
|
||||
frames.push_back({
|
||||
velocity,
|
||||
rec.steering,
|
||||
rec.driving,
|
||||
rec.braking,
|
||||
!!rec.handbrake,
|
||||
rec.position,
|
||||
glm::quat_cast(rotation)
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool ChaseCoordinator::addChaseVehicle(GameObject *vehicle, int index, const std::string &pathFile)
|
||||
{
|
||||
std::vector <ChaseKeyframe> keyframes;
|
||||
bool result = ChaseKeyframe::load(pathFile, keyframes);
|
||||
RW_CHECK(result, "Failed to load chase keyframes: " + pathFile);
|
||||
chaseVehicles[index] = {keyframes, vehicle};
|
||||
return result;
|
||||
}
|
||||
|
||||
GameObject*ChaseCoordinator::getChaseVehicle(int index)
|
||||
{
|
||||
return chaseVehicles.at(index).object;
|
||||
}
|
||||
|
||||
void ChaseCoordinator::removeChaseVehicle(int index)
|
||||
{
|
||||
auto it = chaseVehicles.find(index);
|
||||
RW_CHECK(it != chaseVehicles.end(), "Vehicle not in chase");
|
||||
if (it != chaseVehicles.end())
|
||||
{
|
||||
chaseVehicles.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void ChaseCoordinator::start()
|
||||
{
|
||||
chaseTime = 0.f;
|
||||
}
|
||||
|
||||
void ChaseCoordinator::update(float dt)
|
||||
{
|
||||
chaseTime += dt;
|
||||
size_t frameNum = chaseTime * KEYFRAMES_PER_SECOND;
|
||||
for(auto& it : chaseVehicles)
|
||||
{
|
||||
RW_CHECK(frameNum < it.second.keyframes.size(), "Vehicle out of chase keyframes");
|
||||
if (frameNum >= it.second.keyframes.size()) continue;
|
||||
|
||||
const ChaseKeyframe& kf = it.second.keyframes[frameNum];
|
||||
it.second.object->setPosition(kf.position);
|
||||
it.second.object->setRotation(kf.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void ChaseCoordinator::cleanup()
|
||||
{
|
||||
for(auto& it : chaseVehicles)
|
||||
{
|
||||
it.second.object->engine->destroyObject(it.second.object);
|
||||
}
|
||||
chaseVehicles.clear();
|
||||
}
|
||||
|
@ -701,6 +701,45 @@ void game_set_character_model(const ScriptArguments& args)
|
||||
}
|
||||
}
|
||||
|
||||
void game_start_chase_scene(const ScriptArguments& args)
|
||||
{
|
||||
// Hardcoded list of vehicles, put this somewhere else.
|
||||
|
||||
#define CHASE_VEHICLE(model, x, y, z, hdg, c1, c2, path) \
|
||||
{ \
|
||||
auto vehicle0 = args.getWorld()->createVehicle( \
|
||||
model, \
|
||||
glm::vec3(x, y, z), \
|
||||
glm::angleAxis(glm::radians(hdg), glm::vec3(0.f, 0.f, 1.f))); \
|
||||
vehicle0->setPrimaryColour(c1);\
|
||||
vehicle0->setSecondaryColour(c2);\
|
||||
args.getWorld()->chase.addChaseVehicle(vehicle0, path,\
|
||||
args.getWorld()->data->getDataPath()+"/data/paths/CHASE" #path ".DAT");\
|
||||
}
|
||||
|
||||
CHASE_VEHICLE(116, 273.5422f, -1167.1907f, 24.9906f, 64.f, 2, 1, 0);
|
||||
CHASE_VEHICLE(117, 231.1783f, -1388.832f, 25.9782f, 90.0f, 2, 1, 1);
|
||||
CHASE_VEHICLE(130, -28.9762f, -1031.3367f, 25.9781f, 242.0f, 1, 75, 2);
|
||||
CHASE_VEHICLE(96, 114.1564f, -796.6938f, 24.9782f, 180.0f, 0, 0, 3);
|
||||
CHASE_VEHICLE(110, 184.3156f, -1473.251f, 25.9782f, 0.0f, 6, 6, 4);
|
||||
CHASE_VEHICLE(105, 173.8868f, -1377.6514f, 25.9782f, 0.0f, 4, 5, 6);
|
||||
CHASE_VEHICLE(92, 102.5946f, -943.9363f, 25.9781f, 270.0f, 53,53, 7);
|
||||
CHASE_VEHICLE(105, 177.7157f, -862.1865f, 25.9782f, 155.0f, 41, 1, 10);
|
||||
CHASE_VEHICLE(92, 170.5698f, -889.0236f, 25.9782f, 154.0f, 10, 10,11);
|
||||
CHASE_VEHICLE(111, 402.6081f, -917.4963f, 37.381f, 90.0f, 34, 1, 14);
|
||||
CHASE_VEHICLE(110, -33.4962f, -938.4563f, 25.9781f, 266.0f, 6, 6, 16);
|
||||
CHASE_VEHICLE(111, 49.3631f, -987.605f, 25.9781f, 0.0f, 51, 1, 18);
|
||||
CHASE_VEHICLE(110, 179.0049f, -1154.6686f, 25.9781f, 0.0f, 6, 76, 19);
|
||||
|
||||
args.getWorld()->chase.start();
|
||||
}
|
||||
|
||||
void game_stop_chase_scene(const ScriptArguments& args)
|
||||
{
|
||||
// Clean up remaining vehicles
|
||||
args.getWorld()->chase.cleanup();
|
||||
}
|
||||
|
||||
bool game_collision_loaded(const ScriptArguments& args)
|
||||
{
|
||||
// The paramter to this is actually the island number.
|
||||
@ -836,6 +875,16 @@ void game_set_rampages(const ScriptArguments& args)
|
||||
args.getWorld()->state->gameStats.totalRampages = args[0].integer;
|
||||
}
|
||||
|
||||
void game_remove_chase_car(const ScriptArguments& args)
|
||||
{
|
||||
int chaseCar = args[0].integer;
|
||||
GameObject* car = args.getWorld()->chase.getChaseVehicle(chaseCar);
|
||||
RW_CHECK(car != nullptr, "Tried to remove null car from chase");
|
||||
if (car == nullptr) return;
|
||||
args.getWorld()->chase.removeChaseVehicle(chaseCar);
|
||||
args.getWorld()->destroyObject(car);
|
||||
}
|
||||
|
||||
void game_set_near_clip(const ScriptArguments& args)
|
||||
{
|
||||
args.getWorld()->state->cameraNear = args[0].real;
|
||||
@ -1051,8 +1100,8 @@ GameModule::GameModule()
|
||||
|
||||
bindFunction(0x0352, game_set_character_model, 2, "Set Character Model" );
|
||||
bindUnimplemented( 0x0353, game_refresh_character_model, 1, "Refresh Actor Model" );
|
||||
bindUnimplemented( 0x0354, game_start_chase, 1, "Start Chase Scene" );
|
||||
bindUnimplemented( 0x0355, game_stop_chase, 0, "Stop Chase Scene" );
|
||||
bindFunction( 0x0354, game_start_chase_scene, 1, "Start Chase Scene" );
|
||||
bindFunction( 0x0355, game_stop_chase_scene, 0, "Stop Chase Scene" );
|
||||
|
||||
bindUnimplemented( 0x0373, game_camera_behind_player, 0, "Set Camera Behind Player" );
|
||||
bindUnimplemented( 0x0374, game_set_motion_blur, 1, "Set Motion Blur" );
|
||||
@ -1125,7 +1174,7 @@ GameModule::GameModule()
|
||||
|
||||
bindFunction(0x0408, game_set_rampages, 1, "Set Total Rampage Missions" );
|
||||
bindUnimplemented( 0x0409, game_explode_rc_buggy, 0, "Blow up RC buggy" );
|
||||
bindUnimplemented( 0x040A, game_remove_chase_car, 1, "Remove Chase Car" );
|
||||
bindFunction( 0x040A, game_remove_chase_car, 1, "Remove Chase Car" );
|
||||
|
||||
bindUnimplemented( 0x0418, game_set_object_ontop, 2, "Set Object Draw Ontop" );
|
||||
|
||||
|
15
rwengine/tests/test_chase.cpp
Normal file
15
rwengine/tests/test_chase.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_globals.hpp"
|
||||
#include <data/Chase.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ChaseTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_load_keyframes)
|
||||
{
|
||||
std::vector<ChaseKeyframe> keyframes;
|
||||
BOOST_REQUIRE(ChaseKeyframe::load(Global::getGamePath() + "/data/paths/CHASE0.DAT", keyframes));
|
||||
BOOST_REQUIRE(keyframes.size() == 5400);
|
||||
BOOST_CHECK_CLOSE(keyframes[0].position.x, 273.5422, 0.1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -356,6 +356,8 @@ void RWGame::tick(float dt)
|
||||
world->_work->update();
|
||||
|
||||
State* currState = StateManager::get().states.back();
|
||||
|
||||
world->chase.update(dt);
|
||||
|
||||
static float clockAccumulator = 0.f;
|
||||
if ( currState->shouldWorldUpdate() ) {
|
||||
|
Loading…
Reference in New Issue
Block a user