1 package org.pileus.spades;
4 import java.util.HashMap;
6 import java.nio.ByteBuffer;
7 import java.nio.ByteOrder;
8 import java.nio.FloatBuffer;
10 import javax.microedition.khronos.egl.EGLConfig;
11 import javax.microedition.khronos.opengles.GL10;
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;
24 public class Cards extends GLSurfaceView implements GLSurfaceView.Renderer
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;"
35 + " gl_Position = u_proj"
39 + " v_mapping = a_mapping;"
42 private final String fragSource
43 = "precision mediump float;"
44 + "uniform sampler2D u_texture;"
45 + "uniform vec4 u_color;"
46 + "varying vec2 v_mapping;"
48 + " gl_FragColor = texture2D("
49 + " u_texture, v_mapping);"
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, //
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, //
67 private final float tableCoords[] = {
74 private final float mapCoords[] = {
81 private final float color[] = {
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",
94 private Resources res; // app resources
95 private Options options; // bitmap options
96 private int program; // opengl program
98 private float[] model; // model matrix
99 private float[] view; // view matrix
100 private float[] proj; // projection matrix
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
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
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
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
126 private Map<String,Integer> index; // card name to index map
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)
135 /* GLSurfaceView Methods */
136 public Cards(Context context)
139 Os.debug("Cards: create");
141 this.res = context.getResources();
143 this.model = new float[4*4];
144 this.view = new float[4*4];
145 this.proj = new float[4*4];
147 this.face = new int[52];
151 this.hand = "As Ks Qs Js 10s 9s 8s 7s 6s 5s 4s 3s 2s".split(" ");
152 this.pile = "Ah Ac Ad".split(" ");
155 this.index = new HashMap<String,Integer>(52);
156 for (int i = 0; i < 52; i++)
157 this.index.put(this.cards[i], i);
159 this.setEGLContextClientVersion(2);
160 this.setRenderer(this);
161 this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
164 /* Renderer methods */
166 public void onSurfaceCreated(GL10 unused, EGLConfig config)
168 Os.debug("Cards: onSurfaceCreate");
170 /* Initialize shaders */
171 int vertShader = this.loadShader(GLES20.GL_VERTEX_SHADER, vertSource);
172 int fragShader = this.loadShader(GLES20.GL_FRAGMENT_SHADER, fragSource);
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);
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");
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);
195 /* Prevent texture scaling */
196 this.options = new BitmapFactory.Options();
197 this.options.inScaled = false;
200 for (int i = 0; i < 52; i++) {
201 String name = "card_" + this.cards[i].toLowerCase();
202 this.face[i] = this.loadTexture(name);
204 this.red = this.loadTexture("card_red");
205 this.blue = this.loadTexture("card_blue");
206 this.table = this.loadTexture("table");
209 Os.debug("Cards: onSurfaceCreate");
213 public void onDrawFrame(GL10 unused)
215 //Os.debug("Cards: onDrawFrame");
217 /* Turn on the program */
218 GLES20.glUseProgram(program);
221 GLES20.glClearColor(0, 0, 0, 1);
222 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
224 /* Setup projection matricies */
225 GLES20.glUniformMatrix4fv(this.viewHandle, 1, false, this.view, 0);
226 GLES20.glUniformMatrix4fv(this.projHandle, 1, false, this.proj, 0);
228 /* Setup buffers objects */
229 GLES20.glEnableVertexAttribArray(this.vertHandle);
230 GLES20.glEnableVertexAttribArray(this.mapHandle);
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);
247 public void onSurfaceChanged(GL10 unused, int width, int height)
249 Os.debug("Cards: onSurfaceChanged");
251 GLES20.glViewport(0, 0, width, height);
253 Matrix.setIdentityM(this.model, 0);
254 Matrix.setIdentityM(this.view, 0);
255 Matrix.setIdentityM(this.proj, 0);
259 float yang = xang * ((float)height / (float)width);
261 Matrix.frustumM(this.proj, 0,
262 -1E-6f * xang, // left
263 1E-6f * xang, // right
264 -1E-6f * yang, // bottom
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);
275 public boolean onTouchEvent(MotionEvent event)
277 boolean up = event.getActionMasked() == MotionEvent.ACTION_UP;
279 float x = event.getX() / this.getWidth();
280 float y = 1-(event.getY() / this.getHeight());
284 int num = this.hand.length;
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;
290 if (y < this.ylim && !this.drag) {
291 //Os.debug("Cards: onTouchEvent - starting drag");
295 //Os.debug("Cards: onTouchEvent - move " + x + "," + y);
296 this.requestRender();
298 if (y >= this.ylim && this.drag && up) {
299 //Os.debug("Cards: onTouchEvent - playing card");
300 this.game.onPlay(this.hand[this.pick]);
303 //Os.debug("Cards: onTouchEvent - ending drag");
309 /* Private loading methods */
310 private int loadShader(int type, String code)
312 Os.debug("Cards: loadShader");
314 int shader = GLES20.glCreateShader(type);
315 GLES20.glShaderSource(shader, code);
316 GLES20.glCompileShader(shader);
320 private int loadTexture(String name)
322 //Os.debug("Cards: loadTexture - " + name);
324 final int[] tex = new int[1];
326 /* Lookup the resource ID */
329 id = R.drawable.class.getField(name).getInt(null);
330 } catch(Exception e) {
331 Os.debug("Cards: lookup failed for '" + name + "'", e);
335 /* Load the bitmap */
336 Bitmap bitmap = BitmapFactory.decodeResource(this.res, id, this.options);
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);
348 private FloatBuffer loadBuffer(float[] data)
350 ByteBuffer bytes = ByteBuffer.allocateDirect(data.length * 4);
351 bytes.order(ByteOrder.nativeOrder());
353 FloatBuffer buf = bytes.asFloatBuffer();
360 /* Private drawing methods */
361 private void drawTable()
364 Matrix.setIdentityM(this.model, 0);
365 GLES20.glUniformMatrix4fv(this.modelHandle, 1, false, this.model, 0);
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);
374 private void drawPile()
376 /* Draw played cards */
377 for (int i = 0; i < 4; i++) {
378 if (i >= this.pile.length || this.pile[i] == null)
383 Matrix.setIdentityM(this.model, 0);
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);
390 this.drawCard(this.pile[i]);
394 private void drawHand()
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)
402 Matrix.setIdentityM(this.model, 0);
404 Matrix.rotateM(this.model, 0, 45f, 1f, 0f, 0f);
405 Matrix.translateM(this.model, 0, 0f, -0.3f, 1.20f);
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);
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);
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);
424 this.drawCard(this.hand[i]);
428 private void drawPick()
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]);
439 private void drawCard(String name)
441 if (!this.index.containsKey(name))
443 int idx = this.index.get(name);
444 int front = this.face[idx];
447 /* Set model matrix */
448 GLES20.glUniformMatrix4fv(this.modelHandle, 1, false, this.model, 0);
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);
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);