mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-16 18:14:05 +02:00
#15 Clover NES/SNES mini platforms
This commit is contained in:
@@ -32,6 +32,12 @@
|
|||||||
#define _GAPI_GLES 1
|
#define _GAPI_GLES 1
|
||||||
|
|
||||||
#define DYNGEOM_NO_VBO
|
#define DYNGEOM_NO_VBO
|
||||||
|
#elif __CLOVER__
|
||||||
|
#define _OS_CLOVER 1
|
||||||
|
#define _GAPI_GL 1
|
||||||
|
#define _GAPI_GLES 1
|
||||||
|
|
||||||
|
//#define DYNGEOM_NO_VBO
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#define _OS_LINUX 1
|
#define _OS_LINUX 1
|
||||||
#define _GAPI_GL 1
|
#define _GAPI_GL 1
|
||||||
@@ -704,7 +710,7 @@ namespace Core {
|
|||||||
settings.controls[0].keys[ cInventory ].key = ikTab;
|
settings.controls[0].keys[ cInventory ].key = ikTab;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _OS_RPI
|
#if defined(_OS_RPI) || defined(_OS_CLOVER)
|
||||||
settings.detail.setShadows (Core::Settings::LOW);
|
settings.detail.setShadows (Core::Settings::LOW);
|
||||||
settings.detail.setLighting (Core::Settings::MEDIUM);
|
settings.detail.setLighting (Core::Settings::MEDIUM);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
#define glProgramBinary glProgramBinaryOES
|
#define glProgramBinary glProgramBinaryOES
|
||||||
|
|
||||||
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
|
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
|
||||||
#elif _OS_RPI
|
#elif defined(_OS_RPI) || defined(_OS_CLOVER)
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <GLES2/gl2ext.h>
|
#include <GLES2/gl2ext.h>
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
#define glProgramBinary(...)
|
#define glProgramBinary(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_OS_WIN) || (defined(_OS_LINUX) && !defined(_OS_RPI)) || defined(_OS_ANDROID)
|
#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_ANDROID)
|
||||||
|
|
||||||
#ifdef _OS_ANDROID
|
#ifdef _OS_ANDROID
|
||||||
#define GetProc(x) dlsym(libGL, x);
|
#define GetProc(x) dlsym(libGL, x);
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
return (void*)wglGetProcAddress(name);
|
return (void*)wglGetProcAddress(name);
|
||||||
#elif _OS_LINUX
|
#elif _OS_LINUX
|
||||||
return (void*)glXGetProcAddress((GLubyte*)name);
|
return (void*)glXGetProcAddress((GLubyte*)name);
|
||||||
#elif _OS_RPI
|
#else // EGL
|
||||||
return (void*)eglGetProcAddress(name);
|
return (void*)eglGetProcAddress(name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@
|
|||||||
PFNGLPROGRAMBINARYPROC glProgramBinary;
|
PFNGLPROGRAMBINARYPROC glProgramBinary;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_GAPI_GLES) && !defined(_OS_RPI)
|
#if defined(_GAPI_GLES) && !defined(_OS_RPI) && !defined(_OS_CLOVER)
|
||||||
PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -369,11 +369,16 @@ namespace GAPI {
|
|||||||
}
|
}
|
||||||
sprintf(defines, "%s#define PASS_%s\n", defines, passNames[pass]);
|
sprintf(defines, "%s#define PASS_%s\n", defines, passNames[pass]);
|
||||||
|
|
||||||
#ifdef _OS_RPI
|
#if defined(_OS_RPI) || defined(_OS_CLOVER)
|
||||||
strcat(defines, "#define OPT_VLIGHTPROJ\n");
|
strcat(defines, "#define OPT_VLIGHTPROJ\n");
|
||||||
|
strcat(defines, "#define OPT_VLIGHTVEC\n");
|
||||||
strcat(defines, "#define OPT_SHADOW_ONETAP\n");
|
strcat(defines, "#define OPT_SHADOW_ONETAP\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _OS_CLOVER
|
||||||
|
strcat(defines, "#define OPT_TRAPEZOID\n"); // TODO: only for non Mali-400?
|
||||||
|
#endif
|
||||||
|
|
||||||
char fileName[255];
|
char fileName[255];
|
||||||
// generate shader file path
|
// generate shader file path
|
||||||
if (Core::support.shaderBinary) {
|
if (Core::support.shaderBinary) {
|
||||||
@@ -1068,7 +1073,7 @@ namespace GAPI {
|
|||||||
if (wglSwapIntervalEXT) wglSwapIntervalEXT(enable ? 1 : 0);
|
if (wglSwapIntervalEXT) wglSwapIntervalEXT(enable ? 1 : 0);
|
||||||
#elif _OS_LINUX
|
#elif _OS_LINUX
|
||||||
if (glXSwapIntervalSGI) glXSwapIntervalSGI(enable ? 1 : 0);
|
if (glXSwapIntervalSGI) glXSwapIntervalSGI(enable ? 1 : 0);
|
||||||
#elif _OS_RPI
|
#elif defined(_OS_RPI) || defined(_OS_CLOVER)
|
||||||
eglSwapInterval(display, enable ? 1 : 0);
|
eglSwapInterval(display, enable ? 1 : 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
3
src/platform/clover/build.sh
Executable file
3
src/platform/clover/build.sh
Executable 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__CLOVER__ 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 -o../../../bin/OpenLara
|
||||||
|
strip ../../../bin/OpenLara --strip-all --remove-section=.comment --remove-section=.note
|
590
src/platform/clover/main.cpp
Normal file
590
src/platform/clover/main.cpp
Normal file
@@ -0,0 +1,590 @@
|
|||||||
|
#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 osGetTime() {
|
||||||
|
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 devicbefore 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(¶ms);
|
||||||
|
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;
|
||||||
|
|
||||||
|
static const EGLint eglAttr[] = {
|
||||||
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_ALPHA_SIZE, 8,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static 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) {
|
||||||
|
LOG("eglChooseConfig = EGL_FALSE\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface = eglCreateWindowSurface(display, config, &fb, 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;
|
||||||
|
int joyIndex;
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
case KEY_HOMEPAGE : return ikEscape;
|
||||||
|
// mouse
|
||||||
|
case BTN_LEFT : return ikMouseL;
|
||||||
|
case BTN_RIGHT : return ikMouseR;
|
||||||
|
case BTN_MIDDLE : return ikMouseM;
|
||||||
|
// system keys
|
||||||
|
case KEY_VOLUMEUP :
|
||||||
|
case BTN_MODE :
|
||||||
|
Core::quit();
|
||||||
|
return ikNone;
|
||||||
|
}
|
||||||
|
return ikNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyKey codeToJoyKey(int code) {
|
||||||
|
switch (code) {
|
||||||
|
// gamepad
|
||||||
|
case BTN_TRIGGER_HAPPY1 : return jkLeft;
|
||||||
|
case BTN_TRIGGER_HAPPY2 : return jkRight;
|
||||||
|
case BTN_TRIGGER_HAPPY3 : return jkUp;
|
||||||
|
case BTN_TRIGGER_HAPPY4 : return jkDown;
|
||||||
|
case BTN_A : return jkB;
|
||||||
|
case BTN_B : return jkA;
|
||||||
|
case BTN_X : return jkY;
|
||||||
|
case BTN_Y : return jkX;
|
||||||
|
case BTN_TL : return jkLB;
|
||||||
|
case BTN_TR : return jkRB;
|
||||||
|
case BTN_SELECT : return jkSelect;
|
||||||
|
case BTN_START : return jkStart;
|
||||||
|
case BTN_THUMBL : return jkL;
|
||||||
|
case BTN_THUMBR : return jkR;
|
||||||
|
case BTN_TL2 : return jkLT;
|
||||||
|
case BTN_TR2 : return jkRT;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
item.joyIndex = -1;
|
||||||
|
if (udev_device_get_property_value(device, "ID_INPUT_JOYSTICK")) {
|
||||||
|
|
||||||
|
// TODO get index from /dev/input/js[N]
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) { // up to 4 gamepads
|
||||||
|
bool found = true;
|
||||||
|
for (int j = 0; j < MAX_INPUT_DEVICES; j++) {
|
||||||
|
if (inputDevices[j].joyIndex == i) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
item.joyIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//LOG("input: add %s (%d)\n", node, item.joyIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputDevRemove(const char *node) {
|
||||||
|
int index = inputDevIndex(node);
|
||||||
|
if (index != -1 && inputDevices[index].fd != -1) {
|
||||||
|
close(inputDevices[index].fd);
|
||||||
|
//LOG("input: remove %s\n", node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inputInit() {
|
||||||
|
joyL = joyR = vec2(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_INPUT_DEVICES; i++) {
|
||||||
|
inputDevices[i].fd = -1;
|
||||||
|
inputDevices[i].joyIndex = -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define JOY_DEAD_ZONE_STICK 8192
|
||||||
|
|
||||||
|
float joyAxisValue(int value) {
|
||||||
|
if (value > -JOY_DEAD_ZONE_STICK && value < JOY_DEAD_ZONE_STICK)
|
||||||
|
return 0.0f;
|
||||||
|
return value / 32767.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float joyTrigger(int value) {
|
||||||
|
return min(1.0f, value / 255.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));
|
||||||
|
|
||||||
|
int joyIndex = inputDevices[i].joyIndex;
|
||||||
|
|
||||||
|
input_event *e = events;
|
||||||
|
while (rb > 0) {
|
||||||
|
switch (e->type) {
|
||||||
|
case EV_KEY : {
|
||||||
|
InputKey key = codeToInputKey(e->code);
|
||||||
|
if (key != ikNone) {
|
||||||
|
if (key == ikMouseL || key == ikMouseR || key == ikMouseM)
|
||||||
|
Input::setPos(key, Input::mouse.pos);
|
||||||
|
Input::setDown(key, e->value != 0);
|
||||||
|
} else {
|
||||||
|
if (joyIndex == -1)
|
||||||
|
break;
|
||||||
|
JoyKey key = codeToJoyKey(e->code);
|
||||||
|
Input::setJoyDown(joyIndex, 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 : {
|
||||||
|
if (joyIndex == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
||||||
|
// Left trigger
|
||||||
|
case ABS_Z : Input::setJoyPos(joyIndex, jkLT, joyTrigger(e->value)); break;
|
||||||
|
// Right trigger
|
||||||
|
case ABS_RZ : Input::setJoyPos(joyIndex, jkRT, joyTrigger(e->value)); break;
|
||||||
|
// D-PAD
|
||||||
|
case ABS_HAT0X :
|
||||||
|
case ABS_THROTTLE :
|
||||||
|
Input::setJoyDown(joyIndex, jkLeft, e->value < 0);
|
||||||
|
Input::setJoyDown(joyIndex, jkRight, e->value > 0);
|
||||||
|
break;
|
||||||
|
case ABS_HAT0Y :
|
||||||
|
case ABS_RUDDER :
|
||||||
|
Input::setJoyDown(joyIndex, jkUp, e->value < 0);
|
||||||
|
Input::setJoyDown(joyIndex, jkDown, e->value > 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::setJoyPos(joyIndex, jkL, joyDir(joyL));
|
||||||
|
Input::setJoyPos(joyIndex, jkR, joyDir(joyR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//LOG("input: type = %d, code = %d, value = %d\n", int(e->type), int(e->code), int(e->value));
|
||||||
|
e++;
|
||||||
|
rb -= sizeof(events[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, device);
|
||||||
|
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(int argc, char **argv) {
|
||||||
|
LOG("start\n");
|
||||||
|
if (!eglInit()) {
|
||||||
|
LOG("! can't initialize EGL context\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::width = fb.width;
|
||||||
|
Core::height = fb.height;
|
||||||
|
|
||||||
|
Stream::contentDir[0] = Stream::cacheDir[0] = 0;
|
||||||
|
|
||||||
|
strcpy(Stream::contentDir, argv[0]);
|
||||||
|
int i = strlen(Stream::contentDir);
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (Stream::contentDir[i] == '/') {
|
||||||
|
Stream::contentDir[i + 1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(Stream::cacheDir, Stream::contentDir);
|
||||||
|
strcat(Stream::cacheDir, "cache/");
|
||||||
|
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat(Stream::cacheDir, &st) == -1 && mkdir(Stream::cacheDir, 0777) == -1)
|
||||||
|
Stream::cacheDir[0] = 0;
|
||||||
|
|
||||||
|
LOG("cache dir %s\n", Stream::cacheDir);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
//sndFree();
|
||||||
|
//Game::deinit();
|
||||||
|
|
||||||
|
eglFree();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -18,6 +18,10 @@ R"====(
|
|||||||
|
|
||||||
varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction
|
varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction
|
||||||
|
|
||||||
|
#ifdef OPT_VLIGHTVEC
|
||||||
|
varying vec3 vLightVec;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef OPT_CAUSTICS
|
#ifdef OPT_CAUSTICS
|
||||||
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
|
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
|
||||||
#endif
|
#endif
|
||||||
@@ -182,6 +186,10 @@ uniform vec4 uFogParams;
|
|||||||
vec3 lv2 = (uLightPos[2].xyz - coord) * uLightColor[2].w;
|
vec3 lv2 = (uLightPos[2].xyz - coord) * uLightColor[2].w;
|
||||||
vec3 lv3 = (uLightPos[3].xyz - coord) * uLightColor[3].w;
|
vec3 lv3 = (uLightPos[3].xyz - coord) * uLightColor[3].w;
|
||||||
|
|
||||||
|
#ifdef OPT_VLIGHTVEC
|
||||||
|
vLightVec = lv0;
|
||||||
|
#endif
|
||||||
|
|
||||||
vec4 lum, att;
|
vec4 lum, att;
|
||||||
#ifdef TYPE_ENTITY
|
#ifdef TYPE_ENTITY
|
||||||
lum.x = dot(vNormal.xyz, normalize(lv0));
|
lum.x = dot(vNormal.xyz, normalize(lv0));
|
||||||
@@ -247,7 +255,9 @@ uniform vec4 uFogParams;
|
|||||||
void _uv(vec3 coord) {
|
void _uv(vec3 coord) {
|
||||||
vTexCoord = aTexCoord;
|
vTexCoord = aTexCoord;
|
||||||
#if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE)
|
#if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE)
|
||||||
vTexCoord.xy *= vTexCoord.zw;
|
#ifdef OPT_TRAPEZOID
|
||||||
|
vTexCoord.xy *= vTexCoord.zw;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +388,7 @@ uniform vec4 uFogParams;
|
|||||||
discard;
|
discard;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
vec2 uv = vTexCoord.xy;
|
||||||
vec4 color;
|
vec4 color;
|
||||||
#ifdef TYPE_MIRROR
|
#ifdef TYPE_MIRROR
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
@@ -386,10 +397,11 @@ uniform vec4 uFogParams;
|
|||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE)
|
#if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE)
|
||||||
color = texture2D(sDiffuse, vTexCoord.xy / vTexCoord.zw);
|
#ifdef OPT_TRAPEZOID
|
||||||
#else
|
uv /= vTexCoord.zw;
|
||||||
color = texture2D(sDiffuse, vTexCoord.xy);
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
color = texture2D(sDiffuse, uv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ALPHA_TEST
|
#ifdef ALPHA_TEST
|
||||||
@@ -416,8 +428,12 @@ uniform vec4 uFogParams;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PASS_COMPOSE
|
#ifdef PASS_COMPOSE
|
||||||
vec3 lightVec = (uLightPos[0].xyz - vCoord) * uLightColor[0].w;
|
|
||||||
vec3 normal = normalize(vNormal.xyz);
|
#ifndef OPT_VLIGHTVEC
|
||||||
|
vec3 vLightVec = (uLightPos[0].xyz - vCoord) * uLightColor[0].w;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec3 normal = normalize(vNormal.xyz);
|
||||||
|
|
||||||
#ifdef TYPE_ENTITY
|
#ifdef TYPE_ENTITY
|
||||||
float rSpecular = uMaterial.z + 0.03;
|
float rSpecular = uMaterial.z + 0.03;
|
||||||
@@ -427,7 +443,7 @@ uniform vec4 uFogParams;
|
|||||||
vec3 light = uLightColor[1].xyz * vLight.y + uLightColor[2].xyz * vLight.z + uLightColor[3].xyz * vLight.w;
|
vec3 light = uLightColor[1].xyz * vLight.y + uLightColor[2].xyz * vLight.z + uLightColor[3].xyz * vLight.w;
|
||||||
|
|
||||||
#if defined(TYPE_ENTITY) || defined(TYPE_ROOM)
|
#if defined(TYPE_ENTITY) || defined(TYPE_ROOM)
|
||||||
float rShadow = getShadow(lightVec, normal);
|
float rShadow = getShadow(vLightVec, normal);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TYPE_ENTITY
|
#ifdef TYPE_ENTITY
|
||||||
@@ -458,7 +474,7 @@ uniform vec4 uFogParams;
|
|||||||
color.xyz *= light;
|
color.xyz *= light;
|
||||||
|
|
||||||
#ifdef TYPE_ENTITY
|
#ifdef TYPE_ENTITY
|
||||||
color.xyz += calcSpecular(normal, vViewVec.xyz, lightVec, uLightColor[0], rSpecular);
|
color.xyz += calcSpecular(normal, vViewVec.xyz, vLightVec, uLightColor[0], rSpecular);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef UNDERWATER
|
#ifdef UNDERWATER
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#ifdef _OS_LINUX
|
#if defined(_OS_LINUX) || defined(_OS_RPI) || defined(_OS_CLOVER)
|
||||||
#define debugBreak() raise(SIGTRAP);
|
#define debugBreak() raise(SIGTRAP);
|
||||||
#else
|
#else
|
||||||
#define debugBreak() _asm { int 3 }
|
#define debugBreak() _asm { int 3 }
|
||||||
|
Reference in New Issue
Block a user