tavli

view src/board.cc @ 24:0aadb519b5ee

correct highlighting
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 08 Jul 2015 15:11:58 +0300
parents 3e6430028d54
children
line source
1 #include <float.h>
2 #include "opengl.h"
3 #include "board.h"
4 #include "game.h"
5 #include "meshgen.h"
6 #include "pnoise.h"
7 #include "revol.h"
8 #include "opt.h"
9 #include "shadow.h"
12 #define HSIZE 1.0
13 #define VSIZE (2.0 * HSIZE)
14 #define BOT_THICKNESS (HSIZE * 0.01)
15 #define WALL_THICKNESS (HSIZE * 0.05)
16 #define WALL_HEIGHT (HSIZE * 0.1)
17 #define GAP (HSIZE * 0.025)
18 #define HINGE_RAD (GAP * 0.5)
19 #define HINGE_HEIGHT (VSIZE * 0.075)
20 #define PIECE_RAD (0.45 * HSIZE / 5.0)
21 #define BOARD_OFFSET (HSIZE / 2.0 + WALL_THICKNESS + HINGE_RAD * 0.25)
22 #define PIECES_PER_LAYER 5
23 #define SLOT_WIDTH (HSIZE / 5.0)
24 #define SLOT_HEIGHT (VSIZE * 0.4)
27 static const vec2_t piece_cp[] = {
28 {0, 0.25},
29 {1, 0.25}, // mid0
30 {2, 0.5},
31 {2.5, 0.5}, // mid1
32 {3, 0.5},
33 {4, 0.5}, // mid2
34 {4, 0},
35 {4, -0.5}, // mid3
36 {3, -0.5},
37 {2.5, -0.5}, // mid4
38 {0, -0.5}
39 };
40 static const BezCurve piece_curve = {
41 sizeof piece_cp / sizeof *piece_cp,
42 (vec2_t*)piece_cp,
43 0.25 * PIECE_RAD
44 };
46 #define PIECE_HEIGHT (0.25 * PIECE_RAD)
49 Piece::Piece()
50 {
51 owner = 0;
52 slot = prev_slot = -1;
53 level = 0;
54 move_start = 0;
55 }
57 void Piece::move_to(int slot, int level, bool anim)
58 {
59 int prev_slot = this->slot;
60 int prev_level = this->level;
62 this->slot = slot;
63 this->level = level;
65 if(anim) {
66 this->prev_slot = prev_slot;
67 this->prev_level = prev_level;
68 move_start = cur_time;
69 }
70 }
72 Quad::Quad()
73 {
74 }
76 Quad::Quad(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
77 : tri0(v0, v1, v2), tri1(v0, v2, v3)
78 {
79 }
81 bool Quad::intersect(const Ray &ray, HitPoint *hit) const
82 {
83 return tri0.intersect(ray, hit) || tri1.intersect(ray, hit);
84 }
86 Board::Board()
87 {
88 piece_obj = 0;
89 slot_sel = -1;
90 clear();
92 for(int i=0; i<NUM_SLOTS; i++) {
93 Vector3 p = piece_pos(i, 0);
94 bool top_side = i >= NUM_SLOTS / 2;
96 float z0 = top_side ? -PIECE_RAD : PIECE_RAD;
97 float z1 = top_side ? SLOT_HEIGHT : -SLOT_HEIGHT;
99 slotbb[i] = Quad(p + Vector3(-SLOT_WIDTH / 2.0, 0, z0),
100 p + Vector3(SLOT_WIDTH / 2.0, 0, z0),
101 p + Vector3(SLOT_WIDTH / 2.0, 0, z1),
102 p + Vector3(-SLOT_WIDTH / 2.0, 0, z1));
103 }
104 }
106 Board::~Board()
107 {
108 destroy();
109 }
111 bool Board::init()
112 {
113 if(!generate_textures()) {
114 return false;
115 }
116 if(!generate()) {
117 return false;
118 }
120 return true;
121 }
123 void Board::destroy()
124 {
125 for(size_t i=0; i<obj.size(); i++) {
126 delete obj[i];
127 }
128 obj.clear();
130 delete piece_obj;
131 piece_obj = 0;
132 }
134 void Board::clear()
135 {
136 memset(hist, 0, sizeof hist);
138 for(int i=0; i<MAX_PIECES; i++) {
139 pieces[i].owner = i < PLAYER_PIECES ? MINE : OTHER;
140 move_piece(i, -1, false);
141 }
142 }
144 void Board::setup()
145 {
146 static const int initial[] = { 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
148 clear();
150 int id = 0;
151 for(int i=0; i<NUM_SLOTS; i++) {
152 for(int j=0; j<initial[i]; j++) {
153 move_piece(id, i, false);
154 move_piece(PLAYER_PIECES + id, NUM_SLOTS - i - 1, false);
155 ++id;
156 }
157 }
158 }
160 int Board::slot_pieces(int slot) const
161 {
162 return hist[slot + 1];
163 }
165 bool Board::move_piece(int id, int slot, bool anim)
166 {
167 // TODO do validity checking first
168 int prev_slot = pieces[id].slot;
170 pieces[id].move_to(slot, slot_pieces(slot), anim);
171 --hist[prev_slot + 1];
172 ++hist[slot + 1];
173 return true;
174 }
176 Vector3 Board::piece_pos(int slot, int level) const
177 {
178 int top_side = slot / 10;
179 int sidx = (top_side ? (19 - slot) : slot) % 5;
180 int left_side = (top_side ? (19 - slot) : slot) / 5;
182 Vector3 pos;
184 if(left_side) {
185 pos.x = -(sidx * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0) - PIECE_RAD;
186 } else {
187 pos.x = (4 - sidx) * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0 + PIECE_RAD;
188 }
190 int layer = level / PIECES_PER_LAYER;
191 int layer_level = level % PIECES_PER_LAYER;
193 pos.y = (layer + 0.5) * PIECE_HEIGHT;
195 pos.z = (-VSIZE * 0.5 + PIECE_RAD + PIECE_RAD * 2.0 * layer_level);
196 if(!top_side) {
197 pos.z = -pos.z;
198 }
200 return pos;
201 }
203 int Board::slot_hit(const Ray &ray) const
204 {
205 for(int i=0; i<NUM_SLOTS; i++) {
206 if(slotbb[i].intersect(ray)) {
207 return i;
208 }
209 }
210 return -1;
211 }
213 void Board::select_slot(int idx)
214 {
215 slot_sel = idx < 0 || idx >= NUM_SLOTS ? -1 : idx;
216 }
218 int Board::get_selected_slot() const
219 {
220 return slot_sel;
221 }
223 void Board::draw() const
224 {
225 bool use_shadows = opt.shadows && sdr_shadow;
226 unsigned int board_sdr = use_shadows ? sdr_shadow : sdr_phong;
227 unsigned int piece_sdr = use_shadows ? sdr_shadow_notex : sdr_phong_notex;
229 for(size_t i=0; i<obj.size(); i++) {
230 if(wireframe) {
231 obj[i]->draw_wire();
232 obj[i]->draw_normals(0.075);
233 } else {
234 obj[i]->set_shader(board_sdr);
235 obj[i]->draw();
236 }
237 }
239 for(int i=0; i<MAX_PIECES; i++) {
240 Vector3 pos = piece_pos(pieces[i].slot, pieces[i].level);
241 piece_obj->xform().set_translation(pos);
242 piece_obj->mtl.diffuse = opt.piece_color[pieces[i].owner];
243 piece_obj->set_shader(piece_sdr);
244 piece_obj->draw();
245 }
247 // don't draw any UI stuff and highlighting polygons if we're doing
248 // the shadow buffer pass
249 if(shadow_pass)
250 return;
252 /*static const float pal[][3] = {
253 {1, 0, 0},
254 {0, 1, 0},
255 {0, 0, 1},
256 {1, 1, 0},
257 {0, 1, 1},
258 {1, 0, 1}
259 };*/
260 int idx = slot_sel % NUM_SLOTS;
261 if(idx >= 0) {
262 glUseProgram(0);
264 glPushAttrib(GL_ENABLE_BIT);
265 glDisable(GL_LIGHTING);
266 glDisable(GL_CULL_FACE);
267 glEnable(GL_DEPTH_TEST);
268 glEnable(GL_TEXTURE_2D);
269 glBindTexture(GL_TEXTURE_2D, img_highlight.texture());
270 glEnable(GL_BLEND);
271 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
273 glDepthMask(0);
275 glBegin(GL_TRIANGLES);
276 //glColor3fv(pal[idx % (sizeof pal / sizeof *pal)]);
277 glColor4f(1, 1, 1, 0.6);
278 glTexCoord2f(0, 0);
279 glVertex3f(slotbb[idx].tri0.v[0].x, slotbb[idx].tri0.v[0].y, slotbb[idx].tri0.v[0].z);
280 glTexCoord2f(1, 0);
281 glVertex3f(slotbb[idx].tri0.v[1].x, slotbb[idx].tri0.v[1].y, slotbb[idx].tri0.v[1].z);
282 glTexCoord2f(1, 1);
283 glVertex3f(slotbb[idx].tri0.v[2].x, slotbb[idx].tri0.v[2].y, slotbb[idx].tri0.v[2].z);
284 glTexCoord2f(0, 0);
285 glVertex3f(slotbb[idx].tri1.v[0].x, slotbb[idx].tri1.v[0].y, slotbb[idx].tri1.v[0].z);
286 glTexCoord2f(1, 1);
287 glVertex3f(slotbb[idx].tri1.v[1].x, slotbb[idx].tri1.v[1].y, slotbb[idx].tri1.v[1].z);
288 glTexCoord2f(0, 1);
289 glVertex3f(slotbb[idx].tri1.v[2].x, slotbb[idx].tri1.v[2].y, slotbb[idx].tri1.v[2].z);
290 glEnd();
292 glDepthMask(1);
294 glColor4f(0, 1, 0, 0.6);
295 glEnable(GL_CULL_FACE);
296 glCullFace(GL_FRONT);
297 for(int i=0; i<MAX_PIECES; i++) {
298 if(pieces[i].slot == idx) {
299 Vector3 pos = piece_pos(pieces[i].slot, pieces[i].level);
300 piece_obj->xform().set_translation(pos);
301 piece_obj->xform().scale(Vector3(1.05, 1.05, 1.05));
302 piece_obj->set_shader(0);
303 piece_obj->draw();
304 }
305 }
306 glCullFace(GL_BACK);
308 glPopAttrib();
309 }
310 }
313 bool Board::generate()
314 {
315 static const float board_spec = 0.4;
317 Mesh tmp;
318 Matrix4x4 xform;
320 obj.clear();
322 for(int i=0; i<2; i++) {
323 int sign = i * 2 - 1;
325 // generate bottom
326 Mesh *bottom = new Mesh;
327 gen_box(bottom, HSIZE, BOT_THICKNESS, HSIZE * 2.0);
328 xform.set_translation(Vector3(0, -BOT_THICKNESS / 2.0, 0));
329 bottom->apply_xform(xform);
331 Object *obottom = new Object;
332 obottom->set_mesh(bottom);
333 obottom->xform().set_translation(Vector3(sign * BOARD_OFFSET, 0, 0));
334 obottom->set_texture(img_field.texture());
335 obottom->mtl.specular = Vector3(board_spec, board_spec, board_spec);
336 obj.push_back(obottom);
339 // generate the 4 sides
340 Mesh *sides = new Mesh;
341 gen_box(sides, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
342 xform.set_translation(Vector3(-(HSIZE + WALL_THICKNESS) / 2.0,
343 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
344 sides->apply_xform(xform);
346 gen_box(&tmp, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
347 xform.set_translation(Vector3((HSIZE + WALL_THICKNESS) / 2.0,
348 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
349 tmp.apply_xform(xform);
350 sides->append(tmp);
351 tmp.clear();
353 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
354 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
355 (VSIZE + WALL_THICKNESS) / 2.0));
356 tmp.apply_xform(xform);
357 sides->append(tmp);
358 tmp.clear();
360 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
361 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
362 -(VSIZE + WALL_THICKNESS) / 2.0));
363 tmp.apply_xform(xform);
364 sides->append(tmp);
365 tmp.clear();
367 // generate texture coordinates
368 sides->texcoord_gen_box();
370 Object *osides = new Object;
371 osides->set_mesh(sides);
372 osides->xform() = obottom->xform();
373 osides->set_texture(img_wood.texture());
374 osides->tex_xform().set_scaling(Vector3(2, 2, 2));
375 osides->tex_xform().rotate(-Vector3(1, 0, 0.5), M_PI / 4.0);
376 osides->mtl.specular = Vector3(board_spec, board_spec, board_spec);
377 obj.push_back(osides);
379 }
382 // generate the hinges
383 Mesh *hinges = new Mesh;
384 for(int i=0; i<2; i++) {
385 float sign = i * 2 - 1;
387 // barrel
388 gen_cylinder(&tmp, HINGE_RAD, HINGE_HEIGHT, 8, 1, 1);
389 xform.reset_identity();
390 xform.translate(Vector3(0, WALL_HEIGHT - HINGE_RAD * 0.5, sign * VSIZE / 4.0));
391 xform.rotate(Vector3(-M_PI / 2.0, 0, 0));
392 tmp.apply_xform(xform);
393 hinges->append(tmp);
395 // flange
396 gen_plane(&tmp, HINGE_HEIGHT * 0.6, HINGE_HEIGHT * 0.8);
397 tmp.apply_xform(xform);
399 Matrix4x4 tex_xform;
400 tex_xform.set_rotation(Vector3(0, 0, M_PI / 2.0));
401 tmp.texcoord_apply_xform(tex_xform);
402 hinges->append(tmp);
404 // studs
405 for(int j=0; j<4; j++) {
406 Vector3 pos;
408 pos.x = (float)((j & 1) * 2 - 1) * HINGE_HEIGHT * 0.2;
409 pos.y = (float)((j & 2) - 1) * HINGE_HEIGHT * 0.3;
411 Matrix4x4 stud_xform = xform;
412 stud_xform.translate(pos);
414 Matrix4x4 squash;
415 squash.set_scaling(Vector3(1, 1, 0.5));
417 gen_sphere(&tmp, HINGE_RAD * 0.5, 8, 4);
418 tmp.apply_xform(stud_xform * squash);
419 hinges->append(tmp);
420 }
421 }
423 Object *ohinges = new Object;
424 ohinges->set_mesh(hinges);
425 ohinges->set_texture(img_hinge.texture());
426 obj.push_back(ohinges);
428 // debug object
429 /*
430 Mesh *dbgmesh = new Mesh;
431 gen_box(dbgmesh, 0.5, 0.5, 0.5);
432 xform.set_translation(Vector3(0, 0.4, 0));
433 xform.set_scaling(Vector3(1, 1, 1));
434 dbgmesh->apply_xform(xform);
435 Object *dbgobj = new Object;
436 dbgobj->set_mesh(dbgmesh);
437 dbgobj->set_texture(img_hinge.texture());
438 //dbgobj->tex_xform().set_scaling(Vector3(3, 3, 3));
439 obj.push_back(dbgobj);
440 */
442 Mesh *piece = new Mesh;
443 gen_revol(piece, 18, 17, bezier_revol, bezier_revol_normal, (void*)&piece_curve);
445 Object *opiece = new Object;
446 opiece->set_mesh(piece);
447 opiece->mtl.diffuse = Vector3(0.6, 0.6, 0.6);
448 opiece->mtl.specular = Vector3(0.8, 0.8, 0.8);
449 opiece->xform().set_translation(Vector3(0, 0.2, 0));
450 //obj.push_back(opiece);
452 piece_obj = opiece;
454 // meshgen stats
455 printf("Generated board:\n %u meshes\n", (unsigned int)obj.size());
456 unsigned int polycount = 0;
457 for(size_t i=0; i<obj.size(); i++) {
458 const Mesh *m = obj[i]->get_mesh();
459 polycount += m->get_poly_count();
460 }
461 printf(" %u polygons\n", polycount);
463 return true;
464 }
466 static float wood(float x, float y)
467 {
468 float u = x;
469 float v = y;
470 x += 1.0;
471 x *= 10.0;
472 y *= 20.0;
474 float len = sqrt(x * x + y * y) + turbulence2(u * 6.0, v * 12.0, 2) * 1.2 +
475 turbulence2(u * 0.5, v, 2) * 15.0;
476 float val = fmod(len, 1.0);
478 //val = val * 0.5 + 0.5;
479 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
480 }
482 static float wood_tile(float x, float y)
483 {
484 float u = x;
485 float v = y;
486 x *= 10.0;
487 y *= 10.0;
489 float val = x + pnoise2(u * 6.0, v, 6, 1) * 3.0 +
490 pturbulence2(u * 4, v * 2, 4, 2, 2) * 1.5 + pturbulence2(u * 8, v * 8, 8, 8, 2) * 0.5;
492 val = fmod(val, 1.0);
493 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
494 }
496 static bool spike(float x, float y)
497 {
498 x = fmod(x * 5.0, 1.0);
499 return y < (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x);
500 }
502 static bool circle(float x, float y, float rad)
503 {
504 x = fmod(x * 5.0, 1.0) - 0.5;
505 y = (y - 0.65) * 5.0;
506 float len = sqrt(x * x + y * y);
507 return len < rad;
508 }
510 static bool diamond(float x, float y)
511 {
512 return y >= (1.0 - (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x)) * 0.3333333 + 0.88;
513 }
515 static bool center_circle(float x, float y, float rad)
516 {
517 x = x - 0.5;
518 y = 1.0 - y;
519 return sqrt(x * x + y * y) < rad;
520 }
522 static bool field_pattern(float x, float y)
523 {
524 y = y < 0.5 ? y * 2.0 : 2.0 - y * 2.0;
525 bool inside = false;
527 inside |= (spike(x, y + 0.33333) && !spike(x, y + 0.4)) ||
528 (spike(x, y + 0.5) && !spike(x, y + 0.68));
529 inside |= (circle(x, y, 0.12) && !circle(x, y, 0.1)) || circle(x, y, 0.06);
530 inside |= (diamond(x, y) && !diamond(x, y - 0.015)) ||
531 (diamond(x, y - 0.023) && !diamond(x, y - 0.028));
532 inside |= center_circle(x, y, 0.03);
534 return inside;
535 }
537 bool Board::generate_textures()
538 {
539 // ---- board field texture ----
540 static const Vector3 wcol1 = Vector3(0.6, 0.4, 0.2);
541 static const Vector3 wcol2 = Vector3(0.53, 0.32, 0.1);
542 static const Vector3 wcol3 = Vector3(0.38, 0.25, 0.08);
544 img_field.create(1024, 1024);
545 unsigned char *pptr = img_field.pixels;
546 for(int i=0; i<img_field.height; i++) {
547 float v = (float)i / (float)img_field.height;
549 for(int j=0; j<img_field.width; j++) {
550 float u = (float)j / (float)img_field.width;
552 // pattern mask
553 bool inside = field_pattern(u, v);
555 float wood_val = wood(u, v);
556 Vector3 wood_color = lerp(wcol1, wcol2, wood_val) * 0.9;
557 if(inside) {
558 wood_color = lerp(wcol1, wcol2, 1.0 - wood_val) * 2.0;
559 }
561 int r = (int)(wood_color.x * 255.0);
562 int g = (int)(wood_color.y * 255.0);
563 int b = (int)(wood_color.z * 255.0);
565 pptr[0] = r > 255 ? 255 : r;
566 pptr[1] = g > 255 ? 255 : g;
567 pptr[2] = b > 255 ? 255 : b;
568 pptr[3] = 255;
569 pptr += 4;
570 }
571 }
572 img_field.texture();
575 // ---- slot highlighting texture ----
576 img_highlight.create(128, 256);
577 pptr = img_highlight.pixels;
578 for(int i=0; i<img_highlight.height; i++) {
579 float v = (float)i / (float)img_highlight.height;
580 for(int j=0; j<img_highlight.width; j++) {
581 float u = (float)j / (float)img_highlight.width;
583 bool inside = field_pattern(u / 5.0, v * 0.445);
585 pptr[0] = 255;
586 pptr[1] = 255;
587 pptr[2] = 255;
588 pptr[3] = inside ? 255 : 0;
589 pptr += 4;
590 }
591 }
592 img_highlight.texture();
594 float kern[] = {1, 5, 11, 18, 22, 18, 11, 5, 1};
595 convolve_horiz_image(&img_highlight, kern, sizeof kern / sizeof *kern);
596 convolve_vert_image(&img_highlight, kern, sizeof kern / sizeof *kern);
598 // ---- generic wood texture ----
599 img_wood.create(256, 256);
600 pptr = img_wood.pixels;
601 for(int i=0; i<img_wood.height; i++) {
602 float v = (float)i / (float)img_wood.height;
603 for(int j=0; j<img_wood.width; j++) {
604 float u = (float)j / (float)img_wood.width;
606 float wood_val = wood_tile(u, v);
607 Vector3 wood_color = lerp(wcol2, wcol3, wood_val) * 0.7;
609 int r = (int)(wood_color.x * 255.0);
610 int g = (int)(wood_color.y * 255.0);
611 int b = (int)(wood_color.z * 255.0);
613 pptr[0] = r > 255 ? 255 : r;
614 pptr[1] = g > 255 ? 255 : g;
615 pptr[2] = b > 255 ? 255 : b;
616 pptr[3] = 255;
617 pptr += 4;
618 }
619 }
620 img_wood.texture();
622 // ---- metal hinge diffuse texture ----
623 Vector3 rusty_col1 = Vector3(0.43, 0.46, 0.52);
624 Vector3 rusty_col2 = Vector3(0.52, 0.47, 0.43);
626 img_hinge.create(128, 128);
627 pptr = img_hinge.pixels;
628 for(int i=0; i<img_hinge.height; i++) {
629 float v = (float)i / (float)img_hinge.height;
630 for(int j=0; j<img_hinge.width; j++) {
631 float u = (float)j / (float)img_hinge.width;
633 // rust pattern
634 float w1 = fbm2(u * 4.0, v * 4.0, 3) * 0.5 + 0.5;
635 //float w2 = fbm2(u * 8.0, v * 8.0, 1) * 0.5 + 0.5;
636 Vector3 col = lerp(rusty_col1, rusty_col2 * 0.5, w1);
638 // center hinge split
639 if(fabs(v - 0.5) < 0.01) {
640 col *= 0.5;
641 }
643 int r = (int)(col.x * 255.0);
644 int g = (int)(col.y * 255.0);
645 int b = (int)(col.z * 255.0);
647 pptr[0] = r > 255 ? 255 : (r < 0 ? 0 : r);
648 pptr[1] = g > 255 ? 255 : (g < 0 ? 0 : g);
649 pptr[2] = b > 255 ? 255 : (b < 0 ? 0 : b);
650 pptr[3] = 255;
652 pptr += 4;
653 }
654 }
655 img_hinge.texture();
657 return true;
658 }