SDL_gpu  0.11.0
A hardware-accelerated, cross-platform 2D graphics API
SDL_gpu_matrix.c
Go to the documentation of this file.
1 #include "SDL_gpu.h"
2 #include <math.h>
3 #include <string.h>
4 
5 #ifdef _MSC_VER
6 #define __func__ __FUNCTION__
7 #endif
8 
9 #ifndef M_PI
10 #define M_PI 3.1415926f
11 #endif
12 
13 
14 // Visual C does not support static inline
15 #ifndef static_inline
16  #ifdef _MSC_VER
17  #define static_inline static
18  #else
19  #define static_inline static inline
20  #endif
21 #endif
22 
23 // Old Visual C did not support C99 (which includes a safe snprintf)
24 #if defined(_MSC_VER) && (_MSC_VER < 1900)
25  #define snprintf c99_snprintf
26  // From Valentin Milea: http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
27  static_inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
28  {
29  int count = -1;
30 
31  if (size != 0)
32  count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
33  if (count == -1)
34  count = _vscprintf(format, ap);
35 
36  return count;
37  }
38 
39  static_inline int c99_snprintf(char* str, size_t size, const char* format, ...)
40  {
41  int count;
42  va_list ap;
43 
44  va_start(ap, format);
45  count = c99_vsnprintf(str, size, format, ap);
46  va_end(ap);
47 
48  return count;
49  }
50 #endif
51 
52 
53 
54 // Column-major
55 #define INDEX(row,col) ((col)*4 + (row))
56 
57 
58 float GPU_VectorLength(float* vec3)
59 {
60  return sqrtf(vec3[0] * vec3[0] + vec3[1] * vec3[1] + vec3[2] * vec3[2]);
61 }
62 
63 void GPU_VectorNormalize(float* vec3)
64 {
65  float mag = GPU_VectorLength(vec3);
66  vec3[0] /= mag;
67  vec3[1] /= mag;
68  vec3[2] /= mag;
69 }
70 
71 float GPU_VectorDot(float* A, float* B)
72 {
73  return A[0] * B[0] + A[1] * B[1] + A[2] * B[2];
74 }
75 
76 void GPU_VectorCross(float* result, float* A, float* B)
77 {
78  result[0] = A[1] * B[2] - A[2] * B[1];
79  result[1] = A[2] * B[0] - A[0] * B[2];
80  result[2] = A[0] * B[1] - A[1] * B[0];
81 }
82 
83 void GPU_VectorCopy(float* result, float* A)
84 {
85  result[0] = A[0];
86  result[1] = A[1];
87  result[2] = A[2];
88 }
89 
90 void GPU_VectorApplyMatrix(float* vec3, float* matrix_4x4)
91 {
92  float x = matrix_4x4[INDEX(0, 0)] * vec3[0] + matrix_4x4[INDEX(0, 1)] * vec3[1] + matrix_4x4[INDEX(0, 2)] * vec3[2] + matrix_4x4[INDEX(0, 3)];
93  float y = matrix_4x4[INDEX(1, 0)] * vec3[0] + matrix_4x4[INDEX(1, 1)] * vec3[1] + matrix_4x4[INDEX(1, 2)] * vec3[2] + matrix_4x4[INDEX(1, 3)];
94  float z = matrix_4x4[INDEX(2, 0)] * vec3[0] + matrix_4x4[INDEX(2, 1)] * vec3[1] + matrix_4x4[INDEX(2, 2)] * vec3[2] + matrix_4x4[INDEX(2, 3)];
95  float w = matrix_4x4[INDEX(3, 0)] * vec3[0] + matrix_4x4[INDEX(3, 1)] * vec3[1] + matrix_4x4[INDEX(3, 2)] * vec3[2] + matrix_4x4[INDEX(3, 3)];
96  vec3[0] = x / w;
97  vec3[1] = y / w;
98  vec3[2] = z / w;
99 }
100 
101 
102 // Matrix math implementations based on Wayne Cochran's (wcochran) matrix.c
103 
104 #define FILL_MATRIX_4x4(A, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) \
105  A[0] = a0; \
106  A[1] = a1; \
107  A[2] = a2; \
108  A[3] = a3; \
109  A[4] = a4; \
110  A[5] = a5; \
111  A[6] = a6; \
112  A[7] = a7; \
113  A[8] = a8; \
114  A[9] = a9; \
115  A[10] = a10; \
116  A[11] = a11; \
117  A[12] = a12; \
118  A[13] = a13; \
119  A[14] = a14; \
120  A[15] = a15;
121 
122 void GPU_MatrixCopy(float* result, const float* A)
123 {
124  memcpy(result, A, 16*sizeof(float));
125 }
126 
127 void GPU_MatrixIdentity(float* result)
128 {
129  memset(result, 0, 16*sizeof(float));
130  result[0] = result[5] = result[10] = result[15] = 1;
131 }
132 
133 
134 void GPU_MatrixOrtho(float* result, float left, float right, float bottom, float top, float near, float far)
135 {
136  if(result == NULL)
137  return;
138 
139  {
140 #ifdef ROW_MAJOR
141  float A[16];
142  FILL_MATRIX_4x4(A,
143  2/(right - left), 0, 0, -(right + left)/(right - left),
144  0, 2/(top - bottom), 0, -(top + bottom)/(top - bottom),
145  0, 0, -2/(far - near), -(far + near)/(far - near),
146  0, 0, 0, 1
147  );
148 #else
149  float A[16];
150  FILL_MATRIX_4x4(A,
151  2 / (right - left), 0, 0, 0,
152  0, 2 / (top - bottom), 0, 0,
153  0, 0, -2 / (far - near), 0,
154  -(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1
155  );
156 #endif
157 
158  GPU_MultiplyAndAssign(result, A);
159  }
160 }
161 
162 
163 void GPU_MatrixFrustum(float* result, float left, float right, float bottom, float top, float near, float far)
164 {
165  if(result == NULL)
166  return;
167 
168  {
169  float A[16];
170  FILL_MATRIX_4x4(A,
171  2 * near / (right - left), 0, 0, 0,
172  0, 2 * near / (top - bottom), 0, 0,
173  (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1,
174  0, 0, -(2 * far * near) / (far - near), 0
175  );
176 
177  GPU_MultiplyAndAssign(result, A);
178  }
179 }
180 
181 void GPU_MatrixPerspective(float* result, float fovy, float aspect, float zNear, float zFar)
182 {
183  float fW, fH;
184 
185  // Make it left-handed?
186  fovy = -fovy;
187  aspect = -aspect;
188 
189  fH = tanf(fovy / 360 * M_PI) * zNear;
190  fW = fH * aspect;
191  GPU_MatrixFrustum(result, -fW, fW, -fH, fH, zNear, zFar);
192 }
193 
194 void GPU_MatrixLookAt(float* matrix, float eye_x, float eye_y, float eye_z, float target_x, float target_y, float target_z, float up_x, float up_y, float up_z)
195 {
196  float forward[3] = {target_x - eye_x, target_y - eye_y, target_z - eye_z};
197  float up[3] = {up_x, up_y, up_z};
198  float side[3];
199  float view[16];
200 
201  GPU_VectorNormalize(forward);
203 
204  // Calculate sideways vector
205  GPU_VectorCross(side, forward, up);
206 
207  // Calculate new up vector
208  GPU_VectorCross(up, side, forward);
209 
210  // Set up view matrix
211  view[0] = side[0];
212  view[4] = side[1];
213  view[8] = side[2];
214  view[12] = 0.0f;
215 
216  view[1] = up[0];
217  view[5] = up[1];
218  view[9] = up[2];
219  view[13] = 0.0f;
220 
221  view[2] = -forward[0];
222  view[6] = -forward[1];
223  view[10] = -forward[2];
224  view[14] = 0.0f;
225 
226  view[3] = view[7] = view[11] = 0.0f;
227  view[15] = 1.0f;
228 
229  GPU_MultiplyAndAssign(matrix, view);
230  GPU_MatrixTranslate(matrix, -eye_x, -eye_y, -eye_z);
231 }
232 
233 void GPU_MatrixTranslate(float* result, float x, float y, float z)
234 {
235  if(result == NULL)
236  return;
237 
238  {
239 #ifdef ROW_MAJOR
240  float A[16];
241  FILL_MATRIX_4x4(A,
242  1, 0, 0, x,
243  0, 1, 0, y,
244  0, 0, 1, z,
245  0, 0, 0, 1
246  );
247 #else
248  float A[16];
249  FILL_MATRIX_4x4(A,
250  1, 0, 0, 0,
251  0, 1, 0, 0,
252  0, 0, 1, 0,
253  x, y, z, 1
254  );
255 #endif
256 
257  GPU_MultiplyAndAssign(result, A);
258  }
259 }
260 
261 void GPU_MatrixScale(float* result, float sx, float sy, float sz)
262 {
263  if(result == NULL)
264  return;
265 
266  {
267  float A[16];
268  FILL_MATRIX_4x4(A,
269  sx, 0, 0, 0,
270  0, sy, 0, 0,
271  0, 0, sz, 0,
272  0, 0, 0, 1
273  );
274 
275  GPU_MultiplyAndAssign(result, A);
276  }
277 }
278 
279 void GPU_MatrixRotate(float* result, float degrees, float x, float y, float z)
280 {
281  float p, radians, c, s, c_, zc_, yc_, xzc_, xyc_, yzc_, xs, ys, zs;
282 
283  if(result == NULL)
284  return;
285 
286  p = 1/sqrtf(x*x + y*y + z*z);
287  x *= p; y *= p; z *= p;
288  radians = degrees * (M_PI/180);
289  c = cosf(radians);
290  s = sinf(radians);
291  c_ = 1 - c;
292  zc_ = z*c_;
293  yc_ = y*c_;
294  xzc_ = x*zc_;
295  xyc_ = x*y*c_;
296  yzc_ = y*zc_;
297  xs = x*s;
298  ys = y*s;
299  zs = z*s;
300 
301  {
302 #ifdef ROW_MAJOR
303  float A[16];
304  FILL_MATRIX_4x4(A,
305  x*x*c_ + c, xyc_ - zs, xzc_ + ys, 0,
306  xyc_ + zs, y*yc_ + c, yzc_ - xs, 0,
307  xzc_ - ys, yzc_ + xs, z*zc_ + c, 0,
308  0, 0, 0, 1
309  );
310 #else
311  float A[16];
312  FILL_MATRIX_4x4(A,
313  x*x*c_ + c, xyc_ + zs, xzc_ - ys, 0,
314  xyc_ - zs, y*yc_ + c, yzc_ + xs, 0,
315  xzc_ + ys, yzc_ - xs, z*zc_ + c, 0,
316  0, 0, 0, 1
317  );
318 #endif
319 
320  GPU_MultiplyAndAssign(result, A);
321  }
322 }
323 
324 // Matrix multiply: result = A * B
325 void GPU_Multiply4x4(float* result, float* A, float* B)
326 {
327  float (*matR)[4] = (float(*)[4])result;
328  float (*matA)[4] = (float(*)[4])A;
329  float (*matB)[4] = (float(*)[4])B;
330  matR[0][0] = matB[0][0] * matA[0][0] + matB[0][1] * matA[1][0] + matB[0][2] * matA[2][0] + matB[0][3] * matA[3][0];
331  matR[0][1] = matB[0][0] * matA[0][1] + matB[0][1] * matA[1][1] + matB[0][2] * matA[2][1] + matB[0][3] * matA[3][1];
332  matR[0][2] = matB[0][0] * matA[0][2] + matB[0][1] * matA[1][2] + matB[0][2] * matA[2][2] + matB[0][3] * matA[3][2];
333  matR[0][3] = matB[0][0] * matA[0][3] + matB[0][1] * matA[1][3] + matB[0][2] * matA[2][3] + matB[0][3] * matA[3][3];
334  matR[1][0] = matB[1][0] * matA[0][0] + matB[1][1] * matA[1][0] + matB[1][2] * matA[2][0] + matB[1][3] * matA[3][0];
335  matR[1][1] = matB[1][0] * matA[0][1] + matB[1][1] * matA[1][1] + matB[1][2] * matA[2][1] + matB[1][3] * matA[3][1];
336  matR[1][2] = matB[1][0] * matA[0][2] + matB[1][1] * matA[1][2] + matB[1][2] * matA[2][2] + matB[1][3] * matA[3][2];
337  matR[1][3] = matB[1][0] * matA[0][3] + matB[1][1] * matA[1][3] + matB[1][2] * matA[2][3] + matB[1][3] * matA[3][3];
338  matR[2][0] = matB[2][0] * matA[0][0] + matB[2][1] * matA[1][0] + matB[2][2] * matA[2][0] + matB[2][3] * matA[3][0];
339  matR[2][1] = matB[2][0] * matA[0][1] + matB[2][1] * matA[1][1] + matB[2][2] * matA[2][1] + matB[2][3] * matA[3][1];
340  matR[2][2] = matB[2][0] * matA[0][2] + matB[2][1] * matA[1][2] + matB[2][2] * matA[2][2] + matB[2][3] * matA[3][2];
341  matR[2][3] = matB[2][0] * matA[0][3] + matB[2][1] * matA[1][3] + matB[2][2] * matA[2][3] + matB[2][3] * matA[3][3];
342  matR[3][0] = matB[3][0] * matA[0][0] + matB[3][1] * matA[1][0] + matB[3][2] * matA[2][0] + matB[3][3] * matA[3][0];
343  matR[3][1] = matB[3][0] * matA[0][1] + matB[3][1] * matA[1][1] + matB[3][2] * matA[2][1] + matB[3][3] * matA[3][1];
344  matR[3][2] = matB[3][0] * matA[0][2] + matB[3][1] * matA[1][2] + matB[3][2] * matA[2][2] + matB[3][3] * matA[3][2];
345  matR[3][3] = matB[3][0] * matA[0][3] + matB[3][1] * matA[1][3] + matB[3][2] * matA[2][3] + matB[3][3] * matA[3][3];
346 }
347 
348 void GPU_MultiplyAndAssign(float* result, float* B)
349 {
350  float temp[16];
351  GPU_Multiply4x4(temp, result, B);
352  GPU_MatrixCopy(result, temp);
353 }
354 
355 
356 
357 
358 
359 // Can be used up to two times per line evaluation...
360 const char* GPU_GetMatrixString(float* A)
361 {
362  static char buffer[512];
363  static char buffer2[512];
364  static char flip = 0;
365 
366  char* b = (flip? buffer : buffer2);
367  flip = !flip;
368 
369  snprintf(b, 512, "%.1f %.1f %.1f %.1f\n"
370  "%.1f %.1f %.1f %.1f\n"
371  "%.1f %.1f %.1f %.1f\n"
372  "%.1f %.1f %.1f %.1f",
373  A[0], A[1], A[2], A[3],
374  A[4], A[5], A[6], A[7],
375  A[8], A[9], A[10], A[11],
376  A[12], A[13], A[14], A[15]);
377  return b;
378 }
379 
380 void GPU_MatrixMode(int matrix_mode)
381 {
382  GPU_Target* target = GPU_GetContextTarget();
383  if(target == NULL || target->context == NULL)
384  return;
385 
386  target->context->matrix_mode = matrix_mode;
387 }
388 
389 float* GPU_GetModelView(void)
390 {
391  GPU_Target* target = GPU_GetContextTarget();
392  GPU_MatrixStack* stack;
393 
394  if(target == NULL || target->context == NULL)
395  return NULL;
396  stack = &target->context->modelview_matrix;
397  if(stack->size == 0)
398  return NULL;
399  return stack->matrix[stack->size-1];
400 }
401 
402 float* GPU_GetProjection(void)
403 {
404  GPU_Target* target = GPU_GetContextTarget();
405  GPU_MatrixStack* stack;
406 
407  if(target == NULL || target->context == NULL)
408  return NULL;
409  stack = &target->context->projection_matrix;
410  if(stack->size == 0)
411  return NULL;
412  return stack->matrix[stack->size-1];
413 }
414 
416 {
417  GPU_Target* target = GPU_GetContextTarget();
418  GPU_MatrixStack* stack;
419 
420  if(target == NULL || target->context == NULL)
421  return NULL;
422  if(target->context->matrix_mode == GPU_MODELVIEW)
423  stack = &target->context->modelview_matrix;
424  else
425  stack = &target->context->projection_matrix;
426 
427  if(stack->size == 0)
428  return NULL;
429  return stack->matrix[stack->size-1];
430 }
431 
432 void GPU_PushMatrix(void)
433 {
434  GPU_Target* target = GPU_GetContextTarget();
435  GPU_MatrixStack* stack;
436 
437  if(target == NULL || target->context == NULL)
438  return;
439 
440  stack = (target->context->matrix_mode == GPU_MODELVIEW? &target->context->modelview_matrix : &target->context->projection_matrix);
441  if(stack->size + 1 >= GPU_MATRIX_STACK_MAX)
442  {
443  GPU_PushErrorCode(__func__, GPU_ERROR_USER_ERROR, "Matrix stack is full (%d)", GPU_MATRIX_STACK_MAX);
444  return;
445  }
446  GPU_MatrixCopy(stack->matrix[stack->size], stack->matrix[stack->size-1]);
447  stack->size++;
448 }
449 
450 void GPU_PopMatrix(void)
451 {
452  GPU_Target* target = GPU_GetContextTarget();
453  GPU_MatrixStack* stack;
454 
455  if(target == NULL || target->context == NULL)
456  return;
457 
459  stack = (target->context->matrix_mode == GPU_MODELVIEW? &target->context->modelview_matrix : &target->context->projection_matrix);
460  if(stack->size == 0)
461  {
462  GPU_PushErrorCode(__func__, GPU_ERROR_USER_ERROR, "Matrix stack is empty.");
463  return;
464  }
465  stack->size--;
466 }
467 
469 {
470  float* result = GPU_GetCurrentMatrix();
471  if(result == NULL)
472  return;
473 
475  GPU_MatrixIdentity(result);
476 }
477 
478 void GPU_Ortho(float left, float right, float bottom, float top, float near, float far)
479 {
481  GPU_MatrixOrtho(GPU_GetCurrentMatrix(), left, right, bottom, top, near, far);
482 }
483 
484 void GPU_Frustum(float left, float right, float bottom, float top, float near, float far)
485 {
487  GPU_MatrixFrustum(GPU_GetCurrentMatrix(), left, right, bottom, top, near, far);
488 }
489 
490 void GPU_Translate(float x, float y, float z)
491 {
494 }
495 
496 void GPU_Scale(float sx, float sy, float sz)
497 {
499  GPU_MatrixScale(GPU_GetCurrentMatrix(), sx, sy, sz);
500 }
501 
502 void GPU_Rotate(float degrees, float x, float y, float z)
503 {
505  GPU_MatrixRotate(GPU_GetCurrentMatrix(), degrees, x, y, z);
506 }
507 
508 void GPU_MultMatrix(float* A)
509 {
510  float* result = GPU_GetCurrentMatrix();
511  if(result == NULL)
512  return;
514  // BIG FIXME: All of these matrix stack manipulators should be flushing the blit buffer.
515  // A better solution would be to minimize the matrix stack API and make it clear that MultMatrix flushes.
516  GPU_MultiplyAndAssign(result, A);
517 }
518 
519 void GPU_GetModelViewProjection(float* result)
520 {
521  // MVP = P * MV
523 }
#define M_PI
void GPU_Translate(float x, float y, float z)
#define GPU_MATRIX_STACK_MAX
Definition: SDL_gpu.h:334
void GPU_Multiply4x4(float *result, float *A, float *B)
float matrix[GPU_MATRIX_STACK_MAX][16]
Definition: SDL_gpu.h:342
void GPU_Frustum(float left, float right, float bottom, float top, float near, float far)
DECLSPEC void SDLCALL GPU_FlushBlitBuffer(void)
Definition: SDL_gpu.c:2036
void GPU_MatrixPerspective(float *result, float fovy, float aspect, float zNear, float zFar)
float * GPU_GetProjection(void)
void GPU_VectorCopy(float *result, float *A)
void GPU_VectorNormalize(float *vec3)
#define INDEX(row, col)
GPU_MatrixStack modelview_matrix
Definition: SDL_gpu.h:385
void GPU_MatrixOrtho(float *result, float left, float right, float bottom, float top, float near, float far)
DECLSPEC GPU_Target *SDLCALL GPU_GetContextTarget(void)
Definition: SDL_gpu.c:1293
const char * GPU_GetMatrixString(float *A)
void GPU_MatrixTranslate(float *result, float x, float y, float z)
#define static_inline
float * GPU_GetCurrentMatrix(void)
void GPU_Ortho(float left, float right, float bottom, float top, float near, float far)
void GPU_PopMatrix(void)
void GPU_MatrixScale(float *result, float sx, float sy, float sz)
void GPU_PushMatrix(void)
void GPU_VectorCross(float *result, float *A, float *B)
void GPU_MatrixMode(int matrix_mode)
unsigned int size
Definition: SDL_gpu.h:341
GPU_MatrixStack projection_matrix
Definition: SDL_gpu.h:384
void GPU_MatrixCopy(float *result, const float *A)
int matrix_mode
Definition: SDL_gpu.h:383
void GPU_GetModelViewProjection(float *result)
float GPU_VectorDot(float *A, float *B)
void GPU_VectorApplyMatrix(float *vec3, float *matrix_4x4)
void GPU_LoadIdentity(void)
#define GPU_MODELVIEW
Definition: SDL_gpu.h:330
void GPU_MatrixIdentity(float *result)
void GPU_Scale(float sx, float sy, float sz)
float * GPU_GetModelView(void)
float GPU_VectorLength(float *vec3)
void GPU_MatrixFrustum(float *result, float left, float right, float bottom, float top, float near, float far)
void GPU_MultiplyAndAssign(float *result, float *B)
DECLSPEC void SDLCALL GPU_PushErrorCode(const char *function, GPU_ErrorEnum error, const char *details,...)
Definition: SDL_gpu.c:692
void GPU_MultMatrix(float *A)
#define FILL_MATRIX_4x4(A, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
void GPU_Rotate(float degrees, float x, float y, float z)
GPU_Context * context
Definition: SDL_gpu.h:424
void GPU_MatrixRotate(float *result, float degrees, float x, float y, float z)
void GPU_MatrixLookAt(float *matrix, float eye_x, float eye_y, float eye_z, float target_x, float target_y, float target_z, float up_x, float up_y, float up_z)