1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
5 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27 /**
28 * \file matrix.c
29 * Matrix operations.
30 *
31 * \note
32 * -# 4x4 transformation matrices are stored in memory in column major order.
33 * -# Points/vertices are to be thought of as column vectors.
34 * -# Transformation of a point p by a matrix M is: p' = M * p
35 */
36
37
38 #include "glheader.h"
39
40 #include "context.h"
41 #include "enums.h"
42 #include "macros.h"
43 #include "matrix.h"
44 #include "mtypes.h"
45 #include "math/m_matrix.h"
46 #include "util/bitscan.h"
47
48
49 static struct gl_matrix_stack *
get_named_matrix_stack(struct gl_context * ctx,GLenum mode,const char * caller)50 get_named_matrix_stack(struct gl_context *ctx, GLenum mode, const char* caller)
51 {
52 switch (mode) {
53 case GL_MODELVIEW:
54 return &ctx->ModelviewMatrixStack;
55 case GL_PROJECTION:
56 return &ctx->ProjectionMatrixStack;
57 case GL_TEXTURE:
58 /* This error check is disabled because if we're called from
59 * glPopAttrib() when the active texture unit is >= MaxTextureCoordUnits
60 * we'll generate an unexpected error.
61 * From the GL_ARB_vertex_shader spec it sounds like we should instead
62 * do error checking in other places when we actually try to access
63 * texture matrices beyond MaxTextureCoordUnits.
64 */
65 #if 0
66 if (ctx->Texture.CurrentUnit >= ctx->Const.MaxTextureCoordUnits) {
67 _mesa_error(ctx, GL_INVALID_OPERATION,
68 "glMatrixMode(invalid tex unit %d)",
69 ctx->Texture.CurrentUnit);
70 return;
71 }
72 #endif
73 assert(ctx->Texture.CurrentUnit < ARRAY_SIZE(ctx->TextureMatrixStack));
74 return &ctx->TextureMatrixStack[ctx->Texture.CurrentUnit];
75 case GL_MATRIX0_ARB:
76 case GL_MATRIX1_ARB:
77 case GL_MATRIX2_ARB:
78 case GL_MATRIX3_ARB:
79 case GL_MATRIX4_ARB:
80 case GL_MATRIX5_ARB:
81 case GL_MATRIX6_ARB:
82 case GL_MATRIX7_ARB:
83 if (ctx->API == API_OPENGL_COMPAT
84 && (ctx->Extensions.ARB_vertex_program ||
85 ctx->Extensions.ARB_fragment_program)) {
86 const GLuint m = mode - GL_MATRIX0_ARB;
87 if (m <= ctx->Const.MaxProgramMatrices)
88 return &ctx->ProgramMatrixStack[m];
89 }
90 FALLTHROUGH;
91 default:
92 break;
93 }
94 if (mode >= GL_TEXTURE0 && mode < (GL_TEXTURE0 + ctx->Const.MaxTextureCoordUnits)) {
95 return &ctx->TextureMatrixStack[mode - GL_TEXTURE0];
96 }
97 _mesa_error(ctx, GL_INVALID_ENUM, "%s", caller);
98 return NULL;
99 }
100
101
matrix_frustum(struct gl_matrix_stack * stack,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval,const char * caller)102 static void matrix_frustum(struct gl_matrix_stack* stack,
103 GLdouble left, GLdouble right,
104 GLdouble bottom, GLdouble top,
105 GLdouble nearval, GLdouble farval,
106 const char* caller)
107 {
108 GET_CURRENT_CONTEXT(ctx);
109 if (nearval <= 0.0 ||
110 farval <= 0.0 ||
111 nearval == farval ||
112 left == right ||
113 top == bottom) {
114 _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
115 return;
116 }
117
118 FLUSH_VERTICES(ctx, 0, 0);
119
120 _math_matrix_frustum(stack->Top,
121 (GLfloat) left, (GLfloat) right,
122 (GLfloat) bottom, (GLfloat) top,
123 (GLfloat) nearval, (GLfloat) farval);
124 ctx->NewState |= stack->DirtyFlag;
125 }
126
127
128 /**
129 * Apply a perspective projection matrix.
130 *
131 * \param left left clipping plane coordinate.
132 * \param right right clipping plane coordinate.
133 * \param bottom bottom clipping plane coordinate.
134 * \param top top clipping plane coordinate.
135 * \param nearval distance to the near clipping plane.
136 * \param farval distance to the far clipping plane.
137 *
138 * \sa glFrustum().
139 *
140 * Flushes vertices and validates parameters. Calls _math_matrix_frustum() with
141 * the top matrix of the current matrix stack and sets
142 * __struct gl_contextRec::NewState.
143 */
144 void GLAPIENTRY
_mesa_Frustum(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval)145 _mesa_Frustum( GLdouble left, GLdouble right,
146 GLdouble bottom, GLdouble top,
147 GLdouble nearval, GLdouble farval )
148 {
149 GET_CURRENT_CONTEXT(ctx);
150 matrix_frustum(ctx->CurrentStack,
151 (GLfloat) left, (GLfloat) right,
152 (GLfloat) bottom, (GLfloat) top,
153 (GLfloat) nearval, (GLfloat) farval,
154 "glFrustum");
155 }
156
157
158 void GLAPIENTRY
_mesa_MatrixFrustumEXT(GLenum matrixMode,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval)159 _mesa_MatrixFrustumEXT( GLenum matrixMode,
160 GLdouble left, GLdouble right,
161 GLdouble bottom, GLdouble top,
162 GLdouble nearval, GLdouble farval )
163 {
164 GET_CURRENT_CONTEXT(ctx);
165 struct gl_matrix_stack *stack = get_named_matrix_stack(ctx, matrixMode,
166 "glMatrixFrustumEXT");
167 if (!stack)
168 return;
169
170 matrix_frustum(stack,
171 (GLfloat) left, (GLfloat) right,
172 (GLfloat) bottom, (GLfloat) top,
173 (GLfloat) nearval, (GLfloat) farval,
174 "glMatrixFrustumEXT");
175 }
176
177
178 static void
matrix_ortho(struct gl_matrix_stack * stack,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval,const char * caller)179 matrix_ortho(struct gl_matrix_stack* stack,
180 GLdouble left, GLdouble right,
181 GLdouble bottom, GLdouble top,
182 GLdouble nearval, GLdouble farval,
183 const char* caller)
184 {
185 GET_CURRENT_CONTEXT(ctx);
186
187 if (MESA_VERBOSE & VERBOSE_API)
188 _mesa_debug(ctx, "%s(%f, %f, %f, %f, %f, %f)\n", caller,
189 left, right, bottom, top, nearval, farval);
190
191 if (left == right ||
192 bottom == top ||
193 nearval == farval)
194 {
195 _mesa_error( ctx, GL_INVALID_VALUE, "%s", caller );
196 return;
197 }
198
199 FLUSH_VERTICES(ctx, 0, 0);
200
201 _math_matrix_ortho( stack->Top,
202 (GLfloat) left, (GLfloat) right,
203 (GLfloat) bottom, (GLfloat) top,
204 (GLfloat) nearval, (GLfloat) farval );
205 ctx->NewState |= stack->DirtyFlag;
206 }
207
208
209 /**
210 * Apply an orthographic projection matrix.
211 *
212 * \param left left clipping plane coordinate.
213 * \param right right clipping plane coordinate.
214 * \param bottom bottom clipping plane coordinate.
215 * \param top top clipping plane coordinate.
216 * \param nearval distance to the near clipping plane.
217 * \param farval distance to the far clipping plane.
218 *
219 * \sa glOrtho().
220 *
221 * Flushes vertices and validates parameters. Calls _math_matrix_ortho() with
222 * the top matrix of the current matrix stack and sets
223 * __struct gl_contextRec::NewState.
224 */
225 void GLAPIENTRY
_mesa_Ortho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval)226 _mesa_Ortho( GLdouble left, GLdouble right,
227 GLdouble bottom, GLdouble top,
228 GLdouble nearval, GLdouble farval )
229 {
230 GET_CURRENT_CONTEXT(ctx);
231 matrix_ortho(ctx->CurrentStack,
232 (GLfloat) left, (GLfloat) right,
233 (GLfloat) bottom, (GLfloat) top,
234 (GLfloat) nearval, (GLfloat) farval,
235 "glOrtho");
236 }
237
238
239 void GLAPIENTRY
_mesa_MatrixOrthoEXT(GLenum matrixMode,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval)240 _mesa_MatrixOrthoEXT( GLenum matrixMode,
241 GLdouble left, GLdouble right,
242 GLdouble bottom, GLdouble top,
243 GLdouble nearval, GLdouble farval )
244 {
245 GET_CURRENT_CONTEXT(ctx);
246 struct gl_matrix_stack *stack = get_named_matrix_stack(ctx, matrixMode,
247 "glMatrixOrthoEXT");
248 if (!stack)
249 return;
250
251 matrix_ortho(stack,
252 (GLfloat) left, (GLfloat) right,
253 (GLfloat) bottom, (GLfloat) top,
254 (GLfloat) nearval, (GLfloat) farval,
255 "glMatrixOrthoEXT");
256 }
257
258
259 /**
260 * Set the current matrix stack.
261 *
262 * \param mode matrix stack.
263 *
264 * \sa glMatrixMode().
265 *
266 * Flushes the vertices, validates the parameter and updates
267 * __struct gl_contextRec::CurrentStack and gl_transform_attrib::MatrixMode
268 * with the specified matrix stack.
269 */
270 void GLAPIENTRY
_mesa_MatrixMode(GLenum mode)271 _mesa_MatrixMode( GLenum mode )
272 {
273 struct gl_matrix_stack * stack;
274 GET_CURRENT_CONTEXT(ctx);
275
276 if (ctx->Transform.MatrixMode == mode && mode != GL_TEXTURE)
277 return;
278
279 if (mode >= GL_TEXTURE0 && mode < (GL_TEXTURE0 + ctx->Const.MaxTextureCoordUnits)) {
280 stack = NULL;
281 } else {
282 stack = get_named_matrix_stack(ctx, mode, "glMatrixMode");
283 }
284
285 if (stack) {
286 ctx->CurrentStack = stack;
287 ctx->Transform.MatrixMode = mode;
288 ctx->PopAttribState |= GL_TRANSFORM_BIT;
289 }
290 }
291
292
293 static void
push_matrix(struct gl_context * ctx,struct gl_matrix_stack * stack,GLenum matrixMode,const char * func)294 push_matrix(struct gl_context *ctx, struct gl_matrix_stack *stack,
295 GLenum matrixMode, const char *func)
296 {
297 if (stack->Depth + 1 >= stack->MaxDepth) {
298 if (ctx->Transform.MatrixMode == GL_TEXTURE) {
299 _mesa_error(ctx, GL_STACK_OVERFLOW, "%s(mode=GL_TEXTURE, unit=%d)",
300 func, ctx->Texture.CurrentUnit);
301 } else {
302 _mesa_error(ctx, GL_STACK_OVERFLOW, "%s(mode=%s)",
303 func, _mesa_enum_to_string(matrixMode));
304 }
305 return;
306 }
307
308 if (stack->Depth + 1 >= stack->StackSize) {
309 unsigned new_stack_size = stack->StackSize * 2;
310 unsigned i;
311 GLmatrix *new_stack = realloc(stack->Stack,
312 sizeof(*new_stack) * new_stack_size);
313
314 if (!new_stack) {
315 _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
316 return;
317 }
318
319 for (i = stack->StackSize; i < new_stack_size; i++)
320 _math_matrix_ctr(&new_stack[i]);
321
322 stack->Stack = new_stack;
323 stack->StackSize = new_stack_size;
324 }
325
326 _math_matrix_push_copy(&stack->Stack[stack->Depth + 1],
327 &stack->Stack[stack->Depth]);
328 stack->Depth++;
329 stack->Top = &(stack->Stack[stack->Depth]);
330 }
331
332
333 /**
334 * Push the current matrix stack.
335 *
336 * \sa glPushMatrix().
337 *
338 * Verifies the current matrix stack is not full, and duplicates the top-most
339 * matrix in the stack.
340 * Marks __struct gl_contextRec::NewState with the stack dirty flag.
341 */
342 void GLAPIENTRY
_mesa_PushMatrix(void)343 _mesa_PushMatrix( void )
344 {
345 GET_CURRENT_CONTEXT(ctx);
346 struct gl_matrix_stack *stack = ctx->CurrentStack;
347
348 if (MESA_VERBOSE&VERBOSE_API)
349 _mesa_debug(ctx, "glPushMatrix %s\n",
350 _mesa_enum_to_string(ctx->Transform.MatrixMode));
351
352 push_matrix(ctx, stack, ctx->Transform.MatrixMode, "glPushMatrix");
353 }
354
355
356 void GLAPIENTRY
_mesa_MatrixPushEXT(GLenum matrixMode)357 _mesa_MatrixPushEXT( GLenum matrixMode )
358 {
359 GET_CURRENT_CONTEXT(ctx);
360 struct gl_matrix_stack *stack = get_named_matrix_stack(ctx, matrixMode,
361 "glMatrixPushEXT");
362 ASSERT_OUTSIDE_BEGIN_END(ctx);
363 if (stack)
364 push_matrix(ctx, stack, matrixMode, "glMatrixPushEXT");
365 }
366
367
368 static GLboolean
pop_matrix(struct gl_context * ctx,struct gl_matrix_stack * stack)369 pop_matrix( struct gl_context *ctx, struct gl_matrix_stack *stack )
370 {
371 if (stack->Depth == 0)
372 return GL_FALSE;
373
374 stack->Depth--;
375
376 /* If the popped matrix is the same as the current one, treat it as
377 * a no-op change.
378 */
379 if (memcmp(stack->Top, &stack->Stack[stack->Depth],
380 sizeof(GLmatrix))) {
381 FLUSH_VERTICES(ctx, 0, 0);
382 ctx->NewState |= stack->DirtyFlag;
383 }
384
385 stack->Top = &(stack->Stack[stack->Depth]);
386 return GL_TRUE;
387 }
388
389
390 /**
391 * Pop the current matrix stack.
392 *
393 * \sa glPopMatrix().
394 *
395 * Flushes the vertices, verifies the current matrix stack is not empty, and
396 * moves the stack head down.
397 * Marks __struct gl_contextRec::NewState with the dirty stack flag.
398 */
399 void GLAPIENTRY
_mesa_PopMatrix(void)400 _mesa_PopMatrix( void )
401 {
402 GET_CURRENT_CONTEXT(ctx);
403 struct gl_matrix_stack *stack = ctx->CurrentStack;
404
405 if (MESA_VERBOSE&VERBOSE_API)
406 _mesa_debug(ctx, "glPopMatrix %s\n",
407 _mesa_enum_to_string(ctx->Transform.MatrixMode));
408
409 if (!pop_matrix(ctx, stack)) {
410 if (ctx->Transform.MatrixMode == GL_TEXTURE) {
411 _mesa_error(ctx, GL_STACK_UNDERFLOW,
412 "glPopMatrix(mode=GL_TEXTURE, unit=%d)",
413 ctx->Texture.CurrentUnit);
414 }
415 else {
416 _mesa_error(ctx, GL_STACK_UNDERFLOW, "glPopMatrix(mode=%s)",
417 _mesa_enum_to_string(ctx->Transform.MatrixMode));
418 }
419 }
420 }
421
422
423 void GLAPIENTRY
_mesa_MatrixPopEXT(GLenum matrixMode)424 _mesa_MatrixPopEXT( GLenum matrixMode )
425 {
426 GET_CURRENT_CONTEXT(ctx);
427 struct gl_matrix_stack *stack = get_named_matrix_stack(ctx, matrixMode,
428 "glMatrixPopEXT");
429 if (!stack)
430 return;
431
432 if (!pop_matrix(ctx, stack)) {
433 if (matrixMode == GL_TEXTURE) {
434 _mesa_error(ctx, GL_STACK_UNDERFLOW,
435 "glMatrixPopEXT(mode=GL_TEXTURE, unit=%d)",
436 ctx->Texture.CurrentUnit);
437 }
438 else {
439 _mesa_error(ctx, GL_STACK_UNDERFLOW, "glMatrixPopEXT(mode=%s)",
440 _mesa_enum_to_string(matrixMode));
441 }
442 }
443 }
444
445
446 void
_mesa_load_identity_matrix(struct gl_context * ctx,struct gl_matrix_stack * stack)447 _mesa_load_identity_matrix(struct gl_context *ctx, struct gl_matrix_stack *stack)
448 {
449 FLUSH_VERTICES(ctx, 0, 0);
450
451 _math_matrix_set_identity(stack->Top);
452 ctx->NewState |= stack->DirtyFlag;
453 }
454
455
456 /**
457 * Replace the current matrix with the identity matrix.
458 *
459 * \sa glLoadIdentity().
460 *
461 * Flushes the vertices and calls _math_matrix_set_identity() with the
462 * top-most matrix in the current stack.
463 * Marks __struct gl_contextRec::NewState with the stack dirty flag.
464 */
465 void GLAPIENTRY
_mesa_LoadIdentity(void)466 _mesa_LoadIdentity( void )
467 {
468 GET_CURRENT_CONTEXT(ctx);
469
470 if (MESA_VERBOSE & VERBOSE_API)
471 _mesa_debug(ctx, "glLoadIdentity()\n");
472
473 _mesa_load_identity_matrix(ctx, ctx->CurrentStack);
474 }
475
476
477 void GLAPIENTRY
_mesa_MatrixLoadIdentityEXT(GLenum matrixMode)478 _mesa_MatrixLoadIdentityEXT( GLenum matrixMode )
479 {
480 struct gl_matrix_stack *stack;
481 GET_CURRENT_CONTEXT(ctx);
482 stack = get_named_matrix_stack(ctx, matrixMode, "glMatrixLoadIdentityEXT");
483 if (!stack)
484 return;
485
486 _mesa_load_identity_matrix(ctx, stack);
487 }
488
489
490 void
_mesa_load_matrix(struct gl_context * ctx,struct gl_matrix_stack * stack,const GLfloat * m)491 _mesa_load_matrix(struct gl_context *ctx, struct gl_matrix_stack *stack,
492 const GLfloat *m)
493 {
494 if (memcmp(m, stack->Top->m, 16 * sizeof(GLfloat)) != 0) {
495 FLUSH_VERTICES(ctx, 0, 0);
496 _math_matrix_loadf(stack->Top, m);
497 ctx->NewState |= stack->DirtyFlag;
498 }
499 }
500
501
502 static void
matrix_load(struct gl_context * ctx,struct gl_matrix_stack * stack,const GLfloat * m,const char * caller)503 matrix_load(struct gl_context *ctx, struct gl_matrix_stack *stack,
504 const GLfloat *m, const char* caller)
505 {
506 if (!m) return;
507 if (MESA_VERBOSE & VERBOSE_API)
508 _mesa_debug(ctx,
509 "%s(%f %f %f %f, %f %f %f %f, %f %f %f %f, %f %f %f %f\n",
510 caller,
511 m[0], m[4], m[8], m[12],
512 m[1], m[5], m[9], m[13],
513 m[2], m[6], m[10], m[14],
514 m[3], m[7], m[11], m[15]);
515
516 _mesa_load_matrix(ctx, stack, m);
517 }
518
519
520 /**
521 * Replace the current matrix with a given matrix.
522 *
523 * \param m matrix.
524 *
525 * \sa glLoadMatrixf().
526 *
527 * Flushes the vertices and calls _math_matrix_loadf() with the top-most
528 * matrix in the current stack and the given matrix.
529 * Marks __struct gl_contextRec::NewState with the dirty stack flag.
530 */
531 void GLAPIENTRY
_mesa_LoadMatrixf(const GLfloat * m)532 _mesa_LoadMatrixf( const GLfloat *m )
533 {
534 GET_CURRENT_CONTEXT(ctx);
535 matrix_load(ctx, ctx->CurrentStack, m, "glLoadMatrix");
536 }
537
538
539 /**
540 * Replace the named matrix with a given matrix.
541 *
542 * \param matrixMode matrix to replace
543 * \param m matrix
544 *
545 * \sa glLoadMatrixf().
546 */
547 void GLAPIENTRY
_mesa_MatrixLoadfEXT(GLenum matrixMode,const GLfloat * m)548 _mesa_MatrixLoadfEXT( GLenum matrixMode, const GLfloat *m )
549 {
550 GET_CURRENT_CONTEXT(ctx);
551 struct gl_matrix_stack * stack =
552 get_named_matrix_stack(ctx, matrixMode, "glMatrixLoadfEXT");
553 if (!stack)
554 return;
555
556 matrix_load(ctx, stack, m, "glMatrixLoadfEXT");
557 }
558
559
560 static void
matrix_mult(struct gl_matrix_stack * stack,const GLfloat * m,const char * caller)561 matrix_mult(struct gl_matrix_stack *stack, const GLfloat *m, const char* caller)
562 {
563 GET_CURRENT_CONTEXT(ctx);
564 if (!m ||
565 (m[0] == 1 && m[1] == 0 && m[2] == 0 && m[3] == 0 &&
566 m[4] == 0 && m[5] == 1 && m[6] == 0 && m[7] == 0 &&
567 m[8] == 0 && m[9] == 0 && m[10] == 1 && m[11] == 0 &&
568 m[12] == 0 && m[13] == 0 && m[14] == 0 && m[15] == 1))
569 return;
570 if (MESA_VERBOSE & VERBOSE_API)
571 _mesa_debug(ctx,
572 "%s(%f %f %f %f, %f %f %f %f, %f %f %f %f, %f %f %f %f\n",
573 caller,
574 m[0], m[4], m[8], m[12],
575 m[1], m[5], m[9], m[13],
576 m[2], m[6], m[10], m[14],
577 m[3], m[7], m[11], m[15]);
578
579 FLUSH_VERTICES(ctx, 0, 0);
580 _math_matrix_mul_floats(stack->Top, m);
581 ctx->NewState |= stack->DirtyFlag;
582 }
583
584
585 /**
586 * Multiply the current matrix with a given matrix.
587 *
588 * \param m matrix.
589 *
590 * \sa glMultMatrixf().
591 *
592 * Flushes the vertices and calls _math_matrix_mul_floats() with the top-most
593 * matrix in the current stack and the given matrix. Marks
594 * __struct gl_contextRec::NewState with the dirty stack flag.
595 */
596 void GLAPIENTRY
_mesa_MultMatrixf(const GLfloat * m)597 _mesa_MultMatrixf( const GLfloat *m )
598 {
599 GET_CURRENT_CONTEXT(ctx);
600 matrix_mult(ctx->CurrentStack, m, "glMultMatrix");
601 }
602
603
604 void GLAPIENTRY
_mesa_MatrixMultfEXT(GLenum matrixMode,const GLfloat * m)605 _mesa_MatrixMultfEXT( GLenum matrixMode, const GLfloat *m )
606 {
607 GET_CURRENT_CONTEXT(ctx);
608 struct gl_matrix_stack * stack =
609 get_named_matrix_stack(ctx, matrixMode, "glMatrixMultfEXT");
610 if (!stack)
611 return;
612
613 matrix_mult(stack, m, "glMultMatrix");
614 }
615
616
617 static void
matrix_rotate(struct gl_matrix_stack * stack,GLfloat angle,GLfloat x,GLfloat y,GLfloat z,const char * caller)618 matrix_rotate(struct gl_matrix_stack *stack, GLfloat angle,
619 GLfloat x, GLfloat y, GLfloat z, const char* caller)
620 {
621 GET_CURRENT_CONTEXT(ctx);
622
623 FLUSH_VERTICES(ctx, 0, 0);
624 if (angle != 0.0F) {
625 _math_matrix_rotate(stack->Top, angle, x, y, z);
626 ctx->NewState |=stack->DirtyFlag;
627 }
628 }
629
630
631 /**
632 * Multiply the current matrix with a rotation matrix.
633 *
634 * \param angle angle of rotation, in degrees.
635 * \param x rotation vector x coordinate.
636 * \param y rotation vector y coordinate.
637 * \param z rotation vector z coordinate.
638 *
639 * \sa glRotatef().
640 *
641 * Flushes the vertices and calls _math_matrix_rotate() with the top-most
642 * matrix in the current stack and the given parameters. Marks
643 * __struct gl_contextRec::NewState with the dirty stack flag.
644 */
645 void GLAPIENTRY
_mesa_Rotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z)646 _mesa_Rotatef( GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
647 {
648 GET_CURRENT_CONTEXT(ctx);
649 matrix_rotate(ctx->CurrentStack, angle, x, y, z, "glRotatef");
650 }
651
652
653 void GLAPIENTRY
_mesa_MatrixRotatefEXT(GLenum matrixMode,GLfloat angle,GLfloat x,GLfloat y,GLfloat z)654 _mesa_MatrixRotatefEXT( GLenum matrixMode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
655 {
656 GET_CURRENT_CONTEXT(ctx);
657 struct gl_matrix_stack *stack =
658 get_named_matrix_stack(ctx, matrixMode, "glMatrixRotatefEXT");
659 if (!stack)
660 return;
661
662 matrix_rotate(stack, angle, x, y, z, "glMatrixRotatefEXT");
663 }
664
665
666 /**
667 * Multiply the current matrix with a general scaling matrix.
668 *
669 * \param x x axis scale factor.
670 * \param y y axis scale factor.
671 * \param z z axis scale factor.
672 *
673 * \sa glScalef().
674 *
675 * Flushes the vertices and calls _math_matrix_scale() with the top-most
676 * matrix in the current stack and the given parameters. Marks
677 * __struct gl_contextRec::NewState with the dirty stack flag.
678 */
679 void GLAPIENTRY
_mesa_Scalef(GLfloat x,GLfloat y,GLfloat z)680 _mesa_Scalef( GLfloat x, GLfloat y, GLfloat z )
681 {
682 GET_CURRENT_CONTEXT(ctx);
683
684 FLUSH_VERTICES(ctx, 0, 0);
685 _math_matrix_scale( ctx->CurrentStack->Top, x, y, z);
686 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
687 }
688
689
690 void GLAPIENTRY
_mesa_MatrixScalefEXT(GLenum matrixMode,GLfloat x,GLfloat y,GLfloat z)691 _mesa_MatrixScalefEXT( GLenum matrixMode, GLfloat x, GLfloat y, GLfloat z )
692 {
693 struct gl_matrix_stack *stack;
694 GET_CURRENT_CONTEXT(ctx);
695
696 stack = get_named_matrix_stack(ctx, matrixMode, "glMatrixScalefEXT");
697 if (!stack)
698 return;
699
700 FLUSH_VERTICES(ctx, 0, 0);
701 _math_matrix_scale(stack->Top, x, y, z);
702 ctx->NewState |= stack->DirtyFlag;
703 }
704
705
706 /**
707 * Multiply the current matrix with a translation matrix.
708 *
709 * \param x translation vector x coordinate.
710 * \param y translation vector y coordinate.
711 * \param z translation vector z coordinate.
712 *
713 * \sa glTranslatef().
714 *
715 * Flushes the vertices and calls _math_matrix_translate() with the top-most
716 * matrix in the current stack and the given parameters. Marks
717 * __struct gl_contextRec::NewState with the dirty stack flag.
718 */
719 void GLAPIENTRY
_mesa_Translatef(GLfloat x,GLfloat y,GLfloat z)720 _mesa_Translatef( GLfloat x, GLfloat y, GLfloat z )
721 {
722 GET_CURRENT_CONTEXT(ctx);
723
724 FLUSH_VERTICES(ctx, 0, 0);
725 _math_matrix_translate( ctx->CurrentStack->Top, x, y, z);
726 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
727 }
728
729
730 void GLAPIENTRY
_mesa_MatrixTranslatefEXT(GLenum matrixMode,GLfloat x,GLfloat y,GLfloat z)731 _mesa_MatrixTranslatefEXT( GLenum matrixMode, GLfloat x, GLfloat y, GLfloat z )
732 {
733 GET_CURRENT_CONTEXT(ctx);
734 struct gl_matrix_stack *stack =
735 get_named_matrix_stack(ctx, matrixMode, "glMatrixTranslatefEXT");
736 if (!stack)
737 return;
738
739 FLUSH_VERTICES(ctx, 0, 0);
740 _math_matrix_translate(stack->Top, x, y, z);
741 ctx->NewState |= stack->DirtyFlag;
742 }
743
744
745 void GLAPIENTRY
_mesa_LoadMatrixd(const GLdouble * m)746 _mesa_LoadMatrixd( const GLdouble *m )
747 {
748 GLint i;
749 GLfloat f[16];
750 if (!m) return;
751 for (i = 0; i < 16; i++)
752 f[i] = (GLfloat) m[i];
753 _mesa_LoadMatrixf(f);
754 }
755
756
757 void GLAPIENTRY
_mesa_MatrixLoaddEXT(GLenum matrixMode,const GLdouble * m)758 _mesa_MatrixLoaddEXT( GLenum matrixMode, const GLdouble *m )
759 {
760 GLfloat f[16];
761 if (!m) return;
762 for (unsigned i = 0; i < 16; i++)
763 f[i] = (GLfloat) m[i];
764 _mesa_MatrixLoadfEXT(matrixMode, f);
765 }
766
767
768 void GLAPIENTRY
_mesa_MultMatrixd(const GLdouble * m)769 _mesa_MultMatrixd( const GLdouble *m )
770 {
771 GLint i;
772 GLfloat f[16];
773 if (!m) return;
774 for (i = 0; i < 16; i++)
775 f[i] = (GLfloat) m[i];
776 _mesa_MultMatrixf( f );
777 }
778
779
780 void GLAPIENTRY
_mesa_MatrixMultdEXT(GLenum matrixMode,const GLdouble * m)781 _mesa_MatrixMultdEXT( GLenum matrixMode, const GLdouble *m )
782 {
783 GLfloat f[16];
784 if (!m) return;
785 for (unsigned i = 0; i < 16; i++)
786 f[i] = (GLfloat) m[i];
787 _mesa_MatrixMultfEXT(matrixMode, f);
788 }
789
790
791 void GLAPIENTRY
_mesa_Rotated(GLdouble angle,GLdouble x,GLdouble y,GLdouble z)792 _mesa_Rotated( GLdouble angle, GLdouble x, GLdouble y, GLdouble z )
793 {
794 _mesa_Rotatef((GLfloat) angle, (GLfloat) x, (GLfloat) y, (GLfloat) z);
795 }
796
797
798 void GLAPIENTRY
_mesa_MatrixRotatedEXT(GLenum matrixMode,GLdouble angle,GLdouble x,GLdouble y,GLdouble z)799 _mesa_MatrixRotatedEXT( GLenum matrixMode, GLdouble angle,
800 GLdouble x, GLdouble y, GLdouble z )
801 {
802 _mesa_MatrixRotatefEXT(matrixMode, (GLfloat) angle,
803 (GLfloat) x, (GLfloat) y, (GLfloat) z);
804 }
805
806
807 void GLAPIENTRY
_mesa_Scaled(GLdouble x,GLdouble y,GLdouble z)808 _mesa_Scaled( GLdouble x, GLdouble y, GLdouble z )
809 {
810 _mesa_Scalef((GLfloat) x, (GLfloat) y, (GLfloat) z);
811 }
812
813
814 void GLAPIENTRY
_mesa_MatrixScaledEXT(GLenum matrixMode,GLdouble x,GLdouble y,GLdouble z)815 _mesa_MatrixScaledEXT( GLenum matrixMode, GLdouble x, GLdouble y, GLdouble z )
816 {
817 _mesa_MatrixScalefEXT(matrixMode, (GLfloat) x, (GLfloat) y, (GLfloat) z);
818 }
819
820
821 void GLAPIENTRY
_mesa_Translated(GLdouble x,GLdouble y,GLdouble z)822 _mesa_Translated( GLdouble x, GLdouble y, GLdouble z )
823 {
824 _mesa_Translatef((GLfloat) x, (GLfloat) y, (GLfloat) z);
825 }
826
827
828 void GLAPIENTRY
_mesa_MatrixTranslatedEXT(GLenum matrixMode,GLdouble x,GLdouble y,GLdouble z)829 _mesa_MatrixTranslatedEXT( GLenum matrixMode, GLdouble x, GLdouble y, GLdouble z )
830 {
831 _mesa_MatrixTranslatefEXT(matrixMode, (GLfloat) x, (GLfloat) y, (GLfloat) z);
832 }
833
834
835 void GLAPIENTRY
_mesa_LoadTransposeMatrixf(const GLfloat * m)836 _mesa_LoadTransposeMatrixf( const GLfloat *m )
837 {
838 GLfloat tm[16];
839 if (!m) return;
840 _math_transposef(tm, m);
841 _mesa_LoadMatrixf(tm);
842 }
843
844 void GLAPIENTRY
_mesa_MatrixLoadTransposefEXT(GLenum matrixMode,const GLfloat * m)845 _mesa_MatrixLoadTransposefEXT( GLenum matrixMode, const GLfloat *m )
846 {
847 GLfloat tm[16];
848 if (!m) return;
849 _math_transposef(tm, m);
850 _mesa_MatrixLoadfEXT(matrixMode, tm);
851 }
852
853 void GLAPIENTRY
_mesa_LoadTransposeMatrixd(const GLdouble * m)854 _mesa_LoadTransposeMatrixd( const GLdouble *m )
855 {
856 GLfloat tm[16];
857 if (!m) return;
858 _math_transposefd(tm, m);
859 _mesa_LoadMatrixf(tm);
860 }
861
862 void GLAPIENTRY
_mesa_MatrixLoadTransposedEXT(GLenum matrixMode,const GLdouble * m)863 _mesa_MatrixLoadTransposedEXT( GLenum matrixMode, const GLdouble *m )
864 {
865 GLfloat tm[16];
866 if (!m) return;
867 _math_transposefd(tm, m);
868 _mesa_MatrixLoadfEXT(matrixMode, tm);
869 }
870
871 void GLAPIENTRY
_mesa_MultTransposeMatrixf(const GLfloat * m)872 _mesa_MultTransposeMatrixf( const GLfloat *m )
873 {
874 GLfloat tm[16];
875 if (!m) return;
876 _math_transposef(tm, m);
877 _mesa_MultMatrixf(tm);
878 }
879
880 void GLAPIENTRY
_mesa_MatrixMultTransposefEXT(GLenum matrixMode,const GLfloat * m)881 _mesa_MatrixMultTransposefEXT( GLenum matrixMode, const GLfloat *m )
882 {
883 GLfloat tm[16];
884 if (!m) return;
885 _math_transposef(tm, m);
886 _mesa_MatrixMultfEXT(matrixMode, tm);
887 }
888
889 void GLAPIENTRY
_mesa_MultTransposeMatrixd(const GLdouble * m)890 _mesa_MultTransposeMatrixd( const GLdouble *m )
891 {
892 GLfloat tm[16];
893 if (!m) return;
894 _math_transposefd(tm, m);
895 _mesa_MultMatrixf(tm);
896 }
897
898 void GLAPIENTRY
_mesa_MatrixMultTransposedEXT(GLenum matrixMode,const GLdouble * m)899 _mesa_MatrixMultTransposedEXT( GLenum matrixMode, const GLdouble *m )
900 {
901 GLfloat tm[16];
902 if (!m) return;
903 _math_transposefd(tm, m);
904 _mesa_MatrixMultfEXT(matrixMode, tm);
905 }
906
907 /**********************************************************************/
908 /** \name State management */
909 /*@{*/
910
911
912 /**
913 * Update the projection matrix stack.
914 *
915 * \param ctx GL context.
916 *
917 * Recomputes user clip positions if necessary.
918 *
919 * \note This routine references __struct gl_contextRec::Tranform attribute
920 * values to compute userclip positions in clip space, but is only called on
921 * _NEW_PROJECTION. The _mesa_ClipPlane() function keeps these values up to
922 * date across changes to the __struct gl_contextRec::Transform attributes.
923 */
924 static void
update_projection(struct gl_context * ctx)925 update_projection( struct gl_context *ctx )
926 {
927 /* Recompute clip plane positions in clipspace. This is also done
928 * in _mesa_ClipPlane().
929 */
930 GLbitfield mask = ctx->Transform.ClipPlanesEnabled;
931
932 if (mask) {
933 /* make sure the inverse is up to date */
934 _math_matrix_analyse(ctx->ProjectionMatrixStack.Top);
935
936 do {
937 const int p = u_bit_scan(&mask);
938
939 _mesa_transform_vector(ctx->Transform._ClipUserPlane[p],
940 ctx->Transform.EyeUserPlane[p],
941 ctx->ProjectionMatrixStack.Top->inv);
942 } while (mask);
943 }
944 }
945
946
947 /**
948 * Updates the combined modelview-projection matrix.
949 *
950 * \param ctx GL context.
951 * \param new_state new state bit mask.
952 *
953 * If there is a new model view matrix then analyzes it. If there is a new
954 * projection matrix, updates it. Finally calls
955 * calculate_model_project_matrix() to recalculate the modelview-projection
956 * matrix.
957 */
_mesa_update_modelview_project(struct gl_context * ctx,GLuint new_state)958 void _mesa_update_modelview_project( struct gl_context *ctx, GLuint new_state )
959 {
960 if (new_state & _NEW_MODELVIEW)
961 _math_matrix_analyse( ctx->ModelviewMatrixStack.Top );
962
963 if (new_state & _NEW_PROJECTION)
964 update_projection( ctx );
965
966 /* Calculate ModelViewMatrix * ProjectionMatrix. */
967 _math_matrix_mul_matrix(&ctx->_ModelProjectMatrix,
968 ctx->ProjectionMatrixStack.Top,
969 ctx->ModelviewMatrixStack.Top);
970 }
971
972 /*@}*/
973
974
975 /**********************************************************************/
976 /** Matrix stack initialization */
977 /*@{*/
978
979
980 /**
981 * Initialize a matrix stack.
982 *
983 * \param stack matrix stack.
984 * \param maxDepth maximum stack depth.
985 * \param dirtyFlag dirty flag.
986 *
987 * Allocates an array of \p maxDepth elements for the matrix stack and calls
988 * _math_matrix_ctr() for each element to initialize it.
989 */
990 static void
init_matrix_stack(struct gl_matrix_stack * stack,GLuint maxDepth,GLuint dirtyFlag)991 init_matrix_stack(struct gl_matrix_stack *stack,
992 GLuint maxDepth, GLuint dirtyFlag)
993 {
994 stack->Depth = 0;
995 stack->MaxDepth = maxDepth;
996 stack->DirtyFlag = dirtyFlag;
997 /* The stack will be dynamically resized at glPushMatrix() time */
998 stack->Stack = calloc(1, sizeof(GLmatrix));
999 stack->StackSize = 1;
1000 _math_matrix_ctr(&stack->Stack[0]);
1001 stack->Top = stack->Stack;
1002 }
1003
1004 /**
1005 * Free matrix stack.
1006 *
1007 * \param stack matrix stack.
1008 */
1009 static void
free_matrix_stack(struct gl_matrix_stack * stack)1010 free_matrix_stack( struct gl_matrix_stack *stack )
1011 {
1012 free(stack->Stack);
1013 stack->Stack = stack->Top = NULL;
1014 stack->StackSize = 0;
1015 }
1016
1017 /*@}*/
1018
1019
1020 /**********************************************************************/
1021 /** \name Initialization */
1022 /*@{*/
1023
1024
1025 /**
1026 * Initialize the context matrix data.
1027 *
1028 * \param ctx GL context.
1029 *
1030 * Initializes each of the matrix stacks and the combined modelview-projection
1031 * matrix.
1032 */
_mesa_init_matrix(struct gl_context * ctx)1033 void _mesa_init_matrix( struct gl_context * ctx )
1034 {
1035 GLuint i;
1036
1037 /* Initialize matrix stacks */
1038 init_matrix_stack(&ctx->ModelviewMatrixStack, MAX_MODELVIEW_STACK_DEPTH,
1039 _NEW_MODELVIEW);
1040 init_matrix_stack(&ctx->ProjectionMatrixStack, MAX_PROJECTION_STACK_DEPTH,
1041 _NEW_PROJECTION);
1042 for (i = 0; i < ARRAY_SIZE(ctx->TextureMatrixStack); i++)
1043 init_matrix_stack(&ctx->TextureMatrixStack[i], MAX_TEXTURE_STACK_DEPTH,
1044 _NEW_TEXTURE_MATRIX);
1045 for (i = 0; i < ARRAY_SIZE(ctx->ProgramMatrixStack); i++)
1046 init_matrix_stack(&ctx->ProgramMatrixStack[i],
1047 MAX_PROGRAM_MATRIX_STACK_DEPTH, _NEW_TRACK_MATRIX);
1048 ctx->CurrentStack = &ctx->ModelviewMatrixStack;
1049
1050 /* Init combined Modelview*Projection matrix */
1051 _math_matrix_ctr( &ctx->_ModelProjectMatrix );
1052 }
1053
1054
1055 /**
1056 * Free the context matrix data.
1057 *
1058 * \param ctx GL context.
1059 *
1060 * Frees each of the matrix stacks.
1061 */
_mesa_free_matrix_data(struct gl_context * ctx)1062 void _mesa_free_matrix_data( struct gl_context *ctx )
1063 {
1064 GLuint i;
1065
1066 free_matrix_stack(&ctx->ModelviewMatrixStack);
1067 free_matrix_stack(&ctx->ProjectionMatrixStack);
1068 for (i = 0; i < ARRAY_SIZE(ctx->TextureMatrixStack); i++)
1069 free_matrix_stack(&ctx->TextureMatrixStack[i]);
1070 for (i = 0; i < ARRAY_SIZE(ctx->ProgramMatrixStack); i++)
1071 free_matrix_stack(&ctx->ProgramMatrixStack[i]);
1072
1073 }
1074
1075
1076 /**
1077 * Initialize the context transform attribute group.
1078 *
1079 * \param ctx GL context.
1080 *
1081 * \todo Move this to a new file with other 'transform' routines.
1082 */
_mesa_init_transform(struct gl_context * ctx)1083 void _mesa_init_transform( struct gl_context *ctx )
1084 {
1085 GLuint i;
1086
1087 /* Transformation group */
1088 ctx->Transform.MatrixMode = GL_MODELVIEW;
1089 ctx->Transform.Normalize = GL_FALSE;
1090 ctx->Transform.RescaleNormals = GL_FALSE;
1091 ctx->Transform.RasterPositionUnclipped = GL_FALSE;
1092 for (i=0;i<ctx->Const.MaxClipPlanes;i++) {
1093 ASSIGN_4V( ctx->Transform.EyeUserPlane[i], 0.0, 0.0, 0.0, 0.0 );
1094 }
1095 ctx->Transform.ClipPlanesEnabled = 0;
1096 }
1097
1098
1099 /*@}*/
1100