rev |
line source |
nuclear@28
|
1 #include <stdio.h>
|
nuclear@28
|
2 #include <string.h>
|
nuclear@28
|
3 #include <assert.h>
|
nuclear@28
|
4 #include "mingl.h"
|
nuclear@28
|
5 #include "mglimpl.h"
|
nuclear@28
|
6
|
nuclear@28
|
7 static struct state *st;
|
nuclear@28
|
8
|
nuclear@28
|
9 static float distance_signed(vec3_t p, const struct plane *plane);
|
nuclear@28
|
10 static int intersect(const ray3_t *ray, const struct plane *plane, float *t);
|
nuclear@28
|
11 static int clip_polygon(struct vertex *vout, int *voutnum, const struct vertex *vin, int vnum,
|
nuclear@28
|
12 const struct plane *plane);
|
nuclear@28
|
13
|
nuclear@28
|
14 #define ZNEAR MAX_CLIP_PLANES
|
nuclear@28
|
15
|
nuclear@28
|
16 int mgl_clip_init(struct state *state)
|
nuclear@28
|
17 {
|
nuclear@28
|
18 st = state;
|
nuclear@28
|
19
|
nuclear@28
|
20 memset(st->clip_planes, 0, sizeof st->clip_planes);
|
nuclear@28
|
21
|
nuclear@28
|
22 /* always setup a near clipping plane */
|
nuclear@28
|
23 st->clip_planes[ZNEAR].normal.x = st->clip_planes[ZNEAR].normal.y = 0;
|
nuclear@28
|
24 st->clip_planes[ZNEAR].normal.z = -1;
|
nuclear@28
|
25 st->clip_planes[ZNEAR].pt.x = st->clip_planes[ZNEAR].pt.y = 0;
|
nuclear@28
|
26 st->clip_planes[ZNEAR].pt.z = -0.5;
|
nuclear@28
|
27
|
nuclear@28
|
28 return 0;
|
nuclear@28
|
29 }
|
nuclear@28
|
30
|
nuclear@28
|
31 int mgl_clip_poly(struct vertex *v, int vnum)
|
nuclear@28
|
32 {
|
nuclear@28
|
33 int i, res, clipped_vnum;
|
nuclear@28
|
34 struct vertex tmp[6];
|
nuclear@28
|
35
|
nuclear@28
|
36 for(i=0; i<MAX_CLIP_PLANES + 1; i++) {
|
nuclear@28
|
37 if(i < ZNEAR && !mgl_isenabled(MGL_CLIP_PLANE0 + i)) {
|
nuclear@28
|
38 continue;
|
nuclear@28
|
39 }
|
nuclear@28
|
40
|
nuclear@28
|
41 res = clip_polygon(tmp, &clipped_vnum, v, vnum, st->clip_planes + i);
|
nuclear@28
|
42 if(res == -1) {
|
nuclear@28
|
43 /* the polygon was completely outside */
|
nuclear@28
|
44 return 0;
|
nuclear@28
|
45 }
|
nuclear@28
|
46 if(res == 0) {
|
nuclear@28
|
47 /* the polygon was clipped, update v and vnum */
|
nuclear@28
|
48 vnum = clipped_vnum;
|
nuclear@28
|
49 memcpy(v, tmp, clipped_vnum * sizeof *v);
|
nuclear@28
|
50 }
|
nuclear@28
|
51 /* otherwise the polygon was completely inside, nothing to do... */
|
nuclear@28
|
52 }
|
nuclear@28
|
53
|
nuclear@28
|
54 return vnum;
|
nuclear@28
|
55 }
|
nuclear@28
|
56
|
nuclear@28
|
57 static float distance_signed(vec3_t p, const struct plane *plane)
|
nuclear@28
|
58 {
|
nuclear@28
|
59 vec3_t ptdir;
|
nuclear@28
|
60 ptdir.x = p.x - plane->pt.x;
|
nuclear@28
|
61 ptdir.y = p.y - plane->pt.y;
|
nuclear@28
|
62 ptdir.z = p.z - plane->pt.z;
|
nuclear@28
|
63
|
nuclear@28
|
64 return vec3_dot(ptdir, plane->normal);
|
nuclear@28
|
65 }
|
nuclear@28
|
66
|
nuclear@28
|
67 static int intersect(const ray3_t *ray, const struct plane *plane, float *t)
|
nuclear@28
|
68 {
|
nuclear@28
|
69 vec3_t orig_pt_dir;
|
nuclear@28
|
70
|
nuclear@28
|
71 float ndotdir = vec3_dot(plane->normal, ray->dir);
|
nuclear@28
|
72 if(fabs(ndotdir) < 1e-4) {
|
nuclear@28
|
73 *t = 0.0f;
|
nuclear@28
|
74 return 0;
|
nuclear@28
|
75 }
|
nuclear@28
|
76
|
nuclear@28
|
77 orig_pt_dir.x = plane->pt.x - ray->origin.x;
|
nuclear@28
|
78 orig_pt_dir.y = plane->pt.y - ray->origin.y;
|
nuclear@28
|
79 orig_pt_dir.z = plane->pt.z - ray->origin.z;
|
nuclear@28
|
80
|
nuclear@28
|
81 *t = vec3_dot(plane->normal, orig_pt_dir) / ndotdir;
|
nuclear@28
|
82 return 1;
|
nuclear@28
|
83 }
|
nuclear@28
|
84
|
nuclear@28
|
85 static int clip_edge(struct vertex *poly, int *vnum, const struct vertex *v0,
|
nuclear@28
|
86 const struct vertex *v1, const struct plane *plane);
|
nuclear@28
|
87
|
nuclear@28
|
88 /* returns:
|
nuclear@28
|
89 * 1 -> both inside
|
nuclear@28
|
90 * 0 -> straddling and clipped
|
nuclear@28
|
91 * -1 -> both outside
|
nuclear@28
|
92 *
|
nuclear@28
|
93 * the size of the vout polygon is returned throug voutnum
|
nuclear@28
|
94 */
|
nuclear@28
|
95 static int clip_polygon(struct vertex *vout, int *voutnum, const struct vertex *vin, int vnum,
|
nuclear@28
|
96 const struct plane *plane)
|
nuclear@28
|
97 {
|
nuclear@28
|
98 int i;
|
nuclear@28
|
99 int edges_clipped = 0;
|
nuclear@28
|
100 int out_vnum = 0;
|
nuclear@28
|
101
|
nuclear@28
|
102 for(i=0; i<vnum; i++) {
|
nuclear@28
|
103 int res = clip_edge(vout, &out_vnum, vin + i, vin + (i + 1) % vnum, plane);
|
nuclear@28
|
104 if(res == 0) {
|
nuclear@28
|
105 edges_clipped++;
|
nuclear@28
|
106 }
|
nuclear@28
|
107 }
|
nuclear@28
|
108
|
nuclear@28
|
109 if(out_vnum <= 0) {
|
nuclear@28
|
110 assert(edges_clipped == 0);
|
nuclear@28
|
111 return -1;
|
nuclear@28
|
112 }
|
nuclear@28
|
113
|
nuclear@28
|
114 *voutnum = out_vnum;
|
nuclear@28
|
115 return edges_clipped > 0 ? 0 : 1;
|
nuclear@28
|
116 }
|
nuclear@28
|
117
|
nuclear@28
|
118 /* returns:
|
nuclear@28
|
119 * 1 -> both inside
|
nuclear@28
|
120 * 0 -> straddling and clipped
|
nuclear@28
|
121 * -1 -> both outside
|
nuclear@28
|
122 *
|
nuclear@28
|
123 * also returns the size of the polygon through vnumptr
|
nuclear@28
|
124 */
|
nuclear@28
|
125 static int clip_edge(struct vertex *poly, int *vnumptr, const struct vertex *v0,
|
nuclear@28
|
126 const struct vertex *v1, const struct plane *plane)
|
nuclear@28
|
127 {
|
nuclear@28
|
128 vec3_t pos0, pos1;
|
nuclear@28
|
129 float d0, d1, t;
|
nuclear@28
|
130 ray3_t ray;
|
nuclear@28
|
131 int vnum = *vnumptr;
|
nuclear@28
|
132
|
nuclear@28
|
133 pos0.x = v0->pos.x; pos0.y = v0->pos.y; pos0.z = v0->pos.z;
|
nuclear@28
|
134 pos1.x = v1->pos.x; pos1.y = v1->pos.y; pos1.z = v1->pos.z;
|
nuclear@28
|
135
|
nuclear@28
|
136 d0 = distance_signed(pos0, plane);
|
nuclear@28
|
137 d1 = distance_signed(pos1, plane);
|
nuclear@28
|
138
|
nuclear@28
|
139 ray.origin = pos0;
|
nuclear@28
|
140 ray.dir.x = pos1.x - pos0.x;
|
nuclear@28
|
141 ray.dir.y = pos1.y - pos0.y;
|
nuclear@28
|
142 ray.dir.z = pos1.z - pos0.z;
|
nuclear@28
|
143
|
nuclear@28
|
144 if(d0 >= 0.0) {
|
nuclear@28
|
145 /* start inside */
|
nuclear@28
|
146 if(d1 >= 0.0) {
|
nuclear@28
|
147 /* all inside */
|
nuclear@28
|
148 poly[vnum++] = *v1; /* append v1 */
|
nuclear@28
|
149 *vnumptr = vnum;
|
nuclear@28
|
150 return 1;
|
nuclear@28
|
151 } else {
|
nuclear@28
|
152 /* going out */
|
nuclear@28
|
153 intersect(&ray, plane, &t);
|
nuclear@28
|
154
|
nuclear@28
|
155 ray3_point(poly[vnum].pos, ray, t);
|
nuclear@28
|
156 poly[vnum].pos.w = 1.0;
|
nuclear@28
|
157
|
nuclear@28
|
158 vec3_lerp(poly[vnum].norm, v0->norm, v1->norm, t);
|
nuclear@28
|
159 vec2_lerp(poly[vnum].tc, v0->tc, v1->tc, t);
|
nuclear@28
|
160 poly[vnum].energy = v0->energy + (v1->energy - v0->energy) * t;
|
nuclear@28
|
161 poly[vnum].cidx = v0->cidx;
|
nuclear@28
|
162 vnum++; /* append new vertex on the intersection point */
|
nuclear@28
|
163 }
|
nuclear@28
|
164 } else {
|
nuclear@28
|
165 /* start outside */
|
nuclear@28
|
166 if(d1 >= 0) {
|
nuclear@28
|
167 /* going in */
|
nuclear@28
|
168 intersect(&ray, plane, &t);
|
nuclear@28
|
169
|
nuclear@28
|
170 ray3_point(poly[vnum].pos, ray, t);
|
nuclear@28
|
171 poly[vnum].pos.w = 1.0;
|
nuclear@28
|
172
|
nuclear@28
|
173 vec3_lerp(poly[vnum].norm, v0->norm, v1->norm, t);
|
nuclear@28
|
174 vec2_lerp(poly[vnum].tc, v0->tc, v1->tc, t);
|
nuclear@28
|
175 poly[vnum].energy = v0->energy + (v1->energy - v0->energy) * t;
|
nuclear@28
|
176 poly[vnum].cidx = v0->cidx;
|
nuclear@28
|
177 vnum++; /* append new vertex on the intersection point */
|
nuclear@28
|
178
|
nuclear@28
|
179 /* then append v1 ... */
|
nuclear@28
|
180 poly[vnum++] = *v1;
|
nuclear@28
|
181 } else {
|
nuclear@28
|
182 /* all outside */
|
nuclear@28
|
183 return -1;
|
nuclear@28
|
184 }
|
nuclear@28
|
185 }
|
nuclear@28
|
186
|
nuclear@28
|
187 *vnumptr = vnum;
|
nuclear@28
|
188 return 0;
|
nuclear@28
|
189 }
|