1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5
4 *
5 * Copyright (C) 1999-2006  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 * Authors:
25 *    Keith Whitwell <keith@tungstengraphics.com>
26 */
27
28
29#include "main/glheader.h"
30#include "main/colormac.h"
31#include "main/macros.h"
32#include "main/imports.h"
33#include "main/mtypes.h"
34
35#include "math/m_xform.h"
36
37#include "t_context.h"
38#include "t_pipeline.h"
39
40
41struct normal_stage_data {
42   normal_func NormalTransform;
43   GLvector4f normal;
44};
45
46#define NORMAL_STAGE_DATA(stage) ((struct normal_stage_data *)stage->privatePtr)
47
48
49static GLboolean
50run_normal_stage(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
51{
52   struct normal_stage_data *store = NORMAL_STAGE_DATA(stage);
53   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
54   const GLfloat *lengths;
55
56   if (!store->NormalTransform)
57      return GL_TRUE;
58
59   /* We can only use the display list's saved normal lengths if we've
60    * got a transformation matrix with uniform scaling.
61    */
62   if (_math_matrix_is_general_scale(ctx->ModelviewMatrixStack.Top))
63      lengths = NULL;
64   else
65      lengths = VB->NormalLengthPtr;
66
67   store->NormalTransform( ctx->ModelviewMatrixStack.Top,
68			   ctx->_ModelViewInvScale,
69			   VB->AttribPtr[_TNL_ATTRIB_NORMAL],  /* input normals */
70			   lengths,
71			   &store->normal ); /* resulting normals */
72
73   if (VB->AttribPtr[_TNL_ATTRIB_NORMAL]->count > 1) {
74      store->normal.stride = 4 * sizeof(GLfloat);
75   }
76   else {
77      store->normal.stride = 0;
78   }
79
80   VB->AttribPtr[_TNL_ATTRIB_NORMAL] = &store->normal;
81
82   VB->NormalLengthPtr = NULL;	/* no longer valid */
83   return GL_TRUE;
84}
85
86
87/**
88 * Examine current GL state and set the store->NormalTransform pointer
89 * to point to the appropriate normal transformation routine.
90 */
91static void
92validate_normal_stage(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
93{
94   struct normal_stage_data *store = NORMAL_STAGE_DATA(stage);
95
96   if (ctx->VertexProgram._Current ||
97       (!ctx->Light.Enabled &&
98	!(ctx->Texture._GenFlags & TEXGEN_NEED_NORMALS))) {
99      store->NormalTransform = NULL;
100      return;
101   }
102
103   if (ctx->_NeedEyeCoords) {
104      /* Eye coordinates are needed, for whatever reasons.
105       * Do lighting in eye coordinates, as the GL spec says.
106       */
107      GLuint transform = NORM_TRANSFORM_NO_ROT;
108
109      if (_math_matrix_has_rotation(ctx->ModelviewMatrixStack.Top)) {
110         /* need to do full (3x3) matrix transform */
111	 transform = NORM_TRANSFORM;
112      }
113
114      if (ctx->Transform.Normalize) {
115	 store->NormalTransform = _mesa_normal_tab[transform | NORM_NORMALIZE];
116      }
117      else if (ctx->Transform.RescaleNormals &&
118               ctx->_ModelViewInvScale != 1.0) {
119	 store->NormalTransform = _mesa_normal_tab[transform | NORM_RESCALE];
120      }
121      else {
122	 store->NormalTransform = _mesa_normal_tab[transform];
123      }
124   }
125   else {
126      /* We don't need eye coordinates.
127       * Do lighting in object coordinates.  Thus, we don't need to fully
128       * transform normal vectors (just leave them in object coordinates)
129       * but we still need to do normalization/rescaling if enabled.
130       */
131      if (ctx->Transform.Normalize) {
132	 store->NormalTransform = _mesa_normal_tab[NORM_NORMALIZE];
133      }
134      else if (!ctx->Transform.RescaleNormals &&
135	       ctx->_ModelViewInvScale != 1.0) {
136	 store->NormalTransform = _mesa_normal_tab[NORM_RESCALE];
137      }
138      else {
139	 store->NormalTransform = NULL;
140      }
141   }
142}
143
144
145/**
146 * Allocate stage's private data (storage for transformed normals).
147 */
148static GLboolean
149alloc_normal_data(struct gl_context *ctx, struct tnl_pipeline_stage *stage)
150{
151   TNLcontext *tnl = TNL_CONTEXT(ctx);
152   struct normal_stage_data *store;
153
154   stage->privatePtr = malloc(sizeof(*store));
155   store = NORMAL_STAGE_DATA(stage);
156   if (!store)
157      return GL_FALSE;
158
159   _mesa_vector4f_alloc( &store->normal, 0, tnl->vb.Size, 32 );
160   return GL_TRUE;
161}
162
163
164/**
165 * Free stage's private data.
166 */
167static void
168free_normal_data(struct tnl_pipeline_stage *stage)
169{
170   struct normal_stage_data *store = NORMAL_STAGE_DATA(stage);
171   if (store) {
172      _mesa_vector4f_free( &store->normal );
173      free( store );
174      stage->privatePtr = NULL;
175   }
176}
177
178
179const struct tnl_pipeline_stage _tnl_normal_transform_stage =
180{
181   "normal transform",		/* name */
182   NULL,			/* privatePtr */
183   alloc_normal_data,		/* create */
184   free_normal_data,		/* destroy */
185   validate_normal_stage,	/* validate */
186   run_normal_stage             /* run */
187};
188