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:
parent
3c665e7d2b
commit
5b9c95d346
144
rwengine/include/data/CutsceneData.hpp
Normal file
144
rwengine/include/data/CutsceneData.hpp
Normal 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
|
@ -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.
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
13
rwengine/include/loaders/LoaderCutsceneDAT.hpp
Normal file
13
rwengine/include/loaders/LoaderCutsceneDAT.hpp
Normal 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
|
@ -32,4 +32,4 @@ public:
|
||||
void addGeometry(GeometryBuffer* gbuff);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
82
rwengine/src/loaders/LoaderCutsceneDAT.cpp
Normal file
82
rwengine/src/loaders/LoaderCutsceneDAT.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
})";
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
32
tests/test_cutscene.cpp
Normal 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()
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user