#include <assert.h>
#include "3dgfx.h"
#include "polyfill.h"
+#include "polyclip.h"
#include "inttypes.h"
#include "util.h"
int width, height;
void *pixels;
+
+ int vport[4];
};
static void xform4_vec3(const float *mat, float *vec);
pfill_fb.pixels = pixels;
pfill_fb.width = width;
pfill_fb.height = height;
+
+ st->vport[0] = st->vport[1] = 0;
+ st->vport[2] = width;
+ st->vport[3] = height;
+}
+
+void g3d_viewport(int x, int y, int w, int h)
+{
+ st->vport[0] = x;
+ st->vport[1] = y;
+ st->vport[2] = w;
+ st->vport[3] = h;
}
void g3d_enable(unsigned int opt)
const int16_t *iarr, int iarr_size)
{
int i, j, nfaces;
- struct pvertex pv[4];
- struct g3d_vertex v[4];
+ struct pvertex pv[16];
+ struct g3d_vertex v[16];
int vnum = prim; /* primitive vertex counts correspond to enum values */
int mvtop = st->mtop[G3D_MODELVIEW];
int ptop = st->mtop[G3D_PROJECTION];
xform4_vec3(st->mat[G3D_PROJECTION][ptop], &v[i].x);
}
- /* TODO clipping */
+ /* clipping */
+ for(i=0; i<6; i++) {
+ struct g3d_vertex orig[16];
+ memcpy(orig, v, vnum * sizeof *v);
+
+ if(clip_frustum(v, &vnum, orig, vnum, i) < 0) {
+ /* polygon completely outside of view volume. discard */
+ return;
+ }
+ }
for(i=0; i<vnum; i++) {
if(v[i].w != 0.0f) {
}
/* viewport transformation */
- v[i].x = (v[i].x * 0.5f + 0.5f) * (float)st->width;
- v[i].y = (0.5f - v[i].y * 0.5f) * (float)st->height;
+ v[i].x = (v[i].x * 0.5f + 0.5f) * (float)st->vport[2] + st->vport[0];
+ v[i].y = (0.5f - v[i].y * 0.5f) * (float)st->vport[3] + st->vport[1];
/* convert pos to 24.8 fixed point */
pv[i].x = cround64(v[i].x * 256.0f);
void g3d_destroy(void);
void g3d_framebuffer(int width, int height, void *pixels);
+void g3d_viewport(int x, int y, int w, int h);
void g3d_enable(unsigned int opt);
void g3d_disable(unsigned int opt);
{
int i, j;
- float tsec = time_msec / 1000.0f;
+ float tsec = 0;//time_msec / 1000.0f;
static float phase[] = {0.0, M_PI / 3.0, M_PI * 0.8};
static float speed[] = {0.8, 1.4, 1.0};
static float scale[][3] = {{1, 2, 0.8}, {0.5, 1.6, 0.6}, {1.5, 0.7, 0.5}};
static void draw(void)
{
+ int i, j;
+
update();
memset(fb_pixels, 0, fb_width * fb_height * 2);
+ for(i=0; i<120; i++) {
+ for(j=0; j<160; j++) {
+ fb_pixels[(i + 60) * 320 + (j + 80)] = 0x1e7;
+ }
+ }
+ g3d_viewport(80, 60, 160, 120);
+
g3d_matrix_mode(G3D_MODELVIEW);
g3d_load_identity();
g3d_translate(0, 0, -cam_dist);
static int clip_edge(struct g3d_vertex *poly, int *vnumptr,
const struct g3d_vertex *v0, const struct g3d_vertex *v1,
const struct cplane *plane);
+static int clip_edge_frustum(struct g3d_vertex *poly, int *vnumptr,
+ const struct g3d_vertex *v0, const struct g3d_vertex *v1, int fplane);
static float distance_signed(float *pos, const struct cplane *plane);
static int intersect(const struct ray *ray, const struct cplane *plane, float *t);
for(i=0; i<vnum; i++) {
int res = clip_edge(vout, &out_vnum, vin + i, vin + (i + 1) % vnum, plane);
if(res == 0) {
- edges_clipped++;
+ ++edges_clipped;
+ }
+ }
+
+ if(out_vnum <= 0) {
+ assert(edges_clipped == 0);
+ return -1;
+ }
+
+ *voutnum = out_vnum;
+ return edges_clipped > 0 ? 0 : 1;
+}
+
+
+int clip_frustum(struct g3d_vertex *vout, int *voutnum,
+ const struct g3d_vertex *vin, int vnum, int fplane)
+{
+ int i;
+ int edges_clipped = 0;
+ int out_vnum = 0;
+
+ for(i=0; i<vnum; i++) {
+ int res = clip_edge_frustum(vout, &out_vnum, vin + i, vin + (i + 1) % vnum, fplane);
+ if(res == 0) {
+ ++edges_clipped;
}
}
*t = (plane->nx * orig_pt_dir[0] + plane->ny * orig_pt_dir[1] + plane->nz * orig_pt_dir[2]) / ndotdir;
return 1;
}
+
+/* homogeneous frustum clipper helpers */
+
+static int inside_frustum_plane(const struct g3d_vertex *v, int fplane)
+{
+ switch(fplane) {
+ case CLIP_LEFT:
+ return v->x >= -v->w;
+ case CLIP_RIGHT:
+ return v->x <= v->w;
+ case CLIP_BOTTOM:
+ return v->y >= -v->w;
+ case CLIP_TOP:
+ return v->y <= v->w;
+ case CLIP_NEAR:
+ return v->z >= -v->w;
+ case CLIP_FAR:
+ return v->z <= v->w;
+ }
+ assert(0);
+ return 0;
+}
+
+static float intersect_frustum(const struct g3d_vertex *a, const struct g3d_vertex *b, int fplane)
+{
+ switch(fplane) {
+ case CLIP_LEFT:
+ return (-a->w - a->x) / (b->x - a->x + b->w - a->w);
+ case CLIP_RIGHT:
+ return (a->w - a->x) / (b->x - a->x - b->w + a->w);
+ case CLIP_BOTTOM:
+ return (-a->w - a->y) / (b->y - a->y + b->w - a->w);
+ case CLIP_TOP:
+ return (a->w - a->y) / (b->y - a->y - b->w + a->w);
+ case CLIP_NEAR:
+ return (-a->w - a->z) / (b->z - a->z + b->w - a->w);
+ case CLIP_FAR:
+ return (a->w - a->z) / (b->z - a->z - b->w + a->w);
+ }
+
+ assert(0);
+ return 0;
+}
+
+static int clip_edge_frustum(struct g3d_vertex *poly, int *vnumptr,
+ const struct g3d_vertex *v0, const struct g3d_vertex *v1, int fplane)
+{
+ int vnum = *vnumptr;
+ int in0, in1;
+ float t;
+
+ in0 = inside_frustum_plane(v0, fplane);
+ in1 = inside_frustum_plane(v1, fplane);
+
+ if(in0) {
+ /* start inside */
+ if(in1) {
+ /* all inside */
+ poly[vnum++] = *v1; /* append v1 */
+ *vnumptr = vnum;
+ return 1;
+ } else {
+ /* going out */
+ struct g3d_vertex *vptr = poly + vnum;
+
+ t = intersect_frustum(v0, v1, fplane);
+
+ vptr->x = v0->x + (v1->x - v0->x) * t;
+ vptr->y = v0->y + (v1->y - v0->y) * t;
+ vptr->z = v0->z + (v1->z - v0->z) * t;
+ vptr->w = v0->w + (v1->w - v0->w) * t;
+
+ LERP_VATTR(vptr, v0, v1, t);
+ ++vnum; /* append new vertex on the intersection point */
+ }
+ } else {
+ /* start outside */
+ if(in1) {
+ /* going in */
+ struct g3d_vertex *vptr = poly + vnum;
+
+ t = intersect_frustum(v0, v1, fplane);
+
+ vptr->x = v0->x + (v1->x - v0->x) * t;
+ vptr->y = v0->y + (v1->y - v0->y) * t;
+ vptr->z = v0->z + (v1->z - v0->z) * t;
+ vptr->w = v0->w + (v1->w - v0->w) * t;
+
+ LERP_VATTR(vptr, v0, v1, t);
+ ++vnum; /* append new vertex on the intersection point */
+
+ /* then append v1 ... */
+ poly[vnum++] = *v1;
+ } else {
+ /* all outside */
+ return -1;
+ }
+ }
+
+ *vnumptr = vnum;
+ return 0;
+}
float nx, ny, nz;
};
-/* Polygon clipper
+enum {
+ CLIP_LEFT, CLIP_RIGHT,
+ CLIP_BOTTOM, CLIP_TOP,
+ CLIP_NEAR, CLIP_FAR
+};
+
+/* Generic polygon clipper
* returns:
* 1 -> fully inside, not clipped
* 0 -> straddling the plane and clipped
int clip_poly(struct g3d_vertex *vout, int *voutnum,
const struct g3d_vertex *vin, int vnum, struct cplane *plane);
+/* Special-case frustum clipper (might be slightly faster) */
+int clip_frustum(struct g3d_vertex *vout, int *voutnum,
+ const struct g3d_vertex *vin, int vnum, int fplane);
+
#endif /* POLYCLIP_H_ */
int32_t y1 = pv[next].y;
if((y0 >> 8) == (y1 >> 8)) {
- if(y0 > y1) {
+ /*if(y0 > y1) {*/
+ int i0, i1;
int idx = y0 >> 8;
- left[idx].x = pv[i].x < pv[next].x ? pv[i].x : pv[next].x;
- right[idx].x = pv[i].x < pv[next].x ? pv[next].x : pv[i].x;
- }
+ if(pv[i].x < pv[next].x) {
+ i0 = i;
+ i1 = next;
+ } else {
+ i0 = next;
+ i1 = i;
+ }
+ left[idx].x = pv[i0].x;
+ right[idx].x = pv[i1].x;
+#ifdef GOURAUD
+ left[idx].r = pv[i0].r << COLOR_SHIFT;
+ left[idx].g = pv[i0].g << COLOR_SHIFT;
+ left[idx].b = pv[i0].b << COLOR_SHIFT;
+ right[idx].r = pv[i1].r << COLOR_SHIFT;
+ right[idx].g = pv[i1].g << COLOR_SHIFT;
+ right[idx].b = pv[i1].b << COLOR_SHIFT;
+#endif
+#ifdef TEXMAP
+ left[idx].u = pv[i0].u;
+ left[idx].v = pv[i0].v;
+ right[idx].u = pv[i1].u;
+ right[idx].v = pv[i1].v;
+#endif
+ if(idx > slbot) slbot = idx;
+ if(idx < sltop) sltop = idx;
+ /*}*/
} else {
struct pvertex *edge = y0 > y1 ? left : right;
uint32_t res = SCANEDGE(pv + i, pv + next, edge);