diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index 76843831..f7190473 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -37,6 +37,7 @@ public class Apple2Activity extends Activity { private final static int BUF_SZ = 4096; private final static String PREFS_CONFIGURED = "prefs_configured"; private final static int SOFTKEYBOARD_THRESHOLD = 50; + private final static int MAX_FINGERS = 32;// HACK ... private Apple2View mView = null; private AlertDialog mQuitDialog = null; @@ -48,6 +49,9 @@ public class Apple2Activity extends Activity { private int mHeight = 0; private boolean mSoftKeyboardShowing = false; + private float[] mXCoords = new float[MAX_FINGERS]; + private float[] mYCoords = new float[MAX_FINGERS]; + static { System.loadLibrary("apple2ix"); } @@ -63,7 +67,8 @@ public class Apple2Activity extends Activity { public native void nativeOnResume(); public native void nativeOnPause(); public native void nativeOnQuit(); - public native boolean nativeOnTouch(int action, float x, float y); + public native boolean nativeOnTouch(int action, int pointerCount, int pointerIndex, float[] xCoords, float[] yCoords); + public native void nativeReboot(); public native void nativeRender(); @@ -238,6 +243,45 @@ public class Apple2Activity extends Activity { } } + private String actionToString(int action) { + switch (action) { + case MotionEvent.ACTION_CANCEL: + return "CANCEL:"+action; + case MotionEvent.ACTION_DOWN: + return "DOWN:"+action; + case MotionEvent.ACTION_MOVE: + return "MOVE:"+action; + case MotionEvent.ACTION_UP: + return "UP:"+action; + case MotionEvent.ACTION_POINTER_DOWN: + return "PDOWN:"+action; + case MotionEvent.ACTION_POINTER_UP: + return "PUP:"+action; + default: + return "UNK:"+action; + } + } + + private void printSamples(MotionEvent ev) { + final int historySize = ev.getHistorySize(); + final int pointerCount = ev.getPointerCount(); + + /* + for (int h = 0; h < historySize; h++) { + Log.d(TAG, "Event "+ev.getAction().toString()+" at historical time "+ev.getHistoricalEventTime(h)+" :"); + for (int p = 0; p < pointerCount; p++) { + Log.d(TAG, " pointer "+ev.getPointerId(p)+": ("+ev.getHistoricalX(p, h)+","+ev.getHistoricalY(p, h)+")"); + } + } + */ + int pointerIndex = ev.getActionIndex(); + + Log.d(TAG, "Event "+actionToString(ev.getActionMasked())+" for "+pointerIndex+" at time "+ev.getEventTime()+" :"); + for (int p=0; p #include +enum { + ANDROID_ACTION_DOWN = 0x0, + ANDROID_ACTION_UP = 0x1, + ANDROID_ACTION_MOVE = 0x2, + ANDROID_ACTION_CANCEL = 0x3, + ANDROID_ACTION_POINTER_DOWN = 0x5, + ANDROID_ACTION_POINTER_UP = 0x6, +}; + static bool nativePaused = false; #if TESTING @@ -45,6 +54,26 @@ static bool _run_tests(void) { } #endif +static inline int _androidTouchEvent2JoystickEvent(jint action) { + switch (action) { + case ANDROID_ACTION_DOWN: + return TOUCH_DOWN; + case ANDROID_ACTION_MOVE: + return TOUCH_MOVE; + case ANDROID_ACTION_UP: + return TOUCH_UP; + case ANDROID_ACTION_POINTER_DOWN: + return TOUCH_POINTER_DOWN; + case ANDROID_ACTION_POINTER_UP: + return TOUCH_POINTER_UP; + case ANDROID_ACTION_CANCEL: + return TOUCH_CANCEL; + default: + LOG("Unknown Android event : %d", action); + return TOUCH_CANCEL; + } +} + void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir) { const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0); data_dir = strdup(dataDir); @@ -154,8 +183,23 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnKeyUp(JNIEnv *env, jobjec #endif } -jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnTouch(JNIEnv *env, jobject obj, jint action, jfloat keyCode, jfloat metaState) { - return false; +jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnTouch(JNIEnv *env, jobject obj, jint action, jint pointerCount, jint pointerIndex, jfloatArray xCoords, jfloatArray yCoords) { + //LOG("nativeOnTouch : %d/%d/%d :", action, pointerCount, pointerIndex); + + jfloat *x_coords = (*env)->GetFloatArrayElements(env, xCoords, 0); + jfloat *y_coords = (*env)->GetFloatArrayElements(env, yCoords, 0); + + int joyaction = _androidTouchEvent2JoystickEvent(action); + + //for (unsigned int i=0; iReleaseFloatArrayElements(env, xCoords, x_coords, 0); + (*env)->ReleaseFloatArrayElements(env, yCoords, y_coords, 0); + return consumed; } void Java_org_deadc0de_apple2ix_Apple2Activity_nativeIncreaseCPUSpeed(JNIEnv *env, jobject obj) { diff --git a/Android/jni/sources.mk b/Android/jni/sources.mk index 514b3dad..0476a59d 100644 --- a/Android/jni/sources.mk +++ b/Android/jni/sources.mk @@ -33,5 +33,5 @@ APPLE2_MAIN_SRC = \ $(APPLE2_SRC_PATH)/timing.c $(APPLE2_SRC_PATH)/zlib-helpers.c $(APPLE2_SRC_PATH)/joystick.c $(APPLE2_SRC_PATH)/keys.c \ $(APPLE2_SRC_PATH)/disk.c $(APPLE2_SRC_PATH)/cpu-supp.c -APPLE2_BASE_CFLAGS := -DAPPLE2IX=1 -DMOBILE_DEVICE=1 -DVIDEO_OPENGL=1 -DDEBUGGER=1 -std=gnu11 -I$(APPLE2_SRC_PATH) +APPLE2_BASE_CFLAGS := -DAPPLE2IX=1 -DTOUCH_JOYSTICK=1 -DMOBILE_DEVICE=1 -DVIDEO_OPENGL=1 -DDEBUGGER=1 -std=gnu11 -I$(APPLE2_SRC_PATH) diff --git a/src/joystick.c b/src/joystick.c index 56c0ded5..b0a05873 100644 --- a/src/joystick.c +++ b/src/joystick.c @@ -219,15 +219,7 @@ static void c_calibrate_keypad_joystick() } } #endif // KEYPAD_JOYSTICK -#endif // INTERFACE_CLASSIC -#ifdef TOUCH_JOYSTICK -// TBD ... -#endif - -/* ---------------------------------------------------------------------- */ - -#ifdef INTERFACE_CLASSIC void c_calibrate_joystick() { if (joy_mode == JOY_PCJOY) @@ -245,7 +237,7 @@ void c_calibrate_joystick() #endif // INTERFACE_CLASSIC extern void gldriver_joystick_reset(void); -void c_joystick_reset() +void c_joystick_reset(void) { #if VIDEO_OPENGL && !TESTING if (!is_headless) { @@ -264,3 +256,23 @@ void c_joystick_reset() #endif } +#if TOUCH_JOYSTICK +bool (*joydriver_onTouchEvent)(joystick_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) = NULL; + +// is the touch joystick available +bool (*joydriver_isTouchJoystickAvailable)(void) = NULL; + +// enable/disable touch joystick +void (*joydriver_setTouchJoyEnabled)(bool enabled) = NULL; + +// set the joystick button parameters (7bit ASCII characters or MOUSETEXT values) +void (*joydriver_setTouchButtonValues)(char button0Val, char button1Val) = NULL; + +// set the axis type +void (*joydriver_setTouchAxisType)(touchjoy_axis_type_t axisType) = NULL; + +// set the axis button parameters (7bit ASCII characters or MOUSETEXT values) +void (*joydriver_setTouchAxisValues)(char up, char left, char right, char down) = NULL; + +#endif + diff --git a/src/joystick.h b/src/joystick.h index 85c99ac0..8227f1f6 100644 --- a/src/joystick.h +++ b/src/joystick.h @@ -9,13 +9,6 @@ * */ -/* - * 65c02 CPU Timing Support. - * - * Copyleft 2013 Aaron Culliney - * - */ - #ifndef _JOYSTICK_H_ #define _JOYSTICK_H_ @@ -39,4 +32,40 @@ void c_joystick_reset(void); void c_calibrate_joystick(void); #endif +#if TOUCH_JOYSTICK + +typedef enum joystick_touch_event_t { + TOUCH_CANCEL = 0, + TOUCH_DOWN, + TOUCH_MOVE, + TOUCH_UP, + TOUCH_POINTER_DOWN, + TOUCH_POINTER_UP, +} joystick_touch_event_t; + +typedef enum touchjoy_axis_type_t { + AXIS_EMULATED_DEVICE = 0, // touch joystick axes emulate a physical joystick device + AXIS_EMULATED_KEYBOARD, // touch joystick axes send single key events +} touchjoy_axis_type_t; + +// handle touch event +extern bool (*joydriver_onTouchEvent)(joystick_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords); + +// is the touch joystick available +extern bool (*joydriver_isTouchJoystickAvailable)(void); + +// enable/disable touch joystick +extern void (*joydriver_setTouchJoyEnabled)(bool enabled); + +// set the joystick button parameters (7bit ASCII characters or MOUSETEXT values) +extern void (*joydriver_setTouchButtonValues)(char button0Val, char button1Val); + +// set the axis type +extern void (*joydriver_setTouchAxisType)(touchjoy_axis_type_t axisType); + +// set the axis button parameters (7bit ASCII characters or MOUSETEXT values) +extern void (*joydriver_setTouchAxisValues)(char up, char left, char right, char down); + +#endif // TOUCH_JOYSTICK + #endif // whole file diff --git a/src/video/Basic.fsh b/src/video/Basic.fsh index b056c61e..7da5b57f 100644 --- a/src/video/Basic.fsh +++ b/src/video/Basic.fsh @@ -30,21 +30,37 @@ uniform sampler2D framebufferTexture; // Floating message uniform sampler2D messageTexture; +// Joystick axis +uniform sampler2D axisTexture; + +// Joystick buttons +uniform sampler2D buttonTexture; + #if __VERSION__ >= 140 #define OUTPUT_TEXTURE(TEX) \ vec4 tex = texture(TEX, varTexcoord.st, 0.0); \ - fragColor = vec4(tex.r, tex.g, tex.b, 1.0*aValue) + fragColor = vec4(tex.r, tex.g, tex.b, tex.a*aValue) +#define OUTPUT_RED() \ + fragColor = vec4(1.0, 0.0, 0.0, 1.0) #else #define OUTPUT_TEXTURE(TEX) \ vec4 tex = texture2D(TEX, varTexcoord.st, 0.0); \ - gl_FragColor = vec4(tex.r, tex.g, tex.b, 1.0*aValue) + gl_FragColor = vec4(tex.r, tex.g, tex.b, tex.a*aValue) +#define OUTPUT_RED() \ + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) #endif void main(void) { - if (tex2Use == 1) { - OUTPUT_TEXTURE(messageTexture); - } else { + if (tex2Use == 0) { OUTPUT_TEXTURE(framebufferTexture); + } else if (tex2Use == 1) { + OUTPUT_TEXTURE(messageTexture); + } else if (tex2Use == 2) { + OUTPUT_TEXTURE(axisTexture); + } else if (tex2Use == 3) { + OUTPUT_TEXTURE(buttonTexture); + } else { + //OUTPUT_RED(); -- WTF is this failing? } } diff --git a/src/video/glanimation.c b/src/video/glanimation.c index 8a1bcf60..b1d88749 100644 --- a/src/video/glanimation.c +++ b/src/video/glanimation.c @@ -74,3 +74,11 @@ void gldriver_animation_render(void) { } } +void gldriver_animation_reshape(int w, int h) { + glanim_array_node_t *p = animations; + while (p) { + p->anim->reshape(w, h); + p = p->next; + } +} + diff --git a/src/video/glanimation.h b/src/video/glanimation.h index f6300ec0..7bbc9177 100644 --- a/src/video/glanimation.h +++ b/src/video/glanimation.h @@ -14,14 +14,11 @@ #ifndef _GLANIMATION_H_ #define _GLANIMATION_H_ -typedef void (*glanim_ctor_fn)(void); -typedef void (*glanim_dtor_fn)(void); -typedef void (*glanim_render_fn)(void); - typedef struct glanim_t { - glanim_ctor_fn ctor; - glanim_dtor_fn dtor; - glanim_render_fn render; + void (*ctor)(void); + void (*dtor)(void); + void (*render)(void); + void (*reshape)(int w, int h); } glanim_t; // register an animation @@ -36,5 +33,8 @@ void gldriver_animation_destroy(void); // renders the animation void gldriver_animation_render(void); +// renders the animation +void gldriver_animation_reshape(int w, int h); + #endif diff --git a/src/video/glcpuanim.c b/src/video/glcpuanim.c index e232a650..4f317f3c 100644 --- a/src/video/glcpuanim.c +++ b/src/video/glcpuanim.c @@ -48,10 +48,10 @@ static glanim_t cpuMessageAnimation = { 0 }; static void _create_message_model(void) { const GLfloat messageObj_positions[] = { - -0.3, -0.3, -0.1, 1.0, - 0.3, -0.3, -0.1, 1.0, - -0.3, 0.3, -0.1, 1.0, - 0.3, 0.3, -0.1, 1.0, + -0.3, -0.3, -0.0625, 1.0, + 0.3, -0.3, -0.0625, 1.0, + -0.3, 0.3, -0.0625, 1.0, + 0.3, 0.3, -0.0625, 1.0, }; const GLfloat messageObj_texcoords[] = { 0.0, 1.0, @@ -349,6 +349,10 @@ static void cpuanim_render(void) { _render_message_object(alpha, cpuMessageObjVAOName, cpuMessageObjPosBufferName, cpuMessageObjTexcoordBufferName, cpuMessageObjElementBufferName); } +static void cpuanim_reshape(int w, int h) { + // no-op +} + static void cpuanim_show(void) { if (!animation_subsystem_functional) { return; @@ -403,6 +407,7 @@ static void _init_glcpuanim(void) { cpuMessageAnimation.ctor = &cpuanim_init; cpuMessageAnimation.dtor = &cpuanim_destroy; cpuMessageAnimation.render = &cpuanim_render; + cpuMessageAnimation.reshape = &cpuanim_reshape; gldriver_register_animation(&cpuMessageAnimation); } diff --git a/src/video/gltouchjoy.c b/src/video/gltouchjoy.c index 4ec9bdba..d6a2f72b 100644 --- a/src/video/gltouchjoy.c +++ b/src/video/gltouchjoy.c @@ -9,11 +9,802 @@ * */ -// GL touch joystick -- Created by Aaron Culliney - #include "common.h" #include "video/glvideo.h" -void gldriver_joystick_reset(void) { +#define MAX_FINGERS 32 +#define TOUCHJOY_TEMPLATE_COLS 5 +#define TOUCHJOY_TEMPLATE_ROWS 5 + +// HACK NOTE FIXME TODO : interpolated pixel adjustment still necessary ... +#define TOUCHJOY_FB_WIDTH ((TOUCHJOY_TEMPLATE_COLS * FONT80_WIDTH_PIXELS) + INTERPOLATED_PIXEL_ADJUSTMENT) +#define TOUCHJOY_FB_HEIGHT (TOUCHJOY_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS) + +enum { + TOUCHED_NONE = -1, + TOUCHED_BUTTON0 = 0, + TOUCHED_BUTTON1, + TOUCHED_BOTH, +}; + +static bool isAvailable = false; +static bool isEnabled = true; +static bool isVisible = true; + +static char axisTemplate[TOUCHJOY_TEMPLATE_ROWS][TOUCHJOY_TEMPLATE_COLS+1] = { + "|||||", + "| @ |", + "|@+@|", + "| @ |", + "|||||", +}; + +static char buttonTemplate[TOUCHJOY_TEMPLATE_ROWS][TOUCHJOY_TEMPLATE_COLS+1] = { + " |||", + " |@|", + "|||||", + "|@|+ ", + "||| ", +}; + +static const GLfloat model_width = 0.5; +static const GLfloat model_height = 0.5; +static glanim_t touchjoyAnimation = { 0 }; + +static int viewportWidth = 0; +static int viewportHeight = 0; + +// touch axis variables + +static demoModel *touchAxisObjModel = NULL; +static GLuint touchAxisObjVAOName = UNINITIALIZED_GL; +static GLuint touchAxisObjTextureName = UNINITIALIZED_GL; +static GLuint touchAxisObjPosBufferName = UNINITIALIZED_GL; +static GLuint touchAxisObjTexcoordBufferName = UNINITIALIZED_GL; +static GLuint touchAxisObjElementBufferName = UNINITIALIZED_GL; + +static int touchAxisObjModelScreenX = 0; +static int touchAxisObjModelScreenY = 0; +static int touchAxisObjModelScreenXMax = 0; +static int touchAxisObjModelScreenYMax = 0; + +static uint8_t touchAxisObjFB[TOUCHJOY_FB_WIDTH * TOUCHJOY_FB_HEIGHT] = { 0 }; +static uint8_t touchAxisObjPixels[TOUCHJOY_FB_WIDTH * TOUCHJOY_FB_HEIGHT * 4] = { 0 };// RGBA8888 +static bool axisTextureDirty = true; + +static int trackingAxisIndex = TOUCHED_NONE; + +// button object variables + +static demoModel *buttonObjModel = NULL; +static GLuint buttonObjVAOName = UNINITIALIZED_GL; +static GLuint buttonObjTextureName = UNINITIALIZED_GL; +static GLuint buttonObjPosBufferName = UNINITIALIZED_GL; +static GLuint buttonObjTexcoordBufferName = UNINITIALIZED_GL; +static GLuint buttonObjElementBufferName = UNINITIALIZED_GL; + +static int buttonObj0ScreenX = 0; +static int buttonObj0ScreenY = 0; +static int buttonObj0ScreenXMax = 0; +static int buttonObj0ScreenYMax = 0; + +static int buttonObj1ScreenX = 0; +static int buttonObj1ScreenY = 0; +static int buttonObj1ScreenXMax = 0; +static int buttonObj1ScreenYMax = 0; + +static uint8_t buttonObjFB[TOUCHJOY_FB_WIDTH * TOUCHJOY_FB_HEIGHT] = { 0 }; +static uint8_t buttonObjPixels[TOUCHJOY_FB_WIDTH * TOUCHJOY_FB_HEIGHT * 4] = { 0 };// RGBA8888 +static bool buttonTextureDirty = true; + +static int trackingButton0Index = TOUCHED_NONE; +static int trackingButton1Index = TOUCHED_NONE; +static int trackingButtonBothIndex = TOUCHED_NONE; + +// configurables for current touchjoy + +static uint8_t button0Char = MOUSETEXT_OPENAPPLE; +static uint8_t button1Char = MOUSETEXT_CLOSEDAPPLE; +static touchjoy_axis_type_t touchjoy_axisType = AXIS_EMULATED_DEVICE; +static uint8_t upChar = MOUSETEXT_UP; +static uint8_t leftChar = MOUSETEXT_LEFT; +static uint8_t rightChar = MOUSETEXT_RIGHT; +static uint8_t downChar = MOUSETEXT_DOWN; + +// ---------------------------------------------------------------------------- + +static demoModel *_create_model(GLfloat skew_x, GLfloat skew_y) { + + /* 2...3 + * . + * . + * . + * 0...1 + */ + + const GLfloat obj_positions[] = { + skew_x, skew_y, -0.03125, 1.0, + skew_x+model_width, skew_y, -0.03125, 1.0, + skew_x, skew_y+model_height, -0.03125, 1.0, + skew_x+model_width, skew_y+model_height, -0.03125, 1.0, + }; + const GLfloat obj_texcoords[] = { + 0.0, 1.0, + 1.0, 1.0, + 0.0, 0.0, + 1.0, 0.0, + }; + const GLushort indices[] = { + 0, 1, 2, 2, 1, 3 + }; + + demoModel *obj = calloc(1, sizeof(demoModel)); + obj->numVertices = 4; + obj->numElements = 6; + + obj->positions = malloc(sizeof(obj_positions)); + memcpy(obj->positions, &obj_positions[0], sizeof(obj_positions)); + obj->positionType = GL_FLOAT; + obj->positionSize = 4; // x,y,z coordinates + obj->positionArraySize = sizeof(obj_positions); + + obj->texcoords = malloc(sizeof(obj_texcoords)); + memcpy(obj->texcoords, &obj_texcoords[0], sizeof(obj_texcoords)); + obj->texcoordType = GL_FLOAT; + obj->texcoordSize = 2; // s,t coordinates + obj->texcoordArraySize = sizeof(obj_texcoords); + + obj->normals = NULL; + obj->normalType = GL_NONE; + obj->normalSize = GL_NONE; + obj->normalArraySize = 0; + + obj->elements = malloc(sizeof(indices)); + memcpy(obj->elements, &indices[0], sizeof(indices)); + obj->elementType = GL_UNSIGNED_SHORT; + obj->elementArraySize = sizeof(indices); + + return obj; } +static void _create_VAO_VBOs(const demoModel *model, GLuint *vaoName, GLuint *posBufferName, GLuint *texcoordBufferName, GLuint *elementBufferName) { + + // Create a vertex array object (VAO) to cache model parameters +#if USE_VAO + glGenVertexArrays(1, vaoName); + glBindVertexArray(*vaoName); +#endif + + // Create a vertex buffer object (VBO) to store positions and load data + glGenBuffers(1, posBufferName); + glBindBuffer(GL_ARRAY_BUFFER, *posBufferName); + glBufferData(GL_ARRAY_BUFFER, model->positionArraySize, model->positions, GL_STATIC_DRAW); + +#if USE_VAO + // Enable the position attribute for this VAO + glEnableVertexAttribArray(POS_ATTRIB_IDX); + + // Get the size of the position type so we can set the stride properly + GLsizei posTypeSize = _get_gl_type_size(model->positionType); + + // Set up parmeters for position attribute in the VAO including, + // size, type, stride, and offset in the currenly bound VAO + // This also attaches the position VBO to the VAO + glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) + model->positionSize, // How many elements are there per position? + model->positionType, // What is the type of this data? + GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) + model->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)? + 0); // What is the offset in the VBO to the position data? +#endif + + if (model->texcoords) { + // Create a VBO to store texcoords + glGenBuffers(1, texcoordBufferName); + glBindBuffer(GL_ARRAY_BUFFER, *texcoordBufferName); + + // Allocate and load texcoord data into the VBO + glBufferData(GL_ARRAY_BUFFER, model->texcoordArraySize, model->texcoords, GL_STATIC_DRAW); + +#if USE_VAO + // Enable the texcoord attribute for this VAO + glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX); + + // Get the size of the texcoord type so we can set the stride properly + GLsizei texcoordTypeSize = _get_gl_type_size(model->texcoordType); + + // Set up parmeters for texcoord attribute in the VAO including, + // size, type, stride, and offset in the currenly bound VAO + // This also attaches the texcoord VBO to VAO + glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) + model->texcoordSize, // How many elements are there per texture coord? + model->texcoordType, // What is the type of this data in the array? + GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types) + model->texcoordSize*texcoordTypeSize, // What is the stride (i.e. bytes between texcoords)? + 0); // What is the offset in the VBO to the texcoord data? +#endif + } + + // Create a VBO to vertex array elements + // This also attaches the element array buffer to the VAO + glGenBuffers(1, elementBufferName); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *elementBufferName); + + // Allocate and load vertex array element data into VBO + glBufferData(GL_ELEMENT_ARRAY_BUFFER, model->elementArraySize, model->elements, GL_STATIC_DRAW); + + GL_ERRLOG("gltouchjoy finished creating VAO/VBOs"); +} + +static void _destroy_VAO_VBOs(GLuint vaoName, GLuint posBufferName, GLuint texcoordBufferName, GLuint elementBufferName) { + + // Bind the VAO so we can get data from it +#if USE_VAO + glBindVertexArray(vaoName); + + // For every possible attribute set in the VAO + for (GLuint index = 0; index < 16; index++) { + // Get the VBO set for that attibute + GLuint bufName = 0; + glGetVertexAttribiv(index , GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint*)&bufName); + + // If there was a VBO set... + if (bufName) { + //...delete the VBO + glDeleteBuffers(1, &bufName); + } + } + + // Get any element array VBO set in the VAO + { + GLuint bufName = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint*)&bufName); + + // If there was a element array VBO set in the VAO + if (bufName) { + //...delete the VBO + glDeleteBuffers(1, &bufName); + } + } + + // Finally, delete the VAO + glDeleteVertexArrays(1, &vaoName); +#else + glDeleteBuffers(1, &posBufferName); + glDeleteBuffers(1, &texcoordBufferName); + glDeleteBuffers(1, &elementBufferName); +#endif + + GL_ERRLOG("gltouchjoy destroying VAO/VBOs"); +} + +static GLuint _create_texture(GLvoid *pixels) { + GLuint texName = UNINITIALIZED_GL; + + // Create a texture object to apply to model + glGenTextures(1, &texName); + glBindTexture(GL_TEXTURE_2D, texName); + + // Set up filter and wrap modes for this texture object + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // Indicate that pixel rows are tightly packed + // (defaults to stride of 4 which is kind of only good for + // RGBA or FLOAT data types) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // register texture with OpenGL + glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, TOUCHJOY_FB_WIDTH, TOUCHJOY_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + GL_ERRLOG("gltouchjoy texture"); + + return texName; +} + +static void _setup_object(char *submenu, uint8_t *fb, uint8_t *pixels) { + + // render template into indexed fb + unsigned int submenu_width = TOUCHJOY_TEMPLATE_COLS; + unsigned int submenu_height = TOUCHJOY_TEMPLATE_ROWS; + video_interface_print_submenu_centered_fb(fb, submenu_width, submenu_height, submenu, submenu_width, submenu_height); + + // generate RGBA_8888 from indexed color + unsigned int count = TOUCHJOY_FB_WIDTH * TOUCHJOY_FB_HEIGHT; + for (unsigned int i=0, j=0; ipositions); + unsigned int stride = model->positionSize; + unsigned int len = model->positionArraySize/_get_gl_type_size(model->positionType); + for (unsigned int i=0; i < len; i += stride) { + float x = (positions[i] + 1.f) / 2.f; + if (x < x0) { + x0 = x; + } + if (x > x1) { + x1 = x; + } + float y = (positions[i+1] + 1.f) / 2.f; + LOG("\tmodel x:%f, y:%f", x, y); + if (y < y0) { + y0 = y; + } + if (y > y1) { + y1 = y; + } + } +#endif + + // OpenGL screen origin is bottom-left (Android is top-left) + float yFlip0 = viewportHeight - (y1 * viewportHeight); + float yFlip1 = viewportHeight - (y0 * viewportHeight); + + screenCoords[0] = x0 * viewportWidth; + screenCoords[1] = yFlip0; + screenCoords[2] = x1 * viewportWidth; + screenCoords[3] = yFlip1; +} + +static void gltouchjoy_init(void) { + LOG("gltouchjoy_init ..."); + + mdlDestroyModel(touchAxisObjModel); + mdlDestroyModel(buttonObjModel); + + touchAxisObjModel = _create_model(-1.05, -1.0); + touchAxisObjVAOName = UNINITIALIZED_GL; + touchAxisObjPosBufferName = UNINITIALIZED_GL; + touchAxisObjTexcoordBufferName = UNINITIALIZED_GL; + touchAxisObjElementBufferName = UNINITIALIZED_GL; + _create_VAO_VBOs(touchAxisObjModel, &touchAxisObjVAOName, &touchAxisObjPosBufferName, &touchAxisObjTexcoordBufferName, &touchAxisObjElementBufferName); + if (touchAxisObjPosBufferName == UNINITIALIZED_GL || touchAxisObjTexcoordBufferName == UNINITIALIZED_GL || touchAxisObjElementBufferName == UNINITIALIZED_GL) + { + LOG("gltouchjoy not initializing axis"); + return; + } + + touchAxisObjTextureName = _create_texture(touchAxisObjPixels); + if (touchAxisObjTextureName == UNINITIALIZED_GL) { + LOG("gltouchjoy not initializing axis: texture error"); + return; + } + _setup_axis_object(); + + float screenCoords[4] = { 0 }; + + _model_to_screen(screenCoords, touchAxisObjModel); + touchAxisObjModelScreenX = (int)screenCoords[0]; + touchAxisObjModelScreenY = (int)screenCoords[1]; + touchAxisObjModelScreenXMax = (int)screenCoords[2]; + touchAxisObjModelScreenYMax = (int)screenCoords[3]; + LOG("axis screen coords: [%d,%d] -> [%d,%d]", touchAxisObjModelScreenX, touchAxisObjModelScreenY, touchAxisObjModelScreenXMax, touchAxisObjModelScreenYMax); + + // button object + + buttonObjModel = _create_model(1.05-model_width, -1.0); + buttonObjVAOName = UNINITIALIZED_GL; + buttonObjPosBufferName = UNINITIALIZED_GL; + buttonObjTexcoordBufferName = UNINITIALIZED_GL; + buttonObjElementBufferName = UNINITIALIZED_GL; + _create_VAO_VBOs(buttonObjModel, &buttonObjVAOName, &buttonObjPosBufferName, &buttonObjTexcoordBufferName, &buttonObjElementBufferName); + if (buttonObjPosBufferName == UNINITIALIZED_GL || buttonObjTexcoordBufferName == UNINITIALIZED_GL || buttonObjElementBufferName == UNINITIALIZED_GL) + { + LOG("gltouchjoy not initializing buttons"); + return; + } + + buttonObjTextureName = _create_texture(buttonObjPixels); + if (buttonObjTextureName == UNINITIALIZED_GL) { + LOG("not initializing buttons: texture error"); + return; + } + _setup_button_object(); + + // NOTE : button model is a composite of both button 0 and button 1 (with ability to press both with one touch) + + _model_to_screen(screenCoords, buttonObjModel); + + int button0W = ((int)screenCoords[2] - (int)screenCoords[0]) >>1; + buttonObj0ScreenX = (int)screenCoords[0]+button0W; + buttonObj0ScreenY = (int)screenCoords[1]; + buttonObj0ScreenXMax = (int)screenCoords[2]; + buttonObj0ScreenYMax = (int)screenCoords[3]; + LOG("button 0 screen coords: [%d,%d] -> [%d,%d]", buttonObj0ScreenX, buttonObj0ScreenY, buttonObj0ScreenXMax, buttonObj0ScreenYMax); + + int button1H = ((int)screenCoords[3] - (int)screenCoords[1]) >>1; + buttonObj1ScreenX = (int)screenCoords[0]; + buttonObj1ScreenY = (int)screenCoords[1]+button1H; + buttonObj1ScreenXMax = (int)screenCoords[2]; + buttonObj1ScreenYMax = (int)screenCoords[3]; + LOG("button 1 screen coords: [%d,%d] -> [%d,%d]", buttonObj1ScreenX, buttonObj1ScreenY, buttonObj1ScreenXMax, buttonObj1ScreenYMax); + + isAvailable = true; +} + +static void gltouchjoy_destroy(void) { + LOG("gltouchjoy_destroy ..."); + if (!isAvailable) { + return; + } + + isAvailable = false; + + glDeleteTextures(1, &touchAxisObjTextureName); + touchAxisObjTextureName = UNINITIALIZED_GL; + _destroy_VAO_VBOs(touchAxisObjVAOName, touchAxisObjPosBufferName, touchAxisObjTexcoordBufferName, touchAxisObjElementBufferName); + touchAxisObjVAOName = UNINITIALIZED_GL; + touchAxisObjPosBufferName = UNINITIALIZED_GL; + touchAxisObjTexcoordBufferName = UNINITIALIZED_GL; + touchAxisObjElementBufferName = UNINITIALIZED_GL; + mdlDestroyModel(touchAxisObjModel); + touchAxisObjModel = NULL; + + glDeleteTextures(1, &buttonObjTextureName); + buttonObjTextureName = UNINITIALIZED_GL; + _destroy_VAO_VBOs(buttonObjVAOName, buttonObjPosBufferName, buttonObjTexcoordBufferName, buttonObjElementBufferName); + buttonObjVAOName = UNINITIALIZED_GL; + buttonObjPosBufferName = UNINITIALIZED_GL; + buttonObjTexcoordBufferName = UNINITIALIZED_GL; + buttonObjElementBufferName = UNINITIALIZED_GL; + mdlDestroyModel(buttonObjModel); + buttonObjModel = NULL; +} + +static void _render_object(demoModel *model, GLuint vaoName, GLuint posBufferName, GLuint texcoordBufferName, GLuint elementBufferName) { + + // Bind our vertex array object +#if USE_VAO + glBindVertexArray(vaoName); +#else + glBindBuffer(GL_ARRAY_BUFFER, posBufferName); + + GLsizei posTypeSize = _get_gl_type_size(model->positionType); + GLsizei texcoordTypeSize = _get_gl_type_size(model->texcoordType); + + // Set up parmeters for position attribute in the VAO including, size, type, stride, and offset in the currenly + // bound VAO This also attaches the position VBO to the VAO + glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) + model->positionSize, // How many elements are there per position? + model->positionType, // What is the type of this data? + GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types) + model->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)? + 0); // What is the offset in the VBO to the position data? + glEnableVertexAttribArray(POS_ATTRIB_IDX); + + // Set up parmeters for texcoord attribute in the VAO including, size, type, stride, and offset in the currenly + // bound VAO This also attaches the texcoord VBO to VAO + glBindBuffer(GL_ARRAY_BUFFER, texcoordBufferName); + glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram) + model->texcoordSize, // How many elements are there per texture coord? + model->texcoordType, // What is the type of this data in the array? + GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types) + model->texcoordSize*texcoordTypeSize,// What is the stride (i.e. bytes between texcoords)? + 0); // What is the offset in the VBO to the texcoord data? + glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferName); +#endif + + glUniform1f(alphaValue, 1.0); + + // Draw the object + glDrawElements(GL_TRIANGLES, model->numElements, model->elementType, 0); + GL_ERRLOG("gltouchjoy render"); +} + +static void gltouchjoy_render(void) { + if (!isAvailable) { + return; + } + if (!isEnabled) { + return; + } + if (!isVisible) { + return; + } + + glViewport(0, 0, viewportWidth, viewportHeight); + + glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_AXIS); + glBindTexture(GL_TEXTURE_2D, touchAxisObjTextureName); + if (axisTextureDirty) { + axisTextureDirty = false; + glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, TOUCHJOY_FB_WIDTH, TOUCHJOY_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)touchAxisObjPixels); + } + glUniform1i(uniformTex2Use, TEXTURE_ID_TOUCHJOY_AXIS); + _render_object(touchAxisObjModel, touchAxisObjVAOName, touchAxisObjPosBufferName, touchAxisObjTexcoordBufferName, touchAxisObjElementBufferName); + + glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_BUTTON); + glBindTexture(GL_TEXTURE_2D, buttonObjTextureName); + if (buttonTextureDirty) { + buttonTextureDirty = false; + glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, TOUCHJOY_FB_WIDTH, TOUCHJOY_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)buttonObjPixels); + } + glUniform1i(uniformTex2Use, TEXTURE_ID_TOUCHJOY_BUTTON); + _render_object(buttonObjModel, buttonObjVAOName, buttonObjPosBufferName, buttonObjTexcoordBufferName, buttonObjElementBufferName); +} + +static void gltouchjoy_reshape(int w, int h) { + LOG("gltouchjoy_reshape(%d, %d)", w, h); + + if (w > viewportWidth) { + viewportWidth = w; + } + if (h > viewportHeight) { + viewportHeight = h; + } +} + +static void gltouchjoy_resetJoystick(void) { + // no-op +} + +static inline bool _is_point_on_axis(float x, float y) { + return (x >= touchAxisObjModelScreenX && x <= touchAxisObjModelScreenXMax && y >= touchAxisObjModelScreenY && y <= touchAxisObjModelScreenYMax); +} + +static inline bool _is_point_on_button0(float x, float y) { + return (x >= buttonObj0ScreenX && x <= buttonObj0ScreenXMax && y >= buttonObj0ScreenY && y <= buttonObj0ScreenYMax); +} + +static inline bool _is_point_on_button1(float x, float y) { + return (x >= buttonObj1ScreenX && x <= buttonObj1ScreenXMax && y >= buttonObj1ScreenY && y <= buttonObj1ScreenYMax); +} + +static inline void _move_joystick_axis(float x, float y) { + + int buttonW = touchAxisObjModelScreenXMax - touchAxisObjModelScreenX; + float halfJoyX = buttonW/2.f; + int buttonH = touchAxisObjModelScreenYMax - touchAxisObjModelScreenY; + float halfJoyY = buttonH/2.f; + + float x_mod = 256.f/buttonW; + float y_mod = 256.f/buttonH; + + float x0 = (x - touchAxisObjModelScreenX) * x_mod; + float y0 = (y - touchAxisObjModelScreenY) * y_mod; + + joy_x = (uint16_t)x0; + if (joy_x > 0xff) { + joy_x = 0xff; + } + joy_y = (uint16_t)y0; + if (joy_y > 0xff) { + joy_y = 0xff; + } + + LOG("\tjoystick : (%d,%d)", joy_x, joy_y); +} + +static bool gltouchjoy_onTouchEvent(joystick_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) { + + if (!isAvailable) { + return false; + } + if (!isEnabled) { + return false; + } + + bool axisConsumed = false; + bool buttonConsumed = false; + + float x = x_coords[pointer_idx]; + float y = y_coords[pointer_idx]; + + switch (action) { + case TOUCH_DOWN: + LOG("---TOUCH DOWN"); + case TOUCH_POINTER_DOWN: + if (action == TOUCH_POINTER_DOWN) { + LOG("---TOUCH POINTER DOWN"); + } + if (_is_point_on_axis(x, y)) { + if (trackingAxisIndex >= 0) { + // already tracking a different pointer for the axis ... + axisConsumed = true; + trackingAxisIndex = pointer_idx; + LOG("\taxis event : saw %d but already tracking %d", pointer_idx, trackingAxisIndex); + _move_joystick_axis(x, y); + } else { + axisConsumed = true; + trackingAxisIndex = pointer_idx; + LOG("\taxis event : begin tracking %d", trackingAxisIndex); + _move_joystick_axis(x, y); + } + } else { + bool isOn0 = _is_point_on_button0(x, y); + bool isOn1 = _is_point_on_button1(x, y); + if (isOn0 && isOn1) { + trackingButton0Index = TOUCHED_NONE; + trackingButton1Index = TOUCHED_NONE; + trackingButtonBothIndex = pointer_idx; + buttonConsumed = true; + joy_button0 = 0x80; + joy_button1 = 0x80; + LOG("\tbutton0&1 event : index:%d joy_button0:%02X joy_button1:%02X", pointer_idx, joy_button0, joy_button1); + } else if (isOn0) { + trackingButton0Index = pointer_idx; + trackingButton1Index = TOUCHED_NONE; + trackingButtonBothIndex = TOUCHED_NONE; + buttonConsumed = true; + joy_button0 = 0x80; + joy_button1 = 0x0; + LOG("\tbutton0 event : index:%d joy_button0:%02X", pointer_idx, joy_button0); + } else if (isOn1) { + trackingButton0Index = TOUCHED_NONE; + trackingButton1Index = pointer_idx; + trackingButtonBothIndex = TOUCHED_NONE; + buttonConsumed = true; + joy_button0 = 0x0; + joy_button1 = 0x80; + LOG("\tbutton1 event : index:%d joy_button1:%02X", pointer_idx, joy_button1); + } + } + break; + + case TOUCH_MOVE: + LOG("---TOUCH MOVE"); + if (trackingAxisIndex >= 0) { + axisConsumed = true; + x = x_coords[trackingAxisIndex]; + y = y_coords[trackingAxisIndex]; + LOG("\t...tracking axis:%d (count:%d)", trackingAxisIndex, pointer_count); + _move_joystick_axis(x, y); + } + break; + + case TOUCH_UP: + LOG("---TOUCH UP"); + case TOUCH_POINTER_UP: + if (action == TOUCH_POINTER_UP) { + LOG("---TOUCH POINTER UP"); + } + if (pointer_idx == trackingAxisIndex) { + axisConsumed = true; + trackingAxisIndex = TOUCHED_NONE; + joy_x = HALF_JOY_RANGE; + joy_y = HALF_JOY_RANGE; + LOG("\taxis went up"); + } else if (pointer_idx == trackingButtonBothIndex) { + buttonConsumed = true; + trackingButtonBothIndex = TOUCHED_NONE; + joy_button0 = 0x0; + joy_button1 = 0x0; + LOG("\tbuttons up joy_button0:%02X joy_button1:%02X", joy_button0, joy_button1); + } else if (pointer_idx == trackingButton0Index) { + buttonConsumed = true; + trackingButton0Index = TOUCHED_NONE; + joy_button0 = 0x0; + LOG("\tbutton0 up joy_button0:%02X", joy_button0); + } else if (pointer_idx == trackingButton1Index) { + buttonConsumed = true; + trackingButton1Index = TOUCHED_NONE; + joy_button1 = 0x0; + LOG("\tbutton1 up joy_button1:%02X", joy_button1); + } + break; + + case TOUCH_CANCEL: + LOG("---TOUCH CANCEL"); + trackingAxisIndex = TOUCHED_NONE; + trackingButton0Index = TOUCHED_NONE; + trackingButton1Index = TOUCHED_NONE; + trackingButtonBothIndex = TOUCHED_NONE; + joy_button0 = 0x0; + joy_button1 = 0x0; + joy_x = HALF_JOY_RANGE; + joy_y = HALF_JOY_RANGE; + break; + + default: + LOG("gltouchjoy saw unknown touch event : %d", action); + break; + } + + if (axisConsumed || buttonConsumed) { + LOG("\tconsumed event"); + return true; + } else { + LOG("\tDID NOT consume..."); + return false; + } +} + +static bool gltouchjoy_isTouchJoystickAvailable(void) { + return isAvailable; +} + +static void gltouchjoy_setTouchJoyEnabled(bool enabled) { + isEnabled = enabled; +} + +void gltouchjoy_setTouchButtonValues(char button0Val, char button1Val) { + button0Char = button0Val; + button1Char = button1Val; + _setup_button_object(); +} + +void gltouchjoy_setTouchAxisType(touchjoy_axis_type_t axisType) { + touchjoy_axisType = axisType; + _setup_axis_object(); +} + +void gltouchjoy_setTouchAxisValues(char up, char left, char right, char down) { + upChar = up; + leftChar = left; + rightChar = right; + downChar = down; + if (touchjoy_axisType == AXIS_EMULATED_KEYBOARD) { + _setup_axis_object(); + } +} + +__attribute__((constructor)) +static void _init_gltouchjoy(void) { + LOG("Registering OpenGL software touch joystick"); + + joydriver_onTouchEvent = &gltouchjoy_onTouchEvent; + joydriver_isTouchJoystickAvailable = &gltouchjoy_isTouchJoystickAvailable; + joydriver_setTouchJoyEnabled = &gltouchjoy_setTouchJoyEnabled; + joydriver_setTouchButtonValues = &gltouchjoy_setTouchButtonValues; + joydriver_setTouchAxisType = &gltouchjoy_setTouchAxisType; + joydriver_setTouchAxisValues = &gltouchjoy_setTouchAxisValues; + + touchjoyAnimation.ctor = &gltouchjoy_init; + touchjoyAnimation.dtor = &gltouchjoy_destroy; + touchjoyAnimation.render = &gltouchjoy_render; + touchjoyAnimation.reshape = &gltouchjoy_reshape; + gldriver_register_animation(&touchjoyAnimation); +} + +void gldriver_joystick_reset(void) { +#warning FIXME +#warning TODO +#warning expunge +#warning this +#warning API +#warning ... +} diff --git a/src/video/glvideo.c b/src/video/glvideo.c index 0165e1e9..6a018350 100644 --- a/src/video/glvideo.c +++ b/src/video/glvideo.c @@ -476,6 +476,22 @@ static GLuint _build_program(demoSource *vertexSource, demoSource *fragmentSourc glUniform1i(messageSamplerLoc, TEXTURE_ID_MESSAGE); } +#if TOUCH_JOYSTICK + GLint axisSamplerLoc = glGetUniformLocation(prgName, "axisTexture"); + if (axisSamplerLoc < 0) { + LOG("OOPS, no axisSamplerLoc shader : %d", axisSamplerLoc); + } else { + glUniform1i(axisSamplerLoc, TEXTURE_ID_TOUCHJOY_AXIS); + } + + GLint buttonSamplerLoc = glGetUniformLocation(prgName, "buttonTexture"); + if (buttonSamplerLoc < 0) { + LOG("OOPS, no buttonSamplerLoc shader : %d", buttonSamplerLoc); + } else { + glUniform1i(buttonSamplerLoc, TEXTURE_ID_TOUCHJOY_BUTTON); + } +#endif + uniformMVPIdx = glGetUniformLocation(prgName, "modelViewProjectionMatrix"); if (uniformMVPIdx < 0) { LOG("OOPS, no modelViewProjectionMatrix in shader : %d", uniformMVPIdx); @@ -799,6 +815,9 @@ static void gldriver_reshape(int w, int h) { #endif glViewport(viewportX, viewportY, viewportWidth, viewportHeight); + + // Prep any other objects/animations + gldriver_animation_reshape(w, h); } #if USE_GLUT diff --git a/src/video/glvideo.h b/src/video/glvideo.h index 9dafd89b..0ff29307 100644 --- a/src/video/glvideo.h +++ b/src/video/glvideo.h @@ -44,11 +44,19 @@ enum { enum { TEXTURE_ID_FRAMEBUFFER=0, TEXTURE_ID_MESSAGE, +#if TOUCH_JOYSTICK + TEXTURE_ID_TOUCHJOY_AXIS, + TEXTURE_ID_TOUCHJOY_BUTTON, +#endif }; enum { - TEXTURE_ACTIVE_FRAMEBUFFER = GL_TEXTURE0, - TEXTURE_ACTIVE_MESSAGE = GL_TEXTURE1, + TEXTURE_ACTIVE_FRAMEBUFFER = GL_TEXTURE0, + TEXTURE_ACTIVE_MESSAGE = GL_TEXTURE1, +#if TOUCH_JOYSTICK + TEXTURE_ACTIVE_TOUCHJOY_AXIS = GL_TEXTURE2, + TEXTURE_ACTIVE_TOUCHJOY_BUTTON = GL_TEXTURE3, +#endif }; static inline GLsizei _get_gl_type_size(GLenum type) {