1/**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * \brief  polygon offset state
30 *
31 * \author  Keith Whitwell <keith@tungstengraphics.com>
32 * \author  Brian Paul
33 */
34
35#include "util/u_math.h"
36#include "util/u_memory.h"
37#include "draw_pipe.h"
38
39
40
41struct offset_stage {
42   struct draw_stage stage;
43
44   float scale;
45   float units;
46   float clamp;
47};
48
49
50
51static INLINE struct offset_stage *offset_stage( struct draw_stage *stage )
52{
53   return (struct offset_stage *) stage;
54}
55
56
57
58
59
60/**
61 * Offset tri Z.  Some hardware can handle this, but not usually when
62 * doing unfilled rendering.
63 */
64static void do_offset_tri( struct draw_stage *stage,
65			   struct prim_header *header )
66{
67   const unsigned pos = draw_current_shader_position_output(stage->draw);
68   struct offset_stage *offset = offset_stage(stage);
69   float inv_det = 1.0f / header->det;
70
71   /* Window coords:
72    */
73   float *v0 = header->v[0]->data[pos];
74   float *v1 = header->v[1]->data[pos];
75   float *v2 = header->v[2]->data[pos];
76
77   /* edge vectors e = v0 - v2, f = v1 - v2 */
78   float ex = v0[0] - v2[0];
79   float ey = v0[1] - v2[1];
80   float ez = v0[2] - v2[2];
81   float fx = v1[0] - v2[0];
82   float fy = v1[1] - v2[1];
83   float fz = v1[2] - v2[2];
84
85   /* (a,b) = cross(e,f).xy */
86   float a = ey*fz - ez*fy;
87   float b = ez*fx - ex*fz;
88
89   float dzdx = fabsf(a * inv_det);
90   float dzdy = fabsf(b * inv_det);
91
92   float zoffset = offset->units + MAX2(dzdx, dzdy) * offset->scale;
93
94   if (offset->clamp)
95      zoffset = (offset->clamp < 0.0f) ? MAX2(zoffset, offset->clamp) :
96                                         MIN2(zoffset, offset->clamp);
97
98   /*
99    * Note: we're applying the offset and clamping per-vertex.
100    * Ideally, the offset is applied per-fragment prior to fragment shading.
101    */
102   v0[2] = CLAMP(v0[2] + zoffset, 0.0f, 1.0f);
103   v1[2] = CLAMP(v1[2] + zoffset, 0.0f, 1.0f);
104   v2[2] = CLAMP(v2[2] + zoffset, 0.0f, 1.0f);
105
106   stage->next->tri( stage->next, header );
107}
108
109
110static void offset_tri( struct draw_stage *stage,
111			struct prim_header *header )
112{
113   struct prim_header tmp;
114
115   tmp.det = header->det;
116   tmp.flags = header->flags;
117   tmp.pad = header->pad;
118   tmp.v[0] = dup_vert(stage, header->v[0], 0);
119   tmp.v[1] = dup_vert(stage, header->v[1], 1);
120   tmp.v[2] = dup_vert(stage, header->v[2], 2);
121
122   do_offset_tri( stage, &tmp );
123}
124
125
126static void offset_first_tri( struct draw_stage *stage,
127			      struct prim_header *header )
128{
129   struct offset_stage *offset = offset_stage(stage);
130
131   offset->units = (float) (stage->draw->rasterizer->offset_units * stage->draw->mrd);
132   offset->scale = stage->draw->rasterizer->offset_scale;
133   offset->clamp = stage->draw->rasterizer->offset_clamp;
134
135   stage->tri = offset_tri;
136   stage->tri( stage, header );
137}
138
139
140
141
142static void offset_flush( struct draw_stage *stage,
143			  unsigned flags )
144{
145   stage->tri = offset_first_tri;
146   stage->next->flush( stage->next, flags );
147}
148
149
150static void offset_reset_stipple_counter( struct draw_stage *stage )
151{
152   stage->next->reset_stipple_counter( stage->next );
153}
154
155
156static void offset_destroy( struct draw_stage *stage )
157{
158   draw_free_temp_verts( stage );
159   FREE( stage );
160}
161
162
163/**
164 * Create polygon offset drawing stage.
165 */
166struct draw_stage *draw_offset_stage( struct draw_context *draw )
167{
168   struct offset_stage *offset = CALLOC_STRUCT(offset_stage);
169   if (offset == NULL)
170      goto fail;
171
172   offset->stage.draw = draw;
173   offset->stage.name = "offset";
174   offset->stage.next = NULL;
175   offset->stage.point = draw_pipe_passthrough_point;
176   offset->stage.line = draw_pipe_passthrough_line;
177   offset->stage.tri = offset_first_tri;
178   offset->stage.flush = offset_flush;
179   offset->stage.reset_stipple_counter = offset_reset_stipple_counter;
180   offset->stage.destroy = offset_destroy;
181
182   if (!draw_alloc_temp_verts( &offset->stage, 3 ))
183      goto fail;
184
185   return &offset->stage;
186
187fail:
188   if (offset)
189      offset->stage.destroy( &offset->stage );
190
191   return NULL;
192}
193