1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-18 16:32:32 +02:00

Add Initial Cutscene playback implementation

+ Add CutsceneData structures to store the data required.
+ Implement screen fading in and out (todo: splash screen fading)
+ Add GameData::openFile2() returns a handle to open file memory + size
+ Fix fog implementation
+ Add screenspace rect to GameRenderer for fades and cinematics
This commit is contained in:
Daniel Evans 2014-07-28 02:27:55 +01:00
parent 3c665e7d2b
commit 5b9c95d346
22 changed files with 619 additions and 47 deletions

View File

@ -0,0 +1,144 @@
#pragma once
#ifndef _CUTSCENEDATA_HPP_
#define _CUTSCENEDATA_HPP_
#include <glm/glm.hpp>
#include <map>
#include <vector>
/**
* @brief Stores data from .CUT files
*/
struct CutsceneMetadata
{
struct ModelEntry {
std::string model;
std::string animation;
};
struct TextEntry {
float length;
std::string gxt;
};
/// The origin for coordinates in the cutscene
glm::vec3 sceneOffset;
std::vector<ModelEntry> models;
std::map<float, TextEntry> texts;
};
/**
* @brief Stores the Camera animation data from .DAT files
*/
struct CutsceneTracks
{
std::map<float, float> zoom;
std::map<float, float> rotation;
std::map<float, glm::vec3> position;
std::map<float, glm::vec3> target; /// Rotation is around the direction to this vector
float duration;
glm::vec3 getPositionAt(float time)
{
glm::vec3 p = position.end()->second;
for(auto it = position.begin(); it != position.end(); ++it) {
if( it->first <= time ) {
auto a = it->second;
auto b = it->second;
auto nextIt = it;
float t = it->first;
if( ++nextIt != position.end() ) {
b = nextIt->second;
t = nextIt->first;
}
float tdiff = t - it->first;
p = b;
if( tdiff > 0.f ) {
float fac = (time - it->first) / tdiff;
p = glm::mix(a, b, fac);
}
}
}
return p;
}
glm::vec3 getTargetAt(float time)
{
glm::vec3 p = position.end()->second;
for(auto it = target.begin(); it != target.end(); ++it) {
if( it->first <= time ) {
auto a = it->second;
auto b = it->second;
auto nextIt = it;
float t = it->first;
if( ++nextIt != target.end() ) {
b = nextIt->second;
t = nextIt->first;
}
float tdiff = t - it->first;
p = b;
if( tdiff > 0.f ) {
float fac = (time - it->first) / tdiff;
p = glm::mix(a, b, fac);
}
}
}
return p;
}
float getZoomAt(float time)
{
float r = zoom.end()->second;
for(auto it = zoom.begin(); it != zoom.end(); ++it) {
if( it->first <= time ) {
auto a = it->second;
auto b = it->second;
auto nextIt = it;
float t = it->first;
if( ++nextIt != zoom.end() ) {
b = nextIt->second;
t = nextIt->first;
}
float tdiff = t - it->first;
r = b;
if( tdiff > 0.f ) {
float fac = (time - it->first) / tdiff;
r = glm::mix(a, b, fac);
}
}
}
return r;
}
float getRotationAt(float time)
{
float r = rotation.end()->second;
for(auto it = rotation.begin(); it != rotation.end(); ++it) {
if( it->first <= time ) {
auto a = it->second;
auto b = it->second;
auto nextIt = it;
float t = it->first;
if( ++nextIt != rotation.end() ) {
b = nextIt->second;
t = nextIt->first;
}
float tdiff = t - it->first;
r = b;
if( tdiff > 0.f ) {
float fac = (time - it->first) / tdiff;
r = glm::mix(a, b, fac);
}
}
}
return r;
}
};
struct CutsceneData
{
CutsceneMetadata meta;
CutsceneTracks tracks;
};
#endif

View File

@ -152,6 +152,7 @@ public:
* @return pointer to the data, NULL if it is not available
*/
char* openFile(const std::string& name);
FileHandle openFile2(const std::string& name);
/**
* @brief loadFile Marks a file as open, and opens it.

View File

@ -5,6 +5,7 @@
#include <string>
class PlayerController;
class CutsceneData;
struct GameState
{
@ -17,8 +18,6 @@ struct GameState
unsigned int maxWantedLevel;
PlayerController* player;
glm::i8vec3 fadeColour;
unsigned int currentWeather;
/**
@ -26,12 +25,15 @@ struct GameState
*/
unsigned int *scriptOnMissionFlag;
bool fadeIn;
bool fadeOut;
float fadeStart;
float fadeTime;
bool fadeSound;
glm::u16vec3 fadeColour;
bool isIntroPlaying;
CutsceneData* currentCutscene;
float cutsceneStartTime;
short hour;
@ -54,12 +56,13 @@ struct GameState
player(nullptr),
currentWeather(0),
scriptOnMissionFlag(nullptr),
fadeIn(false),
fadeOut(false),
fadeStart(0.f),
fadeTime(0.f),
fadeSound(false),
isIntroPlaying(false),
cutsceneStartTime(0.f),
currentCutscene(nullptr),
cutsceneStartTime(-1.f),
hour(0),
minute(0),
osTextStyle(0),

View File

@ -229,6 +229,12 @@ public:
WorkContext* _work;
ScriptMachine* script;
/**
* @brief Loads and starts the named cutscene.
* @param name
*/
void loadCutscene(const std::string& name);
};
#endif

View File

@ -6,6 +6,7 @@
#include <map>
#include <string>
#include <glm/glm.hpp>
#include <memory>
#define NO_WATER_INDEX 48
#define WATER_LQ_DATA_SIZE 64
@ -55,7 +56,19 @@ struct RGBA
{
uint8_t r, g, b, a;
};
}
/**
* @brief Returned by GameData::loadFile()
*/
struct FileContentsInfo {
char* data;
size_t length;
~FileContentsInfo() {
delete[] data;
}
};
typedef std::shared_ptr<FileContentsInfo> FileHandle;
#endif

View File

@ -0,0 +1,13 @@
#pragma once
#ifndef _LOADERCUTSCENEDAT_HPP_
#define _LOADERCUTSCENEDAT_HPP_
#include <engine/RWTypes.hpp>
#include <data/CutsceneData.hpp>
class LoaderCutsceneDAT
{
public:
void load(CutsceneTracks& tracks, FileHandle file);
};
#endif

View File

@ -32,4 +32,4 @@ public:
void addGeometry(GeometryBuffer* gbuff);
};
#endif
#endif

View File

@ -148,6 +148,9 @@ public:
GLuint waterProgram, waterMVP, waterHeight, waterTexture, waterSize, waterTime, waterPosition, waterWave;
GLint skyUniView, skyUniProj, skyUniTop, skyUniBottom;
GLuint particleProgram;
GLuint ssRectProgram;
GLint ssRectTexture, ssRectColour, ssRectSize, ssRectOffset;
/** Internal non-descript VAOs */
GLuint vao, debugVAO;
@ -235,6 +238,8 @@ struct SceneUniformData {
glm::mat4 view;
glm::vec4 ambient;
glm::vec4 dynamic;
glm::vec4 fogColour;
glm::vec4 campos;
float fogStart;
float fogEnd;
};

View File

@ -27,6 +27,16 @@ struct Particle {
static const char* FragmentShader;
};
/**
* @brief The ScreenSpaceRect shader
*
* Used to draw black bars, splash screens, fading etc.
*/
struct ScreenSpaceRect {
static const char* VertexShader;
static const char* FragmentShader;
};
}
#endif

View File

@ -38,6 +38,12 @@ class GeometryBuffer {
public:
GeometryBuffer();
template<class T> GeometryBuffer(const std::vector<T>& data)
: vbo(0), num(0)
{
uploadVertices(data);
}
~GeometryBuffer();
GLuint getVBOName() const
@ -64,4 +70,4 @@ public:
{ return attributes; }
};
#endif
#endif

View File

@ -12,7 +12,7 @@ public:
glm::vec3 worldPos;
ViewCamera()
: frustum({0.1f, 5000.f, (-75.f / 180.f) * 3.1415f, 1.f})
: frustum({0.1f, 5000.f, (-45.f / 180.f) * 3.1415f, 1.f})
{
}

View File

@ -657,6 +657,56 @@ char* GameData::openFile(const std::string& name)
return nullptr;
}
FileHandle GameData::openFile2(const std::string &name)
{
auto i = _knownFiles.find(name);
if(i != _knownFiles.end())
{
char* data;
size_t length;
if(i->second.archived)
{
// Find the archive
auto ai = archives.find(i->second.path);
if(ai != archives.end())
{
data = ai->second.loadToMemory(name);
auto& asset = ai->second.getAssetInfo(name);
length = asset.size * 2048;
}
else
{
std::cerr << "Archive not found " << i->second.path << std::endl;
}
}
else
{
std::ifstream dfile(i->second.path);
if ( ! dfile.is_open()) {
std::cerr << "Error opening file " << i->second.path << std::endl;
return nullptr;
}
dfile.seekg(0, std::ios_base::end);
length = dfile.tellg();
dfile.seekg(0);
data = new char[length];
dfile.read(data, length);
}
return FileHandle( new FileContentsInfo{ data, length } );
}
else
{
std::stringstream err;
err << "Unable to locate file " << name;
engine->logError(err.str());
std::cerr << err.str() << std::endl;
}
return nullptr;
}
char* GameData::loadFile(const std::string &name)
{
auto it = loadedFiles.find(name);

View File

@ -15,6 +15,9 @@
#include <objects/InstanceObject.hpp>
#include <objects/VehicleObject.hpp>
#include <data/CutsceneData.hpp>
#include <loaders/LoaderCutsceneDAT.hpp>
class WorldCollisionDispatcher : public btCollisionDispatcher
{
public:
@ -554,3 +557,23 @@ void GameWorld::PhysicsTickCallback(btDynamicsWorld *physWorld, btScalar timeSte
}
}
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;
if( datfile ) {
LoaderCutsceneDAT loaderdat;
loaderdat.load(cutscene->tracks, datfile);
}
if( state.currentCutscene ) {
delete state.currentCutscene;
}
state.currentCutscene = cutscene;
std::cout << "Loaded cutscene: " << name << std::endl;
}

View File

@ -0,0 +1,82 @@
#include <loaders/LoaderCutsceneDAT.hpp>
#include <sstream>
void LoaderCutsceneDAT::load(CutsceneTracks &tracks, FileHandle file)
{
std::string dataStr(file->data, file->length);
std::stringstream ss(dataStr);
int numZooms = 0;
ss >> numZooms;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for(int i = 0; i < numZooms; ++i) {
float t = 0.f;
float z = 0.f;
ss >> t;
ss.ignore(1, ',');
ss >> z;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
tracks.zoom[t] = z;
tracks.duration = std::max(t, tracks.duration);
}
ss.ignore(std::numeric_limits<std::streamsize>::max(), ';');
int numRotations = 0;
ss >> numRotations;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for(int i = 0; i < numRotations; ++i) {
float t = 0.f;
float r = 0.f;
ss >> t;
ss.ignore(1, ',');
ss >> r;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
tracks.rotation[t] = r;
tracks.duration = std::max(t, tracks.duration);
}
ss.ignore(std::numeric_limits<std::streamsize>::max(), ';');
int numPositions = 0;
ss >> numPositions;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for(int i = 0; i < numPositions; ++i) {
float t = 0.f;
glm::vec3 p;
ss >> t;
ss.ignore(1, ',');
ss >> p.x;
ss.ignore(1, ',');
ss >> p.y;
ss.ignore(1, ',');
ss >> p.z;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
tracks.position[t] = p;
tracks.duration = std::max(t, tracks.duration);
}
ss.ignore(std::numeric_limits<std::streamsize>::max(), ';');
int numTargets = 0;
ss >> numTargets;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for(int i = 0; i < numTargets; ++i) {
float t = 0.f;
glm::vec3 p;
ss >> t;
ss.ignore(1, ',');
ss >> p.x;
ss.ignore(1, ',');
ss >> p.y;
ss.ignore(1, ',');
ss >> p.z;
ss.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
tracks.target[t] = p;
tracks.duration = std::max(t, tracks.duration);
}
}

View File

@ -65,6 +65,26 @@ struct ParticleVert {
GeometryBuffer particleGeom;
DrawBuffer particleDraw;
struct VertexP2 {
static const AttributeList vertex_attributes() {
return {
{ATRS_Position, 2, sizeof(VertexP2), 0ul}
};
}
float x, y;
};
std::vector<VertexP2> sspaceRect = {
{-1.f, -1.f},
{ 1.f, -1.f},
{-1.f, 1.f},
{ 1.f, 1.f},
};
GeometryBuffer ssRectGeom;
DrawBuffer ssRectDraw;
GLuint compileShader(GLenum type, const char *source)
{
GLuint shader = glCreateShader(type);
@ -79,8 +99,14 @@ GLuint compileShader(GLenum type, const char *source)
GLchar *buffer = new GLchar[len];
glGetShaderInfoLog(shader, len, NULL, buffer);
std::cerr << "ERROR compiling shader: " << buffer << "\nSource: " << source << std::endl;
GLint sourceLen;
glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceLen);
GLchar *sourceBuff = new GLchar[sourceLen];
glGetShaderSource(shader, sourceLen, nullptr, sourceBuff);
std::cerr << "ERROR compiling shader: " << buffer << "\nSource: " << sourceBuff << std::endl;
delete[] buffer;
delete[] sourceBuff;
exit(1);
}
@ -226,6 +252,17 @@ GameRenderer::GameRenderer(GameWorld* engine)
{-0.5f,-0.5f, 0.f, 0.f, 1.f, 1.f, 1.f}
});
particleDraw.addGeometry(&particleGeom);
ssRectGeom.uploadVertices(sspaceRect);
ssRectDraw.addGeometry(&ssRectGeom);
ssRectProgram = compileProgram(GameShaders::ScreenSpaceRect::VertexShader,
GameShaders::ScreenSpaceRect::FragmentShader);
ssRectTexture = glGetUniformLocation(ssRectProgram, "texture");
ssRectColour = glGetUniformLocation(ssRectProgram, "colour");
ssRectSize = glGetUniformLocation(ssRectProgram, "size");
ssRectOffset = glGetUniformLocation(ssRectProgram, "offset");
}
float mix(uint8_t a, uint8_t b, float num)
@ -250,12 +287,12 @@ void GameRenderer::renderWorld(float alpha)
// Requires a float 0-24
auto weatherID = static_cast<WeatherLoader::WeatherCondition>(engine->state.currentWeather * 24);
auto weather = engine->gameData.weatherLoader.getWeatherData(weatherID, tod / 60.f);
auto weather = engine->gameData.weatherLoader.getWeatherData(weatherID, tod);
glm::vec3 skyTop = weather.skyTopColor;
glm::vec3 skyBottom = weather.skyBottomColor;
glm::vec3 ambient = weather.ambientColor;
glm::vec3 dynamic = weather.directLightColor;
glm::vec3 skyTop = weather.skyTopColor;
glm::vec3 skyBottom = weather.skyBottomColor;
glm::vec3 ambient = weather.ambientColor;
glm::vec3 dynamic = weather.directLightColor;
float theta = (tod/(60.f * 24.f) - 0.5f) * 2 * 3.14159265;
glm::vec3 sunDirection{
@ -263,8 +300,8 @@ void GameRenderer::renderWorld(float alpha)
0.0,
cos(theta),
};
sunDirection = glm::normalize(sunDirection);
camera.frustum.far = weather.farClipping;
sunDirection = glm::normalize(sunDirection);
camera.frustum.far = weather.farClipping;
glUseProgram(worldProgram);
@ -276,8 +313,10 @@ void GameRenderer::renderWorld(float alpha)
{
proj,
view,
glm::vec4{ambient, 1.0f},
glm::vec4{dynamic, 1.0f},
glm::vec4{ambient, 0.0f},
glm::vec4{dynamic, 0.0f},
glm::vec4(skyBottom, 1.f),
glm::vec4(camera.worldPos, 0.f),
weather.fogStart,
camera.frustum.far
});
@ -409,6 +448,54 @@ void GameRenderer::renderWorld(float alpha)
renderParticles();
glActiveTexture(0);
glDisable(GL_DEPTH_TEST);
float fadeTimer = engine->gameTime - engine->state.fadeStart;
if( fadeTimer <= engine->state.fadeTime || !engine->state.fadeOut ) {
glUseProgram(ssRectProgram);
glUniform2f(ssRectOffset, 0.f, 0.f);
glUniform2f(ssRectSize, 1.f, 1.f);
glBindTexture(GL_TEXTURE_2D, 0);
glUniform1i(ssRectTexture, 0);
float fadeFrac = 0.f;
if( engine->state.fadeTime > 0.f ) {
fadeFrac = std::min(fadeTimer / engine->state.fadeTime, 1.f);
}
auto fc = engine->state.fadeColour;
float a = engine->state.fadeOut ? 1.f - fadeFrac : fadeFrac;
glm::vec4 fadeNormed(fc.r / 255.f, fc.g/ 255.f, fc.b/ 255.f, a);
glUniform4fv(ssRectColour, 1, glm::value_ptr(fadeNormed));
glBindVertexArray( ssRectDraw.getVAOName() );
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
if( engine->state.currentCutscene ) {
glUseProgram(ssRectProgram);
const float cinematicExperienceSize = 0.15f;
glUniform2f(ssRectOffset, 0.f, -1.f * (1.f - cinematicExperienceSize));
glUniform2f(ssRectSize, 1.f, cinematicExperienceSize);
glBindTexture(GL_TEXTURE_2D, 0);
glUniform1i(ssRectTexture, 0);
glUniform4f(ssRectColour, 0.f, 0.f, 0.f, 1.f);
glBindVertexArray( ssRectDraw.getVAOName() );
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUniform2f(ssRectOffset, 0.f, 1.f * (1.f - cinematicExperienceSize));
glUniform2f(ssRectSize, 1.f, cinematicExperienceSize);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
glUseProgram(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

View File

@ -65,6 +65,7 @@ const char* WorldObject::VertexShader = R"(
#version 130
#extension GL_ARB_explicit_attrib_location : enable
#extension GL_ARB_uniform_buffer_object : enable
#extension GL_ARB_gpu_shader5 : enable
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec4 _colour;
@ -72,12 +73,15 @@ layout(location = 3) in vec2 texCoords;
out vec3 Normal;
out vec2 TexCoords;
out vec4 Colour;
out vec3 WorldSpace;
layout(std140) uniform SceneData {
mat4 projection;
mat4 view;
vec4 ambient;
vec4 dynamic;
vec4 fogColor;
vec4 campos;
float fogStart;
float fogEnd;
};
@ -94,8 +98,11 @@ void main()
Normal = normal;
TexCoords = texCoords;
Colour = _colour;
vec4 eyeSpace = view * model * vec4(position, 1.0);
gl_Position = projection * eyeSpace;
vec4 worldspace = model * vec4(position, 1.0);
vec4 viewspace = view * worldspace;
gl_Position = projection * viewspace;
WorldSpace = worldspace.xyz;
})";
const char* WorldObject::FragmentShader = R"(
@ -104,6 +111,7 @@ const char* WorldObject::FragmentShader = R"(
in vec3 Normal;
in vec2 TexCoords;
in vec4 Colour;
in vec3 WorldSpace;
uniform sampler2D texture;
layout(std140) uniform SceneData {
@ -111,6 +119,8 @@ layout(std140) uniform SceneData {
mat4 view;
vec4 ambient;
vec4 dynamic;
vec4 fogColor;
vec4 campos;
float fogStart;
float fogEnd;
};
@ -124,14 +134,21 @@ layout(std140) uniform ObjectData {
#define ALPHA_DISCARD_THRESHOLD 0.01
vec4 crunch(vec4 c)
{
return vec4(0.5) + c * 0.5;
}
void main()
{
vec4 c = texture2D(texture, TexCoords);
if(c.a <= ALPHA_DISCARD_THRESHOLD) discard;
float fogZ = (gl_FragCoord.z / gl_FragCoord.w);
float fogfac = clamp( (fogStart-fogZ)/(fogEnd-fogStart), 0.0, 1.0 );
gl_FragColor = mix(ambient, colour * (vec4(0.5) + Colour * 0.5)
* (vec4(0.5) + dynamic * 0.5) * c, 1.f);
float fogZ = length(campos.xyz - WorldSpace.xyz);
float fogfac = 1.0 - clamp( (fogEnd-fogZ)/(fogEnd-fogStart), 0.0, 1.0 );
vec3 tmp = (ambient.rgb + c.rgb) * crunch(Colour).rgb;
gl_FragColor = fogfac * fogColor + (1.0-fogfac) * vec4(colour.rgb * tmp, c.a * colour.a);
//gl_FragColor = mix(ambient, colour * (vec4(0.5) + Colour * 0.5)
// * (vec4(0.5) + dynamic * 0.5) * c, fogfac);
})";
const char* Particle::FragmentShader = R"(
@ -147,6 +164,8 @@ layout(std140) uniform SceneData {
mat4 view;
vec4 ambient;
vec4 dynamic;
vec4 fogColor;
vec4 campos;
float fogStart;
float fogEnd;
};
@ -170,4 +189,36 @@ void main()
gl_FragColor = mix(ambient, colour * (vec4(0.5) + Colour * 0.5)
* (vec4(0.5) + dynamic * 0.5) * c, 1.f);
})";
const char* ScreenSpaceRect::VertexShader = R"(
#version 130
#extension GL_ARB_explicit_attrib_location : enable
#extension GL_ARB_uniform_buffer_object : enable
layout(location = 0) in vec2 position;
out vec2 TexCoords;
out vec4 Colour;
uniform vec2 size;
uniform vec2 offset;
void main()
{
TexCoords = position * 0.5 + vec2(0.5);
gl_Position = vec4(offset + position * size, 0.0, 1.0);
})";
const char* ScreenSpaceRect::FragmentShader = R"(
#version 130
in vec2 TexCoords;
in vec4 Colour;
uniform vec4 colour;
uniform sampler2D texture;
void main()
{
vec4 c = texture2D(texture, TexCoords);
// Set colour to 0, 0, 0, 1 for textured mode.
gl_FragColor = colour;
})";
}

View File

@ -10,6 +10,7 @@
#include <ai/PlayerController.hpp>
#include <ai/DefaultAIController.hpp>
#include <data/CutsceneData.hpp>
#include <glm/gtx/string_cast.hpp>
#include <iostream>
@ -327,11 +328,11 @@ SCMMicrocodeTable ops_game = {
m->getWorld()->state.fadeColour.g = p->at(1).integer;
m->getWorld()->state.fadeColour.b = p->at(2).integer;
})
OPC( 0x016A, "Fade Screen", 2, {
m->getWorld()->state.fadeIn = !!p->at(0).integer;
m->getWorld()->state.fadeTime = p->at(1).integer / 1000.f;
m->getWorld()->state.fadeTime = p->at(0).integer / 1000.f;
m->getWorld()->state.fadeOut = !!p->at(1).integer;
m->getWorld()->state.fadeStart = m->getWorld()->gameTime;
std::cout << "Fade " << p->at(0).integer << " " << p->at(1).integer << std::endl;
})
OPC_COND( 0x016B, "Is Screen Fading", 0, {
return m->getWorld()->gameTime <
@ -462,7 +463,12 @@ SCMMicrocodeTable ops_game = {
return false;
})
OPC_UNIMPLEMENTED_MSG( 0x0244, "Set Cutscene Offset", 3)
OPC( 0x0244, "Set Cutscene Offset", 3, {
glm::vec3 position(p->at(0).real, p->at(1).real, p->at(2).real);
if( m->getWorld()->state.currentCutscene ) {
m->getWorld()->state.currentCutscene->meta.sceneOffset = position;
}
})
OPC_UNIMPLEMENTED_MSG( 0x0245, "Set Character Animation Group", 2)
OPC_UNIMPLEMENTED_MSG( 0x0247, "Request Model Loaded", 1)
@ -516,25 +522,34 @@ SCMMicrocodeTable ops_game = {
return vehicle && (vehicle->vehicle->classType & VehicleData::TAXI) == VehicleData::TAXI;
})
OPC_UNIMPLEMENTED_MSG( 0x02E4, "Load Cutscene Data", 1)
OPC( 0x02E4, "Load Cutscene Data", 1, {
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( 0x02E7, "Start Cutscene", 0, {
m->getWorld()->state.cutsceneStartTime = m->getWorld()->gameTime;
})
OPC( 0x02E8, "Get Cutscene Time", 1, {
/** @todo Implement cutscenes */
float time = m->getWorld()->gameTime - m->getWorld()->state.cutsceneStartTime;
*p->at(0).globalInteger = time * 1000;
})
OPC_COND( 0x02E9, "Is Cutscene Over", 0, {
/** @todo Implement cutscenes */
float time = m->getWorld()->gameTime - m->getWorld()->state.cutsceneStartTime;
return time > 20.f;
if( m->getWorld()->state.currentCutscene ) {
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 */
m->getWorld()->state.cutsceneStartTime = 0.f;
if( m->getWorld()->state.currentCutscene ) {
delete m->getWorld()->state.currentCutscene;
m->getWorld()->state.currentCutscene = nullptr;
}
m->getWorld()->state.cutsceneStartTime = -1.f;
})
OPC_UNIMPLEMENTED_MSG( 0x02EC, "Create Hidden Package", 3 )

View File

@ -233,7 +233,7 @@ void IngameState::handleEvent(const sf::Event &event)
_player->setRunning(false);
break;
case sf::Keyboard::F12:
skipTime(120.f);
skipTime(100.f);
break;
default: break;
}

View File

@ -16,6 +16,8 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <data/CutsceneData.hpp>
#include "loadingstate.hpp"
#include <SFML/Graphics.hpp>
@ -26,6 +28,7 @@
#include "game.hpp"
#define ENV_GAME_PATH_NAME ("OPENRW_GAME_PATH")
#define GAME_TIMESTEP (1.f/20.f)
constexpr int WIDTH = 800,
HEIGHT = 600;
@ -182,10 +185,10 @@ void handleCommandEvent(sf::Event &event)
case sf::Event::KeyPressed:
switch (event.key.code) {
case sf::Keyboard::LBracket:
gta->gameTime -= 60.f;
gta->state.minute -= 30.f;
break;
case sf::Keyboard::RBracket:
gta->gameTime += 60.f;
gta->state.minute += 30.f;
break;
break;
default: break;
@ -202,7 +205,8 @@ void init(std::string gtapath, bool loadWorld)
// This is harcoded in GTA III for some reason
gta->gameData.loadIMG("/models/gta3");
gta->gameData.loadIMG("/models/txd");
gta->gameData.loadIMG("/anim/cuts");
gta->load();
// Load dynamic object data
@ -268,13 +272,38 @@ void render(float alpha)
float qpi = glm::half_pi<float>();
glm::mat4 view;
view = glm::translate(view, glm::mix(lastViewPosition, viewPosition, alpha));
auto va = glm::mix(lastViewAngles, viewAngles, alpha);
view = glm::rotate(view, va.x, glm::vec3(0, 0, 1));
view = glm::rotate(view, va.y - qpi, glm::vec3(1, 0, 0));
view = glm::inverse(view);
/// @todo this probably doesn't belong in main.cpp
if( gta->state.currentCutscene == nullptr || gta->state.cutsceneStartTime <= 0.f ) {
view = glm::translate(view, glm::mix(lastViewPosition, viewPosition, alpha));
auto va = glm::mix(lastViewAngles, viewAngles, alpha);
view = glm::rotate(view, va.x, glm::vec3(0, 0, 1));
view = glm::rotate(view, va.y - qpi, glm::vec3(1, 0, 0));
view = glm::inverse(view);
gta->renderer.camera.worldPos = viewPosition;
}
else {
auto cutscene = gta->state.currentCutscene;
float cutsceneTime = std::min(gta->gameTime - gta->state.cutsceneStartTime,
cutscene->tracks.duration);
cutsceneTime += GAME_TIMESTEP * alpha;
glm::vec3 cameraPos = cutscene->tracks.getPositionAt(cutsceneTime),
targetPos = cutscene->tracks.getTargetAt(cutsceneTime);
float zoom = cutscene->tracks.getZoomAt(cutsceneTime);
gta->renderer.camera.frustum.fov = glm::radians(-zoom);
float tilt = cutscene->tracks.getRotationAt(cutsceneTime);
auto d = glm::normalize(targetPos-cameraPos);
auto qtilt = glm::rotate(glm::quat(), glm::radians(tilt), d);
cameraPos += cutscene->meta.sceneOffset;
targetPos += cutscene->meta.sceneOffset;
view = glm::lookAt(cameraPos, targetPos, qtilt * glm::vec3(0.f, 0.f, -1.f));
gta->renderer.camera.worldPos = cameraPos;
}
gta->renderer.camera.worldPos = viewPosition;
gta->renderer.camera.frustum.view = view;
// Update aspect ratio..
@ -323,6 +352,7 @@ void render(float alpha)
ss << "Game Time: " << gta->gameTime << std::endl;
ss << "Camera: " << viewPosition.x << " " << viewPosition.y << " " << viewPosition.z << std::endl;
ss << "Renderered " << gta->renderer.rendered << " / " << gta->renderer.culled << std::endl;
ss << "Weather: " << gta->state.currentWeather << "\n";
if( player ) {
ss << "Activity: ";
if( player->controller->getCurrentActivity() ) {
@ -423,7 +453,7 @@ int main(int argc, char *argv[])
StateManager::get().enter(new LoadingState);
float ts = 1.f / 20.f;
float ts = GAME_TIMESTEP;
float timescale = 1.f;
// Loop until the window is closed or we run out of state.

View File

@ -96,7 +96,7 @@ void ViewerWidget::paintGL()
glm::mat4 view = glm::lookAt(eye * viewDistance, glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f));
r.uploadUBO<SceneUniformData>(r.uboScene,
{ proj, view, glm::vec4(1.f), glm::vec4(1.f), 90.f, r.camera.frustum.far });
{ proj, view, glm::vec4(1.f), glm::vec4(1.f), glm::vec4(1.f), glm::vec4(0.f), 90.f, r.camera.frustum.far });
if( dummyObject->model->model ) {
gworld->renderer.renderModel(dummyObject->model->model, m, dummyObject);

32
tests/test_cutscene.cpp Normal file
View File

@ -0,0 +1,32 @@
#include <boost/test/unit_test.hpp>
#include <data/CutsceneData.hpp>
#include <loaders/LoaderCutsceneDAT.hpp>
#include "test_globals.hpp"
BOOST_AUTO_TEST_SUITE(CutsceneTests)
BOOST_AUTO_TEST_CASE(test_load)
{
{
auto d = Global::get().e->gameData.openFile2("intro.dat");
CutsceneTracks tracks;
LoaderCutsceneDAT loader;
loader.load( tracks, d );
BOOST_CHECK( tracks.position.find(0.f) != tracks.position.end() );
BOOST_CHECK( tracks.position.find(64.8f) != tracks.position.end() );
BOOST_CHECK( tracks.zoom.find(64.8f) != tracks.zoom.end() );
BOOST_CHECK( tracks.zoom.find(64.8f) != tracks.zoom.end() );
BOOST_CHECK( tracks.target.find(64.8f) != tracks.target.end() );
BOOST_CHECK( tracks.duration == 64.8f );
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -42,6 +42,7 @@ public:
e = new GameWorld(getGamePath());
e->gameData.loadIMG("/models/gta3");
e->gameData.loadIMG("/anim/cuts");
e->load();
for(std::map<std::string, std::string>::iterator it = e->gameData.ideLocations.begin();
it != e->gameData.ideLocations.end();