deepstone

annotate src/mglclip.c @ 28:11d14f688485

added clipping
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 22 Sep 2013 06:38:08 +0300
parents
children
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 }