ThreatIndicatorGLRenderer.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package de.tudarmstadt.informatik.hostage.ui2.fragment.opengl;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.util.Scanner;
  5. import javax.microedition.khronos.egl.EGLConfig;
  6. import javax.microedition.khronos.opengles.GL10;
  7. import android.content.res.AssetManager;
  8. import android.graphics.Bitmap;
  9. import android.graphics.BitmapFactory;
  10. import android.graphics.Color;
  11. import android.opengl.GLES20;
  12. import android.opengl.GLSurfaceView.Renderer;
  13. import android.opengl.GLUtils;
  14. import android.opengl.Matrix;
  15. import android.util.Log;
  16. /**
  17. * @author Fabio Arnold
  18. *
  19. * ThreatIndicatorGLRenderer
  20. * This class is responsible for drawing an animation representing the current threat level.
  21. * Use the method setThreatLevel to set the state (0 to 3).
  22. */
  23. public class ThreatIndicatorGLRenderer implements Renderer {
  24. public static final int kThreatLevelNotMonitoring = 0;
  25. public static final int kThreatLevelNoThreat = 1;
  26. public static final int kThreatLevelPastThreat = 2;
  27. public static final int kThreatLevelPersistentThreat = 3;
  28. /**
  29. * Set the threat level which should be indicated
  30. * @param level 0 to 3 but better use the constants
  31. */
  32. public static void setThreatLevel(int level) {
  33. assert(level >= 0 && level <= 3);
  34. nextThreatLevel = level;
  35. Log.i("threat indicator", "next threat level = " + level);
  36. }
  37. /**
  38. * Match the background color of the view holding this renderer
  39. * @param color 32 bit integer encoding the color
  40. */
  41. public static void setBackgroundColor(int color) {
  42. backgroundColor[0] = (float)Color.red(color) / 255.0f;
  43. backgroundColor[1] = (float)Color.green(color) / 255.0f;
  44. backgroundColor[2] = (float)Color.blue(color) / 255.0f;
  45. }
  46. private static float[] backgroundColor = new float[3];
  47. public static AssetManager assets = null; // needs to be set by the Activity using this class
  48. private int width;
  49. private int height;
  50. private float aspectRatio;
  51. // OpenGL data
  52. private int program;
  53. private float [] modelview;
  54. private float [] projection;
  55. private float [] mvp;
  56. private AnimatedMesh androidMesh = null;
  57. private AnimatedMesh beeMesh = null;
  58. private int androidTexture;
  59. private int beeTexture;
  60. // threat state
  61. private static int nextThreatLevel = kThreatLevelNoThreat;
  62. private int currentThreatLevel = kThreatLevelNoThreat;
  63. private int targetThreatLevel = kThreatLevelNoThreat;
  64. private float threatLevelTransition = 1.0f; // 1.0 means transition is complete
  65. private long startTimeMillis; // for animation
  66. public ThreatIndicatorGLRenderer() {
  67. startTimeMillis = System.currentTimeMillis();
  68. }
  69. /**
  70. * Initialization will be called after GL context is created and is current
  71. */
  72. public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
  73. GLES20.glClearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0f);
  74. GLES20.glEnable(GLES20.GL_DEPTH_TEST);
  75. GLES20.glEnable(GLES20.GL_CULL_FACE);
  76. GLES20.glEnable(GLES20.GL_TEXTURE_2D);
  77. try {
  78. InputStream is = assets.open("meshes/android.amh");
  79. androidMesh = new AnimatedMesh(is);
  80. androidMesh.startAction("greet", false, false);
  81. } catch (IOException e) {
  82. Log.e("gl", "Couldn't open android mesh");
  83. }
  84. androidTexture = loadTexture("textures/android-tex.png");
  85. try {
  86. InputStream is = assets.open("meshes/bee.amh");
  87. beeMesh = new AnimatedMesh(is);
  88. beeMesh.startAction("bee_armatureAct", true, false);
  89. } catch (IOException e) {
  90. Log.e("gl", "Couldn't open bee mesh");
  91. }
  92. beeTexture = loadTexture("textures/bee-tex.png");
  93. modelview = new float[16];
  94. Matrix.setIdentityM(modelview, 0);
  95. projection = new float[16];
  96. mvp = new float[16];
  97. // default shader
  98. String vertexSource = "attribute vec3 position; void main() {gl_Position = vec4(position, 1.0);}";
  99. String fragmentSource = "void main() {gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);}";
  100. try {
  101. vertexSource = inputStreamToString(assets.open("shaders/skinned.vert"));
  102. } catch (IOException e) {
  103. e.printStackTrace();
  104. }
  105. try {fragmentSource = inputStreamToString(assets.open("shaders/skinned.frag"));
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. }
  109. program = loadProgram(vertexSource, fragmentSource);
  110. }
  111. /**
  112. * Will be called every ~16 milliseconds -> 60 Hz
  113. */
  114. public void onDrawFrame(GL10 arg0) {
  115. long timeMillis = System.currentTimeMillis() - startTimeMillis;
  116. double animTime = 0.001 * (double)timeMillis; // in seconds
  117. // threat level state machine
  118. if (targetThreatLevel != currentThreatLevel) {
  119. boolean blocked = false; // block until current action is completed
  120. if (threatLevelTransition == 0.0f) {
  121. if (androidMesh.isActionDone()) {
  122. switch (targetThreatLevel) {
  123. case kThreatLevelNotMonitoring:
  124. androidMesh.startAction("sleep", false, false);
  125. break;
  126. case kThreatLevelNoThreat:
  127. androidMesh.startAction("happy", true, false);
  128. break;
  129. case kThreatLevelPastThreat:
  130. androidMesh.startAction("fear", true, false);
  131. break;
  132. case kThreatLevelPersistentThreat:
  133. androidMesh.startAction("panic", true, false);
  134. break;
  135. }
  136. } else blocked = true;
  137. }
  138. if (!blocked) {
  139. threatLevelTransition += 0.016f;
  140. if (threatLevelTransition >= 1.0f) {
  141. currentThreatLevel = targetThreatLevel;
  142. threatLevelTransition = 1.0f;
  143. }
  144. }
  145. } else {
  146. if (nextThreatLevel != targetThreatLevel) {
  147. targetThreatLevel = nextThreatLevel;
  148. threatLevelTransition = 0.0f;
  149. // HACK!!! reverses the sleep animation to create smooth transition into other states
  150. if (currentThreatLevel == kThreatLevelNotMonitoring)
  151. androidMesh.startAction("sleep", false, true);
  152. }
  153. }
  154. androidMesh.tick(); // animate android
  155. // OpenGL drawing
  156. GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
  157. GLES20.glUseProgram(program);
  158. int colorUniformLoc = GLES20.glGetUniformLocation(program, "color");
  159. int textureUniformLoc = GLES20.glGetUniformLocation(program, "texture");
  160. int mvpUniformLoc = GLES20.glGetUniformLocation(program, "mvp");
  161. // animate color
  162. final float[] whiteColor = {1.0f, 1.0f, 1.0f, 1.0f};
  163. final float[] greyColor = {0.5f, 0.5f, 0.5f, 1.0f};
  164. final float[] redColor = {2.0f, 0.4f, 0.2f, 1.0f};
  165. final float[] yellowColor = {1.1f * 255.0f / 166.0f, 1.2f * 255.0f / 200.0f, 0.0f, 1.0f};
  166. float[] currentColor = whiteColor;
  167. float blink = 0.5f + 0.5f * (float)Math.sin(12.0 * animTime);
  168. switch (currentThreatLevel) {
  169. case kThreatLevelNotMonitoring:
  170. currentColor = greyColor;
  171. break;
  172. case kThreatLevelPastThreat:
  173. currentColor = mixColor(blink, whiteColor, yellowColor);
  174. break;
  175. case kThreatLevelPersistentThreat:
  176. currentColor = mixColor(blink, whiteColor, redColor);
  177. break;
  178. }
  179. if (targetThreatLevel != currentThreatLevel) {
  180. float[] targetColor = whiteColor;
  181. switch (targetThreatLevel) {
  182. case kThreatLevelNotMonitoring:
  183. targetColor = greyColor;
  184. break;
  185. case kThreatLevelPastThreat:
  186. targetColor = mixColor(blink, whiteColor, yellowColor);
  187. break;
  188. case kThreatLevelPersistentThreat:
  189. targetColor = mixColor(blink, whiteColor, redColor);
  190. break;
  191. }
  192. currentColor = mixColor(threatLevelTransition, currentColor, targetColor);
  193. }
  194. GLES20.glUniform4fv(colorUniformLoc, 1, currentColor, 0);
  195. GLES20.glUniform1i(textureUniformLoc, 0);
  196. // animate camera
  197. Matrix.setIdentityM(modelview, 0);
  198. if (currentThreatLevel == kThreatLevelPersistentThreat || targetThreatLevel == kThreatLevelPersistentThreat) {
  199. float delta = 1.0f;
  200. if (threatLevelTransition < 0.4f) { // animate only during the first 40% of the transition
  201. delta = threatLevelTransition / 0.4f;
  202. delta = -2.0f * delta * delta * delta + 3.0f * delta * delta; // ease in/out
  203. }
  204. if (targetThreatLevel != kThreatLevelPersistentThreat)
  205. delta = 1.0f - delta;
  206. Matrix.translateM(modelview, 0, 0.0f, -0.6f - 0.2f * delta, -1.6f - 0.4f * delta); // 0.0f, -0.8f, -2.0f
  207. Matrix.rotateM(modelview, 0, -85.0f + 5.0f * delta, 1.0f, 0.0f, 0.0f); // -80.0f
  208. } else {
  209. Matrix.translateM(modelview, 0, 0.0f, -0.6f, -1.6f);
  210. Matrix.rotateM(modelview, 0, -85.0f, 1.0f, 0.0f, 0.0f);
  211. }
  212. Matrix.multiplyMM(mvp, 0, projection, 0, modelview, 0);
  213. GLES20.glUniformMatrix4fv(mvpUniformLoc, 1, false, mvp, 0);
  214. GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, androidTexture);
  215. androidMesh.draw(program);
  216. // restore color
  217. GLES20.glUniform4fv(colorUniformLoc, 1, whiteColor, 0);
  218. if (currentThreatLevel == kThreatLevelPersistentThreat || targetThreatLevel == kThreatLevelPersistentThreat) {
  219. // draw a bee rotating around the android
  220. float fadeIn = threatLevelTransition;
  221. if (targetThreatLevel != kThreatLevelPersistentThreat) fadeIn = 1.0f - fadeIn; // fade out
  222. float beePositionZ = 2.0f * (1.0f - fadeIn) * (1.0f - fadeIn); // animate the bee going in/out
  223. final float beeSize = 0.2f;
  224. Matrix.rotateM(modelview, 0, (float)((-240.0 * animTime) % 360.0), 0.0f, 0.0f, 1.0f); // rotate around android
  225. Matrix.translateM(modelview, 0, 0.6f, 0.0f, 0.7f + 0.1f * (float)Math.sin(12.0 * animTime) + beePositionZ); // go up and down
  226. Matrix.rotateM(modelview, 0, 20.0f * (float)Math.cos(12.0 * animTime), 1.0f, 0.0f, 0.0f); // rock back and forth
  227. Matrix.scaleM(modelview, 0, beeSize, beeSize, beeSize);
  228. Matrix.multiplyMM(mvp, 0, projection, 0, modelview, 0);
  229. GLES20.glUniformMatrix4fv(mvpUniformLoc, 1, false, mvp, 0);
  230. GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, beeTexture);
  231. beeMesh.tick();
  232. beeMesh.draw(program);
  233. }
  234. }
  235. /**
  236. * Informs renderer of changed surface dimensions
  237. */
  238. public void onSurfaceChanged(GL10 arg0, int w, int h) {
  239. width = w;
  240. height = h;
  241. aspectRatio = (float)w / (float)h;
  242. Matrix.orthoM(projection, 0, -aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);
  243. float near = 0.1f;
  244. float fov = 2.0f;
  245. Matrix.frustumM(projection, 0, near * -aspectRatio, near * aspectRatio, -near, near, fov * near, 100.0f);
  246. GLES20.glViewport(0, 0, width, height);
  247. }
  248. // some private helper methods
  249. private float[] mixColor(float alpha, float[] color1, float[] color2) {
  250. float[] color3 = new float[4];
  251. color3[0] = (1.0f - alpha) * color1[0] + alpha * color2[0];
  252. color3[1] = (1.0f - alpha) * color1[1] + alpha * color2[1];
  253. color3[2] = (1.0f - alpha) * color1[2] + alpha * color2[2];
  254. color3[3] = (1.0f - alpha) * color1[3] + alpha * color2[3];
  255. return color3;
  256. }
  257. private int loadTexture(String filePath) {
  258. Bitmap bitmap = null;
  259. try {
  260. bitmap = BitmapFactory.decodeStream(assets.open(filePath));
  261. } catch (IOException e) {
  262. e.printStackTrace();
  263. return 0;
  264. }
  265. int[] names = {0};
  266. GLES20.glGenTextures(1, names, 0);
  267. int tex = names[0];
  268. GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex);
  269. GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
  270. GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
  271. GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
  272. bitmap.recycle(); // memory is now gpu -> free it
  273. GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex);
  274. return tex;
  275. }
  276. // see http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
  277. private static String inputStreamToString(InputStream is) {
  278. Scanner scanner = new Scanner(is);
  279. Scanner s = scanner.useDelimiter("\\A");
  280. String result = s.hasNext() ? s.next() : "";
  281. scanner.close();
  282. return result;
  283. }
  284. private static int loadShader(int type, String source) {
  285. int shader = GLES20.glCreateShader(type);
  286. GLES20.glShaderSource(shader, source);
  287. GLES20.glCompileShader(shader);
  288. Log.i("gl", GLES20.glGetShaderInfoLog(shader));
  289. return shader;
  290. }
  291. private static int loadProgram(String vertexSource, String fragmentSource) {
  292. int program = GLES20.glCreateProgram();
  293. GLES20.glAttachShader(program, loadShader(GLES20.GL_VERTEX_SHADER, vertexSource));
  294. GLES20.glAttachShader(program, loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource));
  295. GLES20.glLinkProgram(program);
  296. return program;
  297. }
  298. }