comments regarding window sizing
[mesa.git] / src / mesa / main / matrix.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 5.1
4 *
5 * Copyright (C) 1999-2003 Brian Paul 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 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /*
27 * Matrix operations
28 *
29 * NOTES:
30 * 1. 4x4 transformation matrices are stored in memory in column major order.
31 * 2. Points/vertices are to be thought of as column vectors.
32 * 3. Transformation of a point p by a matrix M is: p' = M * p
33 */
34
35
36 #include "glheader.h"
37 #include "imports.h"
38 #include "buffers.h"
39 #include "context.h"
40 #include "enums.h"
41 #include "macros.h"
42 #include "matrix.h"
43 #include "mtypes.h"
44 #include "math/m_matrix.h"
45
46
47
48 void
49 _mesa_Frustum( GLdouble left, GLdouble right,
50 GLdouble bottom, GLdouble top,
51 GLdouble nearval, GLdouble farval )
52 {
53 GET_CURRENT_CONTEXT(ctx);
54 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
55
56 if (nearval <= 0.0 ||
57 farval <= 0.0 ||
58 nearval == farval ||
59 left == right ||
60 top == bottom)
61 {
62 _mesa_error( ctx, GL_INVALID_VALUE, "glFrustum" );
63 return;
64 }
65
66 _math_matrix_frustum( ctx->CurrentStack->Top,
67 (GLfloat) left, (GLfloat) right,
68 (GLfloat) bottom, (GLfloat) top,
69 (GLfloat) nearval, (GLfloat) farval );
70 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
71 }
72
73
74 void
75 _mesa_Ortho( GLdouble left, GLdouble right,
76 GLdouble bottom, GLdouble top,
77 GLdouble nearval, GLdouble farval )
78 {
79 GET_CURRENT_CONTEXT(ctx);
80 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
81
82 if (MESA_VERBOSE & VERBOSE_API)
83 _mesa_debug(ctx, "glFrustum(%f, %f, %f, %f, %f, %f)\n",
84 left, right, bottom, top, nearval, farval);
85
86 if (left == right ||
87 bottom == top ||
88 nearval == farval)
89 {
90 _mesa_error( ctx, GL_INVALID_VALUE, "glOrtho" );
91 return;
92 }
93
94 _math_matrix_ortho( ctx->CurrentStack->Top,
95 (GLfloat) left, (GLfloat) right,
96 (GLfloat) bottom, (GLfloat) top,
97 (GLfloat) nearval, (GLfloat) farval );
98 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
99 }
100
101
102 void
103 _mesa_MatrixMode( GLenum mode )
104 {
105 GET_CURRENT_CONTEXT(ctx);
106 ASSERT_OUTSIDE_BEGIN_END(ctx);
107
108 if (ctx->Transform.MatrixMode == mode && mode != GL_TEXTURE)
109 return;
110 FLUSH_VERTICES(ctx, _NEW_TRANSFORM);
111
112 switch (mode) {
113 case GL_MODELVIEW:
114 ctx->CurrentStack = &ctx->ModelviewMatrixStack;
115 break;
116 case GL_PROJECTION:
117 ctx->CurrentStack = &ctx->ProjectionMatrixStack;
118 break;
119 case GL_TEXTURE:
120 ctx->CurrentStack = &ctx->TextureMatrixStack[ctx->Texture.CurrentUnit];
121 break;
122 case GL_COLOR:
123 ctx->CurrentStack = &ctx->ColorMatrixStack;
124 break;
125 case GL_MATRIX0_NV:
126 case GL_MATRIX1_NV:
127 case GL_MATRIX2_NV:
128 case GL_MATRIX3_NV:
129 case GL_MATRIX4_NV:
130 case GL_MATRIX5_NV:
131 case GL_MATRIX6_NV:
132 case GL_MATRIX7_NV:
133 if (ctx->Extensions.NV_vertex_program) {
134 ctx->CurrentStack = &ctx->ProgramMatrixStack[mode - GL_MATRIX0_NV];
135 }
136 else {
137 _mesa_error( ctx, GL_INVALID_ENUM, "glMatrixMode(mode)" );
138 return;
139 }
140 break;
141 case GL_MATRIX0_ARB:
142 case GL_MATRIX1_ARB:
143 case GL_MATRIX2_ARB:
144 case GL_MATRIX3_ARB:
145 case GL_MATRIX4_ARB:
146 case GL_MATRIX5_ARB:
147 case GL_MATRIX6_ARB:
148 case GL_MATRIX7_ARB:
149 if (ctx->Extensions.ARB_vertex_program ||
150 ctx->Extensions.ARB_fragment_program) {
151 const GLint m = mode - GL_MATRIX0_ARB;
152 if (m > ctx->Const.MaxProgramMatrices) {
153 _mesa_error(ctx, GL_INVALID_ENUM,
154 "glMatrixMode(GL_MATRIX%d_ARB)", m);
155 return;
156 }
157 ctx->CurrentStack = &ctx->ProgramMatrixStack[m];
158 }
159 else {
160 _mesa_error( ctx, GL_INVALID_ENUM, "glMatrixMode(mode)" );
161 return;
162 }
163 break;
164 default:
165 _mesa_error( ctx, GL_INVALID_ENUM, "glMatrixMode(mode)" );
166 return;
167 }
168
169 ctx->Transform.MatrixMode = mode;
170 }
171
172
173
174 void
175 _mesa_PushMatrix( void )
176 {
177 GET_CURRENT_CONTEXT(ctx);
178 struct matrix_stack *stack = ctx->CurrentStack;
179 ASSERT_OUTSIDE_BEGIN_END(ctx);
180
181 if (MESA_VERBOSE&VERBOSE_API)
182 _mesa_debug(ctx, "glPushMatrix %s\n",
183 _mesa_lookup_enum_by_nr(ctx->Transform.MatrixMode));
184
185 if (stack->Depth + 1 >= stack->MaxDepth) {
186 _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushMatrix" );
187 return;
188 }
189 _math_matrix_copy( &stack->Stack[stack->Depth + 1],
190 &stack->Stack[stack->Depth] );
191 stack->Depth++;
192 stack->Top = &(stack->Stack[stack->Depth]);
193 ctx->NewState |= stack->DirtyFlag;
194 }
195
196
197
198 void
199 _mesa_PopMatrix( void )
200 {
201 GET_CURRENT_CONTEXT(ctx);
202 struct matrix_stack *stack = ctx->CurrentStack;
203 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
204
205 if (MESA_VERBOSE&VERBOSE_API)
206 _mesa_debug(ctx, "glPopMatrix %s\n",
207 _mesa_lookup_enum_by_nr(ctx->Transform.MatrixMode));
208
209 if (stack->Depth == 0) {
210 _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopMatrix" );
211 return;
212 }
213 stack->Depth--;
214 stack->Top = &(stack->Stack[stack->Depth]);
215 ctx->NewState |= stack->DirtyFlag;
216 }
217
218
219
220 void
221 _mesa_LoadIdentity( void )
222 {
223 GET_CURRENT_CONTEXT(ctx);
224 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
225
226 if (MESA_VERBOSE & VERBOSE_API)
227 _mesa_debug(ctx, "glLoadIdentity()");
228
229 _math_matrix_set_identity( ctx->CurrentStack->Top );
230 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
231 }
232
233
234 void
235 _mesa_LoadMatrixf( const GLfloat *m )
236 {
237 GET_CURRENT_CONTEXT(ctx);
238 if (!m) return;
239 if (MESA_VERBOSE & VERBOSE_API)
240 _mesa_debug(ctx,
241 "glLoadMatrix(%f %f %f %f, %f %f %f %f, %f %f %f %f, %f %f %f %f\n",
242 m[0], m[4], m[8], m[12],
243 m[1], m[5], m[9], m[13],
244 m[2], m[6], m[10], m[14],
245 m[3], m[7], m[11], m[15]);
246
247 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
248 _math_matrix_loadf( ctx->CurrentStack->Top, m );
249 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
250 }
251
252
253 void
254 _mesa_LoadMatrixd( const GLdouble *m )
255 {
256 GLint i;
257 GLfloat f[16];
258 if (!m) return;
259 for (i = 0; i < 16; i++)
260 f[i] = (GLfloat) m[i];
261 _mesa_LoadMatrixf(f);
262 }
263
264
265
266 /*
267 * Multiply the active matrix by an arbitary matrix.
268 */
269 void
270 _mesa_MultMatrixf( const GLfloat *m )
271 {
272 GET_CURRENT_CONTEXT(ctx);
273 if (!m) return;
274 if (MESA_VERBOSE & VERBOSE_API)
275 _mesa_debug(ctx,
276 "glMultMatrix(%f %f %f %f, %f %f %f %f, %f %f %f %f, %f %f %f %f\n",
277 m[0], m[4], m[8], m[12],
278 m[1], m[5], m[9], m[13],
279 m[2], m[6], m[10], m[14],
280 m[3], m[7], m[11], m[15]);
281 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
282 _math_matrix_mul_floats( ctx->CurrentStack->Top, m );
283 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
284 }
285
286
287 /*
288 * Multiply the active matrix by an arbitary matrix.
289 */
290 void
291 _mesa_MultMatrixd( const GLdouble *m )
292 {
293 GLint i;
294 GLfloat f[16];
295 if (!m) return;
296 for (i = 0; i < 16; i++)
297 f[i] = (GLfloat) m[i];
298 _mesa_MultMatrixf( f );
299 }
300
301
302
303
304 /*
305 * Execute a glRotate call
306 */
307 void
308 _mesa_Rotatef( GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
309 {
310 GET_CURRENT_CONTEXT(ctx);
311 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
312 if (angle != 0.0F) {
313 _math_matrix_rotate( ctx->CurrentStack->Top, angle, x, y, z);
314 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
315 }
316 }
317
318 void
319 _mesa_Rotated( GLdouble angle, GLdouble x, GLdouble y, GLdouble z )
320 {
321 _mesa_Rotatef((GLfloat) angle, (GLfloat) x, (GLfloat) y, (GLfloat) z);
322 }
323
324
325 /*
326 * Execute a glScale call
327 */
328 void
329 _mesa_Scalef( GLfloat x, GLfloat y, GLfloat z )
330 {
331 GET_CURRENT_CONTEXT(ctx);
332 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
333 _math_matrix_scale( ctx->CurrentStack->Top, x, y, z);
334 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
335 }
336
337
338 void
339 _mesa_Scaled( GLdouble x, GLdouble y, GLdouble z )
340 {
341 _mesa_Scalef((GLfloat) x, (GLfloat) y, (GLfloat) z);
342 }
343
344
345 /*
346 * Execute a glTranslate call
347 */
348 void
349 _mesa_Translatef( GLfloat x, GLfloat y, GLfloat z )
350 {
351 GET_CURRENT_CONTEXT(ctx);
352 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
353 _math_matrix_translate( ctx->CurrentStack->Top, x, y, z);
354 ctx->NewState |= ctx->CurrentStack->DirtyFlag;
355 }
356
357
358 void
359 _mesa_Translated( GLdouble x, GLdouble y, GLdouble z )
360 {
361 _mesa_Translatef((GLfloat) x, (GLfloat) y, (GLfloat) z);
362 }
363
364
365 void
366 _mesa_LoadTransposeMatrixfARB( const GLfloat *m )
367 {
368 GLfloat tm[16];
369 if (!m) return;
370 _math_transposef(tm, m);
371 _mesa_LoadMatrixf(tm);
372 }
373
374
375 void
376 _mesa_LoadTransposeMatrixdARB( const GLdouble *m )
377 {
378 GLfloat tm[16];
379 if (!m) return;
380 _math_transposefd(tm, m);
381 _mesa_LoadMatrixf(tm);
382 }
383
384
385 void
386 _mesa_MultTransposeMatrixfARB( const GLfloat *m )
387 {
388 GLfloat tm[16];
389 if (!m) return;
390 _math_transposef(tm, m);
391 _mesa_MultMatrixf(tm);
392 }
393
394
395 void
396 _mesa_MultTransposeMatrixdARB( const GLdouble *m )
397 {
398 GLfloat tm[16];
399 if (!m) return;
400 _math_transposefd(tm, m);
401 _mesa_MultMatrixf(tm);
402 }
403
404
405 /*
406 * Called via glViewport or display list execution.
407 */
408 void
409 _mesa_Viewport( GLint x, GLint y, GLsizei width, GLsizei height )
410 {
411 GET_CURRENT_CONTEXT(ctx);
412 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
413 _mesa_set_viewport(ctx, x, y, width, height);
414 }
415
416
417 /**
418 * Set new viewport parameters and update derived state (the _WindowMap
419 * matrix). Usually called from _mesa_Viewport().
420 * \note We also call _mesa_ResizeBuffersMESA() because this is a good
421 * time to check if the window has been resized. Many device drivers
422 * can't get direct notification from the window system of size changes
423 * so this is an ad-hoc solution to that problem.
424 */
425 void
426 _mesa_set_viewport( GLcontext *ctx, GLint x, GLint y,
427 GLsizei width, GLsizei height )
428 {
429 const GLfloat n = ctx->Viewport.Near;
430 const GLfloat f = ctx->Viewport.Far;
431
432 if (MESA_VERBOSE & VERBOSE_API)
433 _mesa_debug(ctx, "glViewport %d %d %d %d\n", x, y, width, height);
434
435 if (width < 0 || height < 0) {
436 _mesa_error( ctx, GL_INVALID_VALUE,
437 "glViewport(%d, %d, %d, %d)", x, y, width, height );
438 return;
439 }
440
441 /* clamp width, and height to implementation dependent range */
442 width = CLAMP( width, 1, MAX_WIDTH );
443 height = CLAMP( height, 1, MAX_HEIGHT );
444
445 /* Save viewport */
446 ctx->Viewport.X = x;
447 ctx->Viewport.Width = width;
448 ctx->Viewport.Y = y;
449 ctx->Viewport.Height = height;
450
451 /* compute scale and bias values :: This is really driver-specific
452 * and should be maintained elsewhere if at all.
453 */
454 ctx->Viewport._WindowMap.m[MAT_SX] = (GLfloat) width / 2.0F;
455 ctx->Viewport._WindowMap.m[MAT_TX] = ctx->Viewport._WindowMap.m[MAT_SX] + x;
456 ctx->Viewport._WindowMap.m[MAT_SY] = (GLfloat) height / 2.0F;
457 ctx->Viewport._WindowMap.m[MAT_TY] = ctx->Viewport._WindowMap.m[MAT_SY] + y;
458 ctx->Viewport._WindowMap.m[MAT_SZ] = ctx->DepthMaxF * ((f - n) / 2.0F);
459 ctx->Viewport._WindowMap.m[MAT_TZ] = ctx->DepthMaxF * ((f - n) / 2.0F + n);
460 ctx->Viewport._WindowMap.flags = MAT_FLAG_GENERAL_SCALE|MAT_FLAG_TRANSLATION;
461 ctx->Viewport._WindowMap.type = MATRIX_3D_NO_ROT;
462 ctx->NewState |= _NEW_VIEWPORT;
463
464 /* Check if window/buffer has been resized and if so, reallocate the
465 * ancillary buffers. This is an ad-hoc solution to detecting window
466 * size changes. 99% of all GL apps call glViewport when a window is
467 * resized so this is a good time to check for new window dims and
468 * reallocate color buffers and ancilliary buffers.
469 */
470 _mesa_ResizeBuffersMESA();
471
472 if (ctx->Driver.Viewport) {
473 (*ctx->Driver.Viewport)( ctx, x, y, width, height );
474 }
475 }
476
477
478
479 void
480 _mesa_DepthRange( GLclampd nearval, GLclampd farval )
481 {
482 /*
483 * nearval - specifies mapping of the near clipping plane to window
484 * coordinates, default is 0
485 * farval - specifies mapping of the far clipping plane to window
486 * coordinates, default is 1
487 *
488 * After clipping and div by w, z coords are in -1.0 to 1.0,
489 * corresponding to near and far clipping planes. glDepthRange
490 * specifies a linear mapping of the normalized z coords in
491 * this range to window z coords.
492 */
493 GLfloat n, f;
494 GET_CURRENT_CONTEXT(ctx);
495 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
496
497 if (MESA_VERBOSE&VERBOSE_API)
498 _mesa_debug(ctx, "glDepthRange %f %f\n", nearval, farval);
499
500 n = (GLfloat) CLAMP( nearval, 0.0, 1.0 );
501 f = (GLfloat) CLAMP( farval, 0.0, 1.0 );
502
503 ctx->Viewport.Near = n;
504 ctx->Viewport.Far = f;
505 ctx->Viewport._WindowMap.m[MAT_SZ] = ctx->DepthMaxF * ((f - n) / 2.0F);
506 ctx->Viewport._WindowMap.m[MAT_TZ] = ctx->DepthMaxF * ((f - n) / 2.0F + n);
507 ctx->NewState |= _NEW_VIEWPORT;
508
509 if (ctx->Driver.DepthRange) {
510 (*ctx->Driver.DepthRange)( ctx, nearval, farval );
511 }
512 }