diff --git a/alienblaster/project/jni/Android.mk b/alienblaster/project/jni/Android.mk index 3045168b9..dbb9a92f1 100644 --- a/alienblaster/project/jni/Android.mk +++ b/alienblaster/project/jni/Android.mk @@ -13,6 +13,7 @@ SDL_CURDIR_PATH := /data/data/de.schwardtnet.alienblaster/files # sends trackball movement events as rapid KeyDown/KeyUp events, # this will make Up/Down/Left/Right key up events with X frames delay, # so if application expects you to press and hold button it will process the event correctly. +# TODO: create a libsdl config file for that option and for key mapping/on-screen keyboard SDL_TRACKBALL_KEYUP_DELAY := 4 include $(call all-subdir-makefiles) diff --git a/alienblaster/project/jni/alienblaster/src/global.h b/alienblaster/project/jni/alienblaster/src/global.h index 94a73f64b..ce4d62078 100644 --- a/alienblaster/project/jni/alienblaster/src/global.h +++ b/alienblaster/project/jni/alienblaster/src/global.h @@ -56,8 +56,8 @@ int getRandValue( const int *choicesWeights, int nrChoices, int sumWeights=0 ); void initAllSurfaces(); // screen options -const int SCREEN_WIDTH = 320; -const int SCREEN_HEIGHT = 430; +const int SCREEN_WIDTH = 640; +const int SCREEN_HEIGHT = 480; const int BIT_DEPTH = 16; const int MAX_PLAYER_CNT = 2; diff --git a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c index 0562b561a..46ca5603a 100644 --- a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c +++ b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c @@ -36,6 +36,7 @@ #include "SDL_thread.h" #include #include +#include // for memset() #define ANDROIDAUD_DRIVER_NAME "android" diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c index 4357545c2..509e03e3d 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c @@ -55,6 +55,7 @@ #include #include #include +#include // for memset() #define ANDROIDVID_DRIVER_NAME "android" @@ -77,10 +78,9 @@ static int ANDROID_FlipHWSurface(_THIS, SDL_Surface *surface); static void ANDROID_UpdateRects(_THIS, int numrects, SDL_Rect *rects); -#define SDL_NUMMODES 3 - /* Private display data */ +#define SDL_NUMMODES 4 struct SDL_PrivateVideoData { SDL_Rect *SDL_modelist[SDL_NUMMODES+1]; }; @@ -88,19 +88,28 @@ struct SDL_PrivateVideoData { #define SDL_modelist (this->hidden->SDL_modelist) +// The device screen dimensions to draw on static int sWindowWidth = 320; static int sWindowHeight = 480; +// Pointer to in-memory video surface +static int memX = 0; +static int memY = 0; +// Offset if the mem surface is larger than device screen +static float memOffsetX = 0; +static float memOffsetY = 0; +static float memOffsetZ = 0; // Zoom ( not implemented yet, and probably will look ugly ) +// In-memory surfaces +static void * memBuffer1 = NULL; +static void * memBuffer2 = NULL; +static void * memBuffer = NULL; +// We have one Java thread drawing on GL surface, and another native C thread (typically main()) feeding it with video data +static SDL_Thread * mainThread = NULL; +// Some wicked multithreading static SDL_mutex * WaitForNativeRender = NULL; static SDL_cond * WaitForNativeRender1 = NULL; static enum { Render_State_Started, Render_State_Processing, Render_State_Finished } WaitForNativeRenderState = Render_State_Finished; -// Pointer to in-memory video surface -static int memX = 0; -static int memY = 0; -static void * memBuffer1 = NULL; -static void * memBuffer2 = NULL; -static void * memBuffer = NULL; -static SDL_Thread * mainThread = NULL; +// Some wicked GLES stuff static enum { GL_State_Init, GL_State_Ready, GL_State_Uninit, GL_State_Uninit2 } openglInitialized = GL_State_Uninit2; static GLuint texture = 0; @@ -201,7 +210,8 @@ int ANDROID_VideoInit(_THIS, SDL_PixelFormat *vformat) SDL_modelist[0]->w = sWindowWidth; SDL_modelist[0]->h = sWindowHeight; SDL_modelist[1]->w = 320; SDL_modelist[1]->h = 240; // Always available on any screen and any orientation SDL_modelist[2]->w = 320; SDL_modelist[2]->h = 200; // Always available on any screen and any orientation - SDL_modelist[3] = NULL; + SDL_modelist[3]->w = 640; SDL_modelist[3]->h = 480; // Requires accelerometer to scroll large virtual display surface + SDL_modelist[4] = NULL; WaitForNativeRender = SDL_CreateMutex(); WaitForNativeRender1 = SDL_CreateCond(); @@ -226,6 +236,9 @@ SDL_Surface *ANDROID_SetVideoMode(_THIS, SDL_Surface *current, memX = width; memY = height; + memOffsetX = 0; + memOffsetY = 0; + memOffsetZ = 0; memBuffer1 = SDL_malloc(memX * memY * (bpp / 8)); if ( ! memBuffer1 ) { @@ -451,7 +464,7 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeMouse) ( JNIEnv* env, jobject thiz, j if( action == MOUSE_DOWN || action == MOUSE_UP ) SDL_PrivateMouseButton( (action == MOUSE_DOWN) ? SDL_PRESSED : SDL_RELEASED, 1, x, y ); if( action == MOUSE_MOVE ) - SDL_PrivateMouseMotion(0, 0, x, y); + SDL_PrivateMouseMotion(0, 0, x + (int)memOffsetX, y + (int)memOffsetY); } static SDL_keysym *TranslateKey(int scancode, SDL_keysym *keysym) @@ -553,9 +566,9 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jin SDL_PrivateKeyboard( action ? SDL_PRESSED : SDL_RELEASED, TranslateKey(key, &keysym) ); } -/* Call to render the next GL frame */ +// The most wicked routine out there, all wicked multithreading and GL-ES stuff here extern void -JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz ) +JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz, jfloat accX, jfloat accY, jfloat accZ ) { // Set up an array of values to use as the sprite vertices. static GLfloat vertices[] = @@ -584,6 +597,8 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz ) static int clearColorDir = 1; int textX, textY; void * memBufferTemp; + + static float oldAccX = 0, oldAccY = 0, oldAccZ = 0, smoothMoveX = 0, smoothMoveY = 0, smoothMoveZ = 0; if( memBuffer && openglInitialized != GL_State_Uninit2 ) { @@ -660,7 +675,6 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz ) glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - // GLES extension (should be faster) texcoordsCrop[0] = 0; texcoordsCrop[1] = memY; texcoordsCrop[2] = memX; @@ -670,6 +684,11 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz ) glFinish(); SDL_free( textBuffer ); + + oldAccX = accX; + oldAccY = accY; + oldAccZ = accZ; + smoothMoveX = smoothMoveY = smoothMoveZ = 0; } else if( openglInitialized == GL_State_Uninit ) { @@ -711,10 +730,68 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz ) else memBufferTemp = memBuffer; - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, memX, memY, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBufferTemp); + if( sWindowWidth < memX || sWindowHeight < memY ) + { + // Move the large virtual surface with accelerometer + // TODO: configurable coeffs? + #define MIN(x,y) ( (x) < (y) ? (x) : (y) ) + #define SIGN(x) ( (x) > 0 ? 1 : -1 ) + // Make fast movement to move display more than slow one + float threshold = 0.1f; + if( fabs( accX - oldAccX ) > threshold ) + smoothMoveX -= (fabs(accX - oldAccX) - threshold) * SIGN(accX - oldAccX); + if( fabs( accY - oldAccY ) > threshold ) + smoothMoveY -= (fabs(accY - oldAccY) - threshold) * SIGN(accY - oldAccY); + if( fabs( accZ - oldAccZ ) > threshold ) + smoothMoveZ -= (fabs(accZ - oldAccZ) - threshold) * SIGN(accZ - oldAccZ); + oldAccX = accX; + oldAccY = accY; + oldAccZ = accZ; + + float sensitivity = 50.0f; + float maxspeed = 1.3f; + float dX = MIN( maxspeed, fabs(smoothMoveX) ) * SIGN(smoothMoveX); + float dY = MIN( maxspeed, fabs(smoothMoveY) ) * SIGN(smoothMoveY); + float dZ = MIN( maxspeed, fabs(smoothMoveZ) ) * SIGN(smoothMoveZ); + memOffsetX += dX * sensitivity; + memOffsetY += dY * sensitivity; + memOffsetZ += dZ * sensitivity; + + float dampening = 0.4f; + smoothMoveX -= dX; + smoothMoveY -= dY; + smoothMoveZ -= dZ; + + if(memOffsetX < 0) + memOffsetX = 0; + if(memOffsetX > memX - sWindowWidth) + memOffsetX = memX - sWindowWidth; + if(memOffsetY < 0) + memOffsetY = 0; + if(memOffsetY > memY - sWindowHeight) + memOffsetY = memY - sWindowHeight; + // TODO: memOffsetZ unused - add zooming? It will look ugly probably + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, memX, memY, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBufferTemp); + // The data should be contiguous, so we can only omit upper and lower invisible parts of image being copied - + // invisible parts to the left and to the right are copied, so it's not much of an optimization anyway + // I failed to do that optimisation properly anyway + //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, (int)memOffsetY, memX, sWindowHeight, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBufferTemp + ( 2 * memX * (int)memOffsetY ) ); + + texcoordsCrop[0] = (int)memOffsetX; + texcoordsCrop[1] = memY-(int)memOffsetY; + texcoordsCrop[2] = sWindowWidth; + texcoordsCrop[3] = -sWindowHeight; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, texcoordsCrop); + glDrawTexiOES(0, 0, 1, sWindowWidth, sWindowHeight); + } + else + { + // TODO: use accelerometer as joystick + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, memX, memY, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBufferTemp); + glDrawTexiOES(0, sWindowHeight-memY, 1, memX, memY); + } - //glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glDrawTexiOES(0, sWindowHeight-memY, 1, memX, memY); // GLES extension (should be faster) //glFinish(); //glFlush(); } diff --git a/alienblaster/project/src/DemoActivity.java b/alienblaster/project/src/DemoActivity.java index d1377b585..b26a51ea7 100644 --- a/alienblaster/project/src/DemoActivity.java +++ b/alienblaster/project/src/DemoActivity.java @@ -55,6 +55,8 @@ import android.media.AudioManager; import android.media.AudioFormat; import android.os.PowerManager; import android.os.Vibrator; +import android.hardware.SensorManager; +import android.hardware.SensorListener; import android.widget.TextView; import org.apache.http.client.methods.*; @@ -86,8 +88,66 @@ class LoadLibrary { } } + +// Accelerometer code partially ripped from http://karanar.net/ +class AccelerometerReader implements SensorListener { + + private long timekeeper; + + private float [] v; + + public AccelerometerReader(Activity context) { + v = new float[3]; + SensorManager sma = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + if( sma != null ) + { + timekeeper = android.os.SystemClock.uptimeMillis(); + int mask = 0; + mask |= SensorManager.SENSOR_ORIENTATION; + mask |= SensorManager.SENSOR_ACCELEROMETER; + sma.registerListener(this, mask, SensorManager.SENSOR_DELAY_GAME); + } + } + + public synchronized void onSensorChanged(int sensor, float[] values) { + if (android.os.SystemClock.uptimeMillis() < timekeeper + 20) return; + timekeeper = android.os.SystemClock.uptimeMillis(); + + if (sensor == SensorManager.SENSOR_ACCELEROMETER) { + if( values.length >= 1 ) + v[0] = values[0]; + if( values.length >= 2 ) + v[1] = values[1]; + if( values.length >= 3 ) + v[2] = values[2]; + } + + } + + public synchronized void onAccuracyChanged(int i, int i1) { + /* @todo implement method */ + } + + public synchronized float[] readAccelerometer() + { + float [] ret = new float[3]; + ret[0] = v[0]; + ret[1] = v[1]; + ret[2] = v[2]; + return ret; + }; +} + + + class DemoRenderer implements GLSurfaceView.Renderer { + public DemoRenderer(Activity context) + { + super(); + accelerometer = new AccelerometerReader(context); + } + public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInit(); } @@ -98,7 +158,8 @@ class DemoRenderer implements GLSurfaceView.Renderer { } public void onDrawFrame(GL10 gl) { - nativeRender(); + float [] f = accelerometer.readAccelerometer(); + nativeRender(f[0], f[1], f[2]); } public void exitApp() { @@ -107,16 +168,16 @@ class DemoRenderer implements GLSurfaceView.Renderer { private static native void nativeInit(); private static native void nativeResize(int w, int h); - private static native void nativeRender(); + private static native void nativeRender(float accX, float accY, float accZ); private static native void nativeDone(); - + private AccelerometerReader accelerometer = null; } class DemoGLSurfaceView extends GLSurfaceView { public DemoGLSurfaceView(Activity context) { super(context); mParent = context; - mRenderer = new DemoRenderer(); + mRenderer = new DemoRenderer(context); setRenderer(mRenderer); }