2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
28 static int clip_edge(struct vertex *poly, int *vnumptr,
29 const struct vertex *v0, const struct vertex *v1,
30 const struct cplane *plane);
31 static int check_clip_edge(const struct vertex *v0,
32 const struct vertex *v1, const struct cplane *plane);
33 static int clip_edge_frustum(struct vertex *poly, int *vnumptr,
34 const struct vertex *v0, const struct vertex *v1, int fplane);
35 static float distance_signed(float *pos, const struct cplane *plane);
36 static int intersect(const struct ray *ray, const struct cplane *plane, float *t);
37 static int inside_frustum_plane(const struct vertex *v, int fplane);
40 int clip_poly(struct vertex *vout, int *voutnum,
41 const struct vertex *vin, int vnum, struct cplane *plane)
44 int edges_clipped = 0;
48 for(i=0; i<vnum; i++) {
50 if(nextidx >= vnum) nextidx = 0;
51 res = clip_edge(vout, voutnum, vin + i, vin + nextidx, plane);
58 assert(edges_clipped == 0);
62 return edges_clipped > 0 ? 0 : 1;
65 int check_clip_poly(const struct vertex *v, int vnum, struct cplane *plane)
67 int i, nextidx, res = 0;
68 int edges_clipped = 0;
70 for(i=0; i<vnum; i++) {
72 if(nextidx >= vnum) nextidx = 0;
73 res = check_clip_edge(v + i, v + nextidx, plane);
78 return edges_clipped ? 0 : res;
81 int clip_frustum(struct vertex *vout, int *voutnum,
82 const struct vertex *vin, int vnum, int fplane)
85 int edges_clipped = 0;
88 /* special case: point clipping */
89 return inside_frustum_plane(vin, fplane) ? 1 : -1;
94 for(i=0; i<vnum; i++) {
96 if(nextidx >= vnum) nextidx = 0;
97 res = clip_edge_frustum(vout, voutnum, vin + i, vin + nextidx, fplane);
104 assert(edges_clipped == 0);
108 return edges_clipped > 0 ? 0 : 1;
111 #define LERP_VATTR(res, v0, v1, t) \
113 (res)->nx = (v0)->nx + ((v1)->nx - (v0)->nx) * (t); \
114 (res)->ny = (v0)->ny + ((v1)->ny - (v0)->ny) * (t); \
115 (res)->nz = (v0)->nz + ((v1)->nz - (v0)->nz) * (t); \
116 (res)->u = (v0)->u + ((v1)->u - (v0)->u) * (t); \
117 (res)->v = (v0)->v + ((v1)->v - (v0)->v) * (t); \
118 (res)->r = (v0)->r + ((v1)->r - (v0)->r) * (t); \
119 (res)->g = (v0)->g + ((v1)->g - (v0)->g) * (t); \
120 (res)->b = (v0)->b + ((v1)->b - (v0)->b) * (t); \
126 * 0 -> straddling and clipped
129 * also returns the size of the polygon through vnumptr
131 static int clip_edge(struct vertex *poly, int *vnumptr,
132 const struct vertex *v0, const struct vertex *v1,
133 const struct cplane *plane)
135 float pos0[3], pos1[3];
138 int i, vnum = *vnumptr;
140 pos0[0] = v0->x; pos0[1] = v0->y; pos0[2] = v0->z;
141 pos1[0] = v1->x; pos1[1] = v1->y; pos1[2] = v1->z;
143 d0 = distance_signed(pos0, plane);
144 d1 = distance_signed(pos1, plane);
147 ray.origin[i] = pos0[i];
148 ray.dir[i] = pos1[i] - pos0[i];
155 poly[vnum++] = *v1; /* append v1 */
160 struct vertex *vptr = poly + vnum;
162 intersect(&ray, plane, &t);
164 vptr->x = ray.origin[0] + ray.dir[0] * t;
165 vptr->y = ray.origin[1] + ray.dir[1] * t;
166 vptr->z = ray.origin[2] + ray.dir[2] * t;
169 LERP_VATTR(vptr, v0, v1, t);
170 vnum++; /* append new vertex on the intersection point */
176 struct vertex *vptr = poly + vnum;
178 intersect(&ray, plane, &t);
180 vptr->x = ray.origin[0] + ray.dir[0] * t;
181 vptr->y = ray.origin[1] + ray.dir[1] * t;
182 vptr->z = ray.origin[2] + ray.dir[2] * t;
185 LERP_VATTR(vptr, v0, v1, t);
186 vnum++; /* append new vertex on the intersection point */
188 /* then append v1 ... */
200 /* same as above, but only checks for clipping and classifies the edge */
201 static int check_clip_edge(const struct vertex *v0,
202 const struct vertex *v1, const struct cplane *plane)
204 float pos0[3], pos1[3];
207 pos0[0] = v0->x; pos0[1] = v0->y; pos0[2] = v0->z;
208 pos1[0] = v1->x; pos1[1] = v1->y; pos1[2] = v1->z;
210 d0 = distance_signed(pos0, plane);
211 d1 = distance_signed(pos1, plane);
213 if(d0 > 0.0f && d1 > 0.0f) {
216 if(d0 < 0.0f && d1 < 0.0f) {
222 static float distance_signed(float *pos, const struct cplane *plane)
224 float dx = pos[0] - plane->x;
225 float dy = pos[1] - plane->y;
226 float dz = pos[2] - plane->z;
227 return dx * plane->nx + dy * plane->ny + dz * plane->nz;
230 static int intersect(const struct ray *ray, const struct cplane *plane, float *t)
232 float orig_pt_dir[3];
234 float ndotdir = plane->nx * ray->dir[0] + plane->ny * ray->dir[1] + plane->nz * ray->dir[2];
235 if(fabs(ndotdir) < 1e-6) {
240 orig_pt_dir[0] = plane->x - ray->origin[0];
241 orig_pt_dir[1] = plane->y - ray->origin[1];
242 orig_pt_dir[2] = plane->z - ray->origin[2];
244 *t = (plane->nx * orig_pt_dir[0] + plane->ny * orig_pt_dir[1] + plane->nz * orig_pt_dir[2]) / ndotdir;
248 /* homogeneous frustum clipper helpers */
250 static int inside_frustum_plane(const struct vertex *v, int fplane)
254 return v->x >= -v->w;
258 return v->y >= -v->w;
262 return v->z >= -v->w;
270 static float intersect_frustum(const struct vertex *a, const struct vertex *b, int fplane)
274 return (-a->w - a->x) / (b->x - a->x + b->w - a->w);
276 return (a->w - a->x) / (b->x - a->x - b->w + a->w);
278 return (-a->w - a->y) / (b->y - a->y + b->w - a->w);
280 return (a->w - a->y) / (b->y - a->y - b->w + a->w);
282 return (-a->w - a->z) / (b->z - a->z + b->w - a->w);
284 return (a->w - a->z) / (b->z - a->z - b->w + a->w);
291 static int clip_edge_frustum(struct vertex *poly, int *vnumptr,
292 const struct vertex *v0, const struct vertex *v1, int fplane)
298 in0 = inside_frustum_plane(v0, fplane);
299 in1 = inside_frustum_plane(v1, fplane);
305 poly[vnum++] = *v1; /* append v1 */
310 struct vertex *vptr = poly + vnum;
312 t = intersect_frustum(v0, v1, fplane);
314 vptr->x = v0->x + (v1->x - v0->x) * t;
315 vptr->y = v0->y + (v1->y - v0->y) * t;
316 vptr->z = v0->z + (v1->z - v0->z) * t;
317 vptr->w = v0->w + (v1->w - v0->w) * t;
319 LERP_VATTR(vptr, v0, v1, t);
320 ++vnum; /* append new vertex on the intersection point */
326 struct vertex *vptr = poly + vnum;
328 t = intersect_frustum(v0, v1, fplane);
330 vptr->x = v0->x + (v1->x - v0->x) * t;
331 vptr->y = v0->y + (v1->y - v0->y) * t;
332 vptr->z = v0->z + (v1->z - v0->z) * t;
333 vptr->w = v0->w + (v1->w - v0->w) * t;
335 LERP_VATTR(vptr, v0, v1, t);
336 ++vnum; /* append new vertex on the intersection point */
338 /* then append v1 ... */