1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 
5 #define ALLEGRO_UNSTABLE
6 #include "allegro5/allegro.h"
7 #include "allegro5/allegro_primitives.h"
8 
9 #include "common.c"
10 
11 typedef struct
12 {
13    float x, y;
14    float nx, ny, nz;
15 } CUSTOM_VERTEX;
16 
17 #define RING_SIZE 25
18 #define SPHERE_RADIUS 150.1
19 #define SCREEN_WIDTH 640
20 #define SCREEN_HEIGHT 480
21 #define NUM_RINGS (SCREEN_WIDTH / RING_SIZE + 1)
22 #define NUM_SEGMENTS 64
23 #define NUM_VERTICES (NUM_RINGS * NUM_SEGMENTS * 6)
24 #define FIRST_OUTSIDE_RING ((int)(SPHERE_RADIUS / RING_SIZE))
25 
setup_vertex(CUSTOM_VERTEX * vtx,int ring,int segment,bool inside)26 static void setup_vertex(CUSTOM_VERTEX* vtx, int ring, int segment, bool inside)
27 {
28    float len;
29    float x, y, z;
30    x = ring * RING_SIZE * cosf(2 * ALLEGRO_PI * segment / NUM_SEGMENTS);
31    y = ring * RING_SIZE * sinf(2 * ALLEGRO_PI * segment / NUM_SEGMENTS);
32    vtx->x = x + SCREEN_WIDTH / 2;
33    vtx->y = y + SCREEN_HEIGHT / 2;
34 
35    if (inside) {
36       /* This comes from the definition of the normal vector as the
37        * gradient of the 3D surface. */
38       z = sqrtf(SPHERE_RADIUS * SPHERE_RADIUS - x * x - y * y);
39       vtx->nx = x / z;
40       vtx->ny = y / z;
41    }
42    else {
43       vtx->nx = 0;
44       vtx->ny = 0;
45    }
46    vtx->nz = 1.0;
47 
48    len = sqrtf(vtx->nx * vtx->nx + vtx->ny * vtx->ny + vtx->nz * vtx->nz);
49    vtx->nx /= len;
50    vtx->ny /= len;
51    vtx->nz /= len;
52 }
53 
main(int argc,char ** argv)54 int main(int argc, char **argv)
55 {
56    ALLEGRO_DISPLAY *display;
57    ALLEGRO_TIMER *timer;
58    ALLEGRO_EVENT_QUEUE *queue;
59    bool redraw = true;
60    ALLEGRO_SHADER *shader;
61    ALLEGRO_VERTEX_DECL *vertex_decl;
62    ALLEGRO_VERTEX_ELEMENT vertex_elems[] = {
63       {ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(CUSTOM_VERTEX, x)},
64       {ALLEGRO_PRIM_USER_ATTR, ALLEGRO_PRIM_FLOAT_3, offsetof(CUSTOM_VERTEX, nx)},
65       {0, 0, 0}
66    };
67    CUSTOM_VERTEX vertices[NUM_VERTICES];
68    bool quit = false;
69    const char* vertex_shader_file;
70    const char* pixel_shader_file;
71    int vertex_idx = 0;
72    int ring, segment;
73    float diffuse_color[4] = {0.1, 0.1, 0.7, 1.0};
74    float light_position[3] = {0, 0, 100};
75 
76    (void)argc;
77    (void)argv;
78 
79    if (!al_init()) {
80       abort_example("Could not init Allegro.\n");
81    }
82 
83    al_install_mouse();
84    al_install_keyboard();
85    al_install_touch_input();
86    if (!al_init_primitives_addon()) {
87       abort_example("Could not init primitives addon.\n");
88    }
89    init_platform_specific();
90    al_set_new_display_flags(ALLEGRO_PROGRAMMABLE_PIPELINE);
91    display = al_create_display(SCREEN_WIDTH, SCREEN_HEIGHT);
92    if (!display) {
93       abort_example("Error creating display.\n");
94    }
95 
96    vertex_decl = al_create_vertex_decl(vertex_elems, sizeof(CUSTOM_VERTEX));
97    if (!vertex_decl) {
98       abort_example("Error creating vertex declaration.\n");
99    }
100 
101    /* Computes a "spherical" bump ring. The z coordinate is not actually set
102     * appropriately as this is a 2D example, but the normal vectors are computed
103     * correctly for the light shading effect. */
104    for (ring = 0; ring < NUM_RINGS; ring++) {
105       for (segment = 0; segment < NUM_SEGMENTS; segment++) {
106          bool inside = ring < FIRST_OUTSIDE_RING;
107          setup_vertex(&vertices[vertex_idx + 0], ring + 0, segment + 0, inside);
108          setup_vertex(&vertices[vertex_idx + 1], ring + 0, segment + 1, inside);
109          setup_vertex(&vertices[vertex_idx + 2], ring + 1, segment + 0, inside);
110          setup_vertex(&vertices[vertex_idx + 3], ring + 1, segment + 0, inside);
111          setup_vertex(&vertices[vertex_idx + 4], ring + 0, segment + 1, inside);
112          setup_vertex(&vertices[vertex_idx + 5], ring + 1, segment + 1, inside);
113          vertex_idx += 6;
114       }
115    }
116 
117    shader = al_create_shader(ALLEGRO_SHADER_AUTO);
118 
119    if (!shader) {
120       abort_example("Failed to create shader.");
121    }
122 
123    if (al_get_shader_platform(shader) == ALLEGRO_SHADER_GLSL) {
124       vertex_shader_file = "data/ex_prim_shader_vertex.glsl";
125       pixel_shader_file = "data/ex_prim_shader_pixel.glsl";
126    }
127    else {
128       vertex_shader_file = "data/ex_prim_shader_vertex.hlsl";
129       pixel_shader_file = "data/ex_prim_shader_pixel.hlsl";
130    }
131 
132    if (!al_attach_shader_source_file(shader, ALLEGRO_VERTEX_SHADER, vertex_shader_file)) {
133       abort_example("al_attach_shader_source_file for vertex shader failed: %s\n",
134          al_get_shader_log(shader));
135    }
136    if (!al_attach_shader_source_file(shader, ALLEGRO_PIXEL_SHADER, pixel_shader_file)) {
137       abort_example("al_attach_shader_source_file for pixel shader failed: %s\n",
138          al_get_shader_log(shader));
139    }
140 
141    if (!al_build_shader(shader)) {
142       abort_example("al_build_shader for link failed: %s\n", al_get_shader_log(shader));
143    }
144 
145    al_use_shader(shader);
146    al_set_shader_float_vector("diffuse_color", 4, diffuse_color, 1);
147    /* alpha controls shininess, and 25 is very shiny */
148    al_set_shader_float("alpha", 25);
149 
150    timer = al_create_timer(1.0 / 60);
151    queue = al_create_event_queue();
152    al_register_event_source(queue, al_get_keyboard_event_source());
153    al_register_event_source(queue, al_get_mouse_event_source());
154    if (al_is_touch_input_installed()) {
155       al_register_event_source(queue,
156          al_get_touch_input_mouse_emulation_event_source());
157    }
158    al_register_event_source(queue, al_get_display_event_source(display));
159    al_register_event_source(queue, al_get_timer_event_source(timer));
160    al_start_timer(timer);
161 
162    while (!quit) {
163       ALLEGRO_EVENT event;
164       al_wait_for_event(queue, &event);
165 
166       switch (event.type) {
167          case ALLEGRO_EVENT_DISPLAY_CLOSE:
168             quit = true;
169             break;
170          case ALLEGRO_EVENT_MOUSE_AXES:
171             light_position[0] = event.mouse.x;
172             light_position[1] = event.mouse.y;
173             break;
174          case ALLEGRO_EVENT_KEY_CHAR:
175             if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
176                quit = true;
177             break;
178          case ALLEGRO_EVENT_TIMER:
179             redraw = true;
180             break;
181       }
182 
183       if (redraw && al_is_event_queue_empty(queue)) {
184          al_clear_to_color(al_map_rgb_f(0, 0, 0));
185 
186          al_set_shader_float_vector("light_position", 3, light_position, 1);
187          al_draw_prim(vertices, vertex_decl, NULL, 0, NUM_VERTICES, ALLEGRO_PRIM_TRIANGLE_LIST);
188 
189          al_flip_display();
190          redraw = false;
191       }
192    }
193 
194    al_use_shader(NULL);
195    al_destroy_shader(shader);
196    al_destroy_vertex_decl(vertex_decl);
197 
198    return 0;
199 }
200 
201 
202 /* vim: set sts=3 sw=3 et: */
203