tavli

view src/board.cc @ 21:c3fbf9616dbd

slot bounds, and ray testing
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 02 Jul 2015 00:01:39 +0300
parents 37dead56f01e
children c2a2069a49ec
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"
11 #define HSIZE 1.0
12 #define VSIZE (2.0 * HSIZE)
13 #define BOT_THICKNESS (HSIZE * 0.01)
14 #define WALL_THICKNESS (HSIZE * 0.05)
15 #define WALL_HEIGHT (HSIZE * 0.1)
16 #define GAP (HSIZE * 0.025)
17 #define HINGE_RAD (GAP * 0.5)
18 #define HINGE_HEIGHT (VSIZE * 0.075)
19 #define PIECE_RAD (0.45 * HSIZE / 5.0)
20 #define BOARD_OFFSET (HSIZE / 2.0 + WALL_THICKNESS + HINGE_RAD * 0.25)
21 #define PIECES_PER_LAYER 5
22 #define SLOT_WIDTH (HSIZE / 5.0)
23 #define SLOT_HEIGHT (VSIZE * 0.4)
26 static const vec2_t piece_cp[] = {
27 {0, 0.25},
28 {1, 0.25}, // mid0
29 {2, 0.5},
30 {2.5, 0.5}, // mid1
31 {3, 0.5},
32 {4, 0.5}, // mid2
33 {4, 0},
34 {4, -0.5}, // mid3
35 {3, -0.5},
36 {2.5, -0.5}, // mid4
37 {0, -0.5}
38 };
39 static const BezCurve piece_curve = {
40 sizeof piece_cp / sizeof *piece_cp,
41 (vec2_t*)piece_cp,
42 0.25 * PIECE_RAD
43 };
45 #define PIECE_HEIGHT (0.25 * PIECE_RAD)
48 Piece::Piece()
49 {
50 owner = 0;
51 slot = prev_slot = -1;
52 level = 0;
53 move_start = 0;
54 }
56 void Piece::move_to(int slot, int level, bool anim)
57 {
58 int prev_slot = this->slot;
59 int prev_level = this->level;
61 this->slot = slot;
62 this->level = level;
64 if(anim) {
65 this->prev_slot = prev_slot;
66 this->prev_level = prev_level;
67 move_start = cur_time;
68 }
69 }
71 Quad::Quad()
72 {
73 }
75 Quad::Quad(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3)
76 : tri0(v0, v1, v2), tri1(v0, v2, v3)
77 {
78 }
80 bool Quad::intersect(const Ray &ray, HitPoint *hit) const
81 {
82 return tri0.intersect(ray, hit) || tri1.intersect(ray, hit);
83 }
85 Board::Board()
86 {
87 piece_obj = 0;
88 clear();
90 for(int i=0; i<NUM_SLOTS; i++) {
91 Vector3 p = piece_pos(i, 0);
92 bool top_side = i >= NUM_SLOTS / 2;
94 float z0 = top_side ? -PIECE_RAD : PIECE_RAD;
95 float z1 = top_side ? SLOT_HEIGHT : -SLOT_HEIGHT;
97 slotbb[i] = Quad(p + Vector3(-SLOT_WIDTH / 2.0, 0, z0),
98 p + Vector3(SLOT_WIDTH / 2.0, 0, z0),
99 p + Vector3(SLOT_WIDTH / 2.0, 0, z1),
100 p + Vector3(-SLOT_WIDTH / 2.0, 0, z1));
101 }
102 }
104 Board::~Board()
105 {
106 destroy();
107 }
109 bool Board::init()
110 {
111 if(!generate_textures()) {
112 return false;
113 }
114 if(!generate()) {
115 return false;
116 }
118 return true;
119 }
121 void Board::destroy()
122 {
123 for(size_t i=0; i<obj.size(); i++) {
124 delete obj[i];
125 }
126 obj.clear();
128 delete piece_obj;
129 piece_obj = 0;
130 }
132 void Board::clear()
133 {
134 memset(hist, 0, sizeof hist);
136 for(int i=0; i<MAX_PIECES; i++) {
137 pieces[i].owner = i < PLAYER_PIECES ? MINE : OTHER;
138 move_piece(i, -1, false);
139 }
140 }
142 void Board::setup()
143 {
144 static const int initial[] = { 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
146 clear();
148 int id = 0;
149 for(int i=0; i<NUM_SLOTS; i++) {
150 for(int j=0; j<initial[i]; j++) {
151 move_piece(id, i, false);
152 move_piece(PLAYER_PIECES + id, NUM_SLOTS - i - 1, false);
153 ++id;
154 }
155 }
156 }
158 int Board::slot_pieces(int slot) const
159 {
160 return hist[slot + 1];
161 }
163 bool Board::move_piece(int id, int slot, bool anim)
164 {
165 // TODO do validity checking first
166 int prev_slot = pieces[id].slot;
168 pieces[id].move_to(slot, slot_pieces(slot), anim);
169 --hist[prev_slot + 1];
170 ++hist[slot + 1];
171 return true;
172 }
174 Vector3 Board::piece_pos(int slot, int level) const
175 {
176 int top_side = slot / 10;
177 int sidx = (top_side ? (19 - slot) : slot) % 5;
178 int left_side = (top_side ? (19 - slot) : slot) / 5;
180 Vector3 pos;
182 if(left_side) {
183 pos.x = -(sidx * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0) - PIECE_RAD;
184 } else {
185 pos.x = (4 - sidx) * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0 + PIECE_RAD;
186 }
188 int layer = level / PIECES_PER_LAYER;
189 int layer_level = level % PIECES_PER_LAYER;
191 pos.y = (layer + 0.5) * PIECE_HEIGHT;
193 pos.z = (-VSIZE * 0.5 + PIECE_RAD + PIECE_RAD * 2.0 * layer_level);
194 if(!top_side) {
195 pos.z = -pos.z;
196 }
198 return pos;
199 }
201 int Board::slot_hit(const Ray &ray) const
202 {
203 for(int i=0; i<NUM_SLOTS; i++) {
204 if(slotbb[i].intersect(ray)) {
205 return i;
206 }
207 }
208 return -1;
209 }
211 void Board::draw() const
212 {
213 bool use_shadows = opt.shadows && sdr_shadow;
214 unsigned int board_sdr = use_shadows ? sdr_shadow : sdr_phong;
215 unsigned int piece_sdr = use_shadows ? sdr_shadow_notex : sdr_phong_notex;
217 for(size_t i=0; i<obj.size(); i++) {
218 if(wireframe) {
219 obj[i]->draw_wire();
220 obj[i]->draw_normals(0.075);
221 } else {
222 obj[i]->set_shader(board_sdr);
223 obj[i]->draw();
224 }
225 }
227 for(int i=0; i<MAX_PIECES; i++) {
228 Vector3 pos = piece_pos(pieces[i].slot, pieces[i].level);
229 piece_obj->xform().set_translation(pos);
230 piece_obj->mtl.diffuse = opt.piece_color[pieces[i].owner];
231 piece_obj->set_shader(piece_sdr);
232 piece_obj->draw();
233 }
235 // draw the slot bounds
236 /*
237 static const float pal[][3] = {
238 {1, 0, 0},
239 {0, 1, 0},
240 {0, 0, 1},
241 {1, 1, 0},
242 {0, 1, 1},
243 {1, 0, 1}
244 };
245 int idx = dbg_int % NUM_SLOTS;
246 if(idx >= 0) {
247 glUseProgram(0);
249 glPushAttrib(GL_ENABLE_BIT);
250 glDisable(GL_LIGHTING);
251 glDisable(GL_CULL_FACE);
252 glDisable(GL_DEPTH_TEST);
254 glBegin(GL_TRIANGLES);
255 glColor3fv(pal[idx % (sizeof pal / sizeof *pal)]);
256 glVertex3f(slotbb[idx].tri0.v[0].x, slotbb[idx].tri0.v[0].y, slotbb[idx].tri0.v[0].z);
257 glVertex3f(slotbb[idx].tri0.v[1].x, slotbb[idx].tri0.v[1].y, slotbb[idx].tri0.v[1].z);
258 glVertex3f(slotbb[idx].tri0.v[2].x, slotbb[idx].tri0.v[2].y, slotbb[idx].tri0.v[2].z);
259 glVertex3f(slotbb[idx].tri1.v[0].x, slotbb[idx].tri1.v[0].y, slotbb[idx].tri1.v[0].z);
260 glVertex3f(slotbb[idx].tri1.v[1].x, slotbb[idx].tri1.v[1].y, slotbb[idx].tri1.v[1].z);
261 glVertex3f(slotbb[idx].tri1.v[2].x, slotbb[idx].tri1.v[2].y, slotbb[idx].tri1.v[2].z);
262 glEnd();
264 glPopAttrib();
265 }
266 */
267 // TODO slot highlighting
268 }
271 bool Board::generate()
272 {
273 static const float board_spec = 0.4;
275 Mesh tmp;
276 Matrix4x4 xform;
278 obj.clear();
280 for(int i=0; i<2; i++) {
281 int sign = i * 2 - 1;
283 // generate bottom
284 Mesh *bottom = new Mesh;
285 gen_box(bottom, HSIZE, BOT_THICKNESS, HSIZE * 2.0);
286 xform.set_translation(Vector3(0, -BOT_THICKNESS / 2.0, 0));
287 bottom->apply_xform(xform);
289 Object *obottom = new Object;
290 obottom->set_mesh(bottom);
291 obottom->xform().set_translation(Vector3(sign * BOARD_OFFSET, 0, 0));
292 obottom->set_texture(img_field.texture());
293 obottom->mtl.specular = Vector3(board_spec, board_spec, board_spec);
294 obj.push_back(obottom);
297 // generate the 4 sides
298 Mesh *sides = new Mesh;
299 gen_box(sides, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
300 xform.set_translation(Vector3(-(HSIZE + WALL_THICKNESS) / 2.0,
301 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
302 sides->apply_xform(xform);
304 gen_box(&tmp, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
305 xform.set_translation(Vector3((HSIZE + WALL_THICKNESS) / 2.0,
306 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
307 tmp.apply_xform(xform);
308 sides->append(tmp);
309 tmp.clear();
311 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
312 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
313 (VSIZE + WALL_THICKNESS) / 2.0));
314 tmp.apply_xform(xform);
315 sides->append(tmp);
316 tmp.clear();
318 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
319 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
320 -(VSIZE + WALL_THICKNESS) / 2.0));
321 tmp.apply_xform(xform);
322 sides->append(tmp);
323 tmp.clear();
325 // generate texture coordinates
326 sides->texcoord_gen_box();
328 Object *osides = new Object;
329 osides->set_mesh(sides);
330 osides->xform() = obottom->xform();
331 osides->set_texture(img_wood.texture());
332 osides->tex_xform().set_scaling(Vector3(2, 2, 2));
333 osides->tex_xform().rotate(-Vector3(1, 0, 0.5), M_PI / 4.0);
334 osides->mtl.specular = Vector3(board_spec, board_spec, board_spec);
335 obj.push_back(osides);
337 }
340 // generate the hinges
341 Mesh *hinges = new Mesh;
342 for(int i=0; i<2; i++) {
343 float sign = i * 2 - 1;
345 // barrel
346 gen_cylinder(&tmp, HINGE_RAD, HINGE_HEIGHT, 8, 1, 1);
347 xform.reset_identity();
348 xform.translate(Vector3(0, WALL_HEIGHT - HINGE_RAD * 0.5, sign * VSIZE / 4.0));
349 xform.rotate(Vector3(-M_PI / 2.0, 0, 0));
350 tmp.apply_xform(xform);
351 hinges->append(tmp);
353 // flange
354 gen_plane(&tmp, HINGE_HEIGHT * 0.6, HINGE_HEIGHT * 0.8);
355 tmp.apply_xform(xform);
357 Matrix4x4 tex_xform;
358 tex_xform.set_rotation(Vector3(0, 0, M_PI / 2.0));
359 tmp.texcoord_apply_xform(tex_xform);
360 hinges->append(tmp);
362 // studs
363 for(int j=0; j<4; j++) {
364 Vector3 pos;
366 pos.x = (float)((j & 1) * 2 - 1) * HINGE_HEIGHT * 0.2;
367 pos.y = (float)((j & 2) - 1) * HINGE_HEIGHT * 0.3;
369 Matrix4x4 stud_xform = xform;
370 stud_xform.translate(pos);
372 Matrix4x4 squash;
373 squash.set_scaling(Vector3(1, 1, 0.5));
375 gen_sphere(&tmp, HINGE_RAD * 0.5, 8, 4);
376 tmp.apply_xform(stud_xform * squash);
377 hinges->append(tmp);
378 }
379 }
381 Object *ohinges = new Object;
382 ohinges->set_mesh(hinges);
383 ohinges->set_texture(img_hinge.texture());
384 obj.push_back(ohinges);
386 // debug object
387 /*
388 Mesh *dbgmesh = new Mesh;
389 gen_box(dbgmesh, 0.5, 0.5, 0.5);
390 xform.set_translation(Vector3(0, 0.4, 0));
391 xform.set_scaling(Vector3(1, 1, 1));
392 dbgmesh->apply_xform(xform);
393 Object *dbgobj = new Object;
394 dbgobj->set_mesh(dbgmesh);
395 dbgobj->set_texture(img_hinge.texture());
396 //dbgobj->tex_xform().set_scaling(Vector3(3, 3, 3));
397 obj.push_back(dbgobj);
398 */
400 Mesh *piece = new Mesh;
401 gen_revol(piece, 18, 17, bezier_revol, bezier_revol_normal, (void*)&piece_curve);
403 Object *opiece = new Object;
404 opiece->set_mesh(piece);
405 opiece->mtl.diffuse = Vector3(0.6, 0.6, 0.6);
406 opiece->mtl.specular = Vector3(0.8, 0.8, 0.8);
407 opiece->xform().set_translation(Vector3(0, 0.2, 0));
408 //obj.push_back(opiece);
410 piece_obj = opiece;
412 // meshgen stats
413 printf("Generated board:\n %u meshes\n", (unsigned int)obj.size());
414 unsigned int polycount = 0;
415 for(size_t i=0; i<obj.size(); i++) {
416 const Mesh *m = obj[i]->get_mesh();
417 polycount += m->get_poly_count();
418 }
419 printf(" %u polygons\n", polycount);
421 return true;
422 }
424 static float wood(float x, float y)
425 {
426 float u = x;
427 float v = y;
428 x += 1.0;
429 x *= 10.0;
430 y *= 20.0;
432 float len = sqrt(x * x + y * y) + turbulence2(u * 6.0, v * 12.0, 2) * 1.2 +
433 turbulence2(u * 0.5, v, 2) * 15.0;
434 float val = fmod(len, 1.0);
436 //val = val * 0.5 + 0.5;
437 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
438 }
440 static float wood_tile(float x, float y)
441 {
442 float u = x;
443 float v = y;
444 x *= 10.0;
445 y *= 10.0;
447 float val = x + pnoise2(u * 6.0, v, 6, 1) * 3.0 +
448 pturbulence2(u * 4, v * 2, 4, 2, 2) * 1.5 + pturbulence2(u * 8, v * 8, 8, 8, 2) * 0.5;
450 val = fmod(val, 1.0);
451 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
452 }
454 static bool spike(float x, float y)
455 {
456 x = fmod(x * 5.0, 1.0);
457 return y < (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x);
458 }
460 static bool circle(float x, float y, float rad)
461 {
462 x = fmod(x * 5.0, 1.0) - 0.5;
463 y = (y - 0.65) * 5.0;
464 float len = sqrt(x * x + y * y);
465 return len < rad;
466 }
468 static bool diamond(float x, float y)
469 {
470 return y >= (1.0 - (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x)) * 0.3333333 + 0.88;
471 }
473 static bool center_circle(float x, float y, float rad)
474 {
475 x = x - 0.5;
476 y = 1.0 - y;
477 return sqrt(x * x + y * y) < rad;
478 }
480 bool Board::generate_textures()
481 {
482 // ---- board field texture ----
483 static const Vector3 wcol1 = Vector3(0.6, 0.4, 0.2);
484 static const Vector3 wcol2 = Vector3(0.53, 0.32, 0.1);
485 static const Vector3 wcol3 = Vector3(0.38, 0.25, 0.08);
487 img_field.create(1024, 1024);
488 unsigned char *pptr = img_field.pixels;
489 for(int i=0; i<img_field.height; i++) {
490 float v = (float)i / (float)img_field.height;
492 for(int j=0; j<img_field.width; j++) {
493 float u = (float)j / (float)img_field.width;
495 int r = 0, g = 0, b = 0;
497 float wood_val = wood(u, v);
499 // pattern mask
500 float x = u;
501 float y = v < 0.5 ? v * 2.0 : 2.0 - v * 2.0;
502 bool inside = false;
504 inside |= (spike(x, y + 0.33333) && !spike(x, y + 0.4)) ||
505 (spike(x, y + 0.5) && !spike(x, y + 0.68));
506 inside |= (circle(x, y, 0.12) && !circle(x, y, 0.1)) || circle(x, y, 0.06);
507 inside |= (diamond(x, y) && !diamond(x, y - 0.015)) ||
508 (diamond(x, y - 0.023) && !diamond(x, y - 0.028));
509 inside |= center_circle(x, y, 0.03);
511 Vector3 wood_color = lerp(wcol1, wcol2, wood_val) * 0.9;
512 if(inside) {
513 wood_color = lerp(wcol1, wcol2, 1.0 - wood_val) * 2.0;
514 }
516 r = (int)(wood_color.x * 255.0);
517 g = (int)(wood_color.y * 255.0);
518 b = (int)(wood_color.z * 255.0);
520 pptr[0] = r > 255 ? 255 : r;
521 pptr[1] = g > 255 ? 255 : g;
522 pptr[2] = b > 255 ? 255 : b;
523 pptr += 3;
524 }
525 }
526 img_field.texture();
528 // ---- generic wood texture ----
529 img_wood.create(256, 256);
530 pptr = img_wood.pixels;
531 for(int i=0; i<img_wood.height; i++) {
532 float v = (float)i / (float)img_wood.height;
533 for(int j=0; j<img_wood.width; j++) {
534 float u = (float)j / (float)img_wood.width;
536 float wood_val = wood_tile(u, v);
537 Vector3 wood_color = lerp(wcol2, wcol3, wood_val) * 0.7;
539 int r = (int)(wood_color.x * 255.0);
540 int g = (int)(wood_color.y * 255.0);
541 int b = (int)(wood_color.z * 255.0);
543 pptr[0] = r > 255 ? 255 : r;
544 pptr[1] = g > 255 ? 255 : g;
545 pptr[2] = b > 255 ? 255 : b;
546 pptr += 3;
547 }
548 }
549 img_wood.texture();
551 // ---- metal hinge diffuse texture ----
552 Vector3 rusty_col1 = Vector3(0.43, 0.46, 0.52);
553 Vector3 rusty_col2 = Vector3(0.52, 0.47, 0.43);
555 img_hinge.create(128, 128);
556 pptr = img_hinge.pixels;
557 for(int i=0; i<img_hinge.height; i++) {
558 float v = (float)i / (float)img_hinge.height;
559 for(int j=0; j<img_hinge.width; j++) {
560 float u = (float)j / (float)img_hinge.width;
562 // rust pattern
563 float w1 = fbm2(u * 4.0, v * 4.0, 3) * 0.5 + 0.5;
564 //float w2 = fbm2(u * 8.0, v * 8.0, 1) * 0.5 + 0.5;
565 Vector3 col = lerp(rusty_col1, rusty_col2 * 0.5, w1);
567 // center hinge split
568 if(fabs(v - 0.5) < 0.01) {
569 col *= 0.5;
570 }
572 int r = (int)(col.x * 255.0);
573 int g = (int)(col.y * 255.0);
574 int b = (int)(col.z * 255.0);
576 pptr[0] = r > 255 ? 255 : (r < 0 ? 0 : r);
577 pptr[1] = g > 255 ? 255 : (g < 0 ? 0 : g);
578 pptr[2] = b > 255 ? 255 : (b < 0 ? 0 : b);
580 pptr += 3;
581 }
582 }
583 img_hinge.texture();
585 return true;
586 }