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