Support for intro videos

This commit is contained in:
Dima353
2026-07-15 21:10:15 +03:00
parent 3f754594b2
commit 722a9a5209
5 changed files with 5142 additions and 38 deletions
+1 -1
View File
@@ -270,7 +270,7 @@ enum Config {
#define FIX_INCOMPATIBLE_SAVES // try to fix incompatible saves, requires COMPATIBLE_SAVES
#define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS
#define NO_MOVIES // add option to disable intro videos
//#define NO_MOVIES // add option to disable intro videos
#define EXTENDED_OFFSCREEN_DESPAWN_RANGE // Use onscreen despawn range for offscreen peds and vehicles to avoid them despawning in the distance when you look
// away
+656
View File
@@ -0,0 +1,656 @@
// Nintendo Switch / GLFW movie player. Ported from the SDL2 build; only the
// windowing-system glue differs (GLFW proc address / buffer swap / timer).
#if defined RW_GL3 && !defined LIBRW_SDL2
#include "common.h"
// GLFW_INCLUDE_NONE: don't let GLFW pull its own GL headers - librw already
// provides GL symbols through glad, and GLFWwindow is needed by crossplatform.h.
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "MoviePlayer.h"
#include "crossplatform.h"
#include "skeleton.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define PL_MPEG_IMPLEMENTATION
#include "vendor/pl_mpeg/pl_mpeg.h"
typedef unsigned int GLenum;
typedef unsigned int GLuint;
typedef int GLint;
typedef int GLsizei;
typedef float GLfloat;
typedef unsigned char GLboolean;
typedef long GLsizeiptr;
typedef long GLintptr;
typedef char GLchar;
typedef unsigned int GLbitfield;
typedef void (*PFN_glGenTextures)(GLsizei n, GLuint *textures);
typedef void (*PFN_glBindTexture)(GLenum target, GLuint texture);
typedef void (*PFN_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type, const void *pixels);
typedef void (*PFN_glTexParameteri)(GLenum target, GLenum pname, GLint param);
typedef GLuint (*PFN_glCreateShader)(GLenum type);
typedef void (*PFN_glShaderSource)(GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length);
typedef void (*PFN_glCompileShader)(GLuint shader);
typedef void (*PFN_glGetShaderiv)(GLuint shader, GLenum pname, GLint *params);
typedef void (*PFN_glGetShaderInfoLog)(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog);
typedef GLuint (*PFN_glCreateProgram)(void);
typedef void (*PFN_glAttachShader)(GLuint program, GLuint shader);
typedef void (*PFN_glLinkProgram)(GLuint program);
typedef void (*PFN_glGetProgramiv)(GLuint program, GLenum pname, GLint *params);
typedef void (*PFN_glGetProgramInfoLog)(GLuint program, GLsizei maxLength, GLsizei *length, GLchar *infoLog);
typedef void (*PFN_glUseProgram)(GLuint program);
typedef GLint (*PFN_glGetAttribLocation)(GLuint program, const GLchar *name);
typedef GLint (*PFN_glGetUniformLocation)(GLuint program, const GLchar *name);
typedef void (*PFN_glUniform1i)(GLint location, GLint v0);
typedef void (*PFN_glGenBuffers)(GLsizei n, GLuint *buffers);
typedef void (*PFN_glBindBuffer)(GLenum target, GLuint buffer);
typedef void (*PFN_glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (*PFN_glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
typedef void (*PFN_glEnableVertexAttribArray)(GLuint index);
typedef void (*PFN_glDisableVertexAttribArray)(GLuint index);
typedef void (*PFN_glDrawArrays)(GLenum mode, GLint first, GLsizei count);
typedef void (*PFN_glActiveTexture)(GLenum texture);
typedef void (*PFN_glViewport)(GLint x, GLint y, GLsizei width, GLsizei height);
typedef void (*PFN_glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
typedef void (*PFN_glClear)(GLbitfield mask);
typedef void (*PFN_glDisable)(GLenum cap);
typedef void (*PFN_glGetIntegerv)(GLenum pname, GLint *data);
typedef const unsigned char *(*PFN_glGetString)(GLenum name);
typedef void (*PFN_glGenVertexArrays)(GLsizei n, GLuint *arrays);
typedef void (*PFN_glBindVertexArray)(GLuint array);
typedef void (*PFN_glEnable)(GLenum cap);
typedef unsigned char (*PFN_glIsEnabled)(GLenum cap);
namespace
{
PFN_glGenTextures glGenTextures_ = nullptr;
PFN_glBindTexture glBindTexture_ = nullptr;
PFN_glTexImage2D glTexImage2D_ = nullptr;
PFN_glTexParameteri glTexParameteri_ = nullptr;
PFN_glCreateShader glCreateShader_ = nullptr;
PFN_glShaderSource glShaderSource_ = nullptr;
PFN_glCompileShader glCompileShader_ = nullptr;
PFN_glGetShaderiv glGetShaderiv_ = nullptr;
PFN_glGetShaderInfoLog glGetShaderInfoLog_ = nullptr;
PFN_glCreateProgram glCreateProgram_ = nullptr;
PFN_glAttachShader glAttachShader_ = nullptr;
PFN_glLinkProgram glLinkProgram_ = nullptr;
PFN_glGetProgramiv glGetProgramiv_ = nullptr;
PFN_glGetProgramInfoLog glGetProgramInfoLog_ = nullptr;
PFN_glUseProgram glUseProgram_ = nullptr;
PFN_glGetAttribLocation glGetAttribLocation_ = nullptr;
PFN_glGetUniformLocation glGetUniformLocation_ = nullptr;
PFN_glUniform1i glUniform1i_ = nullptr;
PFN_glGenBuffers glGenBuffers_ = nullptr;
PFN_glBindBuffer glBindBuffer_ = nullptr;
PFN_glBufferData glBufferData_ = nullptr;
PFN_glVertexAttribPointer glVertexAttribPointer_ = nullptr;
PFN_glEnableVertexAttribArray glEnableVertexAttribArray_ = nullptr;
PFN_glDisableVertexAttribArray glDisableVertexAttribArray_ = nullptr;
PFN_glDrawArrays glDrawArrays_ = nullptr;
PFN_glActiveTexture glActiveTexture_ = nullptr;
PFN_glViewport glViewport_ = nullptr;
PFN_glClearColor glClearColor_ = nullptr;
PFN_glClear glClear_ = nullptr;
PFN_glDisable glDisable_ = nullptr;
PFN_glGetIntegerv glGetIntegerv_ = nullptr;
PFN_glGetString glGetString_ = nullptr;
// Vertex Array Object entry points. Optional: absent on plain GLES2, but
// required to draw on a desktop GL core profile (which switch-mesa gives us).
PFN_glGenVertexArrays glGenVertexArrays_ = nullptr;
PFN_glBindVertexArray glBindVertexArray_ = nullptr;
PFN_glEnable glEnable_ = nullptr;
PFN_glIsEnabled glIsEnabled_ = nullptr;
bool g_glLoaded = false;
template<typename T>
bool LoadOneGL(const char *name, T &fn)
{
fn = (T)glfwGetProcAddress(name);
return fn != nullptr;
}
bool LoadGL()
{
if (g_glLoaded)
return true;
bool ok = true;
ok &= LoadOneGL("glGenTextures", glGenTextures_);
ok &= LoadOneGL("glBindTexture", glBindTexture_);
ok &= LoadOneGL("glTexImage2D", glTexImage2D_);
ok &= LoadOneGL("glTexParameteri", glTexParameteri_);
ok &= LoadOneGL("glCreateShader", glCreateShader_);
ok &= LoadOneGL("glShaderSource", glShaderSource_);
ok &= LoadOneGL("glCompileShader", glCompileShader_);
ok &= LoadOneGL("glGetShaderiv", glGetShaderiv_);
ok &= LoadOneGL("glGetShaderInfoLog", glGetShaderInfoLog_);
ok &= LoadOneGL("glCreateProgram", glCreateProgram_);
ok &= LoadOneGL("glAttachShader", glAttachShader_);
ok &= LoadOneGL("glLinkProgram", glLinkProgram_);
ok &= LoadOneGL("glGetProgramiv", glGetProgramiv_);
ok &= LoadOneGL("glGetProgramInfoLog", glGetProgramInfoLog_);
ok &= LoadOneGL("glUseProgram", glUseProgram_);
ok &= LoadOneGL("glGetAttribLocation", glGetAttribLocation_);
ok &= LoadOneGL("glGetUniformLocation", glGetUniformLocation_);
ok &= LoadOneGL("glUniform1i", glUniform1i_);
ok &= LoadOneGL("glGenBuffers", glGenBuffers_);
ok &= LoadOneGL("glBindBuffer", glBindBuffer_);
ok &= LoadOneGL("glBufferData", glBufferData_);
ok &= LoadOneGL("glVertexAttribPointer", glVertexAttribPointer_);
ok &= LoadOneGL("glEnableVertexAttribArray", glEnableVertexAttribArray_);
ok &= LoadOneGL("glDisableVertexAttribArray", glDisableVertexAttribArray_);
ok &= LoadOneGL("glDrawArrays", glDrawArrays_);
ok &= LoadOneGL("glActiveTexture", glActiveTexture_);
ok &= LoadOneGL("glViewport", glViewport_);
ok &= LoadOneGL("glClearColor", glClearColor_);
ok &= LoadOneGL("glClear", glClear_);
ok &= LoadOneGL("glDisable", glDisable_);
ok &= LoadOneGL("glGetIntegerv", glGetIntegerv_);
ok &= LoadOneGL("glGetString", glGetString_);
ok &= LoadOneGL("glEnable", glEnable_);
ok &= LoadOneGL("glIsEnabled", glIsEnabled_);
// VAO funcs are optional (GLES2 has none) - don't fail LoadGL if missing.
LoadOneGL("glGenVertexArrays", glGenVertexArrays_);
LoadOneGL("glBindVertexArray", glBindVertexArray_);
g_glLoaded = ok;
if (!g_glLoaded)
printf("MoviePlayer: failed to resolve one or more GL functions via glfwGetProcAddress\n");
return g_glLoaded;
}
}
#define GL_TEXTURE_2D 0x0DE1
#define GL_TEXTURE0 0x84C0
#define GL_RGBA 0x1908
#define GL_UNSIGNED_BYTE 0x1401
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_LINEAR 0x2601
#define GL_VERTEX_SHADER 0x8B31
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
#define GL_ARRAY_BUFFER 0x8892
#define GL_STATIC_DRAW 0x88E4
#define GL_FLOAT 0x1406
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_TRIANGLE_STRIP 0x0005
#define GL_DEPTH_TEST 0x0B71
#define GL_CULL_FACE 0x0B44
#define GL_BLEND 0x0BE2
#define GL_COLOR_BUFFER_BIT 0x4000
#define GL_VIEWPORT 0x0BA2
#define GL_VERSION 0x1F02
#define GL_CURRENT_PROGRAM 0x8B8D
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_VERTEX_ARRAY_BINDING 0x85B5
#define GL_ACTIVE_TEXTURE 0x84E0
#define GL_TEXTURE_BINDING_2D 0x8069
namespace
{
enum class State { Inactive, Active, Stopping };
State g_state = State::Inactive;
plm_t *g_plm = nullptr;
uint8_t *g_rgbaBuffer = nullptr;
int g_videoWidth = 0;
int g_videoHeight = 0;
double g_frameDuration = 1.0 / 30.0; // seconds per video frame, from plm_get_framerate()
ALCdevice *g_alDevice = nullptr;
ALCcontext *g_alContext = nullptr;
ALuint g_alSource = 0;
static const int kNumAudioBuffers = 4;
ALuint g_alBuffers[kNumAudioBuffers] = { 0, 0, 0, 0 };
bool g_audioReady = false;
int g_audioSampleRate = 48000;
double g_audioFrameDuration = (double)PLM_AUDIO_SAMPLES_PER_FRAME / 48000.0; // seconds per audio frame
double g_audioAccumulator = 0.0;
int g_nextFreeBuffer = 0;
double g_lastTime = 0.0; // seconds, from glfwGetTime()
double g_accumulator = 0.0;
bool InitAudio()
{
g_alDevice = alcOpenDevice(nullptr); // default device
if (!g_alDevice) {
printf("MoviePlayer: alcOpenDevice failed, continuing without audio\n");
return false;
}
g_alContext = alcCreateContext(g_alDevice, nullptr);
if (!g_alContext) {
printf("MoviePlayer: alcCreateContext failed, continuing without audio\n");
alcCloseDevice(g_alDevice);
g_alDevice = nullptr;
return false;
}
alcMakeContextCurrent(g_alContext);
alGenSources(1, &g_alSource);
alGenBuffers(kNumAudioBuffers, g_alBuffers);
g_nextFreeBuffer = 0;
g_audioReady = true;
return true;
}
void CloseAudio()
{
if (!g_audioReady)
return;
if (g_alSource) {
alSourceStop(g_alSource);
ALint queued = 0;
alGetSourcei(g_alSource, AL_BUFFERS_QUEUED, &queued);
while (queued-- > 0) {
ALuint buf;
alSourceUnqueueBuffers(g_alSource, 1, &buf);
}
alDeleteSources(1, &g_alSource);
g_alSource = 0;
}
alDeleteBuffers(kNumAudioBuffers, g_alBuffers);
for (int i = 0; i < kNumAudioBuffers; i++)
g_alBuffers[i] = 0;
alcMakeContextCurrent(nullptr);
if (g_alContext) { alcDestroyContext(g_alContext); g_alContext = nullptr; }
if (g_alDevice) { alcCloseDevice(g_alDevice); g_alDevice = nullptr; }
g_audioReady = false;
}
void QueueAudioFrame(plm_samples_t *samples)
{
if (!g_audioReady)
return;
static int16_t pcm[PLM_AUDIO_SAMPLES_PER_FRAME * 2];
for (int i = 0; i < PLM_AUDIO_SAMPLES_PER_FRAME * 2; i++) {
float s = samples->interleaved[i];
if (s > 1.0f) s = 1.0f;
if (s < -1.0f) s = -1.0f;
pcm[i] = (int16_t)(s * 32767.0f);
}
ALuint buffer = 0;
ALint processed = 0;
alGetSourcei(g_alSource, AL_BUFFERS_PROCESSED, &processed);
if (processed > 0) {
alSourceUnqueueBuffers(g_alSource, 1, &buffer);
} else {
if (g_nextFreeBuffer >= kNumAudioBuffers)
return;
buffer = g_alBuffers[g_nextFreeBuffer++];
}
alBufferData(buffer, AL_FORMAT_STEREO16, pcm, sizeof(pcm), (ALsizei)g_audioSampleRate);
alSourceQueueBuffers(g_alSource, 1, &buffer);
ALint state = 0;
alGetSourcei(g_alSource, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING)
alSourcePlay(g_alSource);
}
void CloseMovie()
{
if (g_plm) { plm_destroy(g_plm); g_plm = nullptr; }
if (g_rgbaBuffer) { free(g_rgbaBuffer); g_rgbaBuffer = nullptr; }
CloseAudio();
g_videoWidth = 0;
g_videoHeight = 0;
g_state = State::Inactive;
}
// Two shader variants. librw hands us either a desktop GL core context
// (switch-mesa gives GL 3.3 core) or a GLES context, and the two speak
// different GLSL dialects. Without an explicit #version the source defaults
// to GLSL 110, which a strict core-profile driver rejects - that is why the
// movie played with sound but no picture. Pick the dialect at runtime.
const char *kVertexShaderGL =
"#version 150\n"
"in vec2 aPos;\n"
"in vec2 aTexCoord;\n"
"out vec2 vTexCoord;\n"
"void main() {\n"
" vTexCoord = aTexCoord;\n"
" gl_Position = vec4(aPos, 0.0, 1.0);\n"
"}\n";
const char *kFragmentShaderGL =
"#version 150\n"
"in vec2 vTexCoord;\n"
"out vec4 FragColor;\n"
"uniform sampler2D uTexture;\n"
"void main() {\n"
" FragColor = texture(uTexture, vTexCoord);\n"
"}\n";
const char *kVertexShaderES =
"#version 100\n"
"attribute vec2 aPos;\n"
"attribute vec2 aTexCoord;\n"
"varying vec2 vTexCoord;\n"
"void main() {\n"
" vTexCoord = aTexCoord;\n"
" gl_Position = vec4(aPos, 0.0, 1.0);\n"
"}\n";
const char *kFragmentShaderES =
"#version 100\n"
"precision mediump float;\n"
"varying vec2 vTexCoord;\n"
"uniform sampler2D uTexture;\n"
"void main() {\n"
" gl_FragColor = texture2D(uTexture, vTexCoord);\n"
"}\n";
GLuint g_program = 0;
GLuint g_vbo = 0;
GLuint g_vao = 0;
GLuint g_texture = 0;
GLint g_aPosLoc = -1;
GLint g_aTexCoordLoc = -1;
GLint g_uTextureLoc = -1;
bool g_shaderReady = false;
GLuint CompileShader(GLenum type, const char *src)
{
GLuint shader = glCreateShader_(type);
glShaderSource_(shader, 1, &src, nullptr);
glCompileShader_(shader);
GLint status = 0;
glGetShaderiv_(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
char log[512];
glGetShaderInfoLog_(shader, sizeof(log), nullptr, log);
printf("MoviePlayer: shader compile error: %s\n", log);
}
return shader;
}
bool InitShader()
{
if (g_shaderReady)
return true;
const char *ver = glGetString_ ? (const char *)glGetString_(GL_VERSION) : nullptr;
bool isGLES = ver && strstr(ver, "OpenGL ES") != nullptr;
GLuint vs = CompileShader(GL_VERTEX_SHADER, isGLES ? kVertexShaderES : kVertexShaderGL);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, isGLES ? kFragmentShaderES : kFragmentShaderGL);
g_program = glCreateProgram_();
glAttachShader_(g_program, vs);
glAttachShader_(g_program, fs);
glLinkProgram_(g_program);
GLint linkStatus = 0;
glGetProgramiv_(g_program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
char log[512];
glGetProgramInfoLog_(g_program, sizeof(log), nullptr, log);
printf("MoviePlayer: shader link error: %s\n", log);
return false;
}
g_aPosLoc = glGetAttribLocation_(g_program, "aPos");
g_aTexCoordLoc = glGetAttribLocation_(g_program, "aTexCoord");
g_uTextureLoc = glGetUniformLocation_(g_program, "uTexture");
const GLfloat verts[] = {
// x, y, u, v
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f,
};
// A core-profile context can't draw without a bound VAO, and binding our
// own also stops us from trampling the VAO librw left bound.
if (glGenVertexArrays_ && glBindVertexArray_)
glGenVertexArrays_(1, &g_vao);
glGenBuffers_(1, &g_vbo);
glBindBuffer_(GL_ARRAY_BUFFER, g_vbo);
glBufferData_(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
g_shaderReady = true;
return true;
}
void DrawCurrentFrame()
{
if (!LoadGL() || g_rgbaBuffer == nullptr)
return;
// Save every GL state we are about to touch. librw keeps a render-state
// cache that mirrors what it last set; the current GL state IS that cache.
// If we leave GL changed, librw skips the "redundant" re-set and renders
// nothing after the movie (menu invisible, last frame frozen). Restoring
// GL to what we found keeps librw's cache valid.
GLint sProgram = 0, sArrayBuf = 0, sVao = 0, sActiveTex = 0, sTexBind = 0;
GLint sViewport[4] = { 0, 0, 0, 0 };
glGetIntegerv_(GL_CURRENT_PROGRAM, &sProgram);
glGetIntegerv_(GL_ARRAY_BUFFER_BINDING, &sArrayBuf);
if (glBindVertexArray_)
glGetIntegerv_(GL_VERTEX_ARRAY_BINDING, &sVao);
glGetIntegerv_(GL_ACTIVE_TEXTURE, &sActiveTex);
glGetIntegerv_(GL_VIEWPORT, sViewport);
unsigned char sDepth = glIsEnabled_(GL_DEPTH_TEST);
unsigned char sCull = glIsEnabled_(GL_CULL_FACE);
unsigned char sBlend = glIsEnabled_(GL_BLEND);
glActiveTexture_(GL_TEXTURE0);
glGetIntegerv_(GL_TEXTURE_BINDING_2D, &sTexBind);
if (g_texture == 0)
glGenTextures_(1, &g_texture);
glBindTexture_(GL_TEXTURE_2D, g_texture);
glTexImage2D_(GL_TEXTURE_2D, 0, GL_RGBA, g_videoWidth, g_videoHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, g_rgbaBuffer);
glTexParameteri_(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri_(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (!InitShader()) {
// Restore the texture binding we disturbed before bailing.
glBindTexture_(GL_TEXTURE_2D, (GLuint)sTexBind);
glActiveTexture_((GLenum)sActiveTex);
return;
}
// Size the viewport from the actual framebuffer. At this early boot stage
// the cached GL_VIEWPORT may still be 0x0 (nothing has rendered yet).
int fbWidth = 0, fbHeight = 0;
glfwGetFramebufferSize(PSGLOBAL(window), &fbWidth, &fbHeight);
glViewport_(0, 0, fbWidth, fbHeight);
glDisable_(GL_DEPTH_TEST);
glDisable_(GL_CULL_FACE);
glDisable_(GL_BLEND);
glClearColor_(0.0f, 0.0f, 0.0f, 1.0f);
glClear_(GL_COLOR_BUFFER_BIT);
glUseProgram_(g_program);
if (glBindVertexArray_)
glBindVertexArray_(g_vao);
glBindTexture_(GL_TEXTURE_2D, g_texture);
glUniform1i_(g_uTextureLoc, 0);
glBindBuffer_(GL_ARRAY_BUFFER, g_vbo);
glEnableVertexAttribArray_((GLuint)g_aPosLoc);
glVertexAttribPointer_((GLuint)g_aPosLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray_((GLuint)g_aTexCoordLoc);
glVertexAttribPointer_((GLuint)g_aTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *)(2 * sizeof(GLfloat)));
glDrawArrays_(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray_((GLuint)g_aPosLoc);
glDisableVertexAttribArray_((GLuint)g_aTexCoordLoc);
glfwSwapBuffers(PSGLOBAL(window));
// Restore everything so librw's state cache stays in sync.
if (glBindVertexArray_)
glBindVertexArray_((GLuint)sVao);
glUseProgram_((GLuint)sProgram);
glBindBuffer_(GL_ARRAY_BUFFER, (GLuint)sArrayBuf);
glBindTexture_(GL_TEXTURE_2D, (GLuint)sTexBind);
glActiveTexture_((GLenum)sActiveTex);
glViewport_(sViewport[0], sViewport[1], sViewport[2], sViewport[3]);
if (sDepth) glEnable_(GL_DEPTH_TEST);
if (sCull) glEnable_(GL_CULL_FACE);
if (sBlend) glEnable_(GL_BLEND);
}
}
void MoviePlayer::Play(const char *path)
{
if (g_state != State::Inactive)
CloseMovie();
char *realPath = casepath(path);
const char *openPath = realPath ? realPath : path;
g_plm = plm_create_with_filename(openPath);
if (realPath)
free(realPath);
if (!g_plm) {
printf("MoviePlayer: failed to open %s\n", path);
return;
}
if (!plm_has_headers(g_plm)) {
printf("MoviePlayer: %s has no valid MPEG-PS headers\n", path);
plm_destroy(g_plm);
g_plm = nullptr;
return;
}
g_videoWidth = plm_get_width(g_plm);
g_videoHeight = plm_get_height(g_plm);
if (g_videoWidth <= 0 || g_videoHeight <= 0) {
printf("MoviePlayer: %s has no video stream\n", path);
plm_destroy(g_plm);
g_plm = nullptr;
return;
}
g_rgbaBuffer = (uint8_t *)malloc((size_t)g_videoWidth * (size_t)g_videoHeight * 4);
g_frameDuration = 1.0 / plm_get_framerate(g_plm);
g_audioSampleRate = plm_get_samplerate(g_plm);
if (g_audioSampleRate > 0 && InitAudio()) {
plm_set_audio_enabled(g_plm, TRUE);
plm_set_audio_stream(g_plm, 0);
g_audioFrameDuration = (double)PLM_AUDIO_SAMPLES_PER_FRAME / (double)g_audioSampleRate;
g_audioAccumulator = 0.0;
} else {
plm_set_audio_enabled(g_plm, FALSE);
}
printf("MoviePlayer: playing %s (%dx%d, %.2f fps, duration %.2fs, audio=%s)\n",
path, g_videoWidth, g_videoHeight, plm_get_framerate(g_plm), plm_get_duration(g_plm),
g_audioReady ? "yes" : "no");
g_lastTime = glfwGetTime();
g_accumulator = 0.0;
g_state = State::Active;
}
bool MoviePlayer::IsActive()
{
return g_state != State::Inactive;
}
void MoviePlayer::Stop()
{
if (g_state == State::Active)
g_state = State::Stopping;
}
void MoviePlayer::Draw()
{
if (g_state == State::Inactive)
return;
if (g_state == State::Stopping) {
CloseMovie();
return;
}
double now = glfwGetTime();
double elapsed = now - g_lastTime;
g_lastTime = now;
g_accumulator += elapsed;
const double kMaxAccumulator = g_frameDuration * 4.0;
if (g_accumulator > kMaxAccumulator)
g_accumulator = kMaxAccumulator;
const int kMaxFramesPerCall = 4;
int framesDecoded = 0;
while (g_accumulator >= g_frameDuration && framesDecoded < kMaxFramesPerCall) {
plm_frame_t *frame = plm_decode_video(g_plm);
if (frame == nullptr)
break;
plm_frame_to_rgba(frame, g_rgbaBuffer, g_videoWidth * 4);
DrawCurrentFrame();
g_accumulator -= g_frameDuration;
framesDecoded++;
}
if (g_audioReady) {
g_audioAccumulator += elapsed;
const double kMaxAudioAccumulator = g_audioFrameDuration * 8.0;
if (g_audioAccumulator > kMaxAudioAccumulator)
g_audioAccumulator = kMaxAudioAccumulator;
const int kMaxAudioFramesPerCall = 8;
int audioFramesDecoded = 0;
while (g_audioAccumulator >= g_audioFrameDuration && audioFramesDecoded < kMaxAudioFramesPerCall) {
plm_samples_t *samples = plm_decode_audio(g_plm);
if (samples == nullptr)
break;
QueueAudioFrame(samples);
g_audioAccumulator -= g_audioFrameDuration;
audioFramesDecoded++;
}
}
if (plm_has_ended(g_plm)) {
printf("MoviePlayer: movie finished\n");
g_state = State::Stopping;
}
}
#endif // RW_GL3 && !LIBRW_SDL2
+15
View File
@@ -0,0 +1,15 @@
#ifndef MOVIEPLAYER_H
#define MOVIEPLAYER_H
namespace MoviePlayer
{
void Play(const char *path);
void Draw();
bool IsActive();
void Stop();
}
#endif
+31 -37
View File
@@ -51,6 +51,7 @@ long _dwOperatingSystemVersion;
#include "AnimViewer.h"
#include "Font.h"
#include "MemoryMgr.h"
#include "MoviePlayer.h"
// This is defined on project-level, via premake5 or cmake
#ifdef GET_KEYBOARD_INPUT_FROM_X11
@@ -1857,6 +1858,15 @@ WinMain(HINSTANCE instance,
#endif
#else
// True if the player pressed a button to skip an intro movie (controller A /
// Start, or Enter on a keyboard).
static bool SkipMovieButtonJustDown()
{
return CPad::GetPad(0)->GetCrossJustDown()
|| CPad::GetPad(0)->GetStartJustDown()
|| CPad::GetPad(0)->GetEnterJustDown();
}
int
main(int argc, char *argv[])
{
@@ -2101,7 +2111,7 @@ main(int argc, char *argv[])
case GS_START_UP:
{
#ifdef NO_MOVIES
gGameState = GS_INIT_ONCE;
gGameState = gbNoMovies ? GS_INIT_ONCE : GS_INIT_LOGO_MPEG;
#else
gGameState = GS_INIT_LOGO_MPEG;
#endif
@@ -2111,8 +2121,7 @@ main(int argc, char *argv[])
case GS_INIT_LOGO_MPEG:
{
//if (!startupDeactivate)
// PlayMovieInWindow(cmdShow, "movies\\Logo.mpg");
MoviePlayer::Play("movies/Logo.mpg");
gGameState = GS_LOGO_MPEG;
TRACE("gGameState = GS_LOGO_MPEG;");
break;
@@ -2120,35 +2129,25 @@ main(int argc, char *argv[])
case GS_LOGO_MPEG:
{
// CPad::UpdatePads();
CPad::UpdatePads();
// if (startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0)
if (MoviePlayer::IsActive()) {
MoviePlayer::Draw();
if (SkipMovieButtonJustDown())
MoviePlayer::Stop();
} else {
++gGameState;
// else if (CPad::GetPad(0)->GetLeftMouseJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetEnterJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetCharJustDown(' '))
// ++gGameState;
// else if (CPad::GetPad(0)->GetAltJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetTabJustDown())
// ++gGameState;
}
break;
}
case GS_INIT_INTRO_MPEG:
{
//#ifndef NO_MOVIES
// CloseClip();
// CoUninitialize();
//#endif
//
// if (CMenuManager::OS_Language == LANG_FRENCH || CMenuManager::OS_Language == LANG_GERMAN)
// PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg");
// else
// PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg");
if (CMenuManager::OS_Language == LANG_FRENCH || CMenuManager::OS_Language == LANG_GERMAN)
MoviePlayer::Play("movies/GTAtitlesGER.mpg");
else
MoviePlayer::Play("movies/GTAtitles.mpg");
gGameState = GS_INTRO_MPEG;
TRACE("gGameState = GS_INTRO_MPEG;");
@@ -2157,20 +2156,15 @@ main(int argc, char *argv[])
case GS_INTRO_MPEG:
{
// CPad::UpdatePads();
//
// if (startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0)
CPad::UpdatePads();
if (MoviePlayer::IsActive()) {
MoviePlayer::Draw();
if (SkipMovieButtonJustDown())
MoviePlayer::Stop();
} else {
++gGameState;
// else if (CPad::GetPad(0)->GetLeftMouseJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetEnterJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetCharJustDown(' '))
// ++gGameState;
// else if (CPad::GetPad(0)->GetAltJustDown())
// ++gGameState;
// else if (CPad::GetPad(0)->GetTabJustDown())
// ++gGameState;
}
break;
}
+4439
View File
File diff suppressed because it is too large Load Diff