!ifdef __UNIX__
obj = src/main.obj src/video.obj src/3dgfx.obj src/3dgfx_s.obj &
- src/polyfill.obj src/lut.obj src/vmath.obj src/vmath_s.obj
+ src/polyfill.obj src/lut.obj src/vmath.obj src/vmath_s.obj src/mesh.obj
!else
obj = src\main.obj src\video.obj src\3dgfx.obj src\3dgfx_s.obj &
- src\polyfill.obj src\lut.obj src\vmath.obj src\vmath_s.obj
+ src\polyfill.obj src\lut.obj src\vmath.obj src\vmath_s.obj src\mesh.obj
!endif
bin = low3d.exe
g3d_curcidx = cidx;
}
-static const int primverts[] = {1, 2, 3, 4};
+static const int primverts[] = {0, 1, 2, 3, 4};
void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
{
prim_vnum = primverts[prim];
for(i=0; i<vcount; i+=prim_vnum) {
- g3d_draw_prim(prim, varr);
+ g3d_draw_prim(prim, varr, 0);
varr += prim_vnum;
}
}
+void g3d_draw_indexed(int prim, struct g3d_vertex *varr, unsigned short *idxarr,
+ int idxcount)
+{
+ int i, j, prim_vnum;
+
+ prim_vnum = primverts[prim];
+
+ for(i=0; i<idxcount; i+=prim_vnum) {
+ g3d_draw_prim(prim, varr, idxarr);
+ idxarr += prim_vnum;
+ }
+}
+
int32_t vpscale(int32_t x, int32_t s, int32_t shift);
#pragma aux vpscale = \
"add eax, 0x100" \
modify [eax edx];
-void g3d_draw_prim(int prim, struct g3d_vertex *varr)
+void g3d_draw_prim(int prim, struct g3d_vertex *varr, unsigned short *idxarr)
{
int i, vcount, x, y, x1, y1;
struct g3d_vertex v[4];
vcount = primverts[prim];
for(i=0; i<vcount; i++) {
- v[i] = varr[i];
+ v[i] = idxarr ? varr[idxarr[i]] : varr[i];
/* transform to view space */
g3d_xform(v + i, mvmat);
};
enum {
- G3D_POINTS,
+ G3D_POINTS = 1,
G3D_LINES,
G3D_TRIANGLES,
G3D_QUADS
void g3d_color(int cidx);
void g3d_draw(int prim, struct g3d_vertex *varr, int vcount);
-void g3d_draw_prim(int prim, struct g3d_vertex *varr);
+void g3d_draw_indexed(int prim, struct g3d_vertex *varr, unsigned short *idxarr,
+ int idxcount);
+void g3d_draw_prim(int prim, struct g3d_vertex *varr, unsigned short *idxarr);
/* defined in polyfill.asm */
void g3d_polyfill(struct g3d_vertex *verts);
#include <dos.h>
#include "video.h"
#include "3dgfx.h"
+#include "mesh.h"
#include "vmath.h"
#include "util.h"
static unsigned long nframes;
volatile unsigned long nticks;
-
static void interrupt (*prev_timer_intr)();
+static const char *mesh_fname;
+static struct mesh mesh;
+
+#define VERT(x, y, z) { x << 16, y << 16, z << 16, 0x10000 }
+static struct g3d_vertex cube_varr[] = {
+ VERT(-1, -1, 1), VERT(1, -1, 1), VERT(1, -1, -1), VERT(-1, -1, -1),
+ VERT(-1, 1, 1), VERT(1, 1, 1), VERT(1, 1, -1), VERT(-1, 1, -1)
+};
+static struct meshface cube_faces[] = {
+ {4, {0, 1, 5, 4}},
+ {4, {1, 2, 6, 5}},
+ {4, {2, 3, 7, 6}},
+ {4, {3, 0, 4, 7}},
+ {4, {4, 5, 6, 7}},
+ {4, {3, 2, 1, 0}}
+};
+
int main(int argc, char **argv)
{
+ int i;
int32_t proj[16];
unsigned long rate, total_ticks, sec;
return 1;
}
+ if(mesh_fname) {
+ if(load_mesh(&mesh, mesh_fname) == -1) {
+ return 1;
+ }
+ flip_mesh_winding(&mesh);
+ } else {
+ mesh.varr = cube_varr;
+ mesh.nverts = sizeof cube_varr / sizeof *cube_varr;
+ mesh.faces = cube_faces;
+ mesh.nfaces = sizeof cube_faces / sizeof *cube_faces;
+ }
+ for(i=0; i<mesh.nfaces; i++) {
+ mesh.faces[i].color = (i & 7) + 9;
+ }
+
init_video();
+ for(i=0; i<256; i++) {
+ int r = XCOS(i << 1) >> 5;
+ int b = (255 - r) >> 1;
+ vid_setpalent(i, r, 8, b);
+ }
+ vid_setpalent(0, 0, 0, 0);
+
g3d_init();
g3d_framebuffer(320, 200, 0);
return 0;
}
-#define VERT(x, y, z) { x << 16, y << 16, z << 16, 0x10000 }
-struct g3d_vertex varr[] = {
- VERT(-1, -1, 1), VERT(1, -1, 1), VERT(1, 1, 1), VERT(-1, 1, 1),
- VERT(1, -1, 1), VERT(1, -1, -1), VERT(1, 1, -1), VERT(1, 1, 1),
- VERT(1, -1, -1), VERT(-1, -1, -1), VERT(-1, 1, -1), VERT(1, 1, -1),
- VERT(-1, -1, -1), VERT(-1, -1, 1), VERT(-1, 1, 1), VERT(-1, 1, -1),
- VERT(-1, 1, 1), VERT(1, 1, 1), VERT(1, 1, -1), VERT(-1, 1, -1),
- VERT(-1, -1, -1), VERT(1, -1, -1), VERT(1, -1, 1), VERT(-1, -1, 1)
-};
-
void update(void)
{
+ int i;
int32_t xform[16];
- mat_trans(xform, 0, 0, -0x40000);
+ mat_trans(xform, 0, 0, -0x70000);
mat_mul_rotx(xform, nframes);
mat_mul_roty(xform, nframes);
g3d_modelview(xform);
vid_clearfb();
#endif
- g3d_color(9);
- g3d_draw(G3D_QUADS, varr, 4);
- g3d_color(10);
- g3d_draw(G3D_QUADS, varr + 4, 4);
- g3d_color(11);
- g3d_draw(G3D_QUADS, varr + 8, 4);
- g3d_color(12);
- g3d_draw(G3D_QUADS, varr + 12, 4);
- g3d_color(13);
- g3d_draw(G3D_QUADS, varr + 16, 4);
- g3d_color(14);
- g3d_draw(G3D_QUADS, varr + 20, 4);
+ sort_mesh(&mesh, xform);
+ draw_mesh_zorder(&mesh);
vid_pgflip();
}
_chain_intr(prev_timer_intr);
}
-static const char *helpfmt = "Usage: %s [options]\n"
+static const char *helpfmt = "Usage: %s [options] [mesh file]\n"
"Options:\n"
" -vsync: enable vsync (default)\n"
" -novsync: disable vsync\n"
return -1;
}
} else {
- fprintf(stderr, "unexpected argument: %s\n", argv[i]);
- return -1;
+ if(mesh_fname) {
+ fprintf(stderr, "unexpected argument: %s\n", argv[i]);
+ return -1;
+ }
+ mesh_fname = argv[i];
}
}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "mesh.h"
+
+static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
+ int nverts, int nnorm, int nuv);
+static int resizearr(void **pptr, int *sz, int elemsz);
+static char *clean_line(char *s);
+
+int load_mesh(struct mesh *m, const char *fname)
+{
+ float fx, fy, fz;
+ FILE *fp;
+ char buf[128], *line;
+ struct g3d_vertex *vptr;
+ struct meshface *face;
+ int i, maxverts, maxfaces;
+ int vidx, tidx, nidx;
+
+ memset(m, 0, sizeof *m);
+
+ if(!(fp = fopen(fname, "rb"))) {
+ fprintf(stderr, "failed to open mesh: %s\n", fname);
+ return -1;
+ }
+
+ printf("Loading mesh: %s\n", fname);
+
+ m->nverts = 0;
+ maxverts = 32;
+ if(!(m->varr = malloc(maxverts * sizeof *m->varr))) {
+ fprintf(stderr, "failed to allocate vertex array\n");
+ goto err;
+ }
+
+ m->nfaces = 0;
+ maxfaces = 32;
+ if(!(m->faces = malloc(maxfaces * sizeof *m->faces))) {
+ fprintf(stderr, "failed to allocate mesh face array\n");
+ goto err;
+ }
+
+
+ while(fgets(buf, sizeof buf, fp)) {
+ line = clean_line(buf);
+ if(!*line) continue;
+
+ switch(line[0]) {
+ case 'v':
+ if(!isspace(line[1])) continue;
+ if(sscanf(line + 2, "%f %f %f", &fx, &fy, &fz) != 3) {
+ continue;
+ }
+ if(m->nverts >= maxverts) {
+ if(resizearr(&m->varr, &maxverts, sizeof *m->varr) == -1) {
+ goto err;
+ }
+ }
+ vptr = m->varr + m->nverts++;
+ vptr->x = (int32_t)(fx * 65536.0f);
+ vptr->y = (int32_t)(fy * 65536.0f);
+ vptr->z = (int32_t)(fz * 65536.0f);
+ vptr->w = 0x10000;
+ putchar('v'); fflush(stdout);
+ break;
+
+ case 'f':
+ if(m->nfaces >= maxfaces) {
+ if(resizearr(&m->faces, &maxfaces, sizeof *m->faces) == -1) {
+ goto err;
+ }
+ }
+
+ line += 2;
+ face = m->faces + m->nfaces;
+ for(i=0; i<4; i++) {
+ if(!(line = parse_facev(line, &vidx, &nidx, &tidx, m->nverts, 0, 0))) {
+ if(i < 3) {
+ fprintf(stderr, "invalid face definition\n");
+ goto err;
+ }
+ break;
+ }
+ if(vidx < 0 || vidx >= m->nverts) {
+ fprintf(stderr, "invalid face definition, vertex out of range\n");
+ goto err;
+ }
+ face->vidx[i] = vidx;
+ }
+ face->prim = i;
+ m->nfaces++;
+ putchar('f'); fflush(stdout);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ calc_face_normals(m);
+
+ for(i=0; i<m->nfaces; i++) {
+ int32_t dot;
+ face = m->faces + i;
+ dot = face->norm.z > 0 ? face->norm.z : 0;
+ face->color = dot >> 8;
+ }
+
+ putchar('\n');
+
+ fclose(fp);
+ return 0;
+
+err:
+ if(fp) fclose(fp);
+ free(m->varr);
+ free(m->faces);
+ return -1;
+}
+
+static char *parse_idx(char *s, int *idx, int arrsz)
+{
+ char *endp;
+ int val = strtol(s, &endp, 10);
+ if(endp == s) return 0;
+
+ if(val < 0) { /* convert negative indices */
+ *idx = arrsz + val;
+ } else {
+ *idx = val - 1; /* conv to 0-based */
+ }
+ return endp;
+}
+
+static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
+ int nverts, int nnorm, int nuv)
+{
+ if(!(s = parse_idx(s, vidx, nverts))) {
+ return 0;
+ }
+ if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
+
+ if(*++s == '/') { /* no texcoord */
+ *tidx = -1;
+ s++;
+ } else {
+ if(!(s = parse_idx(s, tidx, nuv))) {
+ return 0;
+ }
+ if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
+ s++;
+ }
+
+ if(!(s = parse_idx(s, nidx, nnorm))) {
+ return 0;
+ }
+ return (!*s || isspace(*s)) ? s : 0;
+}
+
+static char *clean_line(char *s)
+{
+ char *endp;
+
+ while(*s && isspace(*s)) s++;
+
+ if((endp = strchr(s, '#'))) {
+ *endp-- = 0;
+ } else {
+ endp = s + strlen(s) - 1;
+ }
+ while(endp > s && isspace(*endp)) {
+ *endp-- = 0;
+ }
+ return s;
+}
+
+static int resizearr(void **pptr, int *sz, int elemsz)
+{
+ int newsz = *sz ? *sz * 2 : 16;
+ void *newarr;
+
+ if(!(newarr = realloc(*pptr, newsz * elemsz))) {
+ fprintf(stderr, "failed to resize array to %d\n", newsz);
+ return -1;
+ }
+ *pptr = newarr;
+ *sz = newsz;
+ return 0;
+}
+
+
+void destroy_mesh(struct mesh *m)
+{
+ if(!m) return;
+ free(m->varr);
+ free(m->faces);
+}
+
+void flip_mesh_winding(struct mesh *m)
+{
+ int i, j;
+ unsigned short buf[4];
+ struct meshface *face = m->faces;
+
+ for(i=0; i<m->nfaces; i++) {
+ for(j=0; j<face->prim; j++) {
+ buf[face->prim - j - 1] = face->vidx[j];
+ }
+ for(j=0; j<face->prim; j++) {
+ face->vidx[j] = buf[j];
+ }
+ face++;
+ }
+}
+
+int conv_nonidx_mesh(struct mesh *m)
+{
+ struct g3d_vertex *varr, *vptr;
+ struct meshface *face;
+ int i, nverts;
+
+ nverts = m->nfaces * 3;
+ if(!(varr = malloc(nverts * sizeof *varr))) {
+ fprintf(stderr, "failed to allocate nonidx vertex array (%d)\n", nverts);
+ return -1;
+ }
+
+ face = m->faces;
+ vptr = varr;
+ for(i=0; i<m->nfaces; i++) {
+ vptr[0] = m->varr[face->vidx[0]];
+ vptr[1] = m->varr[face->vidx[1]];
+ vptr[2] = m->varr[face->vidx[2]];
+ face++;
+ vptr += 3;
+ }
+
+ free(m->varr);
+ m->varr = varr;
+ m->nverts = nverts;
+ return 0;
+}
+
+static float rsqrt(float x)
+{
+ float xhalf = x * 0.5f;
+ int32_t i = *(int32_t*)&x;
+ i = 0x5f3759df - (i >> 1);
+ x = *(float*)&i;
+ x = x * (1.5f - xhalf * x * x);
+ return x;
+}
+
+void calc_face_normals(struct mesh *m)
+{
+ int i;
+ struct meshface *face;
+ struct g3d_vertex *va, *vb, *vc;
+ int32_t ax, ay, az, bx, by, bz, s;
+ float xsq, ysq, zsq;
+
+ face = m->faces;
+ for(i=0; i<m->nfaces; i++) {
+ va = m->varr + face->vidx[0];
+ vb = m->varr + face->vidx[1];
+ vc = m->varr + face->vidx[2];
+
+ ax = (vc->x - va->x) >> 8;
+ ay = (vc->y - va->y) >> 8;
+ az = (vc->z - va->z) >> 8;
+ bx = (vc->x - vb->x) >> 8;
+ by = (vc->y - vb->y) >> 8;
+ bz = (vc->z - vb->z) >> 8;
+
+ face->norm.x = ay * bz - az * by;
+ face->norm.y = az * bx - ax * bz;
+ face->norm.z = ax * by - ay * bx;
+
+ xsq = (float)face->norm.x; xsq *= xsq;
+ ysq = (float)face->norm.y; ysq *= ysq;
+ zsq = (float)face->norm.z; zsq *= zsq;
+ s = (int32_t)(rsqrt(xsq + ysq + zsq) * 256.0f);
+ face->norm.x = (face->norm.x >> 8) * s;
+ face->norm.y = (face->norm.y >> 8) * s;
+ face->norm.z = (face->norm.z >> 8) * s;
+
+ face++;
+ }
+}
+
+static struct mesh *smesh;
+static const int32_t *smat;
+
+static int zsortcmp(const void *a, const void *b)
+{
+ unsigned short idxa, idxb;
+ struct meshface *fa, *fb;
+ struct g3d_vertex *va, *vb;
+ int32_t za, zb;
+
+ fa = smesh->faces + *(unsigned short*)a;
+ fb = smesh->faces + *(unsigned short*)b;
+
+ va = smesh->varr + fa->vidx[0];
+ vb = smesh->varr + fb->vidx[0];
+
+ za = (smat[8] >> 8) * (va->x >> 8) + (smat[9] >> 8) * (va->y >> 8) +
+ (smat[10] >> 8) * (va->z >> 8);
+ zb = (smat[8] >> 8) * (vb->x >> 8) + (smat[9] >> 8) * (vb->y >> 8) +
+ (smat[10] >> 8) * (vb->z >> 8);
+
+ return za - zb;
+}
+
+void sort_mesh(struct mesh *m, const int32_t *mvmat)
+{
+ int i;
+
+ if(!m->zorder) {
+ if(!(m->zorder = malloc(m->nfaces * sizeof *m->zorder))) {
+ return;
+ }
+ for(i=0; i<m->nfaces; i++) {
+ m->zorder[i] = i;
+ }
+ }
+
+ smesh = m;
+ smat = mvmat;
+ qsort(m->zorder, m->nfaces, sizeof *m->zorder, zsortcmp);
+}
+
+void draw_mesh(struct mesh *m)
+{
+ int i;
+ struct meshface *face = m->faces;
+ for(i=0; i<m->nfaces; i++) {
+ g3d_color(face->color);
+ g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
+ face++;
+ }
+}
+
+void draw_mesh_zorder(struct mesh *m)
+{
+ int i;
+ struct meshface *face;
+
+ for(i=0; i<m->nfaces; i++) {
+ face = m->faces + m->zorder[i];
+ g3d_color(face->color);
+ g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
+ }
+}
--- /dev/null
+#ifndef MESH_H_
+#define MESH_H_
+
+#include "3dgfx.h"
+
+struct vec3 {
+ int32_t x, y, z;
+};
+struct meshface {
+ short prim; /* 3 - triangle / 4 - quad */
+ unsigned short vidx[4];
+ unsigned char color;
+ struct vec3 norm;
+};
+
+struct mesh {
+ struct g3d_vertex *varr;
+ struct meshface *faces;
+ int nverts, nfaces;
+ unsigned short *zorder;
+};
+
+int load_mesh(struct mesh *m, const char *fname);
+void destroy_mesh(struct mesh *m);
+
+void flip_mesh_winding(struct mesh *m);
+int conv_nonidx_mesh(struct mesh *m);
+void calc_face_normals(struct mesh *m);
+
+void sort_mesh(struct mesh *m, const int32_t *mvmat);
+void draw_mesh(struct mesh *m);
+void draw_mesh_zorder(struct mesh *m);
+
+#endif /* MESH_H_ */
static void fillbot(struct g3d_vertex *v0, struct g3d_vertex *v1, struct g3d_vertex *v2);
static void fillspan(unsigned char *dest, int x, int len);
+
void g3d_polyfill(struct g3d_vertex *verts)
{
int i, topidx, botidx, mididx;
pop ax
ret
+ global vid_setpalent_
+vid_setpalent_:
+ mov ah, dl
+ mov dx, 3c8h
+ out dx, al
+ inc dx
+ mov al, ah
+ shr al, 2
+ out dx, al
+ mov al, bl
+ shr al, 2
+ out dx, al
+ mov al, cl
+ shr al, 2
+ out dx, al
+ ret
+
; clear the framebuffer 4 pixels at a time
global vid_clearfb_
vid_clearfb_:
void init_video(void);
void close_video(void);
+void vid_setpalent(int idx, int r, int g, int b);
void vid_clearfb(void);
void vid_clearfb_rect(int x, int y, int w, int h);