1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-24 13:53:05 +02:00

#15 Rapberry Pi support

This commit is contained in:
XProger
2017-07-30 09:07:40 +03:00
parent 33fb9328d6
commit 5d1d03f421
7 changed files with 564 additions and 30 deletions

View File

@@ -43,6 +43,38 @@
#define glProgramBinary glProgramBinaryOES
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
#elif __RPI__
#define MOBILE
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#define GL_CLAMP_TO_BORDER 0x812D
#define GL_TEXTURE_BORDER_COLOR 0x1004
#define GL_TEXTURE_COMPARE_MODE 0x884C
#define GL_TEXTURE_COMPARE_FUNC 0x884D
#define GL_COMPARE_REF_TO_TEXTURE 0x884E
#undef GL_RGBA32F
#undef GL_RGBA16F
#undef GL_HALF_FLOAT
#define GL_RGBA32F GL_RGBA
#define GL_RGBA16F GL_RGBA
#define GL_HALF_FLOAT GL_HALF_FLOAT_OES
#define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
#define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES
#define glGenVertexArrays(...)
#define glDeleteVertexArrays(...)
#define glBindVertexArray(...)
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
#define glGetProgramBinary(...)
#define glProgramBinary(...)
#elif __linux__
#define LINUX 1
#include <GL/gl.h>
@@ -125,7 +157,8 @@ namespace Core {
#include "input.h"
#include "sound.h"
#if defined(WIN32) || defined(LINUX) || defined(ANDROID)
#if defined(WIN32) || (defined(LINUX) && !defined(__RPI__)) || defined(ANDROID)
#ifdef ANDROID
#define GetProc(x) dlsym(libGL, x);
@@ -133,6 +166,8 @@ namespace Core {
void* GetProc(const char *name) {
#ifdef WIN32
return (void*)wglGetProcAddress(name);
#elif __RPI__
return (void*)eglGetProcAddress(name);
#elif LINUX
return (void*)glXGetProcAddress((GLubyte*)name);
#endif
@@ -417,7 +452,7 @@ namespace Core {
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
#endif
#if defined(WIN32) || defined(LINUX) || defined(ANDROID)
#if defined(WIN32) || (defined(LINUX) && !defined(__RPI__)) || defined(ANDROID)
#ifdef WIN32
GetProcOGL(glActiveTexture);
#endif
@@ -485,8 +520,9 @@ namespace Core {
GetProcOGL(glProgramBinary);
#endif
char *ext = (char*)glGetString(GL_EXTENSIONS);
//LOG("%s\n", ext);
LOG("%s\n", ext);
support.shaderBinary = extSupport(ext, "_program_binary");
support.VAO = extSupport(ext, "_vertex_array_object");
@@ -505,7 +541,6 @@ namespace Core {
support.profMarker = extSupport(ext, "_KHR_debug");
support.profTiming = extSupport(ext, "_timer_query");
#endif
char *vendor = (char*)glGetString(GL_VENDOR);
LOG("Vendor : %s\n", vendor);
LOG("Renderer : %s\n", glGetString(GL_RENDERER));
@@ -527,6 +562,7 @@ namespace Core {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFBO);
glGenFramebuffers(1, &FBO);
memset(rtCache, 0, sizeof(rtCache));
defaultTarget = NULL;
@@ -541,7 +577,7 @@ namespace Core {
uint32 data = 0x00000000;
blackTex = new Texture(1, 1, Texture::RGBA, false, &data, false);
data = 0xFFFFFFFF;
whiteTex = new Texture(1, 1, Texture::RGBA, false, &data, false);
whiteTex = new Texture(1, 1, Texture::RGBA, false, &data, false);
}
void free() {

View File

@@ -138,10 +138,10 @@
E( LEADBAR ) \
E( INV_LEADBAR ) \
E( MIDAS_TOUCH ) \
E( KEY_1 ) \
E( KEY_2 ) \
E( KEY_3 ) \
E( KEY_4 ) \
E( KEY_ITEM_1 ) \
E( KEY_ITEM_2 ) \
E( KEY_ITEM_3 ) \
E( KEY_ITEM_4 ) \
E( INV_KEY_1 ) \
E( INV_KEY_2 ) \
E( INV_KEY_3 ) \
@@ -638,7 +638,7 @@ namespace TR {
bool isItem() {
return (type >= PISTOLS && type <= AMMO_UZIS) ||
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
(type >= KEY_1 && type <= KEY_4) ||
(type >= KEY_ITEM_1 && type <= KEY_ITEM_4) ||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_1); // TODO: recheck all items
}
@@ -670,10 +670,10 @@ namespace TR {
case PUZZLE_3 : return INV_PUZZLE_3;
case PUZZLE_4 : return INV_PUZZLE_4;
case KEY_1 : return INV_KEY_1;
case KEY_2 : return INV_KEY_2;
case KEY_3 : return INV_KEY_3;
case KEY_4 : return INV_KEY_4;
case KEY_ITEM_1 : return INV_KEY_1;
case KEY_ITEM_2 : return INV_KEY_2;
case KEY_ITEM_3 : return INV_KEY_3;
case KEY_ITEM_4 : return INV_KEY_4;
case LEADBAR : return INV_LEADBAR;
//case TR::Entity::SCION : return TR::Entity::INV_SCION;
@@ -683,14 +683,14 @@ namespace TR {
static Type getKeyForHole(Type hole) {
switch (hole) {
case PUZZLE_HOLE_1 : return PUZZLE_1; break;
case PUZZLE_HOLE_2 : return PUZZLE_2; break;
case PUZZLE_HOLE_3 : return PUZZLE_3; break;
case PUZZLE_HOLE_4 : return PUZZLE_4; break;
case KEY_HOLE_1 : return KEY_1; break;
case KEY_HOLE_2 : return KEY_2; break;
case KEY_HOLE_3 : return KEY_3; break;
case KEY_HOLE_4 : return KEY_4; break;
case PUZZLE_HOLE_1 : return PUZZLE_1; break;
case PUZZLE_HOLE_2 : return PUZZLE_2; break;
case PUZZLE_HOLE_3 : return PUZZLE_3; break;
case PUZZLE_HOLE_4 : return PUZZLE_4; break;
case KEY_HOLE_1 : return KEY_ITEM_1; break;
case KEY_HOLE_2 : return KEY_ITEM_2; break;
case KEY_HOLE_3 : return KEY_ITEM_3; break;
case KEY_HOLE_4 : return KEY_ITEM_4; break;
default : return NONE;
}
}
@@ -2030,4 +2030,4 @@ namespace TR {
}
}
#endif
#endif

View File

@@ -19,9 +19,9 @@ namespace Game {
void init(Stream *lvl, Stream *snd) {
Core::init();
Core::settings.detail.ambient = true;
Core::settings.detail.ambient = false;
Core::settings.detail.lighting = true;
Core::settings.detail.shadows = true;
Core::settings.detail.shadows = false;
Core::settings.detail.water = Core::support.texFloat || Core::support.texHalf;
Core::settings.detail.contact = false;
@@ -93,4 +93,4 @@ namespace Game {
}
}
#endif
#endif

28
src/platform/rpi/Makefile Normal file
View File

@@ -0,0 +1,28 @@
OBJS=OpenLara.o
BIN=../../../bin/OpenLara.bin
CFLAGS+=-D__RPI__
LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont
INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./
all: $(BIN) $(LIB)
%.o: %.c
@rm -f $@
$(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations
%.o: %.cpp
@rm -f $@
$(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations
%.bin: $(OBJS)
$(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic
%.a: $(OBJS)
$(AR) r $@ $^
clean:
for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done
@rm -f $(BIN) $(LIB)

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

@@ -0,0 +1,3 @@
set -e
clang++ -std=c++11 -Os -s -g -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -Wl,--gc-sections -DNDEBUG -D__RPI__ main.cpp ../../libs/stb_vorbis/stb_vorbis.c -I/opt/vc/include -I../../ -o../../../bin/OpenLara.bin -L/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lX11 -lm -lrt -lpthread -lpulse-simple -lpulse -lbcm_host -ludev
strip ../../../bin/OpenLara --strip-all --remove-section=.comment --remove-section=.note

466
src/platform/rpi/main.cpp Normal file
View File

@@ -0,0 +1,466 @@
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <pthread.h>
#include <bcm_host.h>
#include <EGL/egl.h>
#include <fcntl.h>
#include <linux/input.h>
#include <libudev.h>
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>
#include "game.h"
#define WND_TITLE "OpenLara"
#define SND_FRAME_SIZE 4
#define SND_DATA_SIZE (1024 * SND_FRAME_SIZE)
pa_simple *sndOut;
pthread_t sndThread;
pthread_mutex_t sndMutex;
Sound::Frame *sndData;
void* sndFill(void *arg) {
while (1) {
pthread_mutex_lock(&sndMutex);
Sound::fill(sndData, SND_DATA_SIZE / SND_FRAME_SIZE);
pthread_mutex_unlock(&sndMutex);
pa_simple_write(sndOut, sndData, SND_DATA_SIZE, NULL);
}
return NULL;
}
void sndInit() {
static const pa_sample_spec spec = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
static const pa_buffer_attr attr = {
.maxlength = SND_DATA_SIZE * 2,
.tlength = 0xFFFFFFFF,
.prebuf = 0xFFFFFFFF,
.minreq = SND_DATA_SIZE,
.fragsize = 0xFFFFFFFF,
};
pthread_mutex_init(&sndMutex, NULL);
int error;
if (!(sndOut = pa_simple_new(NULL, WND_TITLE, PA_STREAM_PLAYBACK, NULL, "game", &spec, NULL, &attr, &error))) {
LOG("! sound: pa_simple_new() %s\n", pa_strerror(error));
sndData = NULL;
return;
}
sndData = new Sound::Frame[SND_DATA_SIZE / SND_FRAME_SIZE];
pthread_create(&sndThread, NULL, sndFill, NULL);
}
void sndFree() {
if (sndOut) {
pthread_cancel(sndThread);
pthread_mutex_lock(&sndMutex);
// pa_simple_flush(sndOut, NULL);
// pa_simple_free(sndOut);
pthread_mutex_unlock(&sndMutex);
delete[] sndData;
}
pthread_mutex_destroy(&sndMutex);
}
unsigned int startTime;
int getTime() {
timeval t;
gettimeofday(&t, NULL);
return int((t.tv_sec - startTime) * 1000 + t.tv_usec / 1000);
}
bool wndInit(DISPMANX_DISPLAY_HANDLE_T &display, EGL_DISPMANX_WINDOW_T &window) {
if (graphics_get_display_size(0, (uint32_t*)&window.width, (uint32_t*)&window.height) < 0) {
LOG("! can't get display size\n");
return false;
}
int scale = 1;
window.width /= scale;
window.height /= scale;
VC_RECT_T dstRect, srcRect;
vc_dispmanx_rect_set(&dstRect, 0, 0, window.width, window.height);
vc_dispmanx_rect_set(&srcRect, 0, 0, window.width << 16, window.height << 16);
VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 0xFF, 0 };
display = vc_dispmanx_display_open(0);
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
window.element = vc_dispmanx_element_add(
update, display, 0, &dstRect, 0, &srcRect,
DISPMANX_PROTECTION_NONE, &alpha, NULL, DISPMANX_NO_ROTATE);
vc_dispmanx_update_submit_sync(update);
return true;
}
void wndFree(DISPMANX_DISPLAY_HANDLE_T &display, EGL_DISPMANX_WINDOW_T &window) {
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
vc_dispmanx_element_remove(update, window.element);
vc_dispmanx_update_submit_sync(update);
vc_dispmanx_display_close(display);
}
bool eglInit(EGL_DISPMANX_WINDOW_T &window, EGLDisplay &display, EGLSurface &surface, EGLContext &context) {
static const EGLint eglAttr[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_SAMPLES, 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
static const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY)
return false;
if (eglInitialize(display, NULL, NULL) == EGL_FALSE)
return false;
EGLConfig config;
EGLint configCount;
if (eglChooseConfig(display, eglAttr, &config, 1, &configCount) == EGL_FALSE)
return false;
context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT)
return false;
surface = eglCreateWindowSurface(display, config, &window, NULL);
if (surface == EGL_NO_SURFACE)
return false;
if (eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE)
return false;
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
return false;
//eglSwapInterval(display, 0); // turn off vsync
return true;
}
void eglFree(EGLDisplay display, EGLSurface surface, EGLContext context) {
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
int inputDevices[MAX_INPUT_DEVICES];
udev *udevObj;
udev_monitor *udevMon;
int udevMon_fd;
vec2 joyL, joyR;
InputKey codeToInputKey(int code) {
switch (code) {
// keyboard
case KEY_LEFT : return ikLeft;
case KEY_RIGHT : return ikRight;
case KEY_UP : return ikUp;
case KEY_DOWN : return ikDown;
case KEY_SPACE : return ikSpace;
case KEY_TAB : return ikTab;
case KEY_ENTER : return ikEnter;
case KEY_ESC : return ikEscape;
case KEY_LEFTSHIFT :
case KEY_RIGHTSHIFT : return ikShift;
case KEY_LEFTCTRL :
case KEY_RIGHTCTRL : return ikCtrl;
case KEY_LEFTALT :
case KEY_RIGHTALT : return ikAlt;
case KEY_0 : return ik0;
case KEY_1 : return ik1;
case KEY_2 : return ik2;
case KEY_3 : return ik3;
case KEY_4 : return ik4;
case KEY_5 : return ik5;
case KEY_6 : return ik6;
case KEY_7 : return ik7;
case KEY_8 : return ik8;
case KEY_9 : return ik9;
case KEY_A : return ikA;
case KEY_B : return ikB;
case KEY_C : return ikC;
case KEY_D : return ikD;
case KEY_E : return ikE;
case KEY_F : return ikF;
case KEY_G : return ikG;
case KEY_H : return ikH;
case KEY_I : return ikI;
case KEY_J : return ikJ;
case KEY_K : return ikK;
case KEY_L : return ikL;
case KEY_M : return ikM;
case KEY_N : return ikN;
case KEY_O : return ikO;
case KEY_P : return ikP;
case KEY_Q : return ikQ;
case KEY_R : return ikR;
case KEY_S : return ikS;
case KEY_T : return ikT;
case KEY_U : return ikU;
case KEY_V : return ikV;
case KEY_W : return ikW;
case KEY_X : return ikX;
case KEY_Y : return ikY;
case KEY_Z : return ikZ;
// mouse
case BTN_LEFT : return ikMouseL;
case BTN_RIGHT : return ikMouseR;
case BTN_MIDDLE : return ikMouseM;
// gamepad
case KEY_HOMEPAGE : return ikEscape;
case BTN_A : return ikJoyA;
case BTN_B : return ikJoyB;
case BTN_X : return ikJoyX;
case BTN_Y : return ikJoyY;
case BTN_TL : return ikJoyLB;
case BTN_TR : return ikJoyRB;
case BTN_SELECT : return ikJoySelect;
case BTN_START : return ikJoyStart;
case BTN_THUMBL : return ikJoyL;
case BTN_THUMBR : return ikJoyR;
case BTN_TL2 : return ikJoyLT;
case BTN_TR2 : return ikJoyRT;
}
return ikNone;
}
int inputDevIndex(const char *node) {
const char *str = strstr(node, "/event");
if (str)
return atoi(str + 6);
return -1;
}
void inputDevAdd(const char *node) {
int index = inputDevIndex(node);
if (index != -1) {
inputDevices[index] = open(node, O_RDONLY | O_NONBLOCK);
//ioctl(inputDevices[index], EVIOCGRAB, (void*)1);
LOG("input: add %s\n", node);
}
}
void inputDevRemove(const char *node) {
int index = inputDevIndex(node);
if (index != -1 && inputDevices[index] != -1) {
close(inputDevices[index]);
LOG("input: remove %s\n", node);
}
}
bool inputInit() {
joyL = joyR = vec2(0);
for (int i = 0; i < MAX_INPUT_DEVICES; i++)
inputDevices[i] = -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);
}
udev_enumerate_unref(e);
return true;
}
void inputFree() {
for (int i = 0; i < MAX_INPUT_DEVICES; i++)
if (inputDevices[i] != -1)
close(inputDevices[i]);
udev_monitor_unref(udevMon);
udev_unref(udevObj);
}
void inputUpdate() {
// get input events
input_event events[16];
for (int i = 0; i < MAX_INPUT_DEVICES; i++) {
if (inputDevices[i] == -1) continue;
int rb = read(inputDevices[i], events, sizeof(events));
input_event *e = events;
while (rb > 0) {
switch (e->type) {
case EV_KEY : {
InputKey key = codeToInputKey(e->code);
if (key == ikMouseL || key == ikMouseR || key == ikMouseM)
Input::setPos(key, Input::mouse.pos);
Input::setDown(key, e->value != 0);
break;
}
case EV_REL : {
vec2 delta(0);
delta[e->code] = float(e->value);
Input::setPos(ikMouseL, Input::mouse.pos + delta);
break;
}
case EV_ABS : {
float v = float(e->value) / 128.0f - 1.0f;
switch (e->code) {
case ABS_X : joyL.x = v; break;
case ABS_Y : joyL.y = v; break;
case ABS_Z : joyR.x = v; break;
case ABS_RZ : joyR.y = v; break;
}
}
}
//LOG("input: type = %d, code = %d, value = %d\n", int(e->type), int(e->code), int(e->value));
e++;
rb -= sizeof(events[0]);
}
}
Input::setPos(ikJoyL, joyL);
Input::setPos(ikJoyR, joyR);
// monitoring plug and unplug input devices
fd_set fds;
FD_ZERO(&fds);
FD_SET(udevMon_fd, &fds);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(udevMon_fd + 1, &fds, NULL, NULL, &tv) > 0 && FD_ISSET(udevMon_fd, &fds)) {
udev_device *device = udev_monitor_receive_device(udevMon);
if (device) {
const char *node = udev_device_get_devnode(device);
if (node) {
const char *action = udev_device_get_action(device);
if (!strcmp(action, "add"))
inputDevAdd(node);
if (!strcmp(action, "remove"))
inputDevRemove(node);
}
udev_device_unref(device);
} else
LOG("! input: receive_device\n");
}
}
char Stream::cacheDir[255];
char Stream::contentDir[255];
int main() {
bcm_host_init();
DISPMANX_DISPLAY_HANDLE_T dmDisplay;
EGL_DISPMANX_WINDOW_T dmWindow;
if (!wndInit(dmDisplay, dmWindow))
return 1;
Core::width = dmWindow.width;
Core::height = dmWindow.height;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
if (!eglInit(dmWindow, display, context, surface)) {
LOG("! can't initialize EGL context\n");
return 1;
}
inputInit();
Stream::contentDir[0] = Stream::cacheDir[0] = 0;
const char *home;
if (!(home = getenv("HOME")))
home = getpwuid(getuid())->pw_dir;
strcat(Stream::cacheDir, home);
strcat(Stream::cacheDir, "/.OpenLara/");
struct stat st = {0};
if (stat(Stream::cacheDir, &st) == -1 && mkdir(Stream::cacheDir, 0777) == -1)
Stream::cacheDir[0] = 0;
timeval t;
gettimeofday(&t, NULL);
startTime = t.tv_sec;
sndInit();
Game::init();
int lastTime = getTime();
while (!Input::down[ikEscape]) {
inputUpdate();
int time = getTime();
if (time <= lastTime)
continue;
pthread_mutex_lock(&sndMutex);
Game::update((time - lastTime) * 0.001f);
pthread_mutex_unlock(&sndMutex);
lastTime = time;
Game::render();
eglSwapBuffers(display, surface);
};
sndFree();
Game::free();
inputFree();
eglFree(display, surface, context);
wndFree(dmDisplay, dmWindow);
return 0;
}

View File

@@ -21,11 +21,12 @@
#else
#define ASSERT(expr)
// #ifdef PROFILE
#ifdef LINUX
#define LOG(...) printf(__VA_ARGS__); fflush(stdout)
#else
#define LOG(...) printf(__VA_ARGS__)
// #else
// #define LOG(...) 0
// #endif
// #define LOG(...) 0
#endif
#endif
#ifdef ANDROID