1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-03-18 18:19:39 +01:00

GCW Zero platform support (RG350)

This commit is contained in:
XProger 2020-01-31 17:13:32 +03:00
parent 4a25746bd4
commit 8c8f539a70
14 changed files with 630 additions and 62 deletions

View File

@ -310,10 +310,16 @@ struct Character : Controller {
void bakeEnvironment(Texture *&environment) {
flags.invisible = true;
if (!environment) {
environment = new Texture(256, 256, 1, FMT_RGBA, OPT_CUBEMAP | OPT_MIPMAPS | OPT_TARGET);
uint32 opt = OPT_CUBEMAP | OPT_TARGET;
#ifdef USE_CUBEMAP_MIPS
opt |= OPT_MIPMAPS;
#endif
environment = new Texture(256, 256, 1, FMT_RGB16, opt);
}
game->renderEnvironment(getRoomIndex(), pos - vec3(0.0f, 384.0f, 0.0f), &environment);
environment->generateMipMap();
#ifdef USE_CUBEMAP_MIPS
environment->generateMipMap();
#endif
flags.invisible = false;
}
};

View File

@ -12,6 +12,8 @@
#define OS_FILEIO_CACHE
#define OS_PTHREAD_MT
#define USE_CUBEMAP_MIPS
#ifdef WIN32
#define _OS_WIN 1
#define _GAPI_GL 1
@ -64,6 +66,15 @@
#define _OS_BITTBOY 1
#define _OS_LINUX 1
#define _GAPI_SW 1
#elif __GCW0__
#define _OS_GCW0 1
#define _GAPI_GL 1
#define _GAPI_GLES 1
#define DYNGEOM_NO_VBO
// etnaviv driver has a bug with cubemap mips generator
#undef USE_CUBEMAP_MIPS
#elif __linux__
#define _OS_LINUX 1
#define _GAPI_GL 1
@ -129,7 +140,12 @@
#define SPLIT_BY_CLUT
#endif
#else
#define MERGE_MODELS
// current etnaviv driver implementation uses uncompatible Mesa GLSL compiler
// it produce unimplemented TRUNC/ARL instructions instead of F2I
// so we can't use joints indexing in the shader (see MESH_SKINNING)
#ifndef _OS_GCW0
#define MERGE_MODELS
#endif
#define MERGE_SPRITES
#define GENERATE_WATER_PLANE
#endif
@ -137,8 +153,10 @@
#include "utils.h"
// muse be equal with base shader
#ifdef __OS_3DS
#if defined(_OS_3DS)
#define SHADOW_TEX_SIZE 512
#elif defined(_OS_GCW0)
#define SHADOW_TEX_SIZE 256
#else
#define SHADOW_TEX_SIZE 2048
#endif
@ -235,6 +253,7 @@ namespace Core {
bool tex3D;
bool texRG;
bool texBorder;
bool texMaxLevel;
bool colorFloat, texFloat, texFloatLinear;
bool colorHalf, texHalf, texHalfLinear;
#ifdef PROFILE
@ -454,7 +473,11 @@ struct PSO {
uint32 renderState;
};
typedef uint32 Index;
#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_MAC) || defined(_OS_WEB)
typedef uint32 Index;
#else
typedef uint16 Index;
#endif
struct Edge {
Index a, b;
@ -738,8 +761,9 @@ namespace Core {
lightStackCount = 0;
memset(&support, 0, sizeof(support));
support.texMinSize = 1;
Core::support.derivatives = true;
support.texMinSize = 1;
support.texMaxLevel = true;
support.derivatives = true;
#ifdef USE_INFLATE
tinf_init();
@ -765,6 +789,7 @@ namespace Core {
LOG(" 3D textures : %s\n", support.tex3D ? "true" : "false");
LOG(" RG textures : %s\n", support.texRG ? "true" : "false");
LOG(" border color : %s\n", support.texBorder ? "true" : "false");
LOG(" max level : %s\n", support.texMaxLevel ? "true" : "false");
LOG(" anisotropic : %d\n", support.maxAniso);
LOG(" float textures : float = %s, half = %s\n",
support.colorFloat ? "full" : (support.texFloat ? (support.texFloatLinear ? "linear" : "nearest") : "false"),
@ -899,6 +924,13 @@ namespace Core {
settings.detail.setLighting (Core::Settings::MEDIUM);
#endif
#if defined(_OS_GCW0)
settings.detail.setFilter (Core::Settings::MEDIUM);
settings.detail.setShadows (Core::Settings::MEDIUM);
settings.detail.setLighting (Core::Settings::MEDIUM);
settings.audio.subtitles = false;
#endif
#ifdef _OS_PSC
settings.detail.setLighting (Core::Settings::MEDIUM);
settings.detail.setShadows (Core::Settings::LOW);

View File

@ -58,7 +58,7 @@ namespace Game {
level->init(playLogo, playVideo);
UI::game = level;
#if !defined(_OS_PSP) && !defined(_OS_CLOVER)
#if !defined(INV_GAMEPAD_ONLY)
UI::helpTipTime = 5.0f;
#endif
delete lvl;

View File

@ -116,6 +116,50 @@
#elif defined(_OS_PSC)
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
extern EGLDisplay display;
#elif defined(_OS_GCW0)
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#define GL_TEXTURE_MAX_LEVEL GL_TEXTURE_MAX_LEVEL_APPLE
#define GL_TEXTURE_3D 0
#define GL_TEXTURE_WRAP_R 0
#define GL_TEXTURE_COMPARE_MODE 0x884C
#define GL_TEXTURE_COMPARE_FUNC 0x884D
#define GL_COMPARE_REF_TO_TEXTURE 0x884E
#undef GL_RG
#undef GL_RG32F
#undef GL_RG16F
#undef GL_RGBA32F
#undef GL_RGBA16F
#undef GL_HALF_FLOAT
#define GL_RG GL_RGBA
#define GL_RGBA32F GL_RGBA
#define GL_RGBA16F GL_RGBA
#define GL_RG32F GL_RGBA
#define GL_RG16F GL_RGBA
#define GL_HALF_FLOAT GL_HALF_FLOAT_OES
#define glTexImage3D(...) 0
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
#define PFNGLGENVERTEXARRAYSPROC PFNGLGENVERTEXARRAYSOESPROC
#define PFNGLDELETEVERTEXARRAYSPROC PFNGLDELETEVERTEXARRAYSOESPROC
#define PFNGLBINDVERTEXARRAYPROC PFNGLBINDVERTEXARRAYOESPROC
#define PFNGLGETPROGRAMBINARYPROC PFNGLGETPROGRAMBINARYOESPROC
#define PFNGLPROGRAMBINARYPROC PFNGLPROGRAMBINARYOESPROC
#define glGenVertexArrays glGenVertexArraysOES
#define glDeleteVertexArrays glDeleteVertexArraysOES
#define glBindVertexArray glBindVertexArrayOES
#define glGetProgramBinary glGetProgramBinaryOES
#define glProgramBinary glProgramBinaryOES
extern EGLDisplay display;
#elif defined(_OS_RPI) || defined(_OS_CLOVER)
#include <GLES2/gl2.h>
@ -158,7 +202,7 @@
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
#define glGetProgramBinary(...)
#define glProgramBinary(...)
extern EGLDisplay display;
#elif _OS_SWITCH
#define GL_GLEXT_PROTOTYPES
@ -248,21 +292,17 @@
#define glProgramBinary(...)
#endif
#if defined(_OS_WIN) || defined(_OS_LINUX)
#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_GCW0)
#ifdef _OS_ANDROID
#define GetProc(x) dlsym(libGL, x);
#else
void* GetProc(const char *name) {
#ifdef _OS_WIN
return (void*)wglGetProcAddress(name);
#elif _OS_LINUX
return (void*)glXGetProcAddress((GLubyte*)name);
#else // EGL
return (void*)eglGetProcAddress(name);
#endif
}
#endif
void* GetProc(const char *name) {
#ifdef _OS_WIN
return (void*)wglGetProcAddress(name);
#elif _OS_LINUX
return (void*)glXGetProcAddress((GLubyte*)name);
#else // EGL
return (void*)eglGetProcAddress(name);
#endif
}
#define GetProcOGL(x) x=(decltype(x))GetProc(#x);
@ -341,15 +381,17 @@
PFNGLBUFFERSUBDATAARBPROC glBufferSubData;
#endif
// Vertex Arrays
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
// Binary shaders
PFNGLGETPROGRAMBINARYPROC glGetProgramBinary;
PFNGLPROGRAMBINARYPROC glProgramBinary;
#endif
#if defined(_GAPI_GLES) && !defined(_OS_RPI) && !defined(_OS_CLOVER) && !defined(_OS_PSC) && !defined(_OS_IOS) && !defined(_OS_ANDROID) && !defined(__SDL2__)
PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#if defined(_GAPI_GLES)
PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#endif
#endif
#ifdef PROFILE
@ -560,7 +602,18 @@ namespace GAPI {
sprintf(defines + strlen(defines), "#define %s\n", DefineName[def[i]]);
}
#if defined(_OS_RPI) || defined(_OS_CLOVER) || (defined (__SDL2__) && defined (_GAPI_GLES))
sprintf(defines + strlen(defines), "#define SHADOW_SIZE %d.0\n", SHADOW_TEX_SIZE);
#ifdef MERGE_MODELS
strcat(defines, "#define MESH_SKINNING\n");
#endif
// etnaviv driver has no sin/cos/abs implementation
#if !defined(_OS_GCW0)
strcat(defines, "#define VERT_CAUSTICS\n");
#endif
#if defined(_OS_RPI) || defined(_OS_CLOVER) || defined(_OS_GCW0) || (defined (__SDL2__) && defined(_GAPI_GLES))
strcat(defines, "#define OPT_VLIGHTPROJ\n");
strcat(defines, "#define OPT_VLIGHTVEC\n");
strcat(defines, "#define OPT_SHADOW_ONETAP\n");
@ -858,9 +911,9 @@ namespace GAPI {
glGenerateMipmap(target);
if ((opt & (OPT_VOLUME | OPT_CUBEMAP | OPT_NEAREST)) == 0 && (Core::support.maxAniso > 0)) {
glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.maxAniso), 8));
#if !defined(_OS_RPI) && !defined(_OS_CLOVER) && !(defined (__SDL2__) && defined (_GAPI_GLES))// TODO
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 3);
#endif
if (Core::support.texMaxLevel) {
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 3);
}
}
}
@ -1062,7 +1115,7 @@ namespace GAPI {
//void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
#endif
#if defined(_OS_WIN) || defined(_OS_LINUX)
#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_GCW0)
#ifdef _OS_WIN
GetProcOGL(glActiveTexture);
#endif
@ -1135,10 +1188,11 @@ namespace GAPI {
GetProcOGL(glGenVertexArrays);
GetProcOGL(glDeleteVertexArrays);
GetProcOGL(glBindVertexArray);
GetProcOGL(glGetProgramBinary);
GetProcOGL(glProgramBinary);
#ifdef _GAPI_GLES
#if defined(_GAPI_GLES)
GetProcOGL(glDiscardFramebufferEXT);
#endif
#endif
@ -1162,7 +1216,6 @@ namespace GAPI {
}
}
*/
#ifndef FFP
bool GLES3 = false;
#ifdef _OS_WEB
@ -1173,7 +1226,7 @@ namespace GAPI {
#if defined(__SDL2__)
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &GLES_VERSION);
#else
#if defined(_OS_RPI) || defined(_OS_CLOVER)
#if defined(_OS_RPI) || defined(_OS_CLOVER) || defined(_OS_GCW0)
GLES_VERSION = 2;
#else
glGetIntegerv(GL_MAJOR_VERSION, &GLES_VERSION);
@ -1191,9 +1244,11 @@ namespace GAPI {
support.VAO = GLES3 || extSupport(ext, "_vertex_array_object");
support.depthTexture = GLES3 || extSupport(ext, "_depth_texture");
support.shadowSampler = _GL_EXT_shadow_samplers || _GL_ARB_shadow;
support.discardFrame = extSupport(ext, "_discard_framebuffer");
support.discardFrame = extSupport(ext, "_discard_framebuffer") && (glDiscardFramebufferEXT != NULL);
support.texNPOT = GLES3 || extSupport(ext, "_texture_npot") || extSupport(ext, "_texture_non_power_of_two");
support.texRG = GLES3 || extSupport(ext, "_texture_rg "); // hope that isn't last extension in string ;)
support.texMaxLevel = GLES3 || extSupport(ext, "_texture_max_level");
#ifdef _GAPI_GLES2 // TODO
support.shaderBinary = false;
support.VAO = false;
@ -1400,8 +1455,9 @@ namespace GAPI {
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
} else {
GLenum texTarget = GL_TEXTURE_2D;
if (target->opt & OPT_CUBEMAP)
if (target->opt & OPT_CUBEMAP) {
texTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
}
bool depth = target->fmt == FMT_DEPTH || target->fmt == FMT_SHADOW;
@ -1412,7 +1468,7 @@ namespace GAPI {
glFramebufferRenderbuffer (GL_FRAMEBUFFER, depth ? GL_COLOR_ATTACHMENT0 : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rtCache[!depth].items[rtIndex].ID);
GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
LOG("status: %d\n", (int)status);
LOG("status: 0x%04X\n", (int)status);
}
}
}
@ -1428,9 +1484,7 @@ namespace GAPI {
#ifdef _OS_ANDROID
glInvalidateFramebuffer(GL_FRAMEBUFFER, count, discard);
#else
#if !defined(__SDL2__) && !defined(_OS_PSC)
glDiscardFramebufferEXT(GL_FRAMEBUFFER, count, discard);
#endif
glDiscardFramebufferEXT(GL_FRAMEBUFFER, count, discard);
#endif
}
}

View File

@ -133,11 +133,13 @@ static const OptionItem optDetail[] = {
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_SHADOWS, SETTINGS( detail.shadows ), STR_QUALITY_LOW, 0, 2 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_WATER, SETTINGS( detail.water ), STR_QUALITY_LOW, 0, 2 ),
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_SIMPLE_ITEMS, SETTINGS( detail.simple ), STR_OFF, 0, 1 ),
#if !defined(_OS_3DS) && !defined(_OS_GCW0)
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_RESOLUTION, SETTINGS( detail.scale ), STR_SCALE_100, 0, 3 ),
#endif
#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_PSP) || defined(_OS_RPI) || defined(_OS_PSV)
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_VSYNC, SETTINGS( detail.vsync ), STR_OFF, 0, 1 ),
#endif
#if !defined(_OS_PSP) && !defined(_OS_PSV) && !defined(_OS_3DS)
#if !defined(_OS_PSP) && !defined(_OS_PSV) && !defined(_OS_3DS) && !defined(_OS_GCW0)
OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_STEREO, SETTINGS( detail.stereo ), STR_NO_STEREO, 0,
#if defined(_OS_WIN) || defined(_OS_ANDROID)
4 /* with VR option */
@ -166,9 +168,12 @@ static const OptionItem optSound[] = {
#define INV_GAMEPAD_ONLY
#endif
#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS)
#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_GCW0)
#define INV_SINGLE_PLAYER
#define INV_GAMEPAD_ONLY
#endif
#ifdef INV_SINGLE_PLAYER
#define INV_CTRL_START_OPTION 1
#else
#define INV_CTRL_START_OPTION 2
@ -1192,7 +1197,7 @@ struct Inventory {
else if (Input::down[ikDown] || joy.down[jkDown] || joy.L.y > 0.5f)
key = cDown;
#if defined(_OS_SWITCH) || defined(_OS_3DS)
#if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0)
// swap A/B keys for Nintendo (Japanese) UX style
if (Input::touchTimerVis == 0.0f) {
if (key == cAction) {
@ -1387,9 +1392,11 @@ struct Inventory {
background[0] = NULL;
}
for (int i = 0; i < COUNT(background); i++)
if (!background[i])
background[i] = new Texture(INV_BG_SIZE, INV_BG_SIZE, 1, FMT_RGBA, OPT_TARGET);
for (int i = 0; i < COUNT(background); i++) {
if (!background[i]) {
background[i] = new Texture(INV_BG_SIZE, INV_BG_SIZE, 1, FMT_RGB16, OPT_TARGET);
}
}
return background[view];
}
@ -2061,7 +2068,7 @@ struct Inventory {
const char *bSelect = STR[STR_KEY_FIRST + ikEnter];
const char *bBack = STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[cInventory].key];
#if defined(_OS_SWITCH) || defined(_OS_3DS)
#if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0)
bSelect = "A";
bBack = "B";
#endif

View File

@ -1744,7 +1744,9 @@ struct Level : IGame {
if (e.type == TR::Entity::CRYSTAL) {
Crystal *c = (Crystal*)e.controller;
renderEnvironment(c->getRoomIndex(), c->pos - vec3(0, 512, 0), &c->environment);
c->environment->generateMipMap();
#ifdef USE_CUBEMAP_MIPS
c->environment->generateMipMap();
#endif
}
}
}

View File

@ -1565,6 +1565,8 @@ struct MeshBuilder {
Core::mModel.identity();
Core::mModel.setRot(basis.rot);
Core::mModel.setPos(basis.pos);
#else
Core::active.shader->setParam(uBasis, *(vec4*)&basis, 2);
#endif
#endif

View File

@ -850,7 +850,11 @@ struct Crystal : Controller {
Texture *environment;
Crystal(IGame *game, int entity) : Controller(game, entity) {
environment = new Texture(64, 64, 1, FMT_RGBA, OPT_CUBEMAP | OPT_MIPMAPS | OPT_TARGET);
uint32 opt = OPT_CUBEMAP | OPT_TARGET;
#ifdef USE_CUBEMAP_MIPS
opt |= OPT_MIPMAPS;
#endif
environment = new Texture(64, 64, 1, FMT_RGB16, opt);
activate();
}

3
src/platform/gcw0/build.sh Executable file
View File

@ -0,0 +1,3 @@
set -e
/opt/gcw0-toolchain/usr/bin/mipsel-gcw0-linux-uclibc-g++ -o OpenLara -D__GCW0__ -std=c++11 -O3 -s -g0 -mips32r2 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -Wl,--gc-sections -Wno-invalid-source-encoding main.cpp ../../libs/stb_vorbis/stb_vorbis.c ../../libs/minimp3/minimp3.cpp ../../libs/tinf/tinflate.c -I/opt/vc/include -I../../ -L/opt/vc/lib/ -lGLESv2 -lEGL -lm -lrt -lpthread -lasound -ludev
/opt/gcw0-toolchain/usr/bin/mipsel-gcw0-linux-uclibc-strip ../../../bin/OpenLara --strip-all --remove-section=.comment --remove-section=.note

BIN
src/platform/gcw0/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

425
src/platform/gcw0/main.cpp Normal file
View File

@ -0,0 +1,425 @@
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <linux/fb.h>
#include <linux/vt.h>
#include <unistd.h>
#include <pwd.h>
#include <pthread.h>
#include <EGL/egl.h>
#include <fcntl.h>
#include <linux/input.h>
#include <libudev.h>
#include <alsa/asoundlib.h>
#include "game.h"
#define WND_TITLE "OpenLara"
// timing
unsigned int startTime;
int osGetTimeMS() {
timeval t;
gettimeofday(&t, NULL);
return int((t.tv_sec - startTime) * 1000 + t.tv_usec / 1000);
}
// sound
snd_pcm_uframes_t SND_FRAMES = 512;
snd_pcm_t *sndOut;
Sound::Frame *sndData;
pthread_t sndThread;
void* sndFill(void *arg) {
while (sndOut) {
Sound::fill(sndData, SND_FRAMES);
int count = SND_FRAMES;
while (count > 0) {
int frames = snd_pcm_writei(sndOut, &sndData[SND_FRAMES - count], count);
if (frames < 0) {
frames = snd_pcm_recover(sndOut, frames, 0);
if (frames == -EAGAIN) {
LOG("snd_pcm_writei try again\n");
sleep(1);
continue;
}
if (frames < 0) {
LOG("snd_pcm_writei failed: %s\n", snd_strerror(frames));
sndOut = NULL;
return NULL;
}
}
count -= frames;
}
snd_pcm_prepare(sndOut);
}
return NULL;
}
bool sndInit() {
unsigned int freq = 44100;
int err;
// In the perfect world ReedPlayer-Clover process
// will release ALSA device before app running, but...
for (int i = 0; i < 20; i++) { // 20 * 0.1 = 2 secs
sndOut = NULL;
if ((err = snd_pcm_open(&sndOut, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
LOG("sound: try to snd_pcm_open #%d...\n", i);
usleep(100000); // wait for 100 ms
continue;
}
break;
}
// I've bad news for you
if (!sndOut) {
LOG("! sound: snd_pcm_open %s\n", snd_strerror(err));
return false;
}
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(sndOut, params);
snd_pcm_hw_params_set_access(sndOut, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_channels(sndOut, params, 2);
snd_pcm_hw_params_set_format(sndOut, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(sndOut, params, &freq, NULL);
snd_pcm_hw_params_set_periods(sndOut, params, 4, 0);
snd_pcm_hw_params_set_period_size_near(sndOut, params, &SND_FRAMES, NULL);
snd_pcm_hw_params_get_period_size(params, &SND_FRAMES, 0);
snd_pcm_hw_params(sndOut, params);
snd_pcm_prepare(sndOut);
sndData = new Sound::Frame[SND_FRAMES];
memset(sndData, 0, SND_FRAMES * sizeof(Sound::Frame));
if ((err = snd_pcm_writei(sndOut, sndData, SND_FRAMES)) < 0) {
LOG("! sound: write %s\n", snd_strerror(err));
sndOut = NULL;
}
snd_pcm_start(sndOut);
pthread_create(&sndThread, NULL, sndFill, NULL);
return true;
}
void sndFree() {
pthread_cancel(sndThread);
snd_pcm_drop(sndOut);
snd_pcm_drain(sndOut);
snd_pcm_close(sndOut);
delete[] sndData;
}
// Window
struct FrameBuffer {
unsigned short width;
unsigned short height;
} fb;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
bool eglInit() {
LOG("EGL init context...\n");
fb_var_screeninfo vinfo;
int fd = open("/dev/fb0", O_RDWR);
if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
LOG("! can't get framebuffer size\n");
return false;
}
close(fd);
fb.width = vinfo.xres;
fb.height = vinfo.yres;
const EGLint eglAttr[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_RED_SIZE, 5,
EGL_DEPTH_SIZE, 16,
EGL_NONE
};
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
LOG("eglGetDisplay = EGL_NO_DISPLAY\n");
return false;
}
if (eglInitialize(display, NULL, NULL) == EGL_FALSE) {
LOG("eglInitialize = EGL_FALSE\n");
return false;
}
EGLConfig config;
EGLint configCount;
if (eglChooseConfig(display, eglAttr, &config, 1, &configCount) == EGL_FALSE || configCount == 0) {
LOG("eglChooseConfig = EGL_FALSE\n");
return false;
}
surface = eglCreateWindowSurface(display, config, 0, NULL);
if (surface == EGL_NO_SURFACE) {
LOG("eglCreateWindowSurface = EGL_NO_SURFACE\n");
return false;
}
context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT) {
LOG("eglCreateContext = EGL_NO_CONTEXT\n");
return false;
}
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOG("eglMakeCurrent = EGL_FALSE\n");
return false;
}
return true;
}
void eglFree() {
LOG("EGL release context\n");
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(display, surface);
eglDestroyContext(display, context);
eglTerminate(display);
}
// Input
#define MAX_INPUT_DEVICES 16
struct InputDevice {
int fd;
} inputDevices[MAX_INPUT_DEVICES];
udev *udevObj;
udev_monitor *udevMon;
int udevMon_fd;
vec2 joyL, joyR;
bool osJoyReady(int index) {
return index == 0; // TODO
}
void osJoyVibrate(int index, float L, float R) {
// TODO
}
JoyKey codeToJoyKey(int code) {
switch (code) {
// gamepad
case KEY_LEFT : return jkLeft;
case KEY_RIGHT : return jkRight;
case KEY_UP : return jkUp;
case KEY_DOWN : return jkDown;
case KEY_LEFTCTRL : return jkB;
case KEY_LEFTALT : return jkA;
case KEY_SPACE : return jkY;
case KEY_LEFTSHIFT : return jkX;
case KEY_TAB : return jkLB;
case KEY_BACKSPACE : return jkRB;
case KEY_ESC : return jkSelect;
case KEY_ENTER : return jkStart;
case KEY_KPSLASH : return jkL;
case KEY_KPDOT : return jkR;
case KEY_PAGEUP : return jkLT;
case KEY_PAGEDOWN : return jkRT;
case KEY_POWER : Core::quit();
}
return jkNone;
}
int inputDevIndex(const char *node) {
const char *str = strstr(node, "/event");
if (str)
return atoi(str + 6);
return -1;
}
void inputDevAdd(const char *node, udev_device *device) {
int index = inputDevIndex(node);
if (index != -1) {
InputDevice &item = inputDevices[index];
item.fd = open(node, O_RDONLY | O_NONBLOCK);
//ioctl(item.fd, EVIOCGRAB, 1);
//LOG("input: add %s (%d)\n", node, item.joyIndex);
}
}
bool inputInit() {
joyL = joyR = vec2(0);
for (int i = 0; i < MAX_INPUT_DEVICES; i++) {
inputDevices[i].fd = -1;
}
udevObj = udev_new();
if (!udevObj)
return false;
udevMon = udev_monitor_new_from_netlink(udevObj, "udev");
udev_monitor_filter_add_match_subsystem_devtype(udevMon, "input", NULL);
udev_monitor_enable_receiving(udevMon);
udevMon_fd = udev_monitor_get_fd(udevMon);
udev_enumerate *e = udev_enumerate_new(udevObj);
udev_enumerate_add_match_subsystem(e, "input");
udev_enumerate_scan_devices(e);
udev_list_entry *devices = udev_enumerate_get_list_entry(e);
udev_list_entry *entry;
udev_list_entry_foreach(entry, devices) {
const char *path, *node;
udev_device *device;
path = udev_list_entry_get_name(entry);
device = udev_device_new_from_syspath(udevObj, path);
node = udev_device_get_devnode(device);
if (node)
inputDevAdd(node, device);
}
udev_enumerate_unref(e);
return true;
}
void inputFree() {
for (int i = 0; i < MAX_INPUT_DEVICES; i++)
if (inputDevices[i].fd != -1)
close(inputDevices[i].fd);
udev_monitor_unref(udevMon);
udev_unref(udevObj);
}
float joyAxisValue(int value) {
return value / 1536.0f - 1.0f;
}
vec2 joyDir(const vec2 &value) {
float dist = min(1.0f, value.length());
return value.normal() * dist;
}
void inputUpdate() {
// get input events
input_event events[16];
for (int i = 0; i < MAX_INPUT_DEVICES; i++) {
if (inputDevices[i].fd == -1) continue;
int rb = read(inputDevices[i].fd, events, sizeof(events));
input_event *e = events;
while (rb > 0) {
switch (e->type) {
case EV_KEY : {
JoyKey key = codeToJoyKey(e->code);
Input::setJoyDown(0, key, e->value != 0);
break;
}
case EV_ABS : {
switch (e->code) {
// Left stick
case ABS_X : joyL.x = -joyAxisValue(e->value); break;
case ABS_Y : joyL.y = -joyAxisValue(e->value); break;
// Right stick
case ABS_RX : joyR.x = joyAxisValue(e->value); break;
case ABS_RY : joyR.y = joyAxisValue(e->value); break;
}
Input::setJoyPos(0, jkL, joyDir(joyL));
Input::setJoyPos(0, jkR, joyDir(joyR));
}
}
e++;
rb -= sizeof(events[0]);
}
}
}
int main(int argc, char **argv) {
if (!eglInit()) {
LOG("! can't initialize EGL context\n");
return -1;
}
Core::width = fb.width;
Core::height = fb.height;
cacheDir[0] = saveDir[0] = contentDir[0] = 0;
const char *home;
if (!(home = getenv("HOME")))
home = getpwuid(getuid())->pw_dir;
strcpy(contentDir, home);
strcat(contentDir, "/.openlara/");
LOG("content dir: %s\n", contentDir);
struct stat st = {0};
if (stat(contentDir, &st) == -1) {
LOG("no data directory found, please copy the original game content into /home/.openlara/\n");
return -1;
}
strcpy(saveDir, contentDir);
strcpy(cacheDir, contentDir);
strcat(cacheDir, "cache/");
if (stat(cacheDir, &st) == -1 && mkdir(cacheDir, 0777) == -1) {
cacheDir[0] = 0;
LOG("can't create /home/.openlara/cache/\n");
}
timeval t;
gettimeofday(&t, NULL);
startTime = t.tv_sec;
Game::init();
inputInit();
sndInit();
while (!Core::isQuit) {
inputUpdate();
if (Game::update()) {
Game::render();
Core::waitVBlank();
eglSwapBuffers(display, surface);
} else
usleep(9000);
};
inputFree();
Game::deinit();
eglFree();
sndFree();
return 0;
}

30
src/platform/gcw0/make_opk.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/sh
OPK_NAME=OpenLara.opk
echo ${OPK_NAME}
# create default.gcw0.desktop
cat > default.gcw0.desktop <<EOF
[Desktop Entry]
Name=OpenLara
Comment=Classic Tomb Raider open-source engine
Exec=OpenLara
Terminal=false
Type=Application
StartupNotify=true
Icon=icon
Categories=games;
X-OD-NeedsDownscaling=false
EOF
# create opk
FLIST="OpenLara"
FLIST="${FLIST} default.gcw0.desktop"
FLIST="${FLIST} icon.png"
rm -f ${OPK_NAME}
mksquashfs ${FLIST} ${OPK_NAME} -all-root -no-xattrs -noappend -no-exports
cat default.gcw0.desktop
rm -f default.gcw0.desktop

View File

@ -13,7 +13,7 @@ R"====(
#endif
#ifdef OPT_SHADOW
#define SHADOW_TEXEL vec3(1.0 / 2048.0, 1.0 / 2048.0, 0.0)
#define SHADOW_TEXEL vec3(1.0 / SHADOW_SIZE, 1.0 / SHADOW_SIZE, 0.0)
uniform mat4 uLightProj;
#ifdef OPT_VLIGHTPROJ
@ -95,7 +95,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction
}
vec4 _transform() {
#if defined(TYPE_ENTITY) || defined(TYPE_MIRROR)
#if (defined(TYPE_ENTITY) || defined(TYPE_MIRROR)) && defined(MESH_SKINNING)
int index = int(aCoord.w * 2.0);
vec4 rBasisRot = uBasis[index];
vec4 rBasisPos = uBasis[index + 1];
@ -122,18 +122,16 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction
float fog;
#if defined(UNDERWATER) && !defined(OPT_UNDERWATER_FOG)
float d;
if (uViewPos.y < uParam.y) // TODO: fix for mediump
d = abs((coord.y - uParam.y) / normalize(uViewPos.xyz - coord).y);
else
d = length(uViewPos.xyz - coord);
float d = length(uViewPos.xyz - coord);
if (uViewPos.y < uParam.y) {
d *= (coord.y - uParam.y) / (coord.y - uViewPos.y);
}
fog = d * WATER_FOG_DIST;
fog *= step(uParam.y, coord.y);
vNormal.w = clamp(1.0 / exp(fog), 0.0, 1.0);
#else
fog = length(vViewVec.xyz);
vNormal.w = clamp(1.0 / exp(fog), 0.0, 1.0);
#endif
vNormal.w = clamp(1.0 / exp(fog), 0.0, 1.0);
vCoord = coord;
#endif
@ -189,7 +187,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction
lum.w = dot(vNormal.xyz, normalize(lv3)); att.w = dot(lv3, lv3);
vec4 light = max(vec4(0.0), lum) * max(vec4(0.0), vec4(1.0) - att);
#if (defined(TYPE_ENTITY) || defined(TYPE_ROOM)) && defined(UNDERWATER)
#if (defined(TYPE_ENTITY) || defined(TYPE_ROOM)) && defined(UNDERWATER) && defined(VERT_CAUSTICS)
light.x *= 0.5 + abs(sin(dot(coord.xyz, vec3(1.0 / 1024.0)) + uParam.x)) * 0.75;
#endif

View File

@ -22,9 +22,14 @@ R"====(
}
void main() {
int index = int(aCoord.w * 2.0);
vec4 rBasisRot = uBasis[index];
vec4 rBasisPos = uBasis[index + 1];
#ifdef MESH_SKINNING
int index = int(aCoord.w * 2.0);
vec4 rBasisRot = uBasis[index];
vec4 rBasisPos = uBasis[index + 1];
#else
vec4 rBasisRot = uBasis[0];
vec4 rBasisPos = uBasis[1];
#endif
#ifdef ALPHA_TEST
vTexCoord = aTexCoord.xy;
#endif