t_vb_texgen.c revision 08836341788a9f9d638d9dc8328510ccd18ddeb5
1/* $Id: t_vb_texgen.c,v 1.3 2001/03/03 20:33:31 brianp Exp $ */
2
3/*
4 * Mesa 3-D graphics library
5 * Version:  3.5
6 *
7 * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * Authors:
27 *    Brian Paul <brian@valinux.com>
28 *    Keith Whitwell <keithw@valinux.com>
29 */
30
31
32#include "glheader.h"
33#include "colormac.h"
34#include "context.h"
35#include "macros.h"
36#include "mmath.h"
37#include "mem.h"
38#include "mtypes.h"
39
40#include "math/m_xform.h"
41
42#include "t_context.h"
43#include "t_pipeline.h"
44
45
46/***********************************************************************
47 * Automatic texture coordinate generation (texgen) code.
48 */
49
50
51struct texgen_stage_data;
52
53typedef void (*texgen_func)( GLcontext *ctx,
54			     struct texgen_stage_data *store,
55			     GLuint unit);
56
57
58struct texgen_stage_data {
59
60   /* Per-texunit derived state.
61    */
62   GLuint TexgenSize[MAX_TEXTURE_UNITS];
63   GLuint TexgenHoles[MAX_TEXTURE_UNITS];
64   texgen_func TexgenFunc[MAX_TEXTURE_UNITS];
65
66   /* Temporary values used in texgen.
67    */
68   GLfloat (*tmp_f)[3];
69   GLfloat *tmp_m;
70
71   /* Buffered outputs of the stage.
72    */
73   GLvector4f texcoord[MAX_TEXTURE_UNITS];
74};
75
76
77#define TEXGEN_STAGE_DATA(stage) ((struct texgen_stage_data *)stage->private)
78
79
80
81
82static GLuint all_bits[5] = {
83   0,
84   VEC_SIZE_1,
85   VEC_SIZE_2,
86   VEC_SIZE_3,
87   VEC_SIZE_4,
88};
89
90#define VEC_SIZE_FLAGS (VEC_SIZE_1|VEC_SIZE_2|VEC_SIZE_3|VEC_SIZE_4)
91
92/*
93 */
94static void build_m3(GLfloat f[][3], GLfloat m[],
95		     const GLvector3f *normal,
96		     const GLvector4f *eye )
97{
98   GLuint stride = eye->stride;
99   GLfloat *coord = (GLfloat *)eye->start;
100   GLuint count = eye->count;
101   const GLfloat *norm = normal->start;
102   GLuint i;
103
104
105   /* KW: Had to rearrange this loop to avoid a compiler bug with gcc
106    *     2.7.3.1 at -O3 optimization.  Using -fno-strength-reduce
107    *     also fixed the bug - is this generally necessary?
108    */
109   for (i=0;i<count;i++,STRIDE_F(coord,stride)) {
110      GLfloat u[3], two_nu, fx, fy, fz;
111      COPY_3V( u, coord );
112      NORMALIZE_3FV( u );
113      two_nu = 2.0F * DOT3(norm,u);
114      fx = f[i][0] = u[0] - norm[0] * two_nu;
115      fy = f[i][1] = u[1] - norm[1] * two_nu;
116      fz = f[i][2] = u[2] - norm[2] * two_nu;
117      m[i] = fx * fx + fy * fy + (fz + 1.0F) * (fz + 1.0F);
118      if (m[i] != 0.0F) {
119	 m[i] = 0.5F / (GLfloat) GL_SQRT(m[i]);
120      }
121
122      STRIDE_F(norm, normal->stride);
123   }
124}
125
126
127
128static void build_m2(GLfloat f[][3], GLfloat m[],
129		     const GLvector3f *normal,
130		     const GLvector4f *eye )
131{
132   GLuint stride = eye->stride;
133   GLfloat *coord = eye->start;
134   GLuint count = eye->count;
135
136   GLfloat *norm = normal->start;
137   GLuint i;
138
139   for (i=0;i<count;i++,STRIDE_F(coord,stride)) {
140
141      GLfloat u[3], two_nu, fx, fy, fz;
142      COPY_2V( u, coord );
143      u[2] = 0;
144      NORMALIZE_3FV( u );
145      two_nu = 2.0F * DOT3(norm,u);
146      fx = f[i][0] = u[0] - norm[0] * two_nu;
147      fy = f[i][1] = u[1] - norm[1] * two_nu;
148      fz = f[i][2] = u[2] - norm[2] * two_nu;
149      m[i] = fx * fx + fy * fy + (fz + 1.0F) * (fz + 1.0F);
150      if (m[i] != 0.0F) {
151	 m[i] = 0.5F / (GLfloat) GL_SQRT(m[i]);
152      }
153
154      STRIDE_F(norm, normal->stride);
155   }
156}
157
158
159
160typedef void (*build_m_func)(GLfloat f[][3],
161			     GLfloat m[],
162			     const GLvector3f *normal,
163			     const GLvector4f *eye );
164
165
166
167static build_m_func build_m_tab[5] = {
168   0,
169   0,
170   build_m2,
171   build_m3,
172   build_m3
173};
174
175
176/* This is unusual in that we respect the stride of the output vector
177 * (f).  This allows us to pass in either a texcoord vector4f, or a
178 * temporary vector3f.
179 */
180static void build_f3( GLfloat *f,
181		      GLuint fstride,
182		      const GLvector3f *normal,
183		      const GLvector4f *eye )
184{
185   GLuint stride = eye->stride;
186   GLfloat *coord = eye->start;
187   GLuint count = eye->count;
188
189   GLfloat *norm = normal->start;
190   GLuint i;
191
192   for (i=0;i<count;i++) {
193      GLfloat u[3], two_nu;
194      COPY_3V( u, coord );
195      NORMALIZE_3FV( u );
196      two_nu = 2.0F * DOT3(norm,u);
197      f[0] = u[0] - norm[0] * two_nu;
198      f[1] = u[1] - norm[1] * two_nu;
199      f[2] = u[2] - norm[2] * two_nu;
200      STRIDE_F(coord,stride);
201      STRIDE_F(f,fstride);
202      STRIDE_F(norm, normal->stride);
203   }
204}
205
206
207static void build_f2( GLfloat *f,
208		      GLuint fstride,
209		      const GLvector3f *normal,
210		      const GLvector4f *eye )
211{
212   GLuint stride = eye->stride;
213   GLfloat *coord = eye->start;
214   GLuint count = eye->count;
215   GLfloat *norm = normal->start;
216   GLuint i;
217
218   for (i=0;i<count;i++) {
219
220      GLfloat u[3], two_nu;
221      COPY_2V( u, coord );
222      u[2] = 0;
223      NORMALIZE_3FV( u );
224      two_nu = 2.0F * DOT3(norm,u);
225      f[0] = u[0] - norm[0] * two_nu;
226      f[1] = u[1] - norm[1] * two_nu;
227      f[2] = u[2] - norm[2] * two_nu;
228
229      STRIDE_F(coord,stride);
230      STRIDE_F(f,fstride);
231      STRIDE_F(norm, normal->stride);
232   }
233}
234
235typedef void (*build_f_func)( GLfloat *f,
236			      GLuint fstride,
237			      const GLvector3f *normal_vec,
238			      const GLvector4f *eye );
239
240
241
242/* Just treat 4-vectors as 3-vectors.
243 */
244static build_f_func build_f_tab[5] = {
245   0,
246   0,
247   build_f2,
248   build_f3,
249   build_f3
250};
251
252
253/* Special case texgen functions.
254 */
255static void texgen_reflection_map_nv( GLcontext *ctx,
256				      struct texgen_stage_data *store,
257				      GLuint unit )
258{
259   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
260   GLvector4f *in = VB->TexCoordPtr[unit];
261   GLvector4f *out = &store->texcoord[unit];
262
263   build_f_tab[VB->EyePtr->size]( out->start,
264				  out->stride,
265				  VB->NormalPtr,
266				  VB->EyePtr );
267
268   if (in) {
269      out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_3;
270      out->count = in->count;
271      out->size = MAX2(in->size, 3);
272      if (in->size == 4)
273	 _mesa_copy_tab[0][0x8](out, in, 0);
274   }
275   else {
276      out->flags |= VEC_SIZE_3;
277      out->size = 3;
278      out->count = in->count;
279   }
280
281}
282
283
284
285static void texgen_normal_map_nv( GLcontext *ctx,
286				  struct texgen_stage_data *store,
287				  GLuint unit )
288{
289   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
290   GLvector4f *in = VB->TexCoordPtr[unit];
291   GLvector4f *out = &store->texcoord[unit];
292   GLvector3f *normal = VB->NormalPtr;
293   GLfloat (*texcoord)[4] = (GLfloat (*)[4])out->start;
294   GLuint count = VB->Count;
295   GLuint i;
296   const GLfloat *norm = normal->start;
297
298   for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
299      texcoord[i][0] = norm[0];
300      texcoord[i][1] = norm[1];
301      texcoord[i][2] = norm[2];
302   }
303
304
305   if (in) {
306      out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_3;
307      out->count = in->count;
308      out->size = MAX2(in->size, 3);
309      if (in->size == 4)
310	 _mesa_copy_tab[0][0x8](out, in, 0);
311   }
312   else {
313      out->flags |= VEC_SIZE_3;
314      out->size = 3;
315      out->count = in->count;
316   }
317}
318
319
320static void texgen_sphere_map( GLcontext *ctx,
321			       struct texgen_stage_data *store,
322			       GLuint unit )
323{
324   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
325   GLvector4f *in = VB->TexCoordPtr[unit];
326   GLvector4f *out = &store->texcoord[unit];
327   GLfloat (*texcoord)[4] = (GLfloat (*)[4]) out->start;
328   GLuint count = VB->Count;
329   GLuint i;
330   GLfloat (*f)[3] = store->tmp_f;
331   GLfloat *m = store->tmp_m;
332
333   (build_m_tab[VB->EyePtr->size])( store->tmp_f,
334				    store->tmp_m,
335				    VB->NormalPtr,
336				    VB->EyePtr );
337
338   for (i=0;i<count;i++) {
339      texcoord[i][0] = f[i][0] * m[i] + 0.5F;
340      texcoord[i][1] = f[i][1] * m[i] + 0.5F;
341   }
342
343   if (in) {
344      out->size = MAX2(in->size,2);
345      out->count = in->count;
346      out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_2;
347      if (in->size > 2)
348	 _mesa_copy_tab[0][all_bits[in->size] & ~0x3](out, in, 0);
349   } else {
350      out->size = 2;
351      out->flags |= VEC_SIZE_2;
352      out->count = in->count;
353   }
354}
355
356
357
358static void texgen( GLcontext *ctx,
359		    struct texgen_stage_data *store,
360		    GLuint unit )
361{
362   TNLcontext *tnl = TNL_CONTEXT(ctx);
363   struct vertex_buffer *VB = &tnl->vb;
364   GLvector4f *in = VB->TexCoordPtr[unit];
365   GLvector4f *out = &store->texcoord[unit];
366   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
367   const GLvector4f *obj = VB->ObjPtr;
368   const GLvector4f *eye = VB->EyePtr;
369   const GLvector3f *normal = VB->NormalPtr;
370   GLfloat (*texcoord)[4] = (GLfloat (*)[4])out->data;
371   GLfloat *indata;
372   GLuint count = VB->Count;
373   GLfloat (*f)[3] = store->tmp_f;
374   GLfloat *m = store->tmp_m;
375	 GLuint holes = 0;
376
377
378   if (texUnit->_GenFlags & TEXGEN_NEED_M) {
379      build_m_tab[in->size]( store->tmp_f, store->tmp_m, normal, eye );
380   } else if (texUnit->_GenFlags & TEXGEN_NEED_F) {
381      build_f_tab[in->size]( (GLfloat *)store->tmp_f, 3, normal, eye );
382   }
383
384   if (!in) {
385      ASSERT(0);
386      in = out;
387      in->count = VB->Count;
388
389      out->size = store->TexgenSize[unit];
390      out->flags |= texUnit->TexGenEnabled;
391      out->count = VB->Count;
392      holes = store->TexgenHoles[unit];
393   }
394   else {
395      GLuint copy = (all_bits[in->size] & ~texUnit->TexGenEnabled);
396      if (copy)
397	 _mesa_copy_tab[0][copy](out, in, 0);
398
399      out->size = MAX2(in->size, store->TexgenSize[unit]);
400      out->flags |= (in->flags & VEC_SIZE_FLAGS) | texUnit->TexGenEnabled;
401      out->count = in->count;
402
403      holes = ~all_bits[in->size] & store->TexgenHoles[unit];
404   }
405
406   if (holes) {
407      if (holes & VEC_DIRTY_2) _mesa_vector4f_clean_elem(out, count, 2);
408      if (holes & VEC_DIRTY_1) _mesa_vector4f_clean_elem(out, count, 1);
409      if (holes & VEC_DIRTY_0) _mesa_vector4f_clean_elem(out, count, 0);
410   }
411
412   if (texUnit->TexGenEnabled & S_BIT) {
413      GLuint i;
414      switch (texUnit->GenModeS) {
415      case GL_OBJECT_LINEAR:
416	 (gl_dotprod_tab[0][obj->size])((GLfloat *)out->data,
417					sizeof(out->data[0]), obj,
418					texUnit->ObjectPlaneS, 0);
419	 break;
420      case GL_EYE_LINEAR:
421	 (gl_dotprod_tab[0][eye->size])((GLfloat *)out->data,
422					sizeof(out->data[0]), eye,
423					texUnit->EyePlaneS, 0);
424	 break;
425      case GL_SPHERE_MAP:
426	 for (indata=in->start,i=0 ; i<count ;i++, STRIDE_F(indata,in->stride))
427	    texcoord[i][0] = indata[0] * m[i] + 0.5F;
428	 break;
429      case GL_REFLECTION_MAP_NV:
430	 for (i=0;i<count;i++)
431	     texcoord[i][0] = f[i][0];
432	 break;
433      case GL_NORMAL_MAP_NV: {
434	 const GLfloat *norm = normal->start;
435	 for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
436	     texcoord[i][0] = norm[0];
437	 }
438	 break;
439      }
440      default:
441	 _mesa_problem(ctx, "Bad S texgen");
442      }
443   }
444
445   if (texUnit->TexGenEnabled & T_BIT) {
446      GLuint i;
447      switch (texUnit->GenModeT) {
448      case GL_OBJECT_LINEAR:
449	 (gl_dotprod_tab[0][obj->size])(&(out->data[0][1]),
450					sizeof(out->data[0]), obj,
451					texUnit->ObjectPlaneT, 0);
452	 break;
453      case GL_EYE_LINEAR:
454	 (gl_dotprod_tab[0][eye->size])(&(out->data[0][1]),
455					sizeof(out->data[0]), eye,
456					texUnit->EyePlaneT, 0);
457	 break;
458      case GL_SPHERE_MAP:
459	 for (indata=in->start,i=0; i<count ;i++,STRIDE_F(indata,in->stride))
460	     texcoord[i][1] = indata[1] * m[i] + 0.5F;
461	 break;
462      case GL_REFLECTION_MAP_NV:
463	 for (i=0;i<count;i++)
464	     texcoord[i][0] = f[i][0];
465	 break;
466      case GL_NORMAL_MAP_NV: {
467	 const GLfloat *norm = normal->start;
468	 for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
469	     texcoord[i][1] = norm[1];
470	 }
471	 break;
472      }
473      default:
474	 _mesa_problem(ctx, "Bad T texgen");
475      }
476   }
477
478   if (texUnit->TexGenEnabled & R_BIT) {
479      GLuint i;
480      switch (texUnit->GenModeR) {
481      case GL_OBJECT_LINEAR:
482	 (gl_dotprod_tab[0][obj->size])(&(out->data[0][2]),
483					sizeof(out->data[0]), obj,
484					texUnit->ObjectPlaneR, 0);
485	 break;
486      case GL_EYE_LINEAR:
487	 (gl_dotprod_tab[0][eye->size])(&(out->data[0][2]),
488					sizeof(out->data[0]), eye,
489					texUnit->EyePlaneR, 0);
490	 break;
491      case GL_REFLECTION_MAP_NV:
492	 for (i=0;i<count;i++)
493	     texcoord[i][2] = f[i][2];
494	 break;
495      case GL_NORMAL_MAP_NV: {
496	 const GLfloat *norm = normal->start;
497	 for (i=0;i<count;i++,STRIDE_F(norm, normal->stride)) {
498	     texcoord[i][2] = norm[2];
499	 }
500	 break;
501      }
502      default:
503	 _mesa_problem(ctx, "Bad R texgen");
504      }
505   }
506
507   if (texUnit->TexGenEnabled & Q_BIT) {
508      switch (texUnit->GenModeQ) {
509      case GL_OBJECT_LINEAR:
510	 (gl_dotprod_tab[0][obj->size])(&(out->data[0][3]),
511					sizeof(out->data[0]), obj,
512					texUnit->ObjectPlaneQ, 0);
513	 break;
514      case GL_EYE_LINEAR:
515	 (gl_dotprod_tab[0][eye->size])(&(out->data[0][3]),
516					sizeof(out->data[0]), eye,
517					texUnit->EyePlaneQ, 0);
518	 break;
519      default:
520	 _mesa_problem(ctx, "Bad Q texgen");
521      }
522   }
523}
524
525
526
527static GLboolean run_texgen_stage( GLcontext *ctx,
528				   struct gl_pipeline_stage *stage )
529{
530   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
531   struct texgen_stage_data *store = TEXGEN_STAGE_DATA( stage );
532   GLuint i;
533
534   for (i = 0 ; i < ctx->Const.MaxTextureUnits ; i++)
535      if (ctx->_Enabled & ENABLE_TEXGEN(i)) {
536	 if (stage->changed_inputs & (VERT_EYE | VERT_NORM | VERT_TEX(i)))
537	    store->TexgenFunc[i]( ctx, store, i );
538
539	 VB->TexCoordPtr[i] = &store->texcoord[i];
540      }
541
542   return GL_TRUE;
543}
544
545
546
547
548static GLboolean run_validate_texgen_stage( GLcontext *ctx,
549					    struct gl_pipeline_stage *stage )
550{
551   struct texgen_stage_data *store = TEXGEN_STAGE_DATA(stage);
552   GLuint i;
553
554   for (i = 0 ; i < ctx->Const.MaxTextureUnits ; i++) {
555      struct gl_texture_unit *texUnit = &ctx->Texture.Unit[i];
556
557      if (texUnit->TexGenEnabled) {
558	 GLuint sz;
559
560	 if (texUnit->TexGenEnabled & R_BIT)
561	    sz = 4;
562	 else if (texUnit->TexGenEnabled & Q_BIT)
563	    sz = 3;
564	 else if (texUnit->TexGenEnabled & T_BIT)
565	    sz = 2;
566	 else
567	    sz = 1;
568
569	 store->TexgenSize[i] = sz;
570	 store->TexgenHoles[i] = (all_bits[sz] & ~texUnit->TexGenEnabled);
571	 store->TexgenFunc[i] = texgen;
572
573	 if (texUnit->TexGenEnabled == (S_BIT|T_BIT|R_BIT)) {
574	    if (texUnit->_GenFlags == TEXGEN_REFLECTION_MAP_NV) {
575	       store->TexgenFunc[i] = texgen_reflection_map_nv;
576	    }
577	    else if (texUnit->_GenFlags == TEXGEN_NORMAL_MAP_NV) {
578	       store->TexgenFunc[i] = texgen_normal_map_nv;
579	    }
580	 }
581	 else if (texUnit->TexGenEnabled == (S_BIT|T_BIT) &&
582		  texUnit->_GenFlags == TEXGEN_SPHERE_MAP) {
583	    store->TexgenFunc[i] = texgen_sphere_map;
584	 }
585      }
586   }
587
588   stage->run = run_texgen_stage;
589   return stage->run( ctx, stage );
590}
591
592
593static void check_texgen( GLcontext *ctx, struct gl_pipeline_stage *stage )
594{
595   GLuint i;
596   stage->active = 0;
597
598   if (ctx->_Enabled & ENABLE_TEXGEN_ANY) {
599      GLuint inputs = 0;
600      GLuint outputs = 0;
601
602      if (ctx->Texture._GenFlags & TEXGEN_NEED_VERTICES)
603	 inputs |= VERT_EYE;
604
605      if (ctx->Texture._GenFlags & TEXGEN_NEED_NORMALS)
606	 inputs |= VERT_NORM;
607
608      for (i = 0 ; i < ctx->Const.MaxTextureUnits ; i++)
609	 if (ctx->_Enabled & ENABLE_TEXGEN(i))
610	 {
611	    outputs |= VERT_TEX(i);
612
613	    /* Need the original input in case it contains a Q coord:
614	     * (sigh)
615	     */
616/*  	    if ((ctx->Texture.Unit[i]._ReallyEnabled|Q_BIT) & */
617/*  		~ctx->Texture.Unit[i].TexGenEnabled) */
618	       inputs |= VERT_TEX(i);
619
620	    /* Something for Feedback? */
621	 }
622
623      if (stage->private)
624	 stage->run = run_validate_texgen_stage;
625      stage->active = 1;
626      stage->inputs = inputs;
627      stage->outputs = outputs;
628   }
629}
630
631
632
633
634/* Called the first time stage->run() is invoked.
635 */
636static GLboolean alloc_texgen_data( GLcontext *ctx,
637				    struct gl_pipeline_stage *stage )
638{
639   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
640   struct texgen_stage_data *store;
641   GLuint i;
642
643   stage->private = CALLOC(sizeof(*store));
644   store = TEXGEN_STAGE_DATA(stage);
645   if (!store)
646      return GL_FALSE;
647
648   for (i = 0 ; i < ctx->Const.MaxTextureUnits ; i++)
649      _mesa_vector4f_alloc( &store->texcoord[i], 0, VB->Size, 32 );
650
651   store->tmp_f = (GLfloat (*)[3]) MALLOC(VB->Size * sizeof(GLfloat) * 3);
652   store->tmp_m = (GLfloat *) MALLOC(VB->Size * sizeof(GLfloat));
653
654   /* Now validate and run the stage.
655    */
656   stage->run = run_validate_texgen_stage;
657   return stage->run( ctx, stage );
658}
659
660
661static void free_texgen_data( struct gl_pipeline_stage *stage )
662
663{
664   struct texgen_stage_data *store = TEXGEN_STAGE_DATA(stage);
665   GLuint i;
666
667   if (store) {
668      for (i = 0 ; i < MAX_TEXTURE_UNITS ; i++)
669	 if (store->texcoord[i].data)
670	    _mesa_vector4f_free( &store->texcoord[i] );
671
672
673      if (store->tmp_f) FREE( store->tmp_f );
674      if (store->tmp_m) FREE( store->tmp_m );
675      FREE( store );
676      stage->private = 0;
677   }
678}
679
680
681
682const struct gl_pipeline_stage _tnl_texgen_stage =
683{
684   "texgen",
685   _NEW_TEXTURE,		/* when to call check() */
686   _NEW_TEXTURE,		/* when to invalidate stored data */
687   0,0,0,			/* active, inputs, outputs */
688   0,0,				/* changed_inputs, private */
689   free_texgen_data,		/* destructor */
690   check_texgen,		/* check */
691   alloc_texgen_data		/* run -- initially set to alloc data */
692};
693
694
695