68ee089c095b25f7025d6d31f1dab9f2218d553c
[mesa.git] / src / mesa / tnl / t_vb_texgen.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.0
4 *
5 * Copyright (C) 1999-2004 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 * Brian Paul
26 * Keith Whitwell <keith@tungstengraphics.com>
27 */
28
29 /*
30 * Regarding GL_NV_texgen_reflection:
31 *
32 * Portions of this software may use or implement intellectual
33 * property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
34 * any and all warranties with respect to such intellectual property,
35 * including any use thereof or modifications thereto.
36 */
37
38 #include "glheader.h"
39 #include "colormac.h"
40 #include "context.h"
41 #include "macros.h"
42 #include "imports.h"
43 #include "mtypes.h"
44
45 #include "math/m_xform.h"
46
47 #include "t_context.h"
48 #include "t_pipeline.h"
49
50
51 /***********************************************************************
52 * Automatic texture coordinate generation (texgen) code.
53 */
54
55
56 struct texgen_stage_data;
57
58 typedef void (*texgen_func)( GLcontext *ctx,
59 struct texgen_stage_data *store,
60 GLuint unit);
61
62
63 struct texgen_stage_data {
64
65 /* Per-texunit derived state.
66 */
67 GLuint TexgenSize[MAX_TEXTURE_COORD_UNITS];
68 GLuint TexgenHoles[MAX_TEXTURE_COORD_UNITS];
69 texgen_func TexgenFunc[MAX_TEXTURE_COORD_UNITS];
70
71 /* Temporary values used in texgen.
72 */
73 GLfloat (*tmp_f)[3];
74 GLfloat *tmp_m;
75
76 /* Buffered outputs of the stage.
77 */
78 GLvector4f texcoord[MAX_TEXTURE_COORD_UNITS];
79 };
80
81
82 #define TEXGEN_STAGE_DATA(stage) ((struct texgen_stage_data *)stage->privatePtr)
83
84
85
86 static GLuint all_bits[5] = {
87 0,
88 VEC_SIZE_1,
89 VEC_SIZE_2,
90 VEC_SIZE_3,
91 VEC_SIZE_4,
92 };
93
94 #define VEC_SIZE_FLAGS (VEC_SIZE_1|VEC_SIZE_2|VEC_SIZE_3|VEC_SIZE_4)
95
96 #define TEXGEN_NEED_M (TEXGEN_SPHERE_MAP)
97 #define TEXGEN_NEED_F (TEXGEN_SPHERE_MAP | \
98 TEXGEN_REFLECTION_MAP_NV)
99
100
101
102 static void build_m3( GLfloat f[][3], GLfloat m[],
103 const GLvector4f *normal,
104 const GLvector4f *eye )
105 {
106 GLuint stride = eye->stride;
107 GLfloat *coord = (GLfloat *)eye->start;
108 GLuint count = eye->count;
109 const GLfloat *norm = normal->start;
110 GLuint i;
111
112 for (i=0;i<count;i++,STRIDE_F(coord,stride),STRIDE_F(norm,normal->stride)) {
113 GLfloat u[3], two_nu, fx, fy, fz;
114 COPY_3V( u, coord );
115 NORMALIZE_3FV( u );
116 two_nu = 2.0F * DOT3(norm,u);
117 fx = f[i][0] = u[0] - norm[0] * two_nu;
118 fy = f[i][1] = u[1] - norm[1] * two_nu;
119 fz = f[i][2] = u[2] - norm[2] * two_nu;
120 m[i] = fx * fx + fy * fy + (fz + 1.0F) * (fz + 1.0F);
121 if (m[i] != 0.0F) {
122 m[i] = 0.5F * _mesa_inv_sqrtf(m[i]);
123 }
124 }
125 }
126
127
128
129 static void build_m2( GLfloat f[][3], GLfloat m[],
130 const GLvector4f *normal,
131 const GLvector4f *eye )
132 {
133 GLuint stride = eye->stride;
134 GLfloat *coord = eye->start;
135 GLuint count = eye->count;
136
137 GLfloat *norm = normal->start;
138 GLuint i;
139
140 for (i=0;i<count;i++,STRIDE_F(coord,stride),STRIDE_F(norm,normal->stride)) {
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 * _mesa_inv_sqrtf(m[i]);
152 }
153 }
154 }
155
156
157
158 typedef void (*build_m_func)( GLfloat f[][3],
159 GLfloat m[],
160 const GLvector4f *normal,
161 const GLvector4f *eye );
162
163
164 static build_m_func build_m_tab[5] = {
165 0,
166 0,
167 build_m2,
168 build_m3,
169 build_m3
170 };
171
172
173 /* This is unusual in that we respect the stride of the output vector
174 * (f). This allows us to pass in either a texcoord vector4f, or a
175 * temporary vector3f.
176 */
177 static void build_f3( GLfloat *f,
178 GLuint fstride,
179 const GLvector4f *normal,
180 const GLvector4f *eye )
181 {
182 GLuint stride = eye->stride;
183 GLfloat *coord = eye->start;
184 GLuint count = eye->count;
185
186 GLfloat *norm = normal->start;
187 GLuint i;
188
189 for (i=0;i<count;i++) {
190 GLfloat u[3], two_nu;
191 COPY_3V( u, coord );
192 NORMALIZE_3FV( u );
193 two_nu = 2.0F * DOT3(norm,u);
194 f[0] = u[0] - norm[0] * two_nu;
195 f[1] = u[1] - norm[1] * two_nu;
196 f[2] = u[2] - norm[2] * two_nu;
197 STRIDE_F(coord,stride);
198 STRIDE_F(f,fstride);
199 STRIDE_F(norm, normal->stride);
200 }
201 }
202
203
204 static void build_f2( GLfloat *f,
205 GLuint fstride,
206 const GLvector4f *normal,
207 const GLvector4f *eye )
208 {
209 GLuint stride = eye->stride;
210 GLfloat *coord = eye->start;
211 GLuint count = eye->count;
212 GLfloat *norm = normal->start;
213 GLuint i;
214
215 for (i=0;i<count;i++) {
216
217 GLfloat u[3], two_nu;
218 COPY_2V( u, coord );
219 u[2] = 0;
220 NORMALIZE_3FV( u );
221 two_nu = 2.0F * DOT3(norm,u);
222 f[0] = u[0] - norm[0] * two_nu;
223 f[1] = u[1] - norm[1] * two_nu;
224 f[2] = u[2] - norm[2] * two_nu;
225
226 STRIDE_F(coord,stride);
227 STRIDE_F(f,fstride);
228 STRIDE_F(norm, normal->stride);
229 }
230 }
231
232 typedef void (*build_f_func)( GLfloat *f,
233 GLuint fstride,
234 const GLvector4f *normal_vec,
235 const GLvector4f *eye );
236
237
238
239 /* Just treat 4-vectors as 3-vectors.
240 */
241 static build_f_func build_f_tab[5] = {
242 0,
243 0,
244 build_f2,
245 build_f3,
246 build_f3
247 };
248
249
250 /* Special case texgen functions.
251 */
252 static void texgen_reflection_map_nv( GLcontext *ctx,
253 struct texgen_stage_data *store,
254 GLuint unit )
255 {
256 struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
257 GLvector4f *in = VB->TexCoordPtr[unit];
258 GLvector4f *out = &store->texcoord[unit];
259
260 build_f_tab[VB->EyePtr->size]( out->start,
261 out->stride,
262 VB->NormalPtr,
263 VB->EyePtr );
264
265 if (in) {
266 out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_3;
267 out->count = in->count;
268 out->size = MAX2(in->size, 3);
269 if (in->size == 4)
270 _mesa_copy_tab[0x8]( out, in );
271 }
272 else {
273 out->flags |= VEC_SIZE_3;
274 out->size = 3;
275 out->count = in->count;
276 }
277
278 }
279
280
281
282 static void texgen_normal_map_nv( GLcontext *ctx,
283 struct texgen_stage_data *store,
284 GLuint unit )
285 {
286 struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
287 GLvector4f *in = VB->TexCoordPtr[unit];
288 GLvector4f *out = &store->texcoord[unit];
289 GLvector4f *normal = VB->NormalPtr;
290 GLfloat (*texcoord)[4] = (GLfloat (*)[4])out->start;
291 GLuint count = VB->Count;
292 GLuint i;
293 const GLfloat *norm = normal->start;
294
295 for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
296 texcoord[i][0] = norm[0];
297 texcoord[i][1] = norm[1];
298 texcoord[i][2] = norm[2];
299 }
300
301
302 if (in) {
303 out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_3;
304 out->count = in->count;
305 out->size = MAX2(in->size, 3);
306 if (in->size == 4)
307 _mesa_copy_tab[0x8]( out, in );
308 }
309 else {
310 out->flags |= VEC_SIZE_3;
311 out->size = 3;
312 out->count = in->count;
313 }
314 }
315
316
317 static void texgen_sphere_map( GLcontext *ctx,
318 struct texgen_stage_data *store,
319 GLuint unit )
320 {
321 struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
322 GLvector4f *in = VB->TexCoordPtr[unit];
323 GLvector4f *out = &store->texcoord[unit];
324 GLfloat (*texcoord)[4] = (GLfloat (*)[4]) out->start;
325 GLuint count = VB->Count;
326 GLuint i;
327 GLfloat (*f)[3] = store->tmp_f;
328 GLfloat *m = store->tmp_m;
329
330 (build_m_tab[VB->EyePtr->size])( store->tmp_f,
331 store->tmp_m,
332 VB->NormalPtr,
333 VB->EyePtr );
334
335 for (i=0;i<count;i++) {
336 texcoord[i][0] = f[i][0] * m[i] + 0.5F;
337 texcoord[i][1] = f[i][1] * m[i] + 0.5F;
338 }
339
340 if (in) {
341 out->size = MAX2(in->size,2);
342 out->count = in->count;
343 out->flags |= (in->flags & VEC_SIZE_FLAGS) | VEC_SIZE_2;
344 if (in->size > 2)
345 _mesa_copy_tab[all_bits[in->size] & ~0x3]( out, in );
346 } else {
347 out->size = 2;
348 out->flags |= VEC_SIZE_2;
349 out->count = in->count;
350 }
351 }
352
353
354
355 static void texgen( GLcontext *ctx,
356 struct texgen_stage_data *store,
357 GLuint unit )
358 {
359 TNLcontext *tnl = TNL_CONTEXT(ctx);
360 struct vertex_buffer *VB = &tnl->vb;
361 GLvector4f *in = VB->TexCoordPtr[unit];
362 GLvector4f *out = &store->texcoord[unit];
363 const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
364 const GLvector4f *obj = VB->ObjPtr;
365 const GLvector4f *eye = VB->EyePtr;
366 const GLvector4f *normal = VB->NormalPtr;
367 const GLfloat *m = store->tmp_m;
368 const GLuint count = VB->Count;
369 GLfloat (*texcoord)[4] = (GLfloat (*)[4])out->data;
370 GLfloat (*f)[3] = store->tmp_f;
371 GLuint holes = 0;
372
373 if (texUnit->_GenFlags & TEXGEN_NEED_M) {
374 build_m_tab[eye->size]( store->tmp_f, store->tmp_m, normal, eye );
375 } else if (texUnit->_GenFlags & TEXGEN_NEED_F) {
376 build_f_tab[eye->size]( (GLfloat *)store->tmp_f, 3, normal, eye );
377 }
378
379 if (!in) {
380 ASSERT(0);
381 in = out;
382 in->count = VB->Count;
383
384 out->size = store->TexgenSize[unit];
385 out->flags |= texUnit->TexGenEnabled;
386 out->count = VB->Count;
387 holes = store->TexgenHoles[unit];
388 }
389 else {
390 GLuint copy = (all_bits[in->size] & ~texUnit->TexGenEnabled);
391 if (copy)
392 _mesa_copy_tab[copy]( out, in );
393
394 out->size = MAX2(in->size, store->TexgenSize[unit]);
395 out->flags |= (in->flags & VEC_SIZE_FLAGS) | texUnit->TexGenEnabled;
396 out->count = in->count;
397
398 holes = ~all_bits[in->size] & store->TexgenHoles[unit];
399 }
400
401 if (holes) {
402 if (holes & VEC_DIRTY_3) _mesa_vector4f_clean_elem(out, count, 3);
403 if (holes & VEC_DIRTY_2) _mesa_vector4f_clean_elem(out, count, 2);
404 if (holes & VEC_DIRTY_1) _mesa_vector4f_clean_elem(out, count, 1);
405 if (holes & VEC_DIRTY_0) _mesa_vector4f_clean_elem(out, count, 0);
406 }
407
408 if (texUnit->TexGenEnabled & S_BIT) {
409 GLuint i;
410 switch (texUnit->GenModeS) {
411 case GL_OBJECT_LINEAR:
412 _mesa_dotprod_tab[obj->size]( (GLfloat *)out->data,
413 sizeof(out->data[0]), obj,
414 texUnit->ObjectPlaneS );
415 break;
416 case GL_EYE_LINEAR:
417 _mesa_dotprod_tab[eye->size]( (GLfloat *)out->data,
418 sizeof(out->data[0]), eye,
419 texUnit->EyePlaneS );
420 break;
421 case GL_SPHERE_MAP:
422 for (i = 0; i < count; i++)
423 texcoord[i][0] = f[i][0] * m[i] + 0.5F;
424 break;
425 case GL_REFLECTION_MAP_NV:
426 for (i=0;i<count;i++)
427 texcoord[i][0] = f[i][0];
428 break;
429 case GL_NORMAL_MAP_NV: {
430 const GLfloat *norm = normal->start;
431 for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
432 texcoord[i][0] = norm[0];
433 }
434 break;
435 }
436 default:
437 _mesa_problem(ctx, "Bad S texgen");
438 }
439 }
440
441 if (texUnit->TexGenEnabled & T_BIT) {
442 GLuint i;
443 switch (texUnit->GenModeT) {
444 case GL_OBJECT_LINEAR:
445 _mesa_dotprod_tab[obj->size]( &(out->data[0][1]),
446 sizeof(out->data[0]), obj,
447 texUnit->ObjectPlaneT );
448 break;
449 case GL_EYE_LINEAR:
450 _mesa_dotprod_tab[eye->size]( &(out->data[0][1]),
451 sizeof(out->data[0]), eye,
452 texUnit->EyePlaneT );
453 break;
454 case GL_SPHERE_MAP:
455 for (i = 0; i < count; i++)
456 texcoord[i][1] = f[i][1] * m[i] + 0.5F;
457 break;
458 case GL_REFLECTION_MAP_NV:
459 for (i=0;i<count;i++)
460 texcoord[i][1] = f[i][1];
461 break;
462 case GL_NORMAL_MAP_NV: {
463 const GLfloat *norm = normal->start;
464 for (i=0;i<count;i++, STRIDE_F(norm, normal->stride)) {
465 texcoord[i][1] = norm[1];
466 }
467 break;
468 }
469 default:
470 _mesa_problem(ctx, "Bad T texgen");
471 }
472 }
473
474 if (texUnit->TexGenEnabled & R_BIT) {
475 GLuint i;
476 switch (texUnit->GenModeR) {
477 case GL_OBJECT_LINEAR:
478 _mesa_dotprod_tab[obj->size]( &(out->data[0][2]),
479 sizeof(out->data[0]), obj,
480 texUnit->ObjectPlaneR );
481 break;
482 case GL_EYE_LINEAR:
483 _mesa_dotprod_tab[eye->size]( &(out->data[0][2]),
484 sizeof(out->data[0]), eye,
485 texUnit->EyePlaneR );
486 break;
487 case GL_REFLECTION_MAP_NV:
488 for (i=0;i<count;i++)
489 texcoord[i][2] = f[i][2];
490 break;
491 case GL_NORMAL_MAP_NV: {
492 const GLfloat *norm = normal->start;
493 for (i=0;i<count;i++,STRIDE_F(norm, normal->stride)) {
494 texcoord[i][2] = norm[2];
495 }
496 break;
497 }
498 default:
499 _mesa_problem(ctx, "Bad R texgen");
500 }
501 }
502
503 if (texUnit->TexGenEnabled & Q_BIT) {
504 switch (texUnit->GenModeQ) {
505 case GL_OBJECT_LINEAR:
506 _mesa_dotprod_tab[obj->size]( &(out->data[0][3]),
507 sizeof(out->data[0]), obj,
508 texUnit->ObjectPlaneQ );
509 break;
510 case GL_EYE_LINEAR:
511 _mesa_dotprod_tab[eye->size]( &(out->data[0][3]),
512 sizeof(out->data[0]), eye,
513 texUnit->EyePlaneQ );
514 break;
515 default:
516 _mesa_problem(ctx, "Bad Q texgen");
517 }
518 }
519 }
520
521
522
523 static GLboolean run_texgen_stage( GLcontext *ctx,
524 struct tnl_pipeline_stage *stage )
525 {
526 struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
527 struct texgen_stage_data *store = TEXGEN_STAGE_DATA( stage );
528 GLuint i;
529
530 for (i = 0 ; i < ctx->Const.MaxTextureCoordUnits ; i++)
531 if (ctx->Texture._TexGenEnabled & ENABLE_TEXGEN(i)) {
532 if (stage->changed_inputs & (_TNL_BIT_POS | _TNL_BIT_NORMAL | _TNL_BIT_TEX(i)))
533 store->TexgenFunc[i]( ctx, store, i );
534
535 VB->AttribPtr[VERT_ATTRIB_TEX0+i] =
536 VB->TexCoordPtr[i] = &store->texcoord[i];
537 }
538
539 return GL_TRUE;
540 }
541
542
543
544
545 static GLboolean run_validate_texgen_stage( GLcontext *ctx,
546 struct tnl_pipeline_stage *stage )
547 {
548 struct texgen_stage_data *store = TEXGEN_STAGE_DATA(stage);
549 GLuint i;
550
551 for (i = 0 ; i < ctx->Const.MaxTextureCoordUnits ; i++) {
552 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[i];
553
554 if (texUnit->TexGenEnabled) {
555 GLuint sz;
556
557 if (texUnit->TexGenEnabled & Q_BIT)
558 sz = 4;
559 else if (texUnit->TexGenEnabled & R_BIT)
560 sz = 3;
561 else if (texUnit->TexGenEnabled & T_BIT)
562 sz = 2;
563 else
564 sz = 1;
565
566 store->TexgenSize[i] = sz;
567 store->TexgenHoles[i] = (all_bits[sz] & ~texUnit->TexGenEnabled);
568 store->TexgenFunc[i] = texgen; /* general solution */
569
570 /* look for special texgen cases */
571 if (texUnit->TexGenEnabled == (S_BIT|T_BIT|R_BIT)) {
572 if (texUnit->_GenFlags == TEXGEN_REFLECTION_MAP_NV) {
573 store->TexgenFunc[i] = texgen_reflection_map_nv;
574 }
575 else if (texUnit->_GenFlags == TEXGEN_NORMAL_MAP_NV) {
576 store->TexgenFunc[i] = texgen_normal_map_nv;
577 }
578 }
579 else if (texUnit->TexGenEnabled == (S_BIT|T_BIT) &&
580 texUnit->_GenFlags == TEXGEN_SPHERE_MAP) {
581 store->TexgenFunc[i] = texgen_sphere_map;
582 }
583 }
584 }
585
586 stage->run = run_texgen_stage;
587 return stage->run( ctx, stage );
588 }
589
590
591 static void check_texgen( GLcontext *ctx, struct tnl_pipeline_stage *stage )
592 {
593 GLuint i;
594 stage->active = 0;
595
596 if (ctx->Texture._TexGenEnabled && !ctx->VertexProgram.Enabled) {
597 GLuint inputs = 0;
598 GLuint outputs = 0;
599
600 if (ctx->Texture._GenFlags & (TEXGEN_OBJ_LINEAR | TEXGEN_NEED_EYE_COORD))
601 inputs |= _TNL_BIT_POS;
602
603 if (ctx->Texture._GenFlags & TEXGEN_NEED_NORMALS)
604 inputs |= _TNL_BIT_NORMAL;
605
606 for (i = 0 ; i < ctx->Const.MaxTextureCoordUnits ; i++)
607 if (ctx->Texture._TexGenEnabled & ENABLE_TEXGEN(i))
608 {
609 outputs |= _TNL_BIT_TEX(i);
610
611 /* Need the original input in case it contains a Q coord:
612 * (sigh)
613 */
614 inputs |= _TNL_BIT_TEX(i);
615
616 /* Something for Feedback? */
617 }
618
619 if (stage->privatePtr)
620 stage->run = run_validate_texgen_stage;
621 stage->active = 1;
622 stage->inputs = inputs;
623 stage->outputs = outputs;
624 }
625 }
626
627
628
629
630 /* Called the first time stage->run() is invoked.
631 */
632 static GLboolean alloc_texgen_data( GLcontext *ctx,
633 struct tnl_pipeline_stage *stage )
634 {
635 struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
636 struct texgen_stage_data *store;
637 GLuint i;
638
639 stage->privatePtr = CALLOC(sizeof(*store));
640 store = TEXGEN_STAGE_DATA(stage);
641 if (!store)
642 return GL_FALSE;
643
644 for (i = 0 ; i < ctx->Const.MaxTextureCoordUnits ; i++)
645 _mesa_vector4f_alloc( &store->texcoord[i], 0, VB->Size, 32 );
646
647 store->tmp_f = (GLfloat (*)[3]) MALLOC(VB->Size * sizeof(GLfloat) * 3);
648 store->tmp_m = (GLfloat *) MALLOC(VB->Size * sizeof(GLfloat));
649
650 /* Now validate and run the stage.
651 */
652 stage->run = run_validate_texgen_stage;
653 return stage->run( ctx, stage );
654 }
655
656
657 static void free_texgen_data( struct tnl_pipeline_stage *stage )
658
659 {
660 struct texgen_stage_data *store = TEXGEN_STAGE_DATA(stage);
661 GLuint i;
662
663 if (store) {
664 for (i = 0 ; i < MAX_TEXTURE_COORD_UNITS ; i++)
665 if (store->texcoord[i].data)
666 _mesa_vector4f_free( &store->texcoord[i] );
667
668
669 if (store->tmp_f) FREE( store->tmp_f );
670 if (store->tmp_m) FREE( store->tmp_m );
671 FREE( store );
672 stage->privatePtr = NULL;
673 }
674 }
675
676
677
678 const struct tnl_pipeline_stage _tnl_texgen_stage =
679 {
680 "texgen", /* name */
681 _NEW_TEXTURE, /* when to call check() */
682 _NEW_TEXTURE, /* when to invalidate stored data */
683 GL_FALSE, /* active? */
684 0, /* inputs */
685 0, /* outputs */
686 0, /* changed_inputs */
687 NULL, /* private data */
688 free_texgen_data, /* destructor */
689 check_texgen, /* check */
690 alloc_texgen_data /* run -- initially set to alloc data */
691 };