|
@@ -2,11 +2,15 @@ package de.tudarmstadt.informatik.hostage.ui.fragment.opengl;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.nio.ByteOrder;
|
|
|
+import java.nio.FloatBuffer;
|
|
|
import java.util.Scanner;
|
|
|
|
|
|
import javax.microedition.khronos.egl.EGLConfig;
|
|
|
import javax.microedition.khronos.opengles.GL10;
|
|
|
|
|
|
+import android.content.Context;
|
|
|
import android.content.res.AssetManager;
|
|
|
import android.graphics.Bitmap;
|
|
|
import android.graphics.BitmapFactory;
|
|
@@ -18,6 +22,9 @@ import android.opengl.GLUtils;
|
|
|
import android.opengl.Matrix;
|
|
|
import android.util.Log;
|
|
|
|
|
|
+import de.tudarmstadt.informatik.hostage.R;
|
|
|
+import de.tudarmstadt.informatik.hostage.ui.activity.MainActivity;
|
|
|
+
|
|
|
/**
|
|
|
* @author Fabio Arnold
|
|
|
*
|
|
@@ -52,11 +59,10 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
mBackgroundColor[2] = (float)Color.blue(color) / 255.0f;
|
|
|
}
|
|
|
private static float[] mBackgroundColor = new float[3];
|
|
|
-
|
|
|
- public static AssetManager assets = null; // needs to be set by the Activity using this class
|
|
|
|
|
|
// OpenGL data
|
|
|
- private int mProgram;
|
|
|
+ private int mAnimatedProgram;
|
|
|
+ private int mTexturedProgram;
|
|
|
private float [] mModelview;
|
|
|
private float [] mProjection;
|
|
|
private float [] mMVP;
|
|
@@ -65,6 +71,10 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
private AnimatedMesh beeMesh = null;
|
|
|
private int androidTexture;
|
|
|
private int beeTexture;
|
|
|
+ private int speechBubbleTexture;
|
|
|
+ private int mQuadVertexBuffer;
|
|
|
+
|
|
|
+ private GLFont font = null;
|
|
|
|
|
|
// threat state
|
|
|
private static ThreatLevel mNextThreatLevel = ThreatLevel.NO_THREAT;
|
|
@@ -88,7 +98,8 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
|
|
|
GLES20.glEnable(GLES20.GL_CULL_FACE);
|
|
|
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
|
|
|
-
|
|
|
+
|
|
|
+ AssetManager assets = MainActivity.getInstance().getAssets();
|
|
|
try {
|
|
|
InputStream is = assets.open("meshes/android.amh");
|
|
|
androidMesh = new AnimatedMesh(is);
|
|
@@ -112,6 +123,12 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
Log.e("gl", "Couldn't open bee mesh");
|
|
|
}
|
|
|
beeTexture = loadTexture("textures/bee-tex.png");
|
|
|
+ speechBubbleTexture = loadTexture("textures/speech-bubble.png");
|
|
|
+ int buffers[] = new int[1];
|
|
|
+ GLES20.glGenBuffers(1, buffers, 0); // buffer names
|
|
|
+ mQuadVertexBuffer = buffers[0];
|
|
|
+
|
|
|
+ font = new GLFont("fonts/laCartoonerie.png", "fonts/laCartoonerie.bin");
|
|
|
|
|
|
mModelview = new float[16];
|
|
|
Matrix.setIdentityM(mModelview, 0);
|
|
@@ -130,38 +147,49 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
} catch (IOException e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
- mProgram = loadProgram(vertexSource, fragmentSource);
|
|
|
+ mAnimatedProgram = loadProgram(vertexSource, fragmentSource);
|
|
|
+
|
|
|
+ mTexturedProgram = loadProgram(
|
|
|
+ "uniform vec2 resolution;" // vertex
|
|
|
+ + "attribute vec2 position;"
|
|
|
+ + "attribute vec2 texCoord;"
|
|
|
+ + "varying vec2 vertexTexCoord;"
|
|
|
+ + "void main() {"
|
|
|
+ + " vertexTexCoord = texCoord;"
|
|
|
+ + " gl_Position = vec4(2.0 * (position / resolution) - 1.0, 0.0, 1.0);"
|
|
|
+ + "}",
|
|
|
+ "uniform sampler2D colormap;" // fragment
|
|
|
+ + "uniform vec3 color;"
|
|
|
+ + "varying vec2 vertexTexCoord;"
|
|
|
+ + "void main() {"
|
|
|
+ + " vec4 texel = texture2D(colormap, vertexTexCoord);"
|
|
|
+ + " gl_FragColor = vec4(color * texel.rgb, texel.a);"
|
|
|
+ + "}");
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * Tries to render at 30 Hz (see bottom part)
|
|
|
- */
|
|
|
- public void onDrawFrame(GL10 arg0) {
|
|
|
- long timeMillis = System.currentTimeMillis() - mStartTimeMillis;
|
|
|
- double animTime = 0.001 * (double)timeMillis; // in seconds
|
|
|
-
|
|
|
+
|
|
|
+ private void updateAndroidAndBee() {
|
|
|
// threat level state machine
|
|
|
if (mTargetThreatLevel != mCurrentThreatLevel) {
|
|
|
boolean blocked = false; // block until current action is completed
|
|
|
if (mThreatLevelTransition == 0.0f) {
|
|
|
if (androidMesh.isActionDone()) {
|
|
|
switch (mTargetThreatLevel) {
|
|
|
- case NOT_MONITORING:
|
|
|
- androidMesh.startAction("sleep", false, false);
|
|
|
- break;
|
|
|
- case NO_THREAT:
|
|
|
- androidMesh.startAction("happy", true, false);
|
|
|
- break;
|
|
|
- case PAST_THREAT:
|
|
|
- androidMesh.startAction("fear", true, false);
|
|
|
- break;
|
|
|
- case LIVE_THREAT:
|
|
|
- androidMesh.startAction("panic", true, false);
|
|
|
- break;
|
|
|
+ case NOT_MONITORING:
|
|
|
+ androidMesh.startAction("sleep", false, false);
|
|
|
+ break;
|
|
|
+ case NO_THREAT:
|
|
|
+ androidMesh.startAction("happy", true, false);
|
|
|
+ break;
|
|
|
+ case PAST_THREAT:
|
|
|
+ androidMesh.startAction("fear", true, false);
|
|
|
+ break;
|
|
|
+ case LIVE_THREAT:
|
|
|
+ androidMesh.startAction("panic", true, false);
|
|
|
+ break;
|
|
|
}
|
|
|
} else blocked = true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (!blocked) {
|
|
|
mThreatLevelTransition += 0.016f;
|
|
|
if (mThreatLevelTransition >= 1.0f) {
|
|
@@ -173,60 +201,59 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
if (mNextThreatLevel != mTargetThreatLevel) {
|
|
|
mTargetThreatLevel = mNextThreatLevel;
|
|
|
mThreatLevelTransition = 0.0f;
|
|
|
-
|
|
|
+
|
|
|
// HACK!!! reverses the sleep animation to create smooth transition into other states
|
|
|
if (mCurrentThreatLevel == ThreatLevel.NOT_MONITORING)
|
|
|
androidMesh.startAction("sleep", false, true);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
androidMesh.tick(); // animate android
|
|
|
-
|
|
|
- // OpenGL drawing
|
|
|
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
|
|
|
-
|
|
|
- GLES20.glUseProgram(mProgram);
|
|
|
- int colorUniformLoc = GLES20.glGetUniformLocation(mProgram, "color");
|
|
|
- int textureUniformLoc = GLES20.glGetUniformLocation(mProgram, "texture");
|
|
|
- int mvpUniformLoc = GLES20.glGetUniformLocation(mProgram, "mvp");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void drawAndroidAndBee(double animTime) {
|
|
|
+ GLES20.glUseProgram(mAnimatedProgram);
|
|
|
+ int colorUniformLoc = GLES20.glGetUniformLocation(mAnimatedProgram, "color");
|
|
|
+ int textureUniformLoc = GLES20.glGetUniformLocation(mAnimatedProgram, "texture");
|
|
|
+ int mvpUniformLoc = GLES20.glGetUniformLocation(mAnimatedProgram, "mvp");
|
|
|
|
|
|
// animate color
|
|
|
final float[] whiteColor = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
|
final float[] greyColor = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
|
final float[] redColor = {2.0f, 0.4f, 0.2f, 1.0f};
|
|
|
final float[] yellowColor = {1.1f * 255.0f / 166.0f, 1.2f * 255.0f / 200.0f, 0.0f, 1.0f};
|
|
|
-
|
|
|
+
|
|
|
float[] currentColor = whiteColor;
|
|
|
float blink = 0.5f + 0.5f * (float)Math.sin(12.0 * animTime);
|
|
|
switch (mCurrentThreatLevel) {
|
|
|
- case NOT_MONITORING:
|
|
|
- currentColor = greyColor;
|
|
|
- break;
|
|
|
- case PAST_THREAT:
|
|
|
- currentColor = mixColor(blink, whiteColor, yellowColor);
|
|
|
- break;
|
|
|
- case LIVE_THREAT:
|
|
|
- currentColor = mixColor(blink, whiteColor, redColor);
|
|
|
- break;
|
|
|
- }
|
|
|
- if (mTargetThreatLevel != mCurrentThreatLevel) {
|
|
|
- float[] targetColor = whiteColor;
|
|
|
- switch (mTargetThreatLevel) {
|
|
|
case NOT_MONITORING:
|
|
|
- targetColor = greyColor;
|
|
|
+ currentColor = greyColor;
|
|
|
break;
|
|
|
case PAST_THREAT:
|
|
|
- targetColor = mixColor(blink, whiteColor, yellowColor);
|
|
|
+ currentColor = mixColor(blink, whiteColor, yellowColor);
|
|
|
break;
|
|
|
case LIVE_THREAT:
|
|
|
- targetColor = mixColor(blink, whiteColor, redColor);
|
|
|
+ currentColor = mixColor(blink, whiteColor, redColor);
|
|
|
break;
|
|
|
+ }
|
|
|
+ if (mTargetThreatLevel != mCurrentThreatLevel) {
|
|
|
+ float[] targetColor = whiteColor;
|
|
|
+ switch (mTargetThreatLevel) {
|
|
|
+ case NOT_MONITORING:
|
|
|
+ targetColor = greyColor;
|
|
|
+ break;
|
|
|
+ case PAST_THREAT:
|
|
|
+ targetColor = mixColor(blink, whiteColor, yellowColor);
|
|
|
+ break;
|
|
|
+ case LIVE_THREAT:
|
|
|
+ targetColor = mixColor(blink, whiteColor, redColor);
|
|
|
+ break;
|
|
|
}
|
|
|
currentColor = mixColor(mThreatLevelTransition, currentColor, targetColor);
|
|
|
}
|
|
|
GLES20.glUniform4fv(colorUniformLoc, 1, currentColor, 0);
|
|
|
GLES20.glUniform1i(textureUniformLoc, 0);
|
|
|
-
|
|
|
+
|
|
|
// animate camera
|
|
|
Matrix.setIdentityM(mModelview, 0);
|
|
|
if (mCurrentThreatLevel == ThreatLevel.LIVE_THREAT || mTargetThreatLevel == ThreatLevel.LIVE_THREAT) {
|
|
@@ -239,27 +266,27 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
delta = 1.0f - delta;
|
|
|
Matrix.translateM(mModelview, 0, 0.0f, -0.6f - 0.2f * delta, -1.6f - 0.4f * delta); // 0.0f, -0.8f, -2.0f
|
|
|
Matrix.rotateM(mModelview, 0, -85.0f + 5.0f * delta, 1.0f, 0.0f, 0.0f); // -80.0f
|
|
|
-
|
|
|
+
|
|
|
} else {
|
|
|
Matrix.translateM(mModelview, 0, 0.0f, -0.6f, -1.6f);
|
|
|
Matrix.rotateM(mModelview, 0, -85.0f, 1.0f, 0.0f, 0.0f);
|
|
|
}
|
|
|
Matrix.multiplyMM(mMVP, 0, mProjection, 0, mModelview, 0);
|
|
|
GLES20.glUniformMatrix4fv(mvpUniformLoc, 1, false, mMVP, 0);
|
|
|
-
|
|
|
+
|
|
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, androidTexture);
|
|
|
- androidMesh.draw(mProgram);
|
|
|
-
|
|
|
+ androidMesh.draw(mAnimatedProgram);
|
|
|
+
|
|
|
// restore color
|
|
|
GLES20.glUniform4fv(colorUniformLoc, 1, whiteColor, 0);
|
|
|
|
|
|
if (mCurrentThreatLevel == ThreatLevel.LIVE_THREAT || mTargetThreatLevel == ThreatLevel.LIVE_THREAT) {
|
|
|
// draw a bee rotating around the android
|
|
|
-
|
|
|
+
|
|
|
float fadeIn = mThreatLevelTransition;
|
|
|
if (mTargetThreatLevel != ThreatLevel.LIVE_THREAT) fadeIn = 1.0f - fadeIn; // fade out
|
|
|
float beePositionZ = 2.0f * (1.0f - fadeIn) * (1.0f - fadeIn); // animate the bee going in/out
|
|
|
-
|
|
|
+
|
|
|
final float beeSize = 0.2f;
|
|
|
Matrix.rotateM(mModelview, 0, (float)((-240.0 * animTime) % 360.0), 0.0f, 0.0f, 1.0f); // rotate around android
|
|
|
Matrix.translateM(mModelview, 0, 0.6f, 0.0f, 0.7f + 0.1f * (float)Math.sin(12.0 * animTime) + beePositionZ); // go up and down
|
|
@@ -267,11 +294,66 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
Matrix.scaleM(mModelview, 0, beeSize, beeSize, beeSize);
|
|
|
Matrix.multiplyMM(mMVP, 0, mProjection, 0, mModelview, 0);
|
|
|
GLES20.glUniformMatrix4fv(mvpUniformLoc, 1, false, mMVP, 0);
|
|
|
-
|
|
|
+
|
|
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, beeTexture);
|
|
|
beeMesh.tick();
|
|
|
- beeMesh.draw(mProgram);
|
|
|
+ beeMesh.draw(mAnimatedProgram);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tries to render at 30 Hz (see bottom part)
|
|
|
+ */
|
|
|
+ public void onDrawFrame(GL10 arg0) {
|
|
|
+ Context ctx = MainActivity.getInstance();
|
|
|
+
|
|
|
+ long timeMillis = System.currentTimeMillis() - mStartTimeMillis;
|
|
|
+ double animTime = 0.001 * (double)timeMillis; // in seconds
|
|
|
+
|
|
|
+ // OpenGL drawing
|
|
|
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
|
|
|
+
|
|
|
+ updateAndroidAndBee();
|
|
|
+ drawAndroidAndBee(animTime);
|
|
|
+
|
|
|
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
|
|
|
+ GLES20.glEnable(GLES20.GL_BLEND);
|
|
|
+ GLES20.glDisable(GLES20.GL_CULL_FACE);
|
|
|
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
|
|
|
+ GLES20.glUseProgram(mTexturedProgram);
|
|
|
+ int resolutionUniformLoc = GLES20.glGetUniformLocation(mTexturedProgram, "resolution");
|
|
|
+ int textureUniformLoc = GLES20.glGetUniformLocation(mTexturedProgram, "colormap");
|
|
|
+ int colorUniformLoc = GLES20.glGetUniformLocation(mTexturedProgram, "color");
|
|
|
+ GLES20.glUniform2f(resolutionUniformLoc, 1024.0f, 1024.0f);
|
|
|
+ GLES20.glUniform1i(textureUniformLoc, 0);
|
|
|
+ GLES20.glUniform3f(colorUniformLoc, 1.0f, 1.0f, 1.0f);
|
|
|
+ String message = "???";
|
|
|
+ switch (mNextThreatLevel) {
|
|
|
+ case NOT_MONITORING:
|
|
|
+ message = ctx.getString(R.string.honeypot_not_monitoring);
|
|
|
+ break;
|
|
|
+ case NO_THREAT:
|
|
|
+ message = ctx.getString(R.string.honeypot_no_threat);
|
|
|
+ break;
|
|
|
+ case PAST_THREAT:
|
|
|
+ message = ctx.getString(R.string.honeypot_past_threat);
|
|
|
+ break;
|
|
|
+ case LIVE_THREAT:
|
|
|
+ message = ctx.getString(R.string.honeypot_live_threat);
|
|
|
+ break;
|
|
|
}
|
|
|
+ float textWidth = font.getTextWidth(message);
|
|
|
+ float textHeight = 40.0f;
|
|
|
+ float bubbleSize = textWidth + 100.0f;
|
|
|
+ float y = 0.75f * 1024.0f + 32.0f * (float)Math.sin(2.0*animTime);
|
|
|
+ float x = 0.5f * 1024.0f + 16.0f * (float)Math.cos(1.0*animTime);
|
|
|
+ drawTexturedQuad(speechBubbleTexture, x - 0.5f*bubbleSize, y - 0.25f*bubbleSize, bubbleSize, 0.5f*bubbleSize);
|
|
|
+ GLES20.glUniform3f(colorUniformLoc, 0.0f, 0.0f, 0.0f);
|
|
|
+ font.drawText(mTexturedProgram, message, x - 0.5f*textWidth,
|
|
|
+ y - 0.5f*textHeight);
|
|
|
+ GLES20.glUseProgram(0);
|
|
|
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
|
|
|
+ GLES20.glDisable(GLES20.GL_BLEND);
|
|
|
|
|
|
long deltaTime = System.currentTimeMillis() - mStartTimeMillis - timeMillis; // time for one frame
|
|
|
if (deltaTime < 33) {
|
|
@@ -297,7 +379,42 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
GLES20.glViewport(0, 0, width, height);
|
|
|
}
|
|
|
|
|
|
- // some private helper methods
|
|
|
+ // some helper functions
|
|
|
+ private void drawTexturedQuad(int texture, float x, float y, float w, float h) {
|
|
|
+ int vertexCount = 4;
|
|
|
+ int vertexSize = (2+2)*4; // size in bytes
|
|
|
+ FloatBuffer buffer = ByteBuffer.allocateDirect(vertexCount * vertexSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
|
|
+ buffer.put(x); buffer.put(y);
|
|
|
+ buffer.put(0.0f); buffer.put(0.0f);
|
|
|
+ buffer.put(x+w); buffer.put(y);
|
|
|
+ buffer.put(1.0f); buffer.put(0.0f);
|
|
|
+ buffer.put(x); buffer.put(y+h);
|
|
|
+ buffer.put(0.0f); buffer.put(1.0f);
|
|
|
+ buffer.put(x+w); buffer.put(y+h);
|
|
|
+ buffer.put(1.0f); buffer.put(1.0f);
|
|
|
+ buffer.position(0); // rewind
|
|
|
+
|
|
|
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
|
+
|
|
|
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mQuadVertexBuffer);
|
|
|
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexSize * vertexCount, buffer, GLES20.GL_STREAM_DRAW);
|
|
|
+
|
|
|
+ int positionIndex = GLES20.glGetAttribLocation(mTexturedProgram, "position");
|
|
|
+ int texCoordIndex = GLES20.glGetAttribLocation(mTexturedProgram, "texCoord");
|
|
|
+
|
|
|
+ GLES20.glEnableVertexAttribArray(positionIndex);
|
|
|
+ GLES20.glEnableVertexAttribArray(texCoordIndex);
|
|
|
+
|
|
|
+ GLES20.glVertexAttribPointer(positionIndex, 2, GLES20.GL_FLOAT, false, vertexSize, 0);
|
|
|
+ GLES20.glVertexAttribPointer(texCoordIndex, 2, GLES20.GL_FLOAT, false, vertexSize, 8);
|
|
|
+
|
|
|
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
|
|
|
+
|
|
|
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
|
|
+
|
|
|
+ GLES20.glDisableVertexAttribArray(positionIndex);
|
|
|
+ GLES20.glDisableVertexAttribArray(texCoordIndex);
|
|
|
+ }
|
|
|
|
|
|
private float[] mixColor(float alpha, float[] color1, float[] color2) {
|
|
|
float[] color3 = new float[4];
|
|
@@ -308,7 +425,9 @@ public class ThreatIndicatorGLRenderer implements Renderer {
|
|
|
return color3;
|
|
|
}
|
|
|
|
|
|
- private int loadTexture(String filePath) {
|
|
|
+ public static int loadTexture(String filePath) {
|
|
|
+ AssetManager assets = MainActivity.getInstance().getAssets();
|
|
|
+
|
|
|
Bitmap bitmap;
|
|
|
try {
|
|
|
bitmap = BitmapFactory.decodeStream(assets.open(filePath));
|