nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@17: #include nuclear@0: #include nuclear@21: #include "vmath.h" nuclear@0: #include "mingl.h" nuclear@0: #include "mglimpl.h" nuclear@0: nuclear@3: nuclear@0: #define DOT(a, b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z) nuclear@0: nuclear@11: #define NORMALIZE(v) \ nuclear@11: do { \ nuclear@11: float mag = sqrt(DOT(v, v)); \ nuclear@11: if(fabs(mag) > 1e-6) { \ nuclear@11: float invmag = 1.0 / mag; \ nuclear@11: (v).x *= invmag; \ nuclear@11: (v).y *= invmag; \ nuclear@11: (v).z *= invmag; \ nuclear@11: } \ nuclear@11: } while(0) nuclear@11: nuclear@0: static void transform(vec4_t *res, vec4_t *v, float *mat); nuclear@0: static void transform3(vec3_t *res, vec3_t *v, float *mat); nuclear@28: static void vertex_proc_view(struct vertex *vert); nuclear@28: static int vertex_proc_proj(struct vertex *vert); nuclear@3: static int calc_shiftmask(int val, int *shiftp, unsigned int *maskp); nuclear@0: nuclear@0: static struct state st; nuclear@0: static struct framebuffer fb; nuclear@0: nuclear@0: int mgl_init(int width, int height) nuclear@0: { nuclear@0: int i; nuclear@0: nuclear@0: st.flags = 0; nuclear@0: st.mmode = 0; nuclear@0: nuclear@0: mgl_front_face(MGL_CCW); nuclear@0: mgl_cull_face(MGL_BACK); nuclear@0: nuclear@0: st.curv.cidx = 0; nuclear@0: st.curv.energy = 1.0; nuclear@0: st.curv.norm.x = st.curv.norm.y = st.curv.norm.z = 0.0; nuclear@0: nuclear@0: if(!(fb.pixels = malloc(width * height))) { nuclear@0: return -1; nuclear@0: } nuclear@0: fb.width = width; nuclear@0: fb.height = height; nuclear@0: fb.zbuf = 0; nuclear@0: nuclear@0: if(mgl_rast_init(&st, &fb) == -1) { nuclear@0: free(fb.pixels); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: st.mtop[0] = st.mtop[1] = 0; nuclear@0: nuclear@0: mgl_matrix_mode(MGL_MODELVIEW); nuclear@0: mgl_load_identity(); nuclear@0: mgl_matrix_mode(MGL_PROJECTION); nuclear@0: mgl_load_identity(); nuclear@0: nuclear@0: /* initial viewport in the size of the framebuffer */ nuclear@0: st.vp[0] = st.vp[1] = 0; nuclear@0: st.vp[2] = width; nuclear@0: st.vp[3] = height; nuclear@0: nuclear@0: st.col_range = 256; nuclear@0: for(i=0; i= 0 && ltidx < MAX_LIGHTS); nuclear@0: st.lint[ltidx] = intens; nuclear@0: } nuclear@0: nuclear@11: void mgl_light_position(int ltidx, float x, float y, float z, float w) nuclear@0: { nuclear@11: vec4_t pos; nuclear@0: assert(ltidx >= 0 && ltidx < MAX_LIGHTS); nuclear@0: nuclear@11: pos.x = x; nuclear@11: pos.y = y; nuclear@11: pos.z = z; nuclear@11: pos.w = w; nuclear@11: transform(&st.lpos[ltidx], &pos, st.matrix[MGL_MODELVIEW][st.mtop[MGL_MODELVIEW]]); nuclear@0: nuclear@11: if(fabs(st.lpos[ltidx].w) < 1e-6) { nuclear@11: NORMALIZE(st.lpos[ltidx]); nuclear@11: } else { nuclear@11: st.lpos[ltidx].x /= st.lpos[ltidx].w; nuclear@11: st.lpos[ltidx].y /= st.lpos[ltidx].w; nuclear@11: st.lpos[ltidx].z /= st.lpos[ltidx].w; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void mgl_begin(int prim) nuclear@0: { nuclear@0: st.prim = prim; nuclear@0: st.vidx = 0; nuclear@0: nuclear@0: st.ord = st.frontface; nuclear@0: if(st.cullface == MGL_FRONT) { nuclear@0: st.ord = st.frontface == MGL_CCW ? MGL_CW : MGL_CCW; nuclear@0: } nuclear@0: nuclear@0: /* select the correct rasterizer according to state */ nuclear@0: mgl_rast_prepare(); nuclear@0: } nuclear@0: nuclear@0: void mgl_end(void) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: void mgl_vertex2f(float x, float y) nuclear@0: { nuclear@0: mgl_vertex4f(x, y, 0.0f, 1.0f); nuclear@0: } nuclear@0: nuclear@0: void mgl_vertex3f(float x, float y, float z) nuclear@0: { nuclear@0: mgl_vertex4f(x, y, z, 1.0f); nuclear@0: } nuclear@0: nuclear@0: void mgl_vertex4f(float x, float y, float z, float w) nuclear@0: { nuclear@0: st.v[st.vidx].pos.x = x; nuclear@0: st.v[st.vidx].pos.y = y; nuclear@0: st.v[st.vidx].pos.z = z; nuclear@0: st.v[st.vidx].pos.w = w; nuclear@0: st.v[st.vidx].cidx = st.curv.cidx; nuclear@0: st.v[st.vidx].energy = st.curv.energy; nuclear@0: st.v[st.vidx].norm = st.curv.norm; nuclear@0: st.v[st.vidx].tc = st.curv.tc; nuclear@0: nuclear@28: /* T&L up to view space, to perform user-clipping */ nuclear@28: vertex_proc_view(st.v + st.vidx); nuclear@0: nuclear@0: if(++st.vidx >= st.prim) { nuclear@28: st.vidx = 0; nuclear@28: nuclear@0: switch(st.prim) { nuclear@0: case MGL_POINTS: nuclear@28: vertex_proc_proj(st.v); nuclear@0: mgl_draw_point(st.v); nuclear@0: break; nuclear@0: case MGL_LINES: nuclear@28: vertex_proc_proj(st.v); nuclear@28: vertex_proc_proj(st.v + 1); nuclear@0: mgl_draw_line(st.v, st.v + 1); nuclear@0: break; nuclear@0: case MGL_TRIANGLES: nuclear@0: case MGL_QUADS: nuclear@28: { nuclear@28: int nverts = mgl_clip_poly(st.v, st.prim); nuclear@28: if(nverts > 0) { nuclear@28: int i; nuclear@28: /* passed clipping, perform projection for all verts and draw */ nuclear@28: for(i=0; ix = mat[0] * v->x + mat[4] * v->y + mat[8] * v->z + mat[12] * v->w; nuclear@0: res->y = mat[1] * v->x + mat[5] * v->y + mat[9] * v->z + mat[13] * v->w; nuclear@0: res->z = mat[2] * v->x + mat[6] * v->y + mat[10] * v->z + mat[14] * v->w; nuclear@0: res->w = mat[3] * v->x + mat[7] * v->y + mat[11] * v->z + mat[15] * v->w; nuclear@0: } nuclear@0: nuclear@0: /* the matrix is 4x4 (16 floats), just ignoring anything out of the 3x3 */ nuclear@0: static void transform3(vec3_t *res, vec3_t *v, float *mat) nuclear@0: { nuclear@0: res->x = mat[0] * v->x + mat[4] * v->y + mat[8] * v->z; nuclear@0: res->y = mat[1] * v->x + mat[5] * v->y + mat[9] * v->z; nuclear@0: res->z = mat[2] * v->x + mat[6] * v->y + mat[10] * v->z; nuclear@0: } nuclear@0: nuclear@28: static void vertex_proc_view(struct vertex *vert) nuclear@0: { nuclear@28: vec4_t pview; nuclear@0: nuclear@0: float *mvmat = st.matrix[MGL_MODELVIEW][st.mtop[MGL_MODELVIEW]]; nuclear@0: nuclear@0: /* modelview transformation */ nuclear@0: transform(&pview, &vert->pos, mvmat); nuclear@0: nuclear@28: if(mgl_isenabled(MGL_LIGHTING)) { nuclear@28: if(mgl_isenabled(MGL_SMOOTH) || st.vidx == 0) { nuclear@0: int i; nuclear@0: vec3_t norm; nuclear@28: float irrad = st.ambient; nuclear@0: nuclear@0: transform3(&norm, &vert->norm, mvmat); nuclear@0: nuclear@0: for(i=0; i 1e-6f) { nuclear@11: float ndotl; nuclear@11: vec3_t ldir; nuclear@11: nuclear@11: if(st.lpos[i].w == 0.0) { nuclear@11: ldir.x = st.lpos[i].x; nuclear@11: ldir.y = st.lpos[i].y; nuclear@11: ldir.z = st.lpos[i].z; nuclear@11: } else { nuclear@11: ldir.x = st.lpos[i].x - pview.x; nuclear@11: ldir.y = st.lpos[i].y - pview.y; nuclear@11: ldir.z = st.lpos[i].z - pview.z; nuclear@11: nuclear@11: NORMALIZE(ldir); nuclear@11: } nuclear@11: nuclear@11: ndotl = DOT(norm, ldir); nuclear@0: if(ndotl < 0.0) { nuclear@0: ndotl = 0.0; nuclear@0: } nuclear@0: irrad += ndotl * st.lint[i]; nuclear@0: } nuclear@0: } nuclear@28: vert->energy = irrad > 1.0 ? 1.0 : irrad; nuclear@0: } else { nuclear@0: vert->energy = st.v[0].energy; nuclear@0: } nuclear@0: } nuclear@0: nuclear@28: vert->pos = pview; nuclear@28: } nuclear@28: nuclear@28: static int vertex_proc_proj(struct vertex *vert) nuclear@28: { nuclear@28: vec4_t pclip; nuclear@28: nuclear@28: float *pmat = st.matrix[MGL_PROJECTION][st.mtop[MGL_PROJECTION]]; nuclear@28: nuclear@28: transform(&pclip, &vert->pos, pmat); nuclear@0: /* TODO clipping in homogenous clip space */ nuclear@0: nuclear@28: if(pclip.w < 1e-6) { nuclear@0: vert->pos.x = vert->pos.y = vert->pos.z = vert->pos.w = 0.0f; nuclear@28: return -1; nuclear@0: } nuclear@0: nuclear@0: /* perspective division */ nuclear@0: vert->pos.x = pclip.x / pclip.w; nuclear@0: vert->pos.y = pclip.y / pclip.w; nuclear@0: vert->pos.z = pclip.z / pclip.w; nuclear@0: vert->pos.w = pclip.w; nuclear@0: nuclear@0: /* viewport transformation */ nuclear@0: vert->pos.x = st.vp[0] + st.vp[2] * (vert->pos.x * 0.5 + 0.5); nuclear@0: vert->pos.y = st.vp[1] + st.vp[3] * (-vert->pos.y * 0.5 + 0.5); nuclear@28: nuclear@28: return 0; nuclear@0: } nuclear@0: nuclear@0: void mgl_viewport(int x, int y, int width, int height) nuclear@0: { nuclear@0: st.vp[0] = x; nuclear@0: st.vp[1] = y; nuclear@0: st.vp[2] = width; nuclear@0: st.vp[3] = height; nuclear@0: } nuclear@0: nuclear@0: void mgl_matrix_mode(int mmode) nuclear@0: { nuclear@0: st.mmode = mmode; nuclear@0: } nuclear@0: nuclear@0: void mgl_push_matrix(void) nuclear@0: { nuclear@0: float *topmat; nuclear@0: if(st.mtop[st.mmode] >= MATRIX_STACK_SIZE - 1) { nuclear@0: fprintf(stderr, "mgl_push_matrix: stack overflow\n"); nuclear@0: abort(); nuclear@0: } nuclear@0: nuclear@0: topmat = st.matrix[st.mmode][st.mtop[st.mmode]]; nuclear@0: memcpy(topmat + 16, topmat, 16 * sizeof *topmat); nuclear@0: st.mmode++; nuclear@0: } nuclear@0: nuclear@0: void mgl_pop_matrix(void) nuclear@0: { nuclear@0: if(st.mtop[st.mmode] <= 0) { nuclear@0: fprintf(stderr, "mgl_pop_matrix: stack underflow\n"); nuclear@0: abort(); nuclear@0: } nuclear@0: st.mtop[st.mmode]--; nuclear@0: } nuclear@0: nuclear@0: void mgl_load_matrix(float *mat) nuclear@0: { nuclear@0: float *dest = st.matrix[st.mmode][st.mtop[st.mmode]]; nuclear@0: memcpy(dest, mat, 16 * sizeof *dest); nuclear@0: } nuclear@0: nuclear@17: #define M(i,j) (((i) << 2) + (j)) nuclear@0: void mgl_mult_matrix(float *m2) nuclear@0: { nuclear@0: int i, j; nuclear@0: float m1[16]; nuclear@0: float *dest = st.matrix[st.mmode][st.mtop[st.mmode]]; nuclear@0: nuclear@0: memcpy(m1, dest, sizeof m1); nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: for(j=0; j<4; j++) { nuclear@0: dest[M(i,j)] = m1[M(0,j)] * m2[M(i,0)] + nuclear@0: m1[M(1,j)] * m2[M(i,1)] + nuclear@0: m1[M(2,j)] * m2[M(i,2)] + nuclear@0: m1[M(3,j)] * m2[M(i,3)]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void mgl_load_identity(void) nuclear@0: { nuclear@0: static float id[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: mgl_load_matrix((float*)id); nuclear@0: } nuclear@0: nuclear@0: void mgl_translate(float x, float y, float z) nuclear@0: { nuclear@0: float xform[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: xform[12] = x; nuclear@0: xform[13] = y; nuclear@0: xform[14] = z; nuclear@0: mgl_mult_matrix(xform); nuclear@0: } nuclear@0: nuclear@0: void mgl_rotate(float deg, float x, float y, float z) nuclear@0: { nuclear@0: float xform[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: nuclear@0: float angle = M_PI * deg / 180.0f; nuclear@0: float sina = sin(angle); nuclear@0: float cosa = cos(angle); nuclear@0: float one_minus_cosa = 1.0f - cosa; nuclear@0: float nxsq = x * x; nuclear@0: float nysq = y * y; nuclear@0: float nzsq = z * z; nuclear@0: nuclear@0: xform[0] = nxsq + (1.0f - nxsq) * cosa; nuclear@0: xform[4] = x * y * one_minus_cosa - z * sina; nuclear@0: xform[8] = x * z * one_minus_cosa + y * sina; nuclear@0: xform[1] = x * y * one_minus_cosa + z * sina; nuclear@0: xform[5] = nysq + (1.0 - nysq) * cosa; nuclear@0: xform[9] = y * z * one_minus_cosa - x * sina; nuclear@0: xform[2] = x * z * one_minus_cosa - y * sina; nuclear@0: xform[6] = y * z * one_minus_cosa + x * sina; nuclear@0: xform[10] = nzsq + (1.0 - nzsq) * cosa; nuclear@0: nuclear@0: mgl_mult_matrix(xform); nuclear@0: } nuclear@0: nuclear@0: void mgl_scale(float x, float y, float z) nuclear@0: { nuclear@0: float xform[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: xform[0] = x; nuclear@0: xform[5] = y; nuclear@0: xform[10] = z; nuclear@0: mgl_mult_matrix(xform); nuclear@0: } nuclear@0: nuclear@0: void gl_ortho(float left, float right, float bottom, float top, float nr, float fr) nuclear@0: { nuclear@0: float xform[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: nuclear@0: float dx = right - left; nuclear@0: float dy = top - bottom; nuclear@0: float dz = fr - nr; nuclear@0: nuclear@0: float tx = -(right + left) / dx; nuclear@0: float ty = -(top + bottom) / dy; nuclear@0: float tz = -(fr + nr) / dz; nuclear@0: nuclear@0: float sx = 2.0 / dx; nuclear@0: float sy = 2.0 / dy; nuclear@0: float sz = -2.0 / dz; nuclear@0: nuclear@0: xform[0] = sx; nuclear@0: xform[5] = sy; nuclear@0: xform[10] = sz; nuclear@0: xform[12] = tx; nuclear@0: xform[13] = ty; nuclear@0: xform[14] = tz; nuclear@0: nuclear@0: mgl_mult_matrix(xform); nuclear@0: } nuclear@0: nuclear@0: void mgl_frustum(float left, float right, float bottom, float top, float nr, float fr) nuclear@0: { nuclear@0: float xform[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; nuclear@0: nuclear@0: float dx = right - left; nuclear@0: float dy = top - bottom; nuclear@0: float dz = fr - nr; nuclear@0: nuclear@0: float a = (right + left) / dx; nuclear@0: float b = (top + bottom) / dy; nuclear@0: float c = -(fr + nr) / dz; nuclear@0: float d = -2.0 * fr * nr / dz; nuclear@0: nuclear@0: xform[0] = 2.0 * nr / dx; nuclear@0: xform[5] = 2.0 * nr / dy; nuclear@0: xform[8] = a; nuclear@0: xform[9] = b; nuclear@0: xform[10] = c; nuclear@0: xform[11] = -1.0f; nuclear@0: xform[14] = d; nuclear@0: nuclear@0: mgl_mult_matrix(xform); nuclear@0: } nuclear@0: nuclear@0: void mgl_perspective(float vfov, float aspect, float nr, float fr) nuclear@0: { nuclear@0: float vfov_rad = M_PI * vfov / 180.0; nuclear@0: float x = nr * tan(vfov_rad / 2.0); nuclear@0: mgl_frustum(-aspect * x, aspect * x, -x, x, nr, fr); nuclear@0: } nuclear@3: nuclear@3: void mgl_teximage(int width, int height, unsigned char *pixels) nuclear@3: { nuclear@3: st.tex.width = width; nuclear@3: st.tex.height = height; nuclear@3: st.tex.pixels = pixels; nuclear@3: nuclear@3: if(calc_shiftmask(width, &st.tex.xshift, &st.tex.xmask) == -1 || nuclear@3: calc_shiftmask(height, &st.tex.yshift, &st.tex.ymask) == -1) { nuclear@3: st.tex.pixels = 0; nuclear@3: } nuclear@3: } nuclear@3: nuclear@28: void mgl_clip_plane(int id, float nx, float ny, float nz, float dist) nuclear@28: { nuclear@28: id -= MGL_CLIP_PLANE0; nuclear@28: nuclear@28: if(id < 0 || id > MAX_CLIP_PLANES) { nuclear@28: return; nuclear@28: } nuclear@28: nuclear@28: st.clip_planes[id].normal.x = nx; nuclear@28: st.clip_planes[id].normal.y = ny; nuclear@28: st.clip_planes[id].normal.z = nz; nuclear@28: NORMALIZE(st.clip_planes[id].normal); nuclear@28: nuclear@28: st.clip_planes[id].pt.x = st.clip_planes[id].normal.x * dist; nuclear@28: st.clip_planes[id].pt.y = st.clip_planes[id].normal.y * dist; nuclear@28: st.clip_planes[id].pt.z = st.clip_planes[id].normal.z * dist; nuclear@28: nuclear@28: printf("set clip plane %d -> n[%f %f %f] p[%f %f %f]\n", id, nuclear@28: st.clip_planes[id].normal.x, st.clip_planes[id].normal.y, st.clip_planes[id].normal.z, nuclear@28: st.clip_planes[id].pt.x, st.clip_planes[id].pt.y, st.clip_planes[id].pt.z); nuclear@28: nuclear@28: } nuclear@28: nuclear@3: #define MAX_SHIFT 12 nuclear@3: static int calc_shiftmask(int val, int *shiftp, unsigned int *maskp) nuclear@3: { nuclear@3: int i; nuclear@3: nuclear@3: for(i=0; i> i) == 1) { nuclear@3: *shiftp = i; nuclear@17: *maskp = ~(UINT_MAX << i); nuclear@3: return 0; nuclear@3: } nuclear@3: } nuclear@3: return -1; nuclear@3: }