1 | #include "sl.h"
|
2 |
|
3 | #include "internal/shaders.h"
|
4 | #include "internal/triangle.h"
|
5 | #include "internal/rectangle.h"
|
6 | #include "internal/circle.h"
|
7 | #include "internal/semicircle.h"
|
8 | #include "internal/point.h"
|
9 | #include "internal/line.h"
|
10 | #include "internal/sprite.h"
|
11 | #include "internal/text.h"
|
12 | #include "internal/sound.h"
|
13 | #include "internal/window.h"
|
14 |
|
15 | #include "util/transform.h"
|
16 | #include "util/images.h"
|
17 |
|
18 | #include "config.h"
|
19 |
|
20 | #ifdef __MINGW32__
|
21 | #include "util/gldebugging.h"
|
22 | #endif
|
23 |
|
24 | #ifdef USE_GLES
|
25 | #include <GLES2/gl2.h>
|
26 | #include <GLES2/gl2ext.h>
|
27 | #else
|
28 | #include <GL/glew.h>
|
29 | #endif
|
30 |
|
31 | #include <stdlib.h>
|
32 | #include <stdio.h>
|
33 | #include <stdint.h>
|
34 |
|
35 | #define SL_MATRIX_STACK_SIZE 32
|
36 |
|
37 | #define IDEAL_FRAME_TIME 0.01666667
|
38 |
|
39 | // private vars
|
40 |
|
41 | static int slMousePosX;
|
42 | static int slMousePosY;
|
43 | static uint8_t slMousePosStale = 1;
|
44 |
|
45 | static Mat4 slMatrixStack[SL_MATRIX_STACK_SIZE];
|
46 | static Mat4 *slCurrentMatrix = &slMatrixStack[0];
|
47 | static int slStackSize = 0;
|
48 |
|
49 | static Mat4 slProjectionMatrix;
|
50 |
|
51 | static Vec4 slForeColor;// = {.x = 1.0, .y = 1.0, .z = 1.0, .w = 1.0};
|
52 |
|
53 | static double slSpriteScrollX = 0.0;
|
54 | static double slSpriteScrollY = 0.0;
|
55 | static double slSpriteTilingX = 1.0;
|
56 | static double slSpriteTilingY = 1.0;
|
57 |
|
58 | static int slTextAlign = SL_ALIGN_LEFT;
|
59 |
|
60 | static double slDeltaTime = IDEAL_FRAME_TIME;
|
61 | static double slOldFrameTime = 0.0;
|
62 | static double slNewFrameTime = IDEAL_FRAME_TIME;
|
63 |
|
64 | // private function prototypes
|
65 |
|
66 | static void sliInitResources();
|
67 | static void sliKillResources();
|
68 |
|
69 | // window commands
|
70 |
|
71 | void slWindow(int width, int height, const char *title, int fullScreen)
|
72 | {
|
73 | // error tracking for any window-creation issues we run into
|
74 | #ifndef USE_GLES
|
75 | GLenum error;
|
76 | #endif
|
77 |
|
78 | if(!sliIsWindowOpen())
|
79 | {
|
80 | // use either GLFW or PIGU to set up our window
|
81 | sliOpenWindow(width, height, title, fullScreen);
|
82 |
|
83 | // configure our viewing area
|
84 | glViewport(0, 0, width, height);
|
85 |
|
86 | // enable our extensions handler
|
87 | #ifndef USE_GLES
|
88 | glewExperimental = 1;
|
89 | error = glewInit();
|
90 | if(error != GLEW_OK)
|
91 | {
|
92 | fprintf(stderr, "slWindow() could not initialize GLEW: %s\n" , glewGetErrorString(error));
|
93 | exit(1);
|
94 | }
|
95 | #endif
|
96 |
|
97 | // start with a clean error slate
|
98 | glGetError();
|
99 |
|
100 | // turn on OpenGL debugging
|
101 | #ifdef DEBUG
|
102 | initGLDebugger();
|
103 | #endif
|
104 |
|
105 | // turn on blending and turn depth testing off
|
106 | glEnable(GL_BLEND);
|
107 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
108 | glDisable(GL_DEPTH_TEST);
|
109 |
|
110 | // camera view settings
|
111 | slProjectionMatrix = ortho(0.0f, (double)width, 0.0f, (double)height);
|
112 |
|
113 | // default colors
|
114 | slSetBackColor(0.0, 0.0, 0.0);
|
115 | slSetForeColor(1.0, 1.0, 1.0, 1.0);
|
116 |
|
117 | // initialize any rendering resources
|
118 | sliInitResources();
|
119 |
|
120 | // initialize our first transformation matrix
|
121 | *slCurrentMatrix = identity();
|
122 | }
|
123 | else
|
124 | {
|
125 | fprintf(stderr, "slWindow() cannot be called when a window already exists\n" );
|
126 | exit(1);
|
127 | }
|
128 | }
|
129 |
|
130 | void slShowCursor(int showCursor)
|
131 | {
|
132 | sliShowCursor(showCursor);
|
133 | }
|
134 |
|
135 | void slClose()
|
136 | {
|
137 | if(sliIsWindowOpen())
|
138 | {
|
139 | sliKillResources();
|
140 | sliCloseWindow();
|
141 | }
|
142 | else
|
143 | {
|
144 | fprintf(stderr, "slClose() cannot be called when no window exists\n" );
|
145 | exit(1);
|
146 | }
|
147 | }
|
148 |
|
149 | int slShouldClose()
|
150 | {
|
151 | if(!sliIsWindowOpen())
|
152 | {
|
153 | fprintf(stderr, "slShouldClose() cannot be called because no window exists\n" );
|
154 | exit(1);
|
155 | }
|
156 |
|
157 | return sliShouldClose();
|
158 | }
|
159 |
|
160 | // simple input
|
161 |
|
162 | int slGetKey(int key)
|
163 | {
|
164 | return sliGetKey(key);
|
165 | }
|
166 |
|
167 | int slGetMouseButton(int button)
|
168 | {
|
169 | return sliGetMouseButton(button);
|
170 | }
|
171 |
|
172 | int slGetMouseX()
|
173 | {
|
174 | // make sure a render step has not occurred since we last read the mouse position
|
175 | if(slMousePosStale)
|
176 | {
|
177 | sliGetMousePos(&slMousePosX, &slMousePosY);
|
178 | slMousePosStale = 0;
|
179 | }
|
180 |
|
181 | return slMousePosX;
|
182 | }
|
183 |
|
184 | int slGetMouseY()
|
185 | {
|
186 | // make sure a render step has not occurred since we last read the mouse position
|
187 | if(slMousePosStale)
|
188 | {
|
189 | sliGetMousePos(&slMousePosX, &slMousePosY);
|
190 | slMousePosStale = 0;
|
191 | }
|
192 |
|
193 | return slMousePosY;
|
194 | }
|
195 |
|
196 | // simple frame timing
|
197 |
|
198 | double slGetDeltaTime()
|
199 | {
|
200 | return slDeltaTime;
|
201 | }
|
202 |
|
203 | double slGetTime()
|
204 | {
|
205 | return sliGetTime();
|
206 | }
|
207 |
|
208 | // rendering/clearing commands
|
209 |
|
210 | void slRender()
|
211 | {
|
212 | // value close enough to zero for delta time management
|
213 | const double SL_MIN_DELTA_TIME = 0.00001; // tiny fraction of a second
|
214 | const double SL_MAX_DELTA_TIME = 0.5; // half a second dt max
|
215 |
|
216 | // render any leftover points or lines
|
217 | sliPointsFlush();
|
218 | sliLinesFlush();
|
219 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
220 |
|
221 | // read any input events, show the back buffer, and clear the (previous) front buffer
|
222 | sliPollAndSwap();
|
223 | glClear(GL_COLOR_BUFFER_BIT);
|
224 |
|
225 | // gather time values
|
226 | slOldFrameTime = slNewFrameTime;
|
227 | slNewFrameTime = sliGetTime();
|
228 |
|
229 | // compute delta time value; ensure we don't have any long pauses or tiny time quantums
|
230 | slDeltaTime = (slNewFrameTime - slOldFrameTime);
|
231 | if(slDeltaTime < SL_MIN_DELTA_TIME)
|
232 | slDeltaTime = SL_MIN_DELTA_TIME;
|
233 | if(slDeltaTime > SL_MAX_DELTA_TIME)
|
234 | slDeltaTime = SL_MAX_DELTA_TIME;
|
235 |
|
236 | // set our mouse position as needing refreshing
|
237 | slMousePosStale = 1;
|
238 | }
|
239 |
|
240 | // colour control
|
241 |
|
242 | void slSetBackColor(double red, double green, double blue)
|
243 | {
|
244 | glClearColor((GLclampf)red, (GLclampf)green, (GLclampf)blue, 1.0);
|
245 | }
|
246 |
|
247 | void slSetForeColor(double red, double green, double blue, double alpha)
|
248 | {
|
249 | slForeColor.x = (float)red;
|
250 | slForeColor.y = (float)green;
|
251 | slForeColor.z = (float)blue;
|
252 | slForeColor.w = (float)alpha;
|
253 | }
|
254 |
|
255 | // blending control
|
256 |
|
257 | void slSetAdditiveBlend(int additiveBlend)
|
258 | {
|
259 | sliPointsFlush();
|
260 | sliLinesFlush();
|
261 |
|
262 | glBlendFunc(GL_SRC_ALPHA, additiveBlend ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
|
263 | }
|
264 |
|
265 | // transformations
|
266 |
|
267 | void slPush()
|
268 | {
|
269 | if(slStackSize < SL_MATRIX_STACK_SIZE - 1)
|
270 | {
|
271 | slStackSize ++;
|
272 | slCurrentMatrix ++;
|
273 | *slCurrentMatrix = *(slCurrentMatrix - 1);
|
274 | }
|
275 | else
|
276 | {
|
277 | fprintf(stderr, "slPush() exceeded maximum transform stack size of %d\n" , SL_MATRIX_STACK_SIZE);
|
278 | exit(1);
|
279 | }
|
280 | }
|
281 |
|
282 | void slPop()
|
283 | {
|
284 | if(slStackSize > 0)
|
285 | {
|
286 | slStackSize --;
|
287 | slCurrentMatrix --;
|
288 | }
|
289 | else
|
290 | {
|
291 | fprintf(stderr, "slPop() cannot pop an empty transform stack\n" );
|
292 | exit(1);
|
293 | }
|
294 | }
|
295 |
|
296 | void slTranslate(double x, double y)
|
297 | {
|
298 | *slCurrentMatrix = translate(slCurrentMatrix, x, y);
|
299 | }
|
300 |
|
301 | void slRotate(double degrees)
|
302 | {
|
303 | *slCurrentMatrix = rotate(slCurrentMatrix, degrees);
|
304 | }
|
305 |
|
306 | void slScale(double x, double y)
|
307 | {
|
308 | *slCurrentMatrix = scale(slCurrentMatrix, x, y);
|
309 | }
|
310 |
|
311 | // texture loading
|
312 |
|
313 | int slLoadTexture(const char *filename)
|
314 | {
|
315 | int result = -1;
|
316 |
|
317 | if(sliIsWindowOpen())
|
318 | {
|
319 | result = (int)loadOpenGLTexture(filename);
|
320 | }
|
321 | else
|
322 | {
|
323 | fprintf(stderr, "slLoadTexture() cannot be called before slWindow() is called\n" );
|
324 | exit(1);
|
325 | }
|
326 |
|
327 | return result;
|
328 | }
|
329 |
|
330 | // sound loading and playing
|
331 |
|
332 | int slLoadWAV(const char *filename)
|
333 | {
|
334 | int result = -1;
|
335 |
|
336 | if(sliIsWindowOpen())
|
337 | {
|
338 | result = sliLoadWAV(filename);
|
339 | }
|
340 | else
|
341 | {
|
342 | fprintf(stderr, "slLoadWAV() cannot be called before slWindow() is called\n" );
|
343 | exit(1);
|
344 | }
|
345 |
|
346 | return result;
|
347 | }
|
348 |
|
349 | int slSoundPlay(int sound)
|
350 | {
|
351 | return sliSoundPlay(sound);
|
352 | }
|
353 |
|
354 | int slSoundLoop(int sound)
|
355 | {
|
356 | return sliSoundLoop(sound);
|
357 | }
|
358 |
|
359 | void slSoundPause(int sound)
|
360 | {
|
361 | sliSoundPause(sound);
|
362 | }
|
363 |
|
364 | void slSoundStop(int sound)
|
365 | {
|
366 | sliSoundStop(sound);
|
367 | }
|
368 |
|
369 | void slSoundPauseAll()
|
370 | {
|
371 | sliSoundPauseAll();
|
372 | }
|
373 |
|
374 | void slSoundResumeAll()
|
375 | {
|
376 | sliSoundResumeAll();
|
377 | }
|
378 |
|
379 | void slSoundStopAll()
|
380 | {
|
381 | sliSoundStopAll();
|
382 | }
|
383 |
|
384 | int slSoundPlaying(int sound)
|
385 | {
|
386 | return sliSoundPlaying(sound);
|
387 | }
|
388 |
|
389 | int slSoundLooping(int sound)
|
390 | {
|
391 | return sliSoundLooping(sound);
|
392 | }
|
393 |
|
394 | // simple shape commands
|
395 |
|
396 | void slTriangleFill(double x, double y, double width, double height)
|
397 | {
|
398 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
399 | modelview = scale(&modelview, width, height);
|
400 |
|
401 | sliPointsFlush();
|
402 | sliLinesFlush();
|
403 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
404 | sliTriangleFill(&modelview, &slForeColor);
|
405 | }
|
406 |
|
407 | void slTriangleOutline(double x, double y, double width, double height)
|
408 | {
|
409 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
410 | modelview = scale(&modelview, width, height);
|
411 |
|
412 | sliPointsFlush();
|
413 | sliLinesFlush();
|
414 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
415 | sliTriangleOutline(&modelview, &slForeColor);
|
416 | }
|
417 |
|
418 | void slRectangleFill(double x, double y, double width, double height)
|
419 | {
|
420 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
421 | modelview = scale(&modelview, width, height);
|
422 |
|
423 | sliPointsFlush();
|
424 | sliLinesFlush();
|
425 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
426 | sliRectangleFill(&modelview, &slForeColor);
|
427 | }
|
428 |
|
429 | void slRectangleOutline(double x, double y, double width, double height)
|
430 | {
|
431 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
432 | modelview = scale(&modelview, width, height);
|
433 |
|
434 | sliPointsFlush();
|
435 | sliLinesFlush();
|
436 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
437 | sliRectangleOutline(&modelview, &slForeColor);
|
438 | }
|
439 |
|
440 | void slCircleFill(double x, double y, double radius, int numVertices)
|
441 | {
|
442 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
443 |
|
444 | sliPointsFlush();
|
445 | sliLinesFlush();
|
446 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
447 | sliCircleFill(&modelview, &slForeColor, radius, numVertices);
|
448 | }
|
449 |
|
450 | void slCircleOutline(double x, double y, double radius, int numVertices)
|
451 | {
|
452 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
453 |
|
454 | sliPointsFlush();
|
455 | sliLinesFlush();
|
456 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
457 | sliCircleOutline(&modelview, &slForeColor, radius, numVertices);
|
458 | }
|
459 |
|
460 | void slSemiCircleFill(double x, double y, double radius, int numVertices, double degrees)
|
461 | {
|
462 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
463 |
|
464 | sliPointsFlush();
|
465 | sliLinesFlush();
|
466 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
467 | sliSemiCircleFill(&modelview, &slForeColor, radius, numVertices, degrees);
|
468 | }
|
469 |
|
470 | void slSemiCircleOutline(double x, double y, double radius, int numVertices, double degrees)
|
471 | {
|
472 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
473 |
|
474 | sliPointsFlush();
|
475 | sliLinesFlush();
|
476 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
477 | sliSemiCircleOutline(&modelview, &slForeColor, radius, numVertices, degrees);
|
478 | }
|
479 |
|
480 | void slPoint(double x, double y)
|
481 | {
|
482 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
483 |
|
484 | sliLinesFlush();
|
485 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
486 | sliPoint(&modelview, &slForeColor);
|
487 | }
|
488 |
|
489 | void slLine(double x1, double y1, double x2, double y2)
|
490 | {
|
491 | Mat4 modelview1 = translate(slCurrentMatrix, x1, y1);
|
492 | Mat4 modelview2 = translate(slCurrentMatrix, x2, y2);
|
493 |
|
494 | sliPointsFlush();
|
495 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
496 | sliLine(&slForeColor, modelview1.cols[3].x, modelview1.cols[3].y, modelview2.cols[3].x, modelview2.cols[3].y);
|
497 | }
|
498 |
|
499 | void slSetSpriteTiling(double x, double y)
|
500 | {
|
501 | slSpriteTilingX = x;
|
502 | slSpriteTilingY = y;
|
503 | }
|
504 |
|
505 | void slSetSpriteScroll(double x, double y)
|
506 | {
|
507 | slSpriteScrollX = x;
|
508 | slSpriteScrollY = y;
|
509 | }
|
510 |
|
511 | void slSprite(int texture, double x, double y, double width, double height)
|
512 | {
|
513 | Mat4 modelview;
|
514 |
|
515 | // this shorthand causes compiler errors on MSVC...
|
516 | Vec2 tiling;// = {.x = slSpriteTilingX, .y = slSpriteTilingY};
|
517 | Vec2 scroll;// = {.x = slSpriteScrollX, .y = slSpriteScrollY};
|
518 |
|
519 | // ...so we do it the hard way instead
|
520 | tiling.x = (float)slSpriteTilingX;
|
521 | tiling.y = (float)slSpriteTilingY;
|
522 | scroll.x = (float)slSpriteScrollX;
|
523 | scroll.y = (float)slSpriteScrollY;
|
524 |
|
525 | modelview = translate(slCurrentMatrix, x, y);
|
526 | modelview = scale(&modelview, width, height);
|
527 |
|
528 | sliPointsFlush();
|
529 | sliLinesFlush();
|
530 | sliTextFlush(slCurrentMatrix, &slForeColor);
|
531 | sliSprite(&modelview, &slForeColor, (GLuint)texture, &tiling, &scroll);
|
532 | }
|
533 |
|
534 | // text commands
|
535 |
|
536 | void slSetTextAlign(int fontAlign)
|
537 | {
|
538 | if(fontAlign >= 0 && fontAlign <= 2)
|
539 | {
|
540 | slTextAlign = fontAlign;
|
541 | }
|
542 | else
|
543 | {
|
544 | fprintf(stderr, "slSetTextAlign() only accepts SL_ALIGN_CENTER, SL_ALIGN_RIGHT, or SL_ALIGN_LEFT\n" );
|
545 | exit(1);
|
546 | }
|
547 | }
|
548 |
|
549 | double slGetTextWidth(const char *text)
|
550 | {
|
551 | return sliTextWidth(text);
|
552 | }
|
553 |
|
554 | double slGetTextHeight(const char *text)
|
555 | {
|
556 | return sliTextHeight(text);
|
557 | }
|
558 |
|
559 | int slLoadFont(const char *fontFilename)
|
560 | {
|
561 | int result = -1;
|
562 |
|
563 | if(sliIsWindowOpen())
|
564 | {
|
565 | result = sliLoadFont(fontFilename);
|
566 | }
|
567 | else
|
568 | {
|
569 | fprintf(stderr, "slSetFont() cannot be called before slWindow() is called\n" );
|
570 | exit(1);
|
571 | }
|
572 |
|
573 | return result;
|
574 | }
|
575 |
|
576 | void slSetFont(int font, int fontSize)
|
577 | {
|
578 | if(sliIsWindowOpen())
|
579 | {
|
580 | sliFont(font, fontSize);
|
581 | }
|
582 | else
|
583 | {
|
584 | fprintf(stderr, "slSetFont() cannot be called before slWindow() is called\n" );
|
585 | exit(1);
|
586 | }
|
587 | }
|
588 |
|
589 | void slSetFontSize(int fontSize)
|
590 | {
|
591 | sliFontSize(fontSize);
|
592 | }
|
593 |
|
594 | void slText(double x, double y, const char *text)
|
595 | {
|
596 | Mat4 modelview = translate(slCurrentMatrix, x, y);
|
597 |
|
598 | if(slTextAlign == SL_ALIGN_CENTER)
|
599 | {
|
600 | modelview = translate(&modelview, -slGetTextWidth(text) / 2.0, 0.0);
|
601 | }
|
602 | else if(slTextAlign == SL_ALIGN_RIGHT)
|
603 | {
|
604 | modelview = translate(&modelview, -slGetTextWidth(text), 0.0);
|
605 | }
|
606 |
|
607 | sliPointsFlush();
|
608 | sliLinesFlush();
|
609 | sliText(&modelview, &slForeColor, text);
|
610 | }
|
611 |
|
612 | // private functions
|
613 |
|
614 | void sliInitResources()
|
615 | {
|
616 | slMousePosStale = 1;
|
617 |
|
618 | slCurrentMatrix = &slMatrixStack[0];
|
619 | slStackSize = 0;
|
620 |
|
621 | slSpriteScrollX = 0.0;
|
622 | slSpriteScrollY = 0.0;
|
623 | slSpriteTilingX = 1.0;
|
624 |
|
625 | slTextAlign = SL_ALIGN_LEFT;
|
626 |
|
627 | slDeltaTime = IDEAL_FRAME_TIME;
|
628 | slOldFrameTime = 0.0;
|
629 | slNewFrameTime = IDEAL_FRAME_TIME;
|
630 |
|
631 | sliShadersInit(&slProjectionMatrix);
|
632 | sliTriangleInit();
|
633 | sliRectangleInit();
|
634 | sliCircleInit();
|
635 | sliSemiCircleInit();
|
636 | sliPointInit();
|
637 | sliLineInit();
|
638 | sliSpriteInit();
|
639 | sliTextInit();
|
640 | sliSoundInit();
|
641 | }
|
642 |
|
643 | void sliKillResources()
|
644 | {
|
645 | sliTextDestroy();
|
646 | sliSpriteDestroy();
|
647 | sliLineDestroy();
|
648 | sliPointDestroy();
|
649 | sliCircleDestroy();
|
650 | sliSemiCircleDestroy();
|
651 | sliRectangleDestroy();
|
652 | sliTriangleDestroy();
|
653 | sliShadersDestroy();
|
654 | sliSoundDestroy();
|
655 | }
|
656 | |