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