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