]> Pileus Git - ~andy/spades/blob - src/org/pileus/spades/Cards.java
Add touch handler
[~andy/spades] / src / org / pileus / spades / Cards.java
1 package org.pileus.spades;
2
3 import java.util.Map;
4 import java.util.HashMap;
5
6 import java.nio.ByteBuffer;
7 import java.nio.ByteOrder;
8 import java.nio.FloatBuffer;
9
10 import javax.microedition.khronos.egl.EGLConfig;
11 import javax.microedition.khronos.opengles.GL10;
12
13 import android.content.Context;
14 import android.content.res.Resources;
15 import android.graphics.Bitmap;
16 import android.graphics.BitmapFactory;
17 import android.opengl.GLES20;
18 import android.opengl.GLSurfaceView;
19 import android.opengl.GLUtils;
20 import android.opengl.Matrix;
21 import android.view.MotionEvent;
22
23 public class Cards extends GLSurfaceView implements GLSurfaceView.Renderer
24 {
25         /* Shader data */
26         private final String vertSource
27                 = "uniform   mat4 u_model;"
28                 + "uniform   mat4 u_view;"
29                 + "uniform   mat4 u_proj;"
30                 + "attribute vec4 a_position;"
31                 + "attribute vec2 a_mapping;"
32                 + "varying   vec2 v_mapping;"
33                 + "void main() {"
34                 + "  gl_Position = u_proj"
35                 + "              * u_view"
36                 + "              * u_model"
37                 + "              * a_position;"
38                 + "  v_mapping   = a_mapping;"
39                 + "}";
40
41         private final String fragSource
42                 = "precision mediump   float;"
43                 + "uniform   sampler2D u_texture;"
44                 + "uniform   vec4      u_color;"
45                 + "varying   vec2      v_mapping;"
46                 + "void main() {"
47                 + "  gl_FragColor = texture2D("
48                 + "    u_texture, v_mapping);"
49                 + "}";
50
51         /* Drawing data */
52         private final float  faceCoords[] = {
53                 -0.063f,  0.088f, 0.05f, // Standard poker size:
54                 -0.063f, -0.088f, 0.05f, //   2.5in x 3.5in
55                  0.063f, -0.088f, 0.05f, //   63mm  x 88mm
56                  0.063f,  0.088f, 0.05f, //
57         };
58
59         private final float  backCoords[] = {
60                  0.063f,  0.088f, 0.05f, // Standard poker size:
61                  0.063f, -0.088f, 0.05f, //   2.5in x 3.5in
62                 -0.063f, -0.088f, 0.05f, //   63mm  x 88mm
63                 -0.063f,  0.088f, 0.05f, //
64         };
65
66         private final float  tableCoords[] = {
67                 -0.75f,  0.75f, 0,
68                 -0.75f, -0.75f, 0,
69                  0.75f, -0.75f, 0,
70                  0.75f,  0.75f, 0,
71         };
72
73         private final float  mapCoords[] = {
74                 0.0f, 0.0f,
75                 0.0f, 1.0f,
76                 1.0f, 1.0f,
77                 1.0f, 0.0f,
78         };
79
80         private final float  color[] = {
81                 1, 0, 0, 1
82         };
83
84         /* Cards data */
85         private final String cards[] = {
86                 "As", "Ks", "Qs", "Js", "10s", "9s", "8s", "7s", "6s", "5s", "4s", "3s", "2s",
87                 "Ah", "Kh", "Qh", "Jh", "10h", "9h", "8h", "7h", "6h", "5h", "4h", "3h", "2h",
88                 "Ac", "Kc", "Qc", "Jc", "10c", "9c", "8c", "7c", "6c", "5c", "4c", "3c", "2c",
89                 "Ad", "Kd", "Qd", "Jd", "10d", "9d", "8d", "7d", "6d", "5d", "4d", "3d", "2d",
90         };
91
92         /* Private data */
93         private Resources    res;         // app resources
94         private int          program;     // opengl program
95
96         private float[]      model;       // model matrix
97         private float[]      view;        // view matrix
98         private float[]      proj;        // projection matrix
99
100         private FloatBuffer  faceBuf;     // vertex positions for front of card
101         private FloatBuffer  backBuf;     // vertex positions for back of card
102         private FloatBuffer  tableBuf;    // vertex positions for table
103         private FloatBuffer  mapBuf;      // texture mapping coord buffer
104
105         private int          modelHandle; // model matrix
106         private int          viewHandle;  // view matrix
107         private int          projHandle;  // projection matrix
108         private int          vertHandle;  // vertex positions
109         private int          mapHandle;   // texture mapping coords
110         private int          texHandle;   // texture data
111         private int          colorHandle; // color data
112
113         private int[]        face;        // card face textures
114         private int          red;         // red card back
115         private int          blue;        // blue card back
116         private int          table;       // table top texture
117
118         private boolean      drag;        // currently in drag event
119         private int          pick;        // currently picked card
120         private float        xpos;        // x drag position (0=left   - 1-right)
121         private float        ypos;        // y drag position (0=bottom - 1-top)
122         private float        ylim;        // y limit for a play
123
124         private Map<String,Integer> index; // card name to index map
125
126         /* Properties */
127         public String[]      hand;        // cards to display
128
129         /* GLSurfaceView Methods */
130         public Cards(Context context)
131         {
132                 super(context);
133                 Os.debug("Cards: create");
134
135                 this.res   = context.getResources();
136
137                 this.model = new float[4*4];
138                 this.view  = new float[4*4];
139                 this.proj  = new float[4*4];
140
141                 this.face  = new int[52];
142
143                 this.ylim  = 0.4f;
144
145                 this.hand  = new String[] {
146                         "As", "7s", "6s",  "6h", "2h", "Ac",
147                         "Kc", "3c", "10d", "9d", "8d", "7d", "2d"
148                 };
149
150                 this.index = new HashMap<String,Integer>(52);
151                 for (int i = 0; i < 52; i++)
152                         this.index.put(this.cards[i], i);
153
154                 this.setEGLContextClientVersion(2);
155                 this.setRenderer(this);
156                 this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
157         }
158
159         /* Renderer methods */
160         @Override
161         public void onSurfaceCreated(GL10 unused, EGLConfig config)
162         {
163                 Os.debug("Cards: onSurfaceCreate");
164
165                 /* Initialize shaders */
166                 int vertShader = this.loadShader(GLES20.GL_VERTEX_SHADER,   vertSource);
167                 int fragShader = this.loadShader(GLES20.GL_FRAGMENT_SHADER, fragSource);
168
169                 /* Link shaders into an OpenGL program */
170                 this.program = GLES20.glCreateProgram();
171                 GLES20.glAttachShader(program, vertShader);
172                 GLES20.glAttachShader(program, fragShader);
173                 GLES20.glLinkProgram(program);
174
175                 /* Get shaders attributes */
176                 this.modelHandle = GLES20.glGetUniformLocation(program, "u_model");
177                 this.viewHandle  = GLES20.glGetUniformLocation(program, "u_view");
178                 this.projHandle  = GLES20.glGetUniformLocation(program, "u_proj");
179                 this.vertHandle  = GLES20.glGetAttribLocation(program, "a_position");
180                 this.mapHandle   = GLES20.glGetAttribLocation(program, "a_mapping");
181                 this.texHandle   = GLES20.glGetUniformLocation(program, "u_texture");
182                 this.colorHandle = GLES20.glGetUniformLocation(program, "u_color");
183
184                 /* Create vertex array  */
185                 this.faceBuf  = this.loadBuffer(this.faceCoords);
186                 this.backBuf  = this.loadBuffer(this.backCoords);
187                 this.tableBuf = this.loadBuffer(this.tableCoords);
188                 this.mapBuf   = this.loadBuffer(this.mapCoords);
189
190                 /* Load textures */
191                 for (int i = 0; i < 52; i++) {
192                         String name = "card_" + this.cards[i].toLowerCase();
193                         this.face[i] = this.loadTexture(name);
194                 }
195                 this.red   = this.loadTexture("card_red");
196                 this.blue  = this.loadTexture("card_blue");
197                 this.table = this.loadTexture("table");
198
199                 /* Debug */
200                 Os.debug("Cards: onSurfaceCreate");
201         }
202
203         @Override
204         public void onDrawFrame(GL10 unused)
205         {
206                 Os.debug("Cards: onDrawFrame");
207
208                 /* Turn on the program */
209                 GLES20.glUseProgram(program);
210
211                 /* Reset view */
212                 GLES20.glClearColor(0, 0, 0, 1);
213                 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
214
215                 /* Setup projection matricies */
216                 GLES20.glUniformMatrix4fv(this.viewHandle, 1, false, this.view, 0);
217                 GLES20.glUniformMatrix4fv(this.projHandle, 1, false, this.proj, 0);
218
219                 /* Setup buffers objects */
220                 GLES20.glEnableVertexAttribArray(this.vertHandle);
221                 GLES20.glEnableVertexAttribArray(this.mapHandle);
222
223                 /* Setup texturing */
224                 GLES20.glEnable(GLES20.GL_CULL_FACE);
225                 GLES20.glEnable(GLES20.GL_BLEND);
226                 GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
227                 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
228                 GLES20.glUniform1i(this.texHandle, 0);
229
230                 /* Draw "Table" */
231                 Matrix.setIdentityM(this.model, 0);
232                 GLES20.glUniformMatrix4fv(this.modelHandle, 1, false, this.model, 0);
233                 this.drawTable();
234
235                 /* Draw hand */
236                 int num = this.hand.length;
237                 for (int i = 0; i < num; i++) {
238                         if (this.drag && this.ypos >= this.ylim && i == this.pick)
239                                 continue;
240
241                         Matrix.setIdentityM(this.model, 0);
242
243                         Matrix.rotateM(this.model, 0, 45f, 1f, 0f, 0f);
244                         Matrix.translateM(this.model, 0, 0f, -0.3f, 1.20f);
245
246                         if (this.drag) {
247                                 float pct = (float)(i+0.5) / num;
248                                 float err = this.xpos - pct;
249                                 float y   = (float)this.ypos / this.ylim;
250                                 float lim = Math.min(Math.max(y,0),1);
251                                 float fcn = 0.1f
252                                         * (float)Math.exp(-10*num*Math.pow(y*err,2))
253                                         * (1f-(float)Math.pow(1-lim, 2));
254                                 Matrix.translateM(this.model, 0, 0, fcn, 0);
255                         }
256                         float left  = -20f + 20f*(1f/num);
257                         float right =  54f - 54f*(1f/num);
258                         float ang   = left + i*(right-left)/num;
259                         Matrix.rotateM(this.model, 0, ang, 0f, 0f, -1f);
260                         Matrix.translateM(this.model, 0, 0f, 0.15f, 0f);
261
262                         GLES20.glUniformMatrix4fv(this.modelHandle, 1, false, this.model, 0);
263                         this.drawCard(this.hand[i]);
264                 }
265
266                 /* Draw selected card */
267                 if (this.drag && this.ypos >= this.ylim) {
268                         Matrix.setIdentityM(this.model, 0);
269                         Matrix.rotateM(this.model, 0, 45f, 1f, 0f, 0f);
270                         Matrix.translateM(this.model, 0, 0f, 0f, 1.20f);
271                         GLES20.glUniformMatrix4fv(this.modelHandle, 1, false, this.model, 0);
272                         this.drawCard(this.hand[this.pick]);
273                 }
274         }
275
276         @Override
277         public void onSurfaceChanged(GL10 unused, int width, int height)
278         {
279                 Os.debug("Cards: onSurfaceChanged");
280
281                 GLES20.glViewport(0, 0, width, height);
282
283                 Matrix.setIdentityM(this.model, 0);
284                 Matrix.setIdentityM(this.view, 0);
285                 Matrix.setIdentityM(this.proj, 0);
286
287                 // Setup camera
288                 float xang = 0.5f;
289                 float yang = xang * ((float)height / (float)width);
290
291                 Matrix.frustumM(this.proj, 0,
292                                 -1E-6f * xang, // left
293                                 1E-6f  * xang, // right
294                                 -1E-6f * yang, // bottom
295                                 1E-6f  * yang, // top
296                                 1E-6f,         // near
297                                 10f);          // far
298
299                 Matrix.rotateM(this.view, 0, 10f, 1, 0, 0);
300                 Matrix.translateM(this.view, 0, 0, 0, -1.5f);
301                 Matrix.rotateM(this.view, 0, -45f, 1, 0, 0);
302         }
303
304         @Override
305         public boolean onTouchEvent(MotionEvent event)
306         {
307                 boolean up   = event.getActionMasked() == MotionEvent.ACTION_UP;
308
309                 float x =    event.getX() / this.getWidth();
310                 float y = 1-(event.getY() / this.getHeight());
311
312                 this.ypos = y;
313                 if (y < this.ylim) {
314                         int num = this.hand.length;
315                         this.xpos = x;
316                         this.pick = (int)Math.floor((x*num));
317                         if (this.pick <    0) this.pick = 0;
318                         if (this.pick >= num) this.pick = num-1;
319                 }
320                 if (y < this.ylim && !this.drag) {
321                         Os.debug("Cards: onTouchEvent - starting drag");
322                         this.drag = true;
323                 }
324                 if (this.drag) {
325                         Os.debug("Cards: onTouchEvent - move "
326                                         + x + "," + y);
327                         this.requestRender();
328                 }
329                 if (y >= this.ylim && this.drag && up) {
330                         Os.debug("Cards: onTouchEvent - playing card");
331                 }
332                 if (up) {
333                         Os.debug("Cards: onTouchEvent - ending drag");
334                         this.drag = false;
335                 }
336                 return true;
337         }
338
339         /* Private loading methods */
340         private int loadShader(int type, String code)
341         {
342                 Os.debug("Cards: loadShader");
343
344                 int shader = GLES20.glCreateShader(type);
345                 GLES20.glShaderSource(shader, code);
346                 GLES20.glCompileShader(shader);
347                 return shader;
348         }
349
350         private int loadTexture(String name)
351         {
352                 Os.debug("Cards: loadTexture - " + name);
353
354                 final int[] tex = new int[1];
355
356                 /* Lookup the resource ID */
357                 int id = 0;
358                 try {
359                         id = R.drawable.class.getField(name).getInt(null);
360                 } catch(Exception e) {
361                         Os.debug("Cards: lookup failed for '" + name + "'", e);
362                         return 0;
363                 }
364
365                 /* Load the bitmap */
366                 Bitmap bitmap = BitmapFactory.decodeResource(this.res, id);
367
368                 /* Copy into OpenGL */
369                 GLES20.glGenTextures(1, tex, 0);
370                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex[0]);
371                 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
372                 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
373                 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
374
375                 return tex[0];
376         }
377
378         private FloatBuffer loadBuffer(float[] data)
379         {
380                 ByteBuffer bytes = ByteBuffer.allocateDirect(data.length * 4);
381                 bytes.order(ByteOrder.nativeOrder());
382
383                 FloatBuffer buf = bytes.asFloatBuffer();
384                 buf.put(data);
385                 buf.position(0);
386
387                 return buf;
388         }
389
390         /* Private drawing methods */
391         private void drawTable()
392         {
393                 /* Draw table */
394                 GLES20.glVertexAttribPointer(this.vertHandle, 3, GLES20.GL_FLOAT, false, 3*4, this.tableBuf);
395                 GLES20.glVertexAttribPointer(this.mapHandle,  2, GLES20.GL_FLOAT, false, 2*4, this.mapBuf);
396                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.table);
397                 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
398         }
399
400         private void drawCard(String name)
401         {
402                 int idx   = this.index.get(name);
403                 int front = this.face[idx];
404                 int back  = this.red;
405
406                 /* Draw front */
407                 GLES20.glVertexAttribPointer(this.vertHandle, 3, GLES20.GL_FLOAT, false, 3*4, this.faceBuf);
408                 GLES20.glVertexAttribPointer(this.mapHandle,  2, GLES20.GL_FLOAT, false, 2*4, this.mapBuf);
409                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, front);
410                 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
411
412                 /* Draw back */
413                 GLES20.glVertexAttribPointer(this.vertHandle, 3, GLES20.GL_FLOAT, false, 3*4, this.backBuf);
414                 GLES20.glVertexAttribPointer(this.mapHandle,  2, GLES20.GL_FLOAT, false, 2*4, this.mapBuf);
415                 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, back);
416                 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
417         }
418 }