tavli

view src/board.cc @ 19:37dead56f01e

fixed shadows
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 29 Jun 2015 06:18:45 +0300
parents 986c0b76513f
children c3fbf9616dbd
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
24 static const vec2_t piece_cp[] = {
25 {0, 0.25},
26 {1, 0.25}, // mid0
27 {2, 0.5},
28 {2.5, 0.5}, // mid1
29 {3, 0.5},
30 {4, 0.5}, // mid2
31 {4, 0},
32 {4, -0.5}, // mid3
33 {3, -0.5},
34 {2.5, -0.5}, // mid4
35 {0, -0.5}
36 };
37 static const BezCurve piece_curve = {
38 sizeof piece_cp / sizeof *piece_cp,
39 (vec2_t*)piece_cp,
40 0.25 * PIECE_RAD
41 };
43 #define PIECE_HEIGHT (0.25 * PIECE_RAD)
46 Piece::Piece()
47 {
48 owner = 0;
49 slot = prev_slot = -1;
50 level = 0;
51 move_start = 0;
52 }
54 void Piece::move_to(int slot, int level, bool anim)
55 {
56 int prev_slot = this->slot;
57 int prev_level = this->level;
59 this->slot = slot;
60 this->level = level;
62 if(anim) {
63 this->prev_slot = prev_slot;
64 this->prev_level = prev_level;
65 move_start = cur_time;
66 }
67 }
70 Board::Board()
71 {
72 piece_obj = 0;
73 clear();
74 }
76 Board::~Board()
77 {
78 destroy();
79 }
81 bool Board::init()
82 {
83 if(!generate_textures()) {
84 return false;
85 }
86 if(!generate()) {
87 return false;
88 }
90 return true;
91 }
93 void Board::destroy()
94 {
95 for(size_t i=0; i<obj.size(); i++) {
96 delete obj[i];
97 }
98 obj.clear();
100 delete piece_obj;
101 piece_obj = 0;
102 }
104 void Board::clear()
105 {
106 memset(hist, 0, sizeof hist);
108 for(int i=0; i<MAX_PIECES; i++) {
109 pieces[i].owner = i < PLAYER_PIECES ? MINE : OTHER;
110 move_piece(i, -1, false);
111 }
112 }
114 void Board::setup()
115 {
116 static const int initial[] = { 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
118 clear();
120 int id = 0;
121 for(int i=0; i<NUM_SLOTS; i++) {
122 for(int j=0; j<initial[i]; j++) {
123 move_piece(id, i, false);
124 move_piece(PLAYER_PIECES + id, NUM_SLOTS - i - 1, false);
125 ++id;
126 }
127 }
128 }
130 int Board::slot_pieces(int slot) const
131 {
132 return hist[slot + 1];
133 }
135 bool Board::move_piece(int id, int slot, bool anim)
136 {
137 // TODO do validity checking first
138 int prev_slot = pieces[id].slot;
140 pieces[id].move_to(slot, slot_pieces(slot), anim);
141 --hist[prev_slot + 1];
142 ++hist[slot + 1];
143 return true;
144 }
146 Vector3 Board::piece_pos(int slot, int level) const
147 {
148 int top_side = slot / 10;
149 int sidx = (top_side ? (19 - slot) : slot) % 5;
150 int left_side = (top_side ? (19 - slot) : slot) / 5;
152 Vector3 pos;
154 if(left_side) {
155 pos.x = -(sidx * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0) - PIECE_RAD;
156 } else {
157 pos.x = (4 - sidx) * HSIZE / 5.0 + BOARD_OFFSET - HSIZE / 2.0 + PIECE_RAD;
158 }
160 int layer = level / PIECES_PER_LAYER;
161 int layer_level = level % PIECES_PER_LAYER;
163 pos.y = (layer + 0.5) * PIECE_HEIGHT;
165 pos.z = (-VSIZE * 0.5 + PIECE_RAD + PIECE_RAD * 2.0 * layer_level);
166 if(top_side) {
167 pos.z = -pos.z;
168 }
170 return pos;
171 }
173 void Board::draw() const
174 {
175 bool use_shadows = opt.shadows && sdr_shadow;
176 unsigned int board_sdr = use_shadows ? sdr_shadow : sdr_phong;
177 unsigned int piece_sdr = use_shadows ? sdr_shadow_notex : sdr_phong_notex;
179 for(size_t i=0; i<obj.size(); i++) {
180 if(wireframe) {
181 obj[i]->draw_wire();
182 obj[i]->draw_normals(0.075);
183 } else {
184 obj[i]->set_shader(board_sdr);
185 obj[i]->draw();
186 }
187 }
189 for(int i=0; i<MAX_PIECES; i++) {
190 Vector3 pos = piece_pos(pieces[i].slot, pieces[i].level);
191 piece_obj->xform().set_translation(pos);
192 piece_obj->mtl.diffuse = opt.piece_color[pieces[i].owner];
193 piece_obj->set_shader(piece_sdr);
194 piece_obj->draw();
195 }
196 }
199 bool Board::generate()
200 {
201 static const float board_spec = 0.4;
203 Mesh tmp;
204 Matrix4x4 xform;
206 obj.clear();
208 for(int i=0; i<2; i++) {
209 int sign = i * 2 - 1;
211 // generate bottom
212 Mesh *bottom = new Mesh;
213 gen_box(bottom, HSIZE, BOT_THICKNESS, HSIZE * 2.0);
214 xform.set_translation(Vector3(0, -BOT_THICKNESS / 2.0, 0));
215 bottom->apply_xform(xform);
217 Object *obottom = new Object;
218 obottom->set_mesh(bottom);
219 obottom->xform().set_translation(Vector3(sign * BOARD_OFFSET, 0, 0));
220 obottom->set_texture(img_field.texture());
221 obottom->mtl.specular = Vector3(board_spec, board_spec, board_spec);
222 obj.push_back(obottom);
225 // generate the 4 sides
226 Mesh *sides = new Mesh;
227 gen_box(sides, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
228 xform.set_translation(Vector3(-(HSIZE + WALL_THICKNESS) / 2.0,
229 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
230 sides->apply_xform(xform);
232 gen_box(&tmp, WALL_THICKNESS, WALL_HEIGHT, VSIZE + WALL_THICKNESS * 2);
233 xform.set_translation(Vector3((HSIZE + WALL_THICKNESS) / 2.0,
234 WALL_HEIGHT / 2.0 - BOT_THICKNESS, 0));
235 tmp.apply_xform(xform);
236 sides->append(tmp);
237 tmp.clear();
239 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
240 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
241 (VSIZE + WALL_THICKNESS) / 2.0));
242 tmp.apply_xform(xform);
243 sides->append(tmp);
244 tmp.clear();
246 gen_box(&tmp, HSIZE, WALL_HEIGHT, WALL_THICKNESS);
247 xform.set_translation(Vector3(0, WALL_HEIGHT / 2.0 - BOT_THICKNESS,
248 -(VSIZE + WALL_THICKNESS) / 2.0));
249 tmp.apply_xform(xform);
250 sides->append(tmp);
251 tmp.clear();
253 // generate texture coordinates
254 sides->texcoord_gen_box();
256 Object *osides = new Object;
257 osides->set_mesh(sides);
258 osides->xform() = obottom->xform();
259 osides->set_texture(img_wood.texture());
260 osides->tex_xform().set_scaling(Vector3(2, 2, 2));
261 osides->tex_xform().rotate(-Vector3(1, 0, 0.5), M_PI / 4.0);
262 osides->mtl.specular = Vector3(board_spec, board_spec, board_spec);
263 obj.push_back(osides);
265 }
268 // generate the hinges
269 Mesh *hinges = new Mesh;
270 for(int i=0; i<2; i++) {
271 float sign = i * 2 - 1;
273 // barrel
274 gen_cylinder(&tmp, HINGE_RAD, HINGE_HEIGHT, 8, 1, 1);
275 xform.reset_identity();
276 xform.translate(Vector3(0, WALL_HEIGHT - HINGE_RAD * 0.5, sign * VSIZE / 4.0));
277 xform.rotate(Vector3(-M_PI / 2.0, 0, 0));
278 tmp.apply_xform(xform);
279 hinges->append(tmp);
281 // flange
282 gen_plane(&tmp, HINGE_HEIGHT * 0.6, HINGE_HEIGHT * 0.8);
283 tmp.apply_xform(xform);
285 Matrix4x4 tex_xform;
286 tex_xform.set_rotation(Vector3(0, 0, M_PI / 2.0));
287 tmp.texcoord_apply_xform(tex_xform);
288 hinges->append(tmp);
290 // studs
291 for(int j=0; j<4; j++) {
292 Vector3 pos;
294 pos.x = (float)((j & 1) * 2 - 1) * HINGE_HEIGHT * 0.2;
295 pos.y = (float)((j & 2) - 1) * HINGE_HEIGHT * 0.3;
297 Matrix4x4 stud_xform = xform;
298 stud_xform.translate(pos);
300 Matrix4x4 squash;
301 squash.set_scaling(Vector3(1, 1, 0.5));
303 gen_sphere(&tmp, HINGE_RAD * 0.5, 8, 4);
304 tmp.apply_xform(stud_xform * squash);
305 hinges->append(tmp);
306 }
307 }
309 Object *ohinges = new Object;
310 ohinges->set_mesh(hinges);
311 ohinges->set_texture(img_hinge.texture());
312 obj.push_back(ohinges);
314 // debug object
315 /*
316 Mesh *dbgmesh = new Mesh;
317 gen_box(dbgmesh, 0.5, 0.5, 0.5);
318 xform.set_translation(Vector3(0, 0.4, 0));
319 xform.set_scaling(Vector3(1, 1, 1));
320 dbgmesh->apply_xform(xform);
321 Object *dbgobj = new Object;
322 dbgobj->set_mesh(dbgmesh);
323 dbgobj->set_texture(img_hinge.texture());
324 //dbgobj->tex_xform().set_scaling(Vector3(3, 3, 3));
325 obj.push_back(dbgobj);
326 */
328 Mesh *piece = new Mesh;
329 gen_revol(piece, 18, 17, bezier_revol, bezier_revol_normal, (void*)&piece_curve);
331 Object *opiece = new Object;
332 opiece->set_mesh(piece);
333 opiece->mtl.diffuse = Vector3(0.6, 0.6, 0.6);
334 opiece->mtl.specular = Vector3(0.8, 0.8, 0.8);
335 opiece->xform().set_translation(Vector3(0, 0.2, 0));
336 //obj.push_back(opiece);
338 piece_obj = opiece;
340 // meshgen stats
341 printf("Generated board:\n %u meshes\n", (unsigned int)obj.size());
342 unsigned int polycount = 0;
343 for(size_t i=0; i<obj.size(); i++) {
344 const Mesh *m = obj[i]->get_mesh();
345 polycount += m->get_poly_count();
346 }
347 printf(" %u polygons\n", polycount);
349 return true;
350 }
352 static float wood(float x, float y)
353 {
354 float u = x;
355 float v = y;
356 x += 1.0;
357 x *= 10.0;
358 y *= 20.0;
360 float len = sqrt(x * x + y * y) + turbulence2(u * 6.0, v * 12.0, 2) * 1.2 +
361 turbulence2(u * 0.5, v, 2) * 15.0;
362 float val = fmod(len, 1.0);
364 //val = val * 0.5 + 0.5;
365 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
366 }
368 static float wood_tile(float x, float y)
369 {
370 float u = x;
371 float v = y;
372 x *= 10.0;
373 y *= 10.0;
375 float val = x + pnoise2(u * 6.0, v, 6, 1) * 3.0 +
376 pturbulence2(u * 4, v * 2, 4, 2, 2) * 1.5 + pturbulence2(u * 8, v * 8, 8, 8, 2) * 0.5;
378 val = fmod(val, 1.0);
379 return val < 0.0 ? 0.0 : (val > 1.0 ? 1.0 : val);
380 }
382 static bool spike(float x, float y)
383 {
384 x = fmod(x * 5.0, 1.0);
385 return y < (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x);
386 }
388 static bool circle(float x, float y, float rad)
389 {
390 x = fmod(x * 5.0, 1.0) - 0.5;
391 y = (y - 0.65) * 5.0;
392 float len = sqrt(x * x + y * y);
393 return len < rad;
394 }
396 static bool diamond(float x, float y)
397 {
398 return y >= (1.0 - (x < 0.5 ? 2.0 * x : 2.0 - 2.0 * x)) * 0.3333333 + 0.88;
399 }
401 static bool center_circle(float x, float y, float rad)
402 {
403 x = x - 0.5;
404 y = 1.0 - y;
405 return sqrt(x * x + y * y) < rad;
406 }
408 bool Board::generate_textures()
409 {
410 // ---- board field texture ----
411 static const Vector3 wcol1 = Vector3(0.6, 0.4, 0.2);
412 static const Vector3 wcol2 = Vector3(0.53, 0.32, 0.1);
413 static const Vector3 wcol3 = Vector3(0.38, 0.25, 0.08);
415 img_field.create(1024, 1024);
416 unsigned char *pptr = img_field.pixels;
417 for(int i=0; i<img_field.height; i++) {
418 float v = (float)i / (float)img_field.height;
420 for(int j=0; j<img_field.width; j++) {
421 float u = (float)j / (float)img_field.width;
423 int r = 0, g = 0, b = 0;
425 float wood_val = wood(u, v);
427 // pattern mask
428 float x = u;
429 float y = v < 0.5 ? v * 2.0 : 2.0 - v * 2.0;
430 bool inside = false;
432 inside |= (spike(x, y + 0.33333) && !spike(x, y + 0.4)) ||
433 (spike(x, y + 0.5) && !spike(x, y + 0.68));
434 inside |= (circle(x, y, 0.12) && !circle(x, y, 0.1)) || circle(x, y, 0.06);
435 inside |= (diamond(x, y) && !diamond(x, y - 0.015)) ||
436 (diamond(x, y - 0.023) && !diamond(x, y - 0.028));
437 inside |= center_circle(x, y, 0.03);
439 Vector3 wood_color = lerp(wcol1, wcol2, wood_val) * 0.9;
440 if(inside) {
441 wood_color = lerp(wcol1, wcol2, 1.0 - wood_val) * 2.0;
442 }
444 r = (int)(wood_color.x * 255.0);
445 g = (int)(wood_color.y * 255.0);
446 b = (int)(wood_color.z * 255.0);
448 pptr[0] = r > 255 ? 255 : r;
449 pptr[1] = g > 255 ? 255 : g;
450 pptr[2] = b > 255 ? 255 : b;
451 pptr += 3;
452 }
453 }
454 img_field.texture();
456 // ---- generic wood texture ----
457 img_wood.create(256, 256);
458 pptr = img_wood.pixels;
459 for(int i=0; i<img_wood.height; i++) {
460 float v = (float)i / (float)img_wood.height;
461 for(int j=0; j<img_wood.width; j++) {
462 float u = (float)j / (float)img_wood.width;
464 float wood_val = wood_tile(u, v);
465 Vector3 wood_color = lerp(wcol2, wcol3, wood_val) * 0.7;
467 int r = (int)(wood_color.x * 255.0);
468 int g = (int)(wood_color.y * 255.0);
469 int b = (int)(wood_color.z * 255.0);
471 pptr[0] = r > 255 ? 255 : r;
472 pptr[1] = g > 255 ? 255 : g;
473 pptr[2] = b > 255 ? 255 : b;
474 pptr += 3;
475 }
476 }
477 img_wood.texture();
479 // ---- metal hinge diffuse texture ----
480 Vector3 rusty_col1 = Vector3(0.43, 0.46, 0.52);
481 Vector3 rusty_col2 = Vector3(0.52, 0.47, 0.43);
483 img_hinge.create(128, 128);
484 pptr = img_hinge.pixels;
485 for(int i=0; i<img_hinge.height; i++) {
486 float v = (float)i / (float)img_hinge.height;
487 for(int j=0; j<img_hinge.width; j++) {
488 float u = (float)j / (float)img_hinge.width;
490 // rust pattern
491 float w1 = fbm2(u * 4.0, v * 4.0, 3) * 0.5 + 0.5;
492 //float w2 = fbm2(u * 8.0, v * 8.0, 1) * 0.5 + 0.5;
493 Vector3 col = lerp(rusty_col1, rusty_col2 * 0.5, w1);
495 // center hinge split
496 if(fabs(v - 0.5) < 0.01) {
497 col *= 0.5;
498 }
500 int r = (int)(col.x * 255.0);
501 int g = (int)(col.y * 255.0);
502 int b = (int)(col.z * 255.0);
504 pptr[0] = r > 255 ? 255 : (r < 0 ? 0 : r);
505 pptr[1] = g > 255 ? 255 : (g < 0 ? 0 : g);
506 pptr[2] = b > 255 ? 255 : (b < 0 ? 0 : b);
508 pptr += 3;
509 }
510 }
511 img_hinge.texture();
513 return true;
514 }