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
49static struct gl_matrix_stack *
50get_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
102static 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 */
144void GLAPIENTRY
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
158void GLAPIENTRY
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
178static void
179matrix_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 */
225void GLAPIENTRY
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
239void GLAPIENTRY
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 */
270void GLAPIENTRY
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
293static void
294push_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 */
342void GLAPIENTRY
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
356void GLAPIENTRY
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
368static GLboolean
369pop_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 */
399void GLAPIENTRY
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
423void GLAPIENTRY
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
446void
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 */
465void GLAPIENTRY
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
477void GLAPIENTRY
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
490void
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
502static void
503matrix_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 */
531void GLAPIENTRY
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 */
547void GLAPIENTRY
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
560static void
561matrix_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 */
596void GLAPIENTRY
597_mesa_MultMatrixf( const GLfloat *m )
598{
599   GET_CURRENT_CONTEXT(ctx);
600   matrix_mult(ctx->CurrentStack, m, "glMultMatrix");
601}
602
603
604void GLAPIENTRY
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
617static void
618matrix_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 */
645void GLAPIENTRY
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
653void GLAPIENTRY
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 */
679void GLAPIENTRY
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
690void GLAPIENTRY
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 */
719void GLAPIENTRY
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
730void GLAPIENTRY
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
745void GLAPIENTRY
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
757void GLAPIENTRY
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
768void GLAPIENTRY
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
780void GLAPIENTRY
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
791void GLAPIENTRY
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
798void GLAPIENTRY
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
807void GLAPIENTRY
808_mesa_Scaled( GLdouble x, GLdouble y, GLdouble z )
809{
810   _mesa_Scalef((GLfloat) x, (GLfloat) y, (GLfloat) z);
811}
812
813
814void GLAPIENTRY
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
821void GLAPIENTRY
822_mesa_Translated( GLdouble x, GLdouble y, GLdouble z )
823{
824   _mesa_Translatef((GLfloat) x, (GLfloat) y, (GLfloat) z);
825}
826
827
828void GLAPIENTRY
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
835void GLAPIENTRY
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
844void GLAPIENTRY
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
853void GLAPIENTRY
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
862void GLAPIENTRY
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
871void GLAPIENTRY
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
880void GLAPIENTRY
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
889void GLAPIENTRY
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
898void GLAPIENTRY
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 */
924static void
925update_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 */
958void _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 */
990static void
991init_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 */
1009static void
1010free_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 */
1033void _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 */
1062void _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 */
1083void _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