/* Author: Mauro Persano (mauro_persano@yahoo.com) 27 Nov 2005 * * Todo: * - support for 4-bit / packed 24-bit visuals. * * Revision history: * 06-Dec-2005: fixed stupid off-by-one buf; (preliminar) monochrome support. * 05-Dec-2005: more speed ups. * 27-Nov-2005: speed ups. * 21-Nov-2005: initial version */ #ifdef STANDALONE #define MODE_fzort #define PROGCLASS "Fzort" #define HACK_INIT init_fzort #define HACK_DRAW draw_fzort #define fzort_opts xlockmode_opts #define DEFAULTS "*delay: 0 \n" #define UNIFORM_COLORS #include "xlockmore.h" /* xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_fzort #include #include #include #include #include #include ModeSpecOpt fzort_opts = {0, (XrmOptionDescRec *)NULL, 0, (argtype *)NULL, (OptionStruct *)NULL }; #ifdef USE_MODULES ModStruct fzort_description = { "fzort", "init_fzort", "draw_fzort", "release_fzort", "refresh_fzort", "init_fzort", (char *)NULL, &fzort_opts, 10000, 1, 1, 1, 64, 1.0, "", "Shows a metallic-looking fzort.", 0, NULL }; #endif #define MESH_DENSITY 22 /* #define MESH_DENSITY 44 */ #define MAX_WIDTH 800 #define MAX_HEIGHT 800 #define TEXTURE_SIZE 256 /* use a power of 2 preferably */ extern int XShmGetEventBase(Display *); #define MAX_POLY_VTX 20 struct vector { float x, y, z; }; struct matrix { float m11, m12, m13, m14; float m21, m22, m23, m24; float m31, m32, m33, m34; }; struct vertex { struct vector p; struct vector normal; }; struct pvertex { struct vector p; int x_scr, y_scr; long u_txt, v_txt; }; struct polygon { int nvtx; int vtx_index[MAX_POLY_VTX]; struct vector normal; }; struct mesh { int npoly, nvtx; struct vertex *vtx; struct polygon *poly; }; struct box { int x_min, y_min, x_max, y_max; }; struct clip_limits { int x_min; int x_max; int y_min; int y_max; }; struct raster { int width; int height; int pitch; int bpp; void *bits; }; struct render_order { float z; unsigned zi; struct polygon *poly; }; typedef struct fzort_ctx { Bool initialized; int palette[256]; int using_shm; XShmSegmentInfo shm_info; XImage *image; struct clip_limits clip; struct raster raster; struct mesh *mesh; void *texture; /* render ctx */ float lx, ly, cx, cy; void(*fill_triangle_fn)(struct fzort_ctx *, struct pvertex *, struct pvertex *, struct pvertex *); struct pvertex *pvtx; struct render_order *order_in, *order_out; long start_ticks; struct box cur_box, prev_box; int first_frame; } fzort_ctx; static fzort_ctx *fzorts = (fzort_ctx *) NULL; static int shm_completion_event; static unsigned char texture8[TEXTURE_SIZE*TEXTURE_SIZE]; static int texture_initialized = False; /* ordered dithering matrix */ static unsigned int dither6[8][8] = { { 1, 59, 15, 55, 2, 56, 12, 52, }, { 33, 17, 47, 31, 34, 18, 44, 28, }, { 9, 49, 5, 63, 10, 50, 6, 60, }, { 41, 25, 37, 21, 42, 26, 38, 22, }, { 3, 57, 13, 53, 0, 58, 14, 54, }, { 35, 19, 45, 29, 32, 16, 46, 30, }, { 11, 51, 7, 61, 8, 48, 4, 62, }, { 43, 27, 39, 23, 40, 24, 36, 20 } }; /* * f i x p o i n t */ #if defined(__GNUC__) && defined(__i386__) #define FP_SHIFT 16 #else #define FP_SHIFT 10 #endif #define FP_SHIFT_COMPL (32 - FP_SHIFT) #define FP_MULTIPLIER (1L << FP_SHIFT) #define FP_ONE (1L << FP_SHIFT) #define FP_HALF (1L << (FP_SHIFT - 1)) #define INT_TO_FP(a) ((a) << FP_SHIFT) #define FP_TO_INT(f) ((f) >> FP_SHIFT) #define FLOAT_TO_FP(f) ((long)((f) * (float)FP_MULTIPLIER)) #define FP_TO_FLOAT(f) ((float)(f) / (float)FP_MULTIPLIER) #define FRAC_PART(f) ((f) & (FP_MULTIPLIER - 1)) #define FP_CEIL(f) (((f) + FP_MULTIPLIER - 1) & \ ~(FP_MULTIPLIER - 1)) #define FP_FLOOR(f) ((f) & ~(FP_MULTIPLIER - 1)) #define FP_ICEIL(f) (((f) + FP_ONE - 1) >> FP_SHIFT) #define FP_IFLOOR(f) ((f) >> FP_SHIFT) /* a / b */ #if defined(__GNUC__) && defined(__i386__) static inline long fp_div(long a, long b) { long c; asm volatile("movl %%eax,%%edx; shll %3,%%eax; sarl %4,%%edx; idivl %2" : "=a"(c) : "0"(a), "m"(b), "i"(FP_SHIFT), "i"(32 - FP_SHIFT) : "edx"); return c; } #else static inline long fp_div(long a, long b) { return (a << FP_SHIFT)/b; } #endif /* a * b */ #if defined(__GNUC__) && defined(__i386__) static inline long fp_mul(long a, long b) { long c; asm volatile("imull %2; shrdl %3,%%edx,%%eax" : "=a"(c) : "0"(a), "m"(b), "i"(FP_SHIFT) : "edx"); return c; } #else static inline long fp_mul(long a, long b) { return (a*b) >> FP_SHIFT; } #endif /* s + a * b */ #if defined(__GNUC__) && defined(__i386__) static inline long fp_mul_add(long s, long a, long b) { long c; asm volatile("imull %2; shrdl %3,%%edx,%%eax; addl %4,%%eax" : "=a"(c) : "0"(a), "m"(b), "i"(FP_SHIFT), "m"(s) : "edx"); return c; } #else static inline long fp_mul_add(long s, long a, long b) { return s + ((a*b) >> FP_SHIFT); } #endif /* * v e c t o r */ static inline void vec_copy(struct vector *r, struct vector *a) { memcpy(r, a, sizeof *r); } static inline void vec_set(struct vector *r, float x, float y, float z) { r->x = x; r->y = y; r->z = z; } static inline void vec_neg_copy(struct vector *r, struct vector *a) { r->x = -a->x; r->y = -a->y; r->z = -a->z; } static inline void vec_neg(struct vector *r) { vec_neg_copy(r, r); } static inline void vec_add(struct vector *r, struct vector *a, struct vector *b) { r->x = a->x + b->x; r->y = a->y + b->y; r->z = a->z + b->z; } static inline void vec_add_to(struct vector *a, struct vector *b) { a->x += b->x; a->y += b->y; a->z += b->z; } static inline void vec_sub(struct vector *r, struct vector *a, struct vector *b) { r->x = a->x - b->x; r->y = a->y - b->y; r->z = a->z - b->z; } static inline void vec_sub_from(struct vector *a, struct vector *b) { a->x -= b->x; a->y -= b->y; a->z -= b->z; } static inline float vec_dot_product(struct vector *a, struct vector *b) { return a->x*b->x + a->y*b->y + a->z*b->z; } static inline float vec_length_squared(struct vector *a) { return vec_dot_product(a, a); } static inline void vec_cross_product(struct vector *r, struct vector *a, struct vector *b) { r->x = a->y*b->z - a->z*b->y; r->y = a->z*b->x - a->x*b->z; r->z = a->x*b->y - a->y*b->x; } static inline void vec_scalar_mul(struct vector *a, float s) { a->x *= s; a->y *= s; a->z *= s; } static inline void vec_scalar_mul_copy(struct vector *r, struct vector *a, float s) { r->x = a->x*s; r->y = a->y*s; r->z = a->z*s; } static float vec_length(struct vector *a) { return sqrt(vec_length_squared(a)); } static void vec_normalize(struct vector *a) { float l; l = vec_length(a); if (l == 0.f) a->x = 1.f, a->y = 0.f, a->z = 0.f; else vec_scalar_mul(a, 1.f/l); } /* * m a t r i x */ static inline void mat_copy(struct matrix *r, struct matrix *m) { memcpy(r, m, sizeof *r); } static const struct matrix identity = { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f }; static void mat_make_identity(struct matrix *m) { memcpy(m, &identity, sizeof *m); } static void mat_make_rotation_around_x(struct matrix *m, float ang) { float c, s; c = cos(ang); s = sin(ang); mat_make_identity(m); m->m22 = c; m->m23 = -s; m->m32 = s; m->m33 = c; } static void mat_make_rotation_around_y(struct matrix *m, float ang) { float c, s; c = cos(ang); s = sin(ang); mat_make_identity(m); m->m11 = c; m->m13 = -s; m->m31 = s; m->m33 = c; } #if 0 static void mat_make_rotation_around_z(struct matrix *m, float ang) { float c, s; c = cos(ang); s = sin(ang); mat_make_identity(m); m->m11 = c; m->m12 = -s; m->m21 = s; m->m22 = c; } #endif static void mat_make_translation(struct matrix *m, float x, float y, float z) { mat_make_identity(m); m->m14 = x; m->m24 = y; m->m34 = z; } static void mat_mul_copy(struct matrix *r, struct matrix *a, struct matrix *b) { float l11, l12, l13, l14; float l21, l22, l23, l24; float l31, l32, l33, l34; l11 = a->m11*b->m11 + a->m12*b->m21 + a->m13*b->m31; l12 = a->m11*b->m12 + a->m12*b->m22 + a->m13*b->m32; l13 = a->m11*b->m13 + a->m12*b->m23 + a->m13*b->m33; l14 = a->m11*b->m14 + a->m12*b->m24 + a->m13*b->m34 + a->m14; l21 = a->m21*b->m11 + a->m22*b->m21 + a->m23*b->m31; l22 = a->m21*b->m12 + a->m22*b->m22 + a->m23*b->m32; l23 = a->m21*b->m13 + a->m22*b->m23 + a->m23*b->m33; l24 = a->m21*b->m14 + a->m22*b->m24 + a->m23*b->m34 + a->m24; l31 = a->m31*b->m11 + a->m32*b->m21 + a->m33*b->m31; l32 = a->m31*b->m12 + a->m32*b->m22 + a->m33*b->m32; l33 = a->m31*b->m13 + a->m32*b->m23 + a->m33*b->m33; l34 = a->m31*b->m14 + a->m32*b->m24 + a->m33*b->m34 + a->m34; r->m11 = l11; r->m12 = l12; r->m13 = l13; r->m14 = l14; r->m21 = l21; r->m22 = l22; r->m23 = l23; r->m24 = l24; r->m31 = l31; r->m32 = l32; r->m33 = l33; r->m34 = l34; } #if 0 static void mat_mul(struct matrix *a, struct matrix *b) { mat_mul_copy(a, a, b); } #endif static void mat_transform_copy(struct vector *r, struct matrix *m, struct vector *v) { float x, y, z; x = m->m11*v->x + m->m12*v->y + m->m13*v->z + m->m14; y = m->m21*v->x + m->m22*v->y + m->m23*v->z + m->m24; z = m->m31*v->x + m->m32*v->y + m->m33*v->z + m->m34; r->x = x; r->y = y; r->z = z; } static inline void mat_rotate_copy(struct vector *r, struct matrix *m, struct vector *v) { float x, y, z; x = m->m11*v->x + m->m12*v->y + m->m13*v->z; y = m->m21*v->x + m->m22*v->y + m->m23*v->z; z = m->m31*v->x + m->m32*v->y + m->m33*v->z; r->x = x; r->y = y; r->z = z; } static inline void mat_rotate(struct vector *v, struct matrix *m) { mat_rotate_copy(v, m, v); } /* * t r i a n g l e */ struct edge { struct pvertex *v0, *v1; int dx, dy; int du, dv; int sy; long sx; long dxdy; int lines; int adjy; }; static inline void init_edge(fzort_ctx *ctx, struct edge *edge, struct pvertex *v0, struct pvertex *v1) { float y_max; edge->v0 = v0; edge->v1 = v1; edge->dx = v1->x_scr - v0->x_scr; edge->dy = v1->y_scr - v0->y_scr; edge->du = v1->u_txt - v0->u_txt; edge->dv = v1->v_txt - v0->v_txt; edge->sy = v0->y_scr; if (edge->sy < ctx->clip.y_min) { edge->adjy = ctx->clip.y_min - edge->sy; edge->sy = ctx->clip.y_min; } else { edge->adjy = 0; } y_max = v1->y_scr; if (y_max > ctx->clip.y_max) y_max = ctx->clip.y_max; if (y_max <= edge->sy) { edge->lines = 0; } else { edge->lines = (int) (y_max - edge->sy); edge->dxdy = fp_div(edge->dx, edge->dy); edge->sx = INT_TO_FP(v0->x_scr) + edge->adjy*edge->dxdy; } } #define CONCAT(a, b) a ## b #define NAME fill_triangle_1bpp_lsb_to_msb #define PIXEL_TYPE unsigned char #define LSB_TO_MSB #define MONOCHROME #include "fz_filler.h" #define NAME fill_triangle_1bpp_msb_to_lsb #define PIXEL_TYPE unsigned char #define MONOCHROME #include "fz_filler.h" #define NAME fill_triangle_8bpp #define PIXEL_TYPE unsigned char #include "fz_filler.h" #define NAME fill_triangle_16bpp #define PIXEL_TYPE unsigned short #include "fz_filler.h" #define NAME fill_triangle_32bpp #define PIXEL_TYPE unsigned int #include "fz_filler.h" /* * r e n d e r */ static void radix_sort(struct render_order *out, struct render_order *in, int rshift, int n) { static int count[256], index[256]; int i, b; memset(count, 0, sizeof count); for (i = 0; i < n; i++) { b = (in[i].zi >> rshift) & 0xff; count[b]++; } index[0] = 0; for (i = 1; i < 256; i++) index[i] = index[i - 1] + count[i - 1]; for (i = 0; i < n; i++) { b = (in[i].zi >> rshift) & 0xff; out[index[b]] = in[i]; index[b]++; } } static void render_process_mesh(fzort_ctx *ctx, struct matrix *m) { struct polygon *pin, *pend; struct vertex *v, *vend; struct pvertex *pv; int i, j, npoly, x_scr_min, x_scr_max, y_scr_min, y_scr_max; /* vertices */ ctx->cur_box.x_min = ctx->cur_box.y_min = ctx->cur_box.x_max = ctx->cur_box.y_max = 0; x_scr_min = ctx->clip.x_max + 1; x_scr_max = ctx->clip.x_min - 1; y_scr_min = ctx->clip.y_max + 1; y_scr_max = ctx->clip.y_min - 1; vend = ctx->mesh->vtx + ctx->mesh->nvtx; pv = ctx->pvtx; for (v = ctx->mesh->vtx; v != vend; v++) { struct vector normal; float w; mat_transform_copy(&pv->p, m, &v->p); mat_rotate_copy(&normal, m, &v->normal); vec_normalize(&normal); pv->u_txt = FLOAT_TO_FP((TEXTURE_SIZE/2) + (TEXTURE_SIZE/2)*normal.y + 0.5); pv->v_txt = FLOAT_TO_FP((TEXTURE_SIZE/2) + (TEXTURE_SIZE/2)*normal.x + 0.5); w = ctx->lx/pv->p.z; pv->x_scr = (int) (ctx->cx + w*pv->p.x + 0.5); pv->y_scr = (int) (ctx->cy + w*pv->p.y + 0.5); if (pv->x_scr < x_scr_min) x_scr_min = pv->x_scr; if (pv->x_scr > x_scr_max) x_scr_max = pv->x_scr; if (pv->y_scr < y_scr_min) y_scr_min = pv->y_scr; if (pv->y_scr > y_scr_max) y_scr_max = pv->y_scr; pv++; } if (x_scr_min > ctx->clip.x_max) return; ctx->cur_box.x_min = x_scr_min < ctx->clip.x_min ? ctx->clip.x_min : x_scr_min; if (x_scr_max < ctx->clip.x_min) return; ctx->cur_box.x_max = x_scr_max > ctx->clip.x_max ? ctx->clip.x_max : x_scr_max; if (y_scr_min > ctx->clip.y_max) return; ctx->cur_box.y_min = y_scr_min < ctx->clip.y_min ? ctx->clip.y_min : y_scr_min; if (y_scr_max < ctx->clip.y_min) return; ctx->cur_box.y_max = y_scr_max > ctx->clip.y_max ? ctx->clip.y_max : y_scr_max; /* polygons */ npoly = 0; pend = ctx->mesh->poly + ctx->mesh->npoly; for (pin = ctx->mesh->poly; pin != pend; pin++) { int nclip_x_min, nclip_x_max, nclip_y_min, nclip_y_max; int *idx, *iend; nclip_x_min = nclip_x_max = nclip_y_min = nclip_y_max = 0; iend = &pin->vtx_index[pin->nvtx]; for (idx = &pin->vtx_index[0]; idx != iend; idx++) { pv = &ctx->pvtx[*idx]; nclip_x_min += (pv->x_scr < ctx->clip.x_min); nclip_x_max += (pv->x_scr > ctx->clip.x_max); nclip_y_min += (pv->x_scr < ctx->clip.y_min); nclip_y_max += (pv->y_scr > ctx->clip.y_max); } if (nclip_x_min != pin->nvtx && nclip_x_max != pin->nvtx && nclip_y_min != pin->nvtx && nclip_y_max != pin->nvtx) { struct pvertex *p0, *p1, *p2, *p3; int ok; p0 = &ctx->pvtx[pin->vtx_index[0]]; p1 = &ctx->pvtx[pin->vtx_index[1]]; p2 = &ctx->pvtx[pin->vtx_index[2]]; ok = (p2->x_scr - p0->x_scr)*(p1->y_scr - p0->y_scr) - (p1->x_scr - p0->x_scr)*(p2->y_scr - p0->y_scr) >= 0; if (pin->nvtx == 4) { p3 = &ctx->pvtx[pin->vtx_index[3]]; ok |= (p3->x_scr - p0->x_scr)*(p2->y_scr - p0->y_scr) - (p2->x_scr - p0->x_scr)*(p3->y_scr - p0->y_scr) >= 0; } if (ok) { ctx->order_in[npoly].poly = pin; ctx->order_in[npoly].z = p0->p.z; npoly++; } } } /* draw */ if (npoly) { float z_min, z_max, z_scale, z; struct polygon *poly; z_min = z_max = 0.f; for (i = 0; i < npoly; i++) { z = ctx->order_in[i].z; if (i == 0 || z < z_min) z_min = z; if (i == 0 || z > z_max) z_max = z; } z_scale = 255.f/(z_max - z_min); for (i = 0; i < npoly; i++) ctx->order_in[i].zi = (unsigned int) (z_scale*(ctx->order_in[i].z - z_min)); radix_sort(ctx->order_out, ctx->order_in, 0, npoly); for (i = npoly - 1; i >= 0; i--) { poly = ctx->order_out[i].poly; for (j = 1; j < poly->nvtx - 1; j++) { ctx->fill_triangle_fn(ctx, &ctx->pvtx[poly->vtx_index[0]], &ctx->pvtx[poly->vtx_index[j]], &ctx->pvtx[poly->vtx_index[j + 1]]); } } } } /* * m e s h */ static void mesh_free(struct mesh *m) { if (m->vtx != NULL) free(m->vtx); if (m->poly != NULL) free(m->poly); free(m); } static inline float radius_offset(float phi, float theta, float phase, float amp) { return (amp + 4.*amp*sin(3.*phase + theta))*sin(phase + 5.*phi) + (amp + 4.*amp*sin(2.*phase + 2.*phi))*sin(phase + 3.*theta); } static inline void calc_coord(struct vector *v, float radius, float phi, float theta, float phase, float amp) { float mr, r; /* coordinates */ r = radius + radius_offset(phi, theta, phase, amp); v->z = -r*cos(phi); mr = r*sin(phi); v->y = -mr*sin(theta); v->x = -mr*cos(theta); } static void calc_mesh_vertices(struct mesh *mesh, int density, float radius, float phase, float amp) { struct vertex *vtx; double phi, theta, dphi, dtheta; int u, v, a, b, c; struct vector v0, v1; float mr, r; float cos_theta, sin_theta, cos_phi, sin_phi; float cos_3theta, sin_3theta; float sin_phase, cos_phase; /*float sin_2phase, cos_2phase;*/ float sin_3phase, cos_3phase; float cos_dphi, sin_dphi, cos_dtheta, sin_dtheta, st, ct; float offs; float k0, k1; vtx = mesh->vtx; /* vertices */ calc_coord(&vtx->p, radius, 0., M_PI, phase, amp); vtx++; sin_phase = sin(phase); cos_phase = cos(phase); /*sin_2phase = 2.*sin_phase*cos_phase; cos_2phase = 1. - 2.*sin_phase*sin_phase;*/ sin_3phase = 3.*sin_phase - 4.*sin_phase*sin_phase*sin_phase; cos_3phase = 4.*cos_phase*cos_phase*cos_phase - 3.*cos_phase; dtheta = 2.*M_PI/density; dphi = M_PI/(density + 1); sin_dphi = sin(dphi); cos_dphi = cos(dphi); sin_dtheta = sin(dtheta); cos_dtheta = cos(dtheta); cos_phi = cos_dphi; sin_phi = sin_dphi; for (phi = dphi, u = 0; u < density; u++, phi += dphi) { /* XXX: work these out */ k0 = sin(phase + 5.*phi); k1 = amp + 4.*amp*sin(2.*phase + 2.*phi); cos_theta = 1.; sin_theta = 0.; for (theta = 0., v = 0; v < density; v++, theta += dtheta) { sin_3theta = 3*sin_theta - 4.*sin_theta*sin_theta*sin_theta; cos_3theta = 4*cos_theta*cos_theta*cos_theta - 3.*cos_theta; /* (amp + 4.*amp*sin(3.*phase + theta))* sin(phase + 5.*phi) + (amp + 4.*amp*sin(2.*phase + 2.*phi))* sin(phase + 3.*theta); */ offs = (amp + 4.*amp* (sin_3phase*cos_theta + sin_theta*cos_3phase))*k0 + k1*(sin_phase*cos_3theta + cos_phase*sin_3theta); r = radius + offs; vtx->p.z = -r*cos_phi; mr = r*sin_phi; vtx->p.y = -mr*sin_theta; vtx->p.x = -mr*cos_theta; vtx++; st = sin_theta*cos_dtheta + cos_theta*sin_dtheta; ct = cos_theta*cos_dtheta - sin_theta*sin_dtheta; sin_theta = st; cos_theta = ct; } st = sin_phi*cos_dphi + cos_phi*sin_dphi; ct = cos_phi*cos_dphi - sin_phi*sin_dphi; sin_phi = st; cos_phi = ct; } calc_coord(&vtx->p, radius, M_PI, M_PI, phase, amp); /* normals */ vec_set(&mesh->vtx[0].normal, 0., 0., -1.); vec_set(&mesh->vtx[1 + density*density].normal, 0., 0., 1.); for (u = 0; u < density; u++) { for (v = 0; v < density; v++) { a = u*density + v + 1; b = u*density + (v + 1)%density + 1; c = (u + 1)*density + 1; if (u < density - 1) c += v; vec_sub(&v0, &mesh->vtx[b].p, &mesh->vtx[a].p); vec_sub(&v1, &mesh->vtx[c].p, &mesh->vtx[a].p); vec_cross_product(&mesh->vtx[a].normal, &v0, &v1); } } } static void set_triangle(struct polygon *poly, int a, int b, int c) { poly->nvtx = 3; poly->vtx_index[0] = a; poly->vtx_index[1] = b; poly->vtx_index[2] = c; } static void set_quad(struct polygon *poly, int a, int b, int c, int d) { poly->nvtx = 4; poly->vtx_index[0] = a; poly->vtx_index[1] = b; poly->vtx_index[2] = c; poly->vtx_index[3] = d; } static struct mesh * make_sphere(int density) { int u, v, a, b, c, d; struct polygon *poly; struct mesh *mesh; if ((mesh = (struct mesh *) malloc(sizeof *mesh)) == NULL) { return NULL; } mesh->npoly = mesh->nvtx = 0; /* vertices */ mesh->nvtx = 2 + density*density; mesh->vtx = malloc(mesh->nvtx * sizeof *mesh->vtx); /* polygons */ mesh->npoly = (density + 1)*density; poly = mesh->poly = malloc(mesh->npoly * sizeof *mesh->poly); for (u = 0; u < density; u++) { for (v = 0; v < density; v++) { a = u*density + v + 1; b = u*density + (v + 1)%density + 1; if (u == 0) { set_triangle(poly++, a, 0, b); } else { c = (u - 1)*density + v + 1; d = (u - 1)*density + (v + 1)%density + 1; set_quad(poly++, a, c, d, b); } } } for (v = 0; v < density; v++) { a = density*density + 1; d = (density - 1)*density + ((v + 1) % density) + 1; c = (density - 1)*density + v + 1; set_triangle(poly++, a, c, d); } return mesh; } /* * t e x t u r e g e n e r a t i o n */ static int getpix(unsigned char *dest, int x, int y) { return dest[(y&(TEXTURE_SIZE-1))*TEXTURE_SIZE + (x&(TEXTURE_SIZE - 1))]; } static void putpix(unsigned char *dest, int x, int y, int v) { dest[(y&(TEXTURE_SIZE-1))*TEXTURE_SIZE + (x&(TEXTURE_SIZE-1))] = v; } static int randnum(int range) { return LRAND() % (2*(range + 1)) - range; } static int plasmaavg(unsigned char *dest, int x1, int y1, int x2, int y2) { int average, distance; average = (getpix(dest, x1, y1) + getpix(dest, x2, y2)) / 2; distance = abs(x1 - x2) + abs(y1 - y2); if (distance > 2) distance -= 2; return average + randnum(distance); } static void rplasma(unsigned char *dest, int left, int top, int right, int bottom) { int x, y; if (abs(top - bottom) < 2 && abs(right - left) < 2) return; x = left; y = (top + bottom) / 2; if (!getpix(dest, x, y)) putpix(dest, x, y, plasmaavg(dest, left, top, left, bottom)); x = right; if (!getpix(dest, x, y)) putpix(dest, x, y, plasmaavg(dest, right, top, right, bottom)); x = (left + right) / 2; y = top; if (!getpix(dest, x, y)) putpix(dest, x, y, plasmaavg(dest, left, top, right, top)); y = bottom; if (!getpix(dest, x, y)) putpix(dest, x, y, plasmaavg(dest, left, bottom, right, bottom)); y = (top + bottom) / 2; if (!getpix(dest, x, y)) putpix(dest, x, y, (plasmaavg(dest, x, top, x, bottom) + plasmaavg(dest, left, y, right, y)) / 2 + randnum(right - left)); rplasma(dest, left, top, x, y); rplasma(dest, x, top, right, y); rplasma(dest, x, y, right, bottom); rplasma(dest, left, y, x, bottom); } static void flare(unsigned char *dest) { int i, j, r; float a; unsigned s; struct vector n, l; vec_set(&l, -.4, .4, 1.); vec_normalize(&l); for (i = 0; i < TEXTURE_SIZE; i++) { for (j = 0; j < TEXTURE_SIZE; j++) { n.x = j - TEXTURE_SIZE/2; n.y = i - TEXTURE_SIZE/2; n.z = -TEXTURE_SIZE/2; vec_normalize(&n); a = vec_dot_product(&n, &l); a *= a; a *= a; a *= a; a *= a; a *= a; a *= a; r = (int) (a*255); s = (dest[i*TEXTURE_SIZE + j]>>1) + r; s = s > 255 ? 255 : s; dest[i*TEXTURE_SIZE + j] = s; } } } static void * init_texture(int *palette, int depth) { int i, j; unsigned int v, pixel_size; void *texture; switch (depth) { case 1: case 8: pixel_size = sizeof(unsigned char); break; case 16: pixel_size = sizeof(unsigned short); break; case 24: case 32: pixel_size = sizeof(unsigned int); break; default: return NULL; /* ugh */ } if ((texture = malloc(TEXTURE_SIZE*TEXTURE_SIZE*pixel_size)) == NULL) { return NULL; } if (!texture_initialized) { memset(texture8, 0, sizeof texture8); rplasma(texture8, 0, 0, TEXTURE_SIZE, TEXTURE_SIZE); flare(texture8); texture_initialized = True; } switch (depth) { case 1: memcpy(texture, texture8, TEXTURE_SIZE*TEXTURE_SIZE); break; case 8: for (i = 0; i < TEXTURE_SIZE; i++) { for (j = 0; j < TEXTURE_SIZE; j++) { v = texture8[i*TEXTURE_SIZE + j]; ((unsigned char *)texture)[i*TEXTURE_SIZE + j] = palette[v]; } } break; case 16: for (i = 0; i < TEXTURE_SIZE; i++) { for (j = 0; j < TEXTURE_SIZE; j++) { v = texture8[i*TEXTURE_SIZE + j]; ((unsigned short *)texture)[i*TEXTURE_SIZE + j] = palette[v]; } } break; case 24: case 32: for (i = 0; i < TEXTURE_SIZE; i++) { for (j = 0; j < TEXTURE_SIZE; j++) { v = texture8[i*TEXTURE_SIZE + j]; ((unsigned int *)texture)[i*TEXTURE_SIZE + j] = palette[v]; } } break; } return texture; } /* * x i m a g e */ static int caught_x_error; static int x_error_handler(Display *display, XErrorEvent *error) { caught_x_error = 1; return 0; } static XImage * create_xshmimage(ModeInfo *mi, XShmSegmentInfo *si, int width, int height) { XImage *img; XErrorHandler prev_handler; int depth, format; if (MI_DEPTH(mi) == 1 || MI_NPIXELS(mi) > 2) { depth = MI_DEPTH(mi); format = ZPixmap; } else { depth = 1; format = XYBitmap; } XSync(MI_DISPLAY(mi), False); prev_handler = XSetErrorHandler(x_error_handler); caught_x_error = 0; img = XShmCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi), depth, format, NULL, si, width, height); if (img == NULL || caught_x_error) goto failed_0; if ((si->shmid = shmget(IPC_PRIVATE, img->bytes_per_line*img->height, IPC_CREAT|0777)) == -1) goto failed_1; si->readOnly = False; img->data = si->shmaddr = shmat(si->shmid, 0, 0); caught_x_error = 0; if (XShmAttach(MI_DISPLAY(mi), si) == False || caught_x_error) goto failed_2; XSetErrorHandler(prev_handler); XSync(MI_DISPLAY(mi), False); shmctl(si->shmid, IPC_RMID, 0); return img; failed_2: shmdt(si->shmaddr); failed_1: XDestroyImage(img); XSync(MI_DISPLAY(mi), False); failed_0: XSetErrorHandler(prev_handler); return NULL; } static void destroy_xshmimage(ModeInfo *mi, XImage *img, XShmSegmentInfo *si) { XErrorHandler prev_handler; XSync(MI_DISPLAY(mi), False); prev_handler = XSetErrorHandler(x_error_handler); XShmDetach(MI_DISPLAY(mi), si); XSetErrorHandler(prev_handler); XDestroyImage(img); XSync(MI_DISPLAY(mi), False); shmdt(si->shmaddr); XSync(MI_DISPLAY(mi), False); } static XImage * create_ximage(ModeInfo *mi, int width, int height) { XImage *img; int depth, format; if (MI_DEPTH(mi) == 1 || MI_NPIXELS(mi) > 2) { depth = MI_DEPTH(mi); format = ZPixmap; } else { depth = 1; format = XYBitmap; } if ((img = XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi), depth, format, 0, 0, width, height, 8, 0)) == NULL) { return NULL; } if ((img->data = calloc(img->height, img->bytes_per_line)) == NULL) { XDestroyImage(img); XSync(MI_DISPLAY(mi), False); return NULL; } return img; } static int make_image(ModeInfo *mi, struct fzort_ctx *fz) { int img_width, img_height; img_width = MIN(MI_WIDTH(mi), MI_HEIGHT(mi)); if (img_width > MAX_WIDTH) img_width = MAX_WIDTH; img_height = img_width; if (XShmQueryExtension(MI_DISPLAY(mi))) { fz->image = create_xshmimage(mi, &fz->shm_info, img_width, img_height); fz->using_shm = fz->image != NULL; } if (fz->image == NULL) { if ((fz->image = create_ximage(mi, img_width, img_height)) == NULL) { return -1; } } return 0; } static void free_image(ModeInfo *mi, struct fzort_ctx *fz) { if (fz->using_shm) { destroy_xshmimage(mi, fz->image, &fz->shm_info); } else { free(fz->image->data); fz->image->data = NULL; XDestroyImage(fz->image); XSync(MI_DISPLAY(mi), False); } fz->image = NULL; } /* * f z o r t */ static void release_fzort_ctx(ModeInfo *mi, fzort_ctx *fz) { if (fz->initialized) { free(fz->pvtx); free(fz->order_in); free(fz->order_out); free(fz->texture); mesh_free(fz->mesh); free_image(mi, fz); fz->initialized = False; } } static void init_color_map(ModeInfo *mi, fzort_ctx *fz, float r, float g, float b, float kd, float ks, float n) { float red, green, blue, ang; unsigned ib, ig, ir; float ka, kb; int i; XColor xc; for (i = 0; i < 256; i++) { ang = (255 - i)*(M_PI/2.)/256.0; ka = kd * cos(ang); kb = ks * pow(cos(ang), n); red = r*ka + kb; green = g*ka + kb; blue = b*ka + kb; ib = (unsigned int) ((blue > 1.0) ? 255 : 255*blue); ig = (unsigned int) ((green > 1.0) ? 255 : 255*green); ir = (unsigned int) ((red > 1.0) ? 255 : 255*red); xc.red = ir<<8; xc.green = ig<<8; xc.blue = ib<<8; xc.flags = DoRed|DoGreen|DoBlue; XAllocColor(MI_DISPLAY(mi), MI_COLORMAP(mi), &xc); fz->palette[i] = xc.pixel; } } static void init_fzort_ctx(ModeInfo *mi, fzort_ctx *fz) { struct timeval tv; if (fz->initialized) { release_fzort_ctx(mi, fz); } /* 0. create image for double-buffering */ if (make_image(mi, fz) < 0) { goto failed_0; } fz->clip.x_min = 0; fz->clip.y_min = 0; fz->clip.x_max = fz->image->width - 1; fz->clip.y_max = fz->image->height - 1; fz->raster.width = fz->image->width; fz->raster.height = fz->image->height; fz->raster.pitch = fz->image->bytes_per_line; fz->raster.bits = fz->image->data; fz->raster.bpp = fz->image->bits_per_pixel; fz->lx = fz->raster.width; fz->ly = fz->raster.height; fz->cx = fz->raster.width/2; fz->cy = fz->raster.height/2; switch (fz->raster.bpp) { case 1: /* check if the leftmost pixel is represented by the least * significant bit or vice-versa */ fz->image->data[0] = 0; XPutPixel(fz->image, 0, 0, 1); if (fz->image->data[0] == 1) fz->fill_triangle_fn = fill_triangle_1bpp_lsb_to_msb; else fz->fill_triangle_fn = fill_triangle_1bpp_msb_to_lsb; break; case 8: fz->fill_triangle_fn = fill_triangle_8bpp; break; case 16: fz->fill_triangle_fn = fill_triangle_16bpp; break; case 24: case 32: fz->fill_triangle_fn = fill_triangle_32bpp; break; } /* 1. create mesh */ if ((fz->mesh = make_sphere(MESH_DENSITY)) == NULL) { goto failed_1; } /* 2. initialize palette */ if (fz->image->depth >= 8) { init_color_map(mi, fz, .2, .2, 1., .9, .5, 4.); } /* 3. create texture */ if ((fz->texture = init_texture(fz->palette, fz->image->bits_per_pixel)) == NULL) { goto failed_2; } /* 4. allocate memory for vertices and polygons */ if ((fz->pvtx = malloc(fz->mesh->nvtx * sizeof *fz->pvtx)) == NULL) { goto failed_3; } if ((fz->order_in = malloc(fz->mesh->npoly * sizeof *fz->order_in)) == NULL) { goto failed_4; } if ((fz->order_out = malloc(fz->mesh->npoly * sizeof *fz->order_out)) == NULL) { goto failed_5; } gettimeofday(&tv, NULL); fz->start_ticks = tv.tv_sec*1000 + tv.tv_usec/1000; fz->prev_box.x_min = 0; fz->prev_box.x_max = fz->clip.x_max; fz->prev_box.y_min = 0; fz->prev_box.y_max = fz->clip.y_max; fz->initialized = True; MI_CLEARWINDOW(mi); return; failed_5: free(fz->order_in); failed_4: free(fz->pvtx); failed_3: free(fz->texture); failed_2: mesh_free(fz->mesh); failed_1: free_image(mi, fz); failed_0: return; } static void render(fzort_ctx *fz, long ticks) { struct matrix t, v; float a, b/* ,c*/; struct matrix rmatrix, ry, rx; a = (float)ticks/2000.; calc_mesh_vertices(fz->mesh, MESH_DENSITY, 120., /* 4.*a, 12. + 5.*sin(2.*a)); */ 4.*a, 7. + 7.*sin(2.*a)); /* 0., 0.); */ b = 1.5*a; /*c = 2.2*a;*/ mat_make_rotation_around_y(&ry, 1.5*a); mat_make_rotation_around_x(&rx, 2.2*a); mat_mul_copy(&rmatrix, &ry, &rx); mat_make_translation(&t, 220.*sin(b), 220.*cos(a), 900. + 80.*cos(b)*sin(a)); mat_mul_copy(&v, &t, &rmatrix); render_process_mesh(fz, &v); } /* * P u b l i c i n t e r f a c e */ void init_fzort(ModeInfo *mi) { fzort_ctx *fz; if (fzorts == NULL) { if ((fzorts = (fzort_ctx *) calloc(MI_NUM_SCREENS(mi), sizeof (fzort_ctx))) == NULL) return; } fz = &fzorts[MI_SCREEN(mi)]; init_fzort_ctx(mi, fz); shm_completion_event = XShmGetEventBase(MI_DISPLAY(mi)) + ShmCompletion; } void draw_fzort(ModeInfo *mi) { fzort_ctx *fz; int x0, y0, src_x_min, src_y_min, src_x_max, src_y_max; int lines, bytes_per_pixel, bytes_per_block; char *ptr; long cur_ticks; struct timeval tv; if (fzorts == NULL) return; fz = &fzorts[MI_SCREEN(mi)]; #if 0 printf("Dies after here on Solaris/gcc\n"); if (!fz->initialized) return; printf("Dies before here on Solaris/gcc\n"); #else if (!fz->initialized) return; #endif /* clear what was used by previous iteration */ if (fz->image->bits_per_pixel < 8) { /* monochrome pixmaps use so little memory that it's probably cheaper * to do a single memset */ memset(fz->image->data, 0, fz->image->height*fz->image->bytes_per_line); } else { bytes_per_pixel = fz->image->bits_per_pixel/8; lines = fz->prev_box.y_max - fz->prev_box.y_min + 1; ptr = fz->image->data + fz->prev_box.y_min*fz->image->bytes_per_line + fz->prev_box.x_min*bytes_per_pixel; bytes_per_block= (fz->prev_box.x_max - fz->prev_box.x_min + 1)* bytes_per_pixel; while (lines--) { memset(ptr, 0, bytes_per_block); ptr += fz->image->bytes_per_line; } } gettimeofday(&tv, NULL); cur_ticks = tv.tv_sec*1000 + tv.tv_usec/1000; /* render */ render(fz, cur_ticks - fz->start_ticks); /* display image */ x0 = (MI_WIDTH(mi) - fz->image->width)/2; y0 = (MI_HEIGHT(mi) - fz->image->height)/2; if (fz->image->width <= 200) { src_x_min = src_y_min = 0; src_x_max = fz->image->width - 1; src_y_max = fz->image->height - 1; } else { src_x_min = MIN(fz->prev_box.x_min, fz->cur_box.x_min); src_y_min = MIN(fz->prev_box.y_min, fz->cur_box.y_min); src_x_max = MAX(fz->prev_box.x_max, fz->cur_box.x_max); src_y_max = MAX(fz->prev_box.y_max, fz->cur_box.y_max); } XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); XSetBackground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); if (fz->using_shm) { XEvent event; XShmPutImage(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), fz->image, src_x_min, src_y_min, x0 + src_x_min, y0 + src_y_min, src_x_max - src_x_min + 1, src_y_max - src_y_min + 1, 1); while (XCheckTypedEvent(MI_DISPLAY(mi), shm_completion_event, &event) == False) ; } else { XPutImage(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), fz->image, src_x_min, src_y_min, x0 + src_x_min, y0 + src_y_min, src_x_max - src_x_min + 1, src_y_max - src_y_min + 1); } fz->prev_box = fz->cur_box; } void release_fzort(ModeInfo *mi) { int i; if (fzorts != NULL) { for (i = 0; i < MI_NUM_SCREENS(mi); i++) { release_fzort_ctx(mi, &fzorts[i]); } } free(fzorts); fzorts = (fzort_ctx *) NULL; } void refresh_fzort(ModeInfo *mi) { MI_CLEARWINDOW(mi); } #endif /* MODE_fzort */