/* * The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * SphereDesigner v0.4 - creates textured spheres * by Vidar Madsen * * Status: Last updated 1999-09-11 * * Known issues: * - Might crash if you click OK or Cancel before first preview is rendered * - Phong might look weird with transparent textures * * Todo: * - Saving / Loading of presets needs an overhaul * - Antialiasing * - Global controls: Gamma, ++ * - Beautification of GUI * - Clean up messy source (lots of Glade remnants) * - (Probably more. ;-) */ #define PLUG_IN_NAME "SphereDesigner" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "libgimp/stdplugins-intl.h" #ifndef SRAND_FUNC #define SRAND_FUNC srand #endif #ifndef RAND_FUNC #define RAND_FUNC rand #endif #define PREVIEWSIZE 150 /* These must be adjusted as more functionality is added */ #define MAXOBJECT 5 #define MAXLIGHT 5 #define MAXTEXTURE 20 #define MAXTEXTUREPEROBJ 20 #define MAXNORMAL 20 #define MAXNORMALPEROBJ 20 #define MAXATMOS 1 #define MAXCOLPERGRADIENT 5 static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; enum { TRIANGLE, DISC, PLANE, SPHERE, CYLINDER, LIGHT }; enum { SOLID, CHECKER, MARBLE, LIZARD, IMAGE, PHONG, REFLECTION, REFRACTION, PERLIN, WOOD, TRANSPARENT, SPIRAL, SPOTS, SMOKE }; enum { PERSPECTIVE, ORTHOGONAL, FISHEYE }; enum { FOG }; /* World-flags */ #define SMARTAMBIENT 0x00000001 /* Object-flags */ #define NOSHADOW 0x00000001 /* Texture-flags */ #define GRADIENT 0x00000001 typedef struct { double x,y,z,w; } vector; typedef struct { short xsize, ysize; unsigned char *rgb; } image; typedef struct { short numcol; double pos[MAXCOLPERGRADIENT]; vector color[MAXCOLPERGRADIENT]; } gradient; typedef struct { int majtype; int type; unsigned long flags; vector color1, color2; gradient gradient; vector ambient, diffuse; double oscale; vector scale, translate, rotate; image image; vector reflection; vector refraction; vector transparent; double ior; vector phongcolor; double phongsize; double amount; double exp; vector turbulence; } texture; typedef struct { short type; double density; vector color; double turbulence; } atmos; typedef struct { short type; unsigned long flags; short numtexture; texture texture[MAXTEXTUREPEROBJ]; short numnormal; texture normal[MAXNORMALPEROBJ]; } common; typedef struct { common com; vector a,b,c; } triangle; typedef struct { common com; vector a; double b, r; } disc; typedef struct { common com; vector a; double r; } sphere; typedef struct { common com; vector a, b, c; } cylinder; typedef struct { common com; vector a; double b; } plane; typedef struct { common com; vector color; vector a; } light; typedef struct { vector v1, v2; short inside; double ior; } ray; typedef union { common com; triangle tri; disc disc; plane plane; sphere sphere; cylinder cylinder; } object; struct world_t { int numobj; object obj[MAXOBJECT]; int numlight; light light[MAXLIGHT]; int numtexture; texture texture[MAXTEXTURE]; unsigned long flags; short quality; double smartambient; short numatmos; atmos atmos[MAXATMOS]; }; struct camera_t { vector location, lookat, up, right; short type; double fov, tilt; }; int traceray(ray *r, vector *col, int level, double imp); GtkWidget *drawarea = NULL; unsigned char img[PREVIEWSIZE*PREVIEWSIZE*3]; int running = 0; sphere s; struct textures_t { int index; char *s; long n; }; struct textures_t textures[] = { {0,N_("Solid"), SOLID}, {1,N_("Checker"),CHECKER}, {2,N_("Marble"),MARBLE}, {3,N_("Lizard"),LIZARD}, {4,N_("Phong"),PHONG}, {5,N_("Noise"),PERLIN}, {6,N_("Wood"),WOOD}, {7,N_("Spiral"),SPIRAL}, {8,N_("Spots"),SPOTS}, {0,NULL,0} }; struct { int solid, phong, light; } settings = {1,1,1}; inline void vset(vector *v, double a, double b, double c); gint restartrender(void); void drawcolor1(GtkWidget *w); void drawcolor2(GtkWidget *w); void render(void); void realrender(GimpDrawable *drawable); void fileselect(int); #define COLORBUTTONWIDTH 30 #define COLORBUTTONHEIGHT 20 GtkWidget *texturelist = NULL; GtkObject *scalexscale,*scaleyscale,*scalezscale; GtkObject *rotxscale,*rotyscale,*rotzscale; GtkObject *posxscale,*posyscale,*poszscale; GtkObject *scalescale; GtkObject *turbulencescale; GtkObject *amountscale; GtkObject *expscale; GtkWidget *typemenu_menu; GtkWidget *texturemenu_menu; GtkWidget *typemenu; GtkWidget *texturemenu; #define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) #define B 256 static int p[B + B + 2]; static double g[B + B + 2][3]; static int start = 1; void init(void) { int i, j, k; double v[3], s; /* Create an array of random gradient vectors uniformly on the unit sphere */ SRAND_FUNC(1); /* Use static seed, to get reproducable results */ for (i = 0 ; i < B ; i++) { do { /* Choose uniformly in a cube */ for (j=0 ; j<3 ; j++) v[j] = (double)((RAND_FUNC() % (B + B)) - B) / B; s = DOT(v,v); } while (s > 1.0); /* If not in sphere try again */ s = sqrt(s); for (j = 0 ; j < 3 ; j++) /* Else normalize */ g[i][j] = v[j] / s; } /* Create a pseudorandom permutation of [1..B] */ for (i = 0 ; i < B ; i++) p[i] = i; for (i = B ; i > 0 ; i -= 2) { k = p[i]; p[i] = p[j = RAND_FUNC() % B]; p[j] = k; } /* Extend g and p arrays to allow for faster indexing */ for (i = 0 ; i < B + 2 ; i++) { p[B + i] = p[i]; for (j = 0 ; j < 3 ; j++) g[B + i][j] = g[i][j]; } } #define setup(i,b0,b1,r0,r1) \ t = vec[i] + 10000.; \ b0 = ((int)t) & (B-1); \ b1 = (b0+1) & (B-1); \ r0 = t - (int)t; \ r1 = r0 - 1.; double noise3(double *vec) { int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; double rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v; int i, j; if(start) { start = 0; init(); } setup(0, bx0,bx1, rx0,rx1); setup(1, by0,by1, ry0,ry1); setup(2, bz0,bz1, rz0,rz1); i = p[ bx0 ]; j = p[ bx1 ]; b00 = p[ i + by0 ]; b10 = p[ j + by0 ]; b01 = p[ i + by1 ]; b11 = p[ j + by1 ]; #define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] ) #define surve(t) ( t * t * (3. - 2. * t) ) #define lerp(t, a, b) ( a + t * (b - a) ) sx = surve(rx0); sy = surve(ry0); sz = surve(rz0); q = g[ b00 + bz0 ] ; u = at(rx0,ry0,rz0); q = g[ b10 + bz0 ] ; v = at(rx1,ry0,rz0); a = lerp(sx, u, v); q = g[ b01 + bz0 ] ; u = at(rx0,ry1,rz0); q = g[ b11 + bz0 ] ; v = at(rx1,ry1,rz0); b = lerp(sx, u, v); c = lerp(sy, a, b); /* interpolate in y at lo x */ q = g[ b00 + bz1 ] ; u = at(rx0,ry0,rz1); q = g[ b10 + bz1 ] ; v = at(rx1,ry0,rz1); a = lerp(sx, u, v); q = g[ b01 + bz1 ] ; u = at(rx0,ry1,rz1); q = g[ b11 + bz1 ] ; v = at(rx1,ry1,rz1); b = lerp(sx, u, v); d = lerp(sy, a, b); /* interpolate in y at hi x */ return 1.5 * lerp(sz, c, d); /* interpolate in z */ } double turbulence(double *point, double lofreq, double hifreq) { double noise3(), freq, t, p[3]; p[0] = point[0] + 123.456; p[1] = point[1] + 234.567; p[2] = point[2] + 345.678; t = 0; for (freq = lofreq ; freq < hifreq ; freq *= 2.) { t += noise3(p) / freq; p[0] *= 2.; p[1] *= 2.; p[2] *= 2.; } return t - 0.3; /* readjust to make mean value = 0.0 */ } struct camera_t camera; struct world_t world; inline void vcopy(vector *a, vector *b) { a->x = b->x; a->y = b->y; a->z = b->z; a->w = b->w; } inline void vcross(vector *r, vector *a, vector *b) { vector t; t.x = a->y * b->z - a->z * b->y; t.y = -(a->x * b->z - a->z * b->x); t.z = a->x * b->y - a->y * b->x; vcopy(r, &t); } inline double vdot(vector *a, vector *b) { double s; s = a->x * b->x; s += a->y * b->y; s += a->z * b->z; return s; } inline double vdist(vector *a, vector *b) { double x,y,z; x = a->x - b->x; y = a->y - b->y; z = a->z - b->z; return sqrt(x*x+y*y+z*z); } inline double vlen(vector *a) { double l; l = sqrt(a->x*a->x + a->y*a->y + a->z*a->z); return l; } inline void vnorm(vector *a, double v) { double d; d = sqrt(a->x*a->x + a->y*a->y + a->z*a->z); a->x *= v/d; a->y *= v/d; a->z *= v/d; } inline void vrotate(vector *axis, double ang, vector *vector) { double rad = ang / 180.0 * G_PI; double ax = vector->x; double ay = vector->y; double az = vector->z; double x = axis->x; double y = axis->y; double z = axis->z; double c = cos(rad); double s = sin(rad); double c1 = 1.0 - c; double xx = c1 * x * x; double yy = c1 * y * y; double zz = c1 * z * z; double xy = c1 * x * y; double xz = c1 * x * z; double yz = c1 * y * z; double sx = s * x; double sy = s * y; double sz = s * z; vector->x = (xx + c)*ax + (xy + sz)*ay + (xz - sy)*az; vector->y = (xy - sz)*ax + (yy + c)*ay + (yz + sx)*az; vector->z = (xz + sy)*ax + (yz - sx)*ay + (zz + c )*az; } inline void vset(vector *v, double a, double b, double c) { v->x = a; v->y = b; v->z = c; v->w = 1.0; } inline void vcset(vector *v, double a, double b, double c, double d) { v->x = a; v->y = b; v->z = c; v->w = d; } inline void vvrotate(vector *p, vector *rot) { vector axis; if(rot->x != 0.0) { vset(&axis, 1,0,0); vrotate(&axis, rot->x, p); } if(rot->y != 0.0) { vset(&axis, 0,1,0); vrotate(&axis, rot->y, p); } if(rot->z != 0.0) { vset(&axis, 0,0,1); vrotate(&axis, rot->z, p); } } inline void vsub(vector *a, vector *b) { a->x -= b->x; a->y -= b->y; a->z -= b->z; a->w -= b->w; } inline void vadd(vector *a, vector *b) { a->x += b->x; a->y += b->y; a->z += b->z; a->w += b->w; } inline void vneg(vector *a) { a->x = -a->x; a->y = -a->y; a->z = -a->z; a->w = -a->w; } inline void vmul(vector *v, double a) { v->x *= a; v->y *= a; v->z *= a; v->w *= a; } inline void vvmul(vector *a, vector *b) { a->x *= b->x; a->y *= b->y; a->z *= b->z; a->w *= b->w; } inline void vvdiv(vector *a, vector *b) { a->x /= b->x; a->y /= b->y; a->z /= b->z; } void vmix(vector *r, vector *a, vector *b, double v) { double i = 1.0 - v; r->x = a->x * v + b->x * i; r->y = a->y * v + b->y * i; r->z = a->z * v + b->z * i; r->w = a->w * v + b->w * i; } double vmax(vector *a) { double max = fabs(a->x); if(fabs(a->y) > max) max = fabs(a->y); if(fabs(a->z) > max) max = fabs(a->z); if(fabs(a->w) > max) max = fabs(a->w); return max; } void vavg(vector *a) { double s = (a->x + a->y + a->z) / 3.0; a->x = a->y = a->z = s; } void trianglenormal(vector *n, double *t, triangle *tri) { triangle tmp; vcopy(&tmp.b, &tri->b); vcopy(&tmp.c, &tri->c); vsub(&tmp.b, &tri->a); vsub(&tmp.c, &tri->a); vset(&tmp.a, 0,0,0); vcross(n, &tmp.b, &tmp.c); if(t) *t = vdot(&tmp.b, &tmp.c); } double checkdisc(ray *r, disc *disc) { vector p, *v = &disc->a; double t, d; double i,j,k; i = r->v2.x - r->v1.x; j = r->v2.y - r->v1.y; k = r->v2.z - r->v1.z; t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - disc->b) / (v->x * i + v->y * j + v->z * k); p.x = r->v1.x + i * t; p.y = r->v1.y + j * t; p.z = r->v1.z + k * t; d = vdist(&p, v); if(d > disc->r) t = 0.0; return t; } double checksphere(ray *r, sphere *sphere) { vector cendir, rdir; double dirproj, cdlensq; double linear, constant, rsq, quadratic, discriminant; double smallzero, solmin, solmax, tolerance = 0.001; vcopy(&rdir, &r->v2); vsub(&rdir, &r->v1); rsq = sphere->r * sphere->r; vcopy(&cendir, &r->v1); vsub(&cendir, &sphere->a); dirproj = vdot(&rdir, &cendir); cdlensq = vdot(&cendir, &cendir); if((cdlensq >= rsq) && (dirproj > 0.0)) return 0.0; linear = 2.0 * dirproj; constant = cdlensq - rsq; quadratic = vdot(&rdir, &rdir); smallzero = (constant / linear); if((smallzero < tolerance) && (smallzero > -tolerance)) { solmin = -linear / quadratic; if (solmin > tolerance) { return solmin; /* *hits = solmin; return 1; */ } else return 0.0; } discriminant = linear * linear - 4.0 * quadratic * constant; if(discriminant < 0.0) return 0.0; quadratic *= 2.0; discriminant = sqrt(discriminant); solmax = (-linear + discriminant) / (quadratic); solmin = (-linear - discriminant) / (quadratic); if (solmax < tolerance) return 0.0; if(solmin < tolerance) { return solmax; /* *hits = solmax; return 1; */ } else { return solmin; /* *hits++ = solmin; *hits = solmax; return 2; */ } } double checkcylinder(ray *r, cylinder *cylinder) { /* fixme */ return 0; } double checkplane(ray *r, plane *plane) { vector *v = &plane->a; double t; double i,j,k; i = r->v2.x - r->v1.x; j = r->v2.y - r->v1.y; k = r->v2.z - r->v1.z; t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - plane->b) / (v->x * i + v->y * j + v->z * k); return t; } double checktri(ray *r, triangle *tri) { vector ed1, ed2; vector tvec, pvec, qvec; double det, idet, t, u, v; vector *orig, dir; orig = &r->v1; memcpy(&dir, &r->v2, sizeof(vector)); vsub(&dir, orig); ed1.x = tri->c.x - tri->a.x; ed1.y = tri->c.y - tri->a.y; ed1.z = tri->c.z - tri->a.z; ed2.x = tri->b.x - tri->a.x; ed2.y = tri->b.y - tri->a.y; ed2.z = tri->b.z - tri->a.z; vcross(&pvec, &dir, &ed2); det = vdot(&ed1, &pvec); idet = 1.0 / det; tvec.x = orig->x; tvec.y = orig->y; tvec.z = orig->z; vsub(&tvec, &tri->a); u = vdot(&tvec, &pvec) * idet; if(u < 0.0) return 0; if(u > 1.0) return 0; vcross(&qvec, &tvec, &ed1); v = vdot(&dir, &qvec) * idet; if((v < 0.0) || (u+v > 1.0)) return 0; t = vdot(&ed2, &qvec) * idet; return t; } double turbulence(double *point, double lofreq, double hifreq); void transformpoint(vector *p, texture *t) { double point[3], f; if((t->rotate.x != 0.0) || (t->rotate.y != 0.0) || (t->rotate.z != 0.0)) vvrotate(p, &t->rotate); vvdiv(p, &t->scale); vsub(p, &t->translate); if((t->turbulence.x != 0.0) || (t->turbulence.y != 0.0) || (t->turbulence.z != 0.0)) { point[0] = p->x; point[1] = p->y; point[2] = p->z; f = turbulence(point,1,256); p->x += t->turbulence.x * f; p->y += t->turbulence.y * f; p->z += t->turbulence.z * f; } } void checker(vector *q, vector *col, texture *t) { int c = 0; vector p; vcopy(&p, q); transformpoint(&p, t); vmul(&p, 0.25); p.x += 0.00001; p.y += 0.00001; p.z += 0.00001; if(p.x < 0.0) p.x = 0.5 - p.x; if(p.y < 0.0) p.y = 0.5 - p.y; if(p.z < 0.0) p.z = 0.5 - p.z; if((p.x - (int)p.x) < 0.5) c ^= 1; if((p.y - (int)p.y) < 0.5) c ^= 1; if((p.z - (int)p.z) < 0.5) c ^= 1; if(c) { vcopy(col, &t->color1); } else { vcopy(col, &t->color2); } } void gradcolor(vector *col, gradient *t, double val) { int i; double d; vector tmpcol; if(val < 0.0) val = 0.0; if(val > 1.0) val = 1.0; for(i = 0; i < t->numcol; i++) { if(t->pos[i] == val) { vcopy(col, &t->color[i]); return; } if(t->pos[i] > val) { d = (val - t->pos[i-1]) / (t->pos[i] - t->pos[i-1]); vcopy(&tmpcol, &t->color[i]); vmul(&tmpcol, d); vcopy(col, &tmpcol); vcopy(&tmpcol, &t->color[i-1]); vmul(&tmpcol, 1.0 - d); vadd(col, &tmpcol); return; } } fprintf(stderr, "Error in gradient!\n"); vset(col, 0,1,0); } void marble(vector *q, vector *col, texture *t) { double f; vector p; vcopy(&p, q); transformpoint(&p, t); f = sin(p.x*4)/2 + 0.5; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void lizard(vector *q, vector *col, texture *t) { double f; vector p; vcopy(&p, q); transformpoint(&p, t); f = fabs(sin(p.x*4)); f += fabs(sin(p.y*4)); f += fabs(sin(p.z*4)); f /= 3.0; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void wood(vector *q, vector *col, texture *t) { double f; vector p; vcopy(&p, q); transformpoint(&p, t); f = fabs(p.x); f = f - (int)f; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void spiral(vector *q, vector *col, texture *t) { double f; vector p; vcopy(&p, q); transformpoint(&p, t); f = fabs(atan2(p.x, p.z)/G_PI/2 + p.y + 99999); f = f - (int)f; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void spots(vector *q, vector *col, texture *t) { double f; vector p, r; vcopy(&p, q); transformpoint(&p, t); p.x += 10000.0; p.y += 10000.0; p.z += 10000.0; vset(&r, (int)(p.x+0.5), (int)(p.y+0.5), (int)(p.z+0.5)); f = vdist(&p,&r); f = cos(f*G_PI); if(f < 0.0) f = 0.0; else if(f > 1.0) f = 1.0; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void perlin(vector *q, vector *col, texture *t) { double f, point[3]; vector p; vcopy(&p, q); transformpoint(&p, t); point[0] = p.x; point[1] = p.y; point[2] = p.z; f = turbulence(point,1,256)*0.3 + 0.5; f = pow(f, t->exp); if(t->flags & GRADIENT) gradcolor(col, &t->gradient, f); else vmix(col, &t->color1, &t->color2, f); } void imagepixel(vector *q, vector *col, texture *t) { vector p; int x,y; unsigned char *rgb; vcopy(&p, q); transformpoint(&p, t); x = (p.x * t->image.xsize); y = (p.y * t->image.ysize); x = (x % t->image.xsize + t->image.xsize) % t->image.xsize; y = (y % t->image.ysize + t->image.ysize) % t->image.ysize; rgb = &t->image.rgb[x*3 + (t->image.ysize-1-y)*t->image.xsize*3]; vset(col, rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0); } double frand(double v) { return (RAND_FUNC() / (double) G_MAXRAND - 0.5) * v; } int traceray(ray *r, vector *col, int level, double imp); void objcolor(vector *col, vector *p, common *obj) { int i; texture *t; vector tmpcol; vcset(col, 0,0,0,0); for(i = 0; i < obj->numtexture; i++) { t = &obj->texture[i]; if(world.quality < 1) { vadd(col, &t->color1); continue; } vset(&tmpcol, 0,0,0); switch(t->type) { case SOLID: vcopy(&tmpcol, &t->color1); break; case CHECKER: checker(p, &tmpcol, t); break; case MARBLE: marble(p, &tmpcol, t); break; case LIZARD: lizard(p, &tmpcol, t); break; case PERLIN: perlin(p, &tmpcol, t); break; case WOOD: wood(p, &tmpcol, t); break; case SPIRAL: spiral(p, &tmpcol, t); break; case SPOTS: spots(p, &tmpcol, t); break; case IMAGE: imagepixel(p, &tmpcol, t); break; case PHONG: case REFRACTION: case REFLECTION: case TRANSPARENT: case SMOKE: /* Silently ignore non-color textures */ continue; break; default: fprintf(stderr, "Warning: unknown texture %d\n", t->type); break; } vmul(&tmpcol, t->amount); vadd(col, &tmpcol); } if(!i) { fprintf(stderr, "Warning: object %p has no textures\n", obj); } } void objnormal(vector *res, common *obj, vector *p) { int i; switch(obj->type) { case TRIANGLE: trianglenormal(res, NULL, (triangle *)obj); break; case DISC: vcopy(res, &((disc *)obj)->a); break; case PLANE: vcopy(res, &((plane *)obj)->a); break; case SPHERE: vcopy(res, &((sphere *)obj)->a); vsub(res, p); break; case CYLINDER: vset(res, 1,1,1); /* fixme */ break; default: fprintf(stderr, "objnormal(): Unsupported object type!?\n"); exit(0); } vnorm(res, 1.0); for(i = 0; i < obj->numnormal; i++) { int k; vector tmpcol[6]; vector q[6], nres; texture *t = &obj->normal[i]; double nstep = 0.1; vset(&nres, 0,0,0); for(k = 0; k < 6; k++) { vcopy(&q[k], p); } q[0].x += nstep; q[1].x -= nstep; q[2].y += nstep; q[3].y -= nstep; q[4].z += nstep; q[5].z -= nstep; switch(t->type) { case MARBLE: for(k = 0; k < 6; k++) marble(&q[k], &tmpcol[k], t); break; case LIZARD: for(k = 0; k < 6; k++) lizard(&q[k], &tmpcol[k], t); break; case PERLIN: for(k = 0; k < 6; k++) perlin(&q[k], &tmpcol[k], t); break; case WOOD: for(k = 0; k < 6; k++) wood(&q[k], &tmpcol[k], t); break; case SPIRAL: for(k = 0; k < 6; k++) spiral(&q[k], &tmpcol[k], t); break; case SPOTS: for(k = 0; k < 6; k++) spots(&q[k], &tmpcol[k], t); break; case IMAGE: for(k = 0; k < 6; k++) imagepixel(&q[k], &tmpcol[k], t); break; case CHECKER: case SOLID: case PHONG: case REFRACTION: case REFLECTION: case TRANSPARENT: case SMOKE: continue; break; default: fprintf(stderr, "Warning: unknown texture %d\n", t->type); break; } nres.x = tmpcol[0].x - tmpcol[1].x; nres.y = tmpcol[2].x - tmpcol[3].x; nres.z = tmpcol[4].x - tmpcol[5].x; vadd(&nres, res); vnorm(&nres, 1.0); vmul(&nres, t->amount); vadd(res, &nres); vnorm(res, 1.0); } } /* Quality: 0 = Color only 1 = Textures 2 = Light + Normals 3 = Shadows 4 = Phong 5 = Reflection + Refraction */ void calclight(vector *col, vector *point, common *obj) { int i, j, o; ray r; double d, b, a; vector lcol; vector norm; vector pcol; vcset(col, 0,0,0,0); objcolor(&pcol, point, obj); a = pcol.w; if(world.quality < 2) { vcopy(col, &pcol); return; } for(i = 0; i < obj->numtexture; i++) { if(obj->texture[i].type == PHONG) continue; if(obj->texture[i].type == REFLECTION) continue; if(obj->texture[i].type == REFRACTION) continue; if(obj->texture[i].type == TRANSPARENT) continue; if(obj->texture[i].type == SMOKE) continue; vcopy(&lcol, &pcol); vvmul(&lcol, &obj->texture[i].ambient); vadd(col, &lcol); } objnormal(&norm, obj, point); vnorm(&norm, 1.0); r.inside = -1; r.ior = 1.0; for(i = 0; i < world.numlight; i++) { vcopy(&r.v1, point); vcopy(&r.v2, &world.light[i].a); vmix(&r.v1, &r.v1, &r.v2, 0.9999); d = vdist(&r.v1, &r.v2); vsub(&r.v1, &r.v2); vnorm(&r.v1, 1.0); b = vdot(&r.v1, &norm); if(b < 0.0) continue; for(j = 0; j < obj->numtexture; j++) { if(obj->texture[j].type == PHONG) continue; if(obj->texture[j].type == REFLECTION) continue; if(obj->texture[j].type == REFRACTION) continue; if(obj->texture[j].type == TRANSPARENT) continue; if(obj->texture[j].type == SMOKE) continue; vcopy(&lcol, &pcol); vvmul(&lcol, &world.light[i].color); vvmul(&lcol, &obj->texture[j].diffuse); vmul(&lcol, b); vadd(col, &lcol); } } col->w = a; } void calcphong(common *obj, ray *r2, vector *col) { int i, j, o; ray r; double d, b; vector lcol; vector norm; vector pcol; double ps; vcopy(&pcol, col); vcopy(&norm, &r2->v2); vsub(&norm, &r2->v1); vnorm(&norm, 1.0); r.inside = -1; r.ior = 1.0; for(i = 0; i < world.numlight; i++) { vcopy(&r.v1, &r2->v1); vcopy(&r.v2, &world.light[i].a); vmix(&r.v1, &r.v1, &r.v2, 0.9999); d = vdist(&r.v1, &r.v2); o = traceray(&r, NULL, -1, 1.0); if(o) { continue; } /* OK, light is visible */ vsub(&r.v1, &r.v2); vnorm(&r.v1, 1.0); b = -vdot(&r.v1, &norm); for(j = 0; j < obj->numtexture; j++) { if(obj->texture[j].type != PHONG) continue; ps = obj->texture[j].phongsize; if(b < (1-ps)) continue; ps = (b-(1-ps))/ps; vcopy(&lcol, &obj->texture[j].phongcolor); vvmul(&lcol, &world.light[i].color); vmul(&lcol, ps); vadd(col, &lcol); } } } int traceray(ray *r, vector *col, int level, double imp) { int i, b = -1; double t = -1.0, min = 0.0; int type = -1; common *obj, *bobj = NULL; int hits = 0; vector p; if((level == 0) || (imp < 0.005)) { vset(col, 0,1,0); return 0; } for(i = 0; i < world.numobj; i++) { obj = (common *)&world.obj[i]; switch(obj->type) { case TRIANGLE: t = checktri(r, (triangle *)&world.obj[i]); break; case DISC: t = checkdisc(r, (disc *)&world.obj[i]); break; case PLANE: t = checkplane(r, (plane *)&world.obj[i]); break; case SPHERE: t = checksphere(r, (sphere *)&world.obj[i]); break; case CYLINDER: t = checkcylinder(r, (cylinder *)&world.obj[i]); break; default: fprintf(stderr, "Illegal object!!\n"); exit(0); } if(t <= 0.0) continue; if(!(obj->flags & NOSHADOW) && (level == -1)) { return i+1; } hits++; if((!bobj) || (t < min)) { min = t; b = i; type = obj->type; bobj = obj; } } if(level == -1) return 0; if(bobj) { p.x = r->v1.x + (r->v2.x - r->v1.x) * min; p.y = r->v1.y + (r->v2.y - r->v1.y) * min; p.z = r->v1.z + (r->v2.z - r->v1.z) * min; calclight(col, &p, bobj); if(world.flags & SMARTAMBIENT) { double ambient = 0.3 * exp(-min/world.smartambient); vector lcol; objcolor(&lcol, &p, bobj); vmul(&lcol, ambient); vadd(col, &lcol); } for(i = 0; i < bobj->numtexture; i++) { if((world.quality >= 4) && ((bobj->texture[i].type == REFLECTION) || (bobj->texture[i].type == PHONG))) { vector refcol, norm, ocol; ray ref; objcolor(&ocol, &p, bobj); vcopy(&ref.v1, &p); vcopy(&ref.v2, &r->v1); ref.inside = r->inside; ref.ior = r->ior; vmix(&ref.v1, &ref.v1, &ref.v2, 0.9999); /* push it a tad */ vsub(&ref.v2, &p); objnormal(&norm, bobj, &p); vnorm(&norm, 1.0); vrotate(&norm, 180.0, &ref.v2); vmul(&norm, -0.0001); /* push it a tad */ vadd(&ref.v1, &norm); vnorm(&ref.v2, 1.0); vadd(&ref.v2, &p); if((world.quality >= 5) && (bobj->texture[i].type == REFLECTION)) { traceray(&ref, &refcol, level - 1, imp * vmax(&bobj->texture[i].reflection)); vvmul(&refcol, &bobj->texture[i].reflection); refcol.w = ocol.w; vadd(col, &refcol); } if(bobj->texture[i].type == PHONG) { vcset(&refcol,0,0,0,0); calcphong(bobj, &ref, &refcol); refcol.w = ocol.w; vadd(col, &refcol); } } if((world.quality >= 5) && (col->w < 1.0)) { vector refcol; ray ref; vcopy(&ref.v1, &p); vcopy(&ref.v2, &p); vsub(&ref.v2, &r->v1); vnorm(&ref.v2, 1.0); vadd(&ref.v2, &p); vmix(&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */ traceray(&ref, &refcol, level - 1, imp * (1.0 - col->w)); vmul(&refcol, (1.0 - col->w)); vadd(col, &refcol); } if((world.quality >= 5) && (bobj->texture[i].type == TRANSPARENT)) { vector refcol; ray ref; vcopy(&ref.v1, &p); vcopy(&ref.v2, &p); vsub(&ref.v2, &r->v1); vnorm(&ref.v2, 1.0); vadd(&ref.v2, &p); vmix(&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */ traceray(&ref, &refcol, level - 1, imp * vmax(&bobj->texture[i].transparent)); vvmul(&refcol, &bobj->texture[i].transparent); vadd(col, &refcol); } if((world.quality >= 5) && (bobj->texture[i].type == SMOKE)) { vector smcol, raydir, norm; double tran; ray ref; vcopy(&ref.v1, &p); vcopy(&ref.v2, &p); vsub(&ref.v2, &r->v1); vnorm(&ref.v2, 1.0); vadd(&ref.v2, &p); objnormal(&norm, bobj, &p); vcopy(&raydir, &r->v2); vsub(&raydir, &r->v1); vnorm(&raydir, 1.0); tran = vdot(&norm, &raydir); if(tran < 0.0) continue; tran *= tran; vcopy(&smcol, &bobj->texture[i].color1); vmul(&smcol, tran); vadd(col, &smcol); } if((world.quality >= 5) && (bobj->texture[i].type == REFRACTION)) { vector refcol, norm, tmpv; ray ref; double c1, c2, n1, n2, n; vcopy(&ref.v1, &p); vcopy(&ref.v2, &p); vsub(&ref.v2, &r->v1); vadd(&ref.v2, &r->v2); vmix(&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */ vsub(&ref.v2, &p); objnormal(&norm, bobj, &p); if(r->inside == b) { ref.inside = -1; ref.ior = 1.0; } else { ref.inside = b; ref.ior = bobj->texture[i].ior; } c1 = vdot(&norm, &ref.v2); if(ref.inside < 0) c1 = -c1; n1 = r->ior; /* IOR of current media */ n2 = ref.ior; /* IOR of new media */ n = n1 / n2; c2 = 1.0 - n*n * (1.0 - c1*c1); if(c2 < 0.0) { /* FIXME: Internal reflection should occur */ c2 = sqrt(-c2); } else { c2 = sqrt(c2); } vmul(&ref.v2, n); vcopy(&tmpv, &norm); vmul(&tmpv, n * c1 - c2); vadd(&ref.v2, &tmpv); vnorm(&ref.v2, 1.0); vadd(&ref.v2, &p); traceray(&ref, &refcol, level - 1, imp * vmax(&bobj->texture[i].refraction)); vvmul(&refcol, &bobj->texture[i].refraction); vadd(col, &refcol); } } } else { vcset(col, 0,0,0,0); min = 10000.0; vcset(&p,0,0,0,0); } for(i = 0; i < world.numatmos; i++) { vector tmpcol; if(world.atmos[i].type == FOG) { double v, pt[3]; pt[0] = p.x; pt[1] = p.y; pt[2] = p.z; if((v = world.atmos[i].turbulence) > 0.0) v = turbulence(pt,1,256) * world.atmos[i].turbulence; v = exp(-(min+v) / world.atmos[i].density); vmul(col, v); vcopy(&tmpcol, &world.atmos[i].color); vmul(&tmpcol, 1.0 - v); vadd(col, &tmpcol); } } return hits; } void setdefaults(texture *t) { memset(t, 0, sizeof(texture)); t->type = SOLID; vcset(&t->color1, 1,1,1,1); vcset(&t->color2, 0,0,0,1); vcset(&t->diffuse, 1,1,1,1); vcset(&t->ambient, 0,0,0,1); vset(&t->scale,1,1,1); vset(&t->rotate,0,0,0); vset(&t->translate,0,0,0); t->oscale = 1.0; t->amount = 1.0; t->exp = 1.0; } char *mklabel(texture *t) { struct textures_t *l; static char tmps[100]; if(t->majtype == 0) strcpy(tmps, _("Texture")); else if(t->majtype == 1) strcpy(tmps, _("Bumpmap")); else if(t->majtype == 2) strcpy(tmps, _("Light")); else strcpy(tmps, _("(unknown!?)")); if((t->majtype == 0) || (t->majtype == 1)) { strcat(tmps, " / "); l = textures; while(l->s) { if(t->type == l->n) { strcat(tmps, gettext(l->s)); break; } l++; } } return tmps; } GtkWidget *currentitem(GtkWidget *list) { GList *h; GtkWidget *tmpw; h = GTK_LIST(list)->selection; if(!h) return NULL; tmpw = h->data; return tmpw; } texture *currenttexture(void) { GtkWidget *tmpw; texture *t; tmpw = currentitem(texturelist); if(!tmpw) return NULL; t = gtk_object_get_data(GTK_OBJECT(tmpw), "texture"); return t; } void relabel(void) { GtkWidget *tmpw = currentitem(texturelist); texture *t = currenttexture(); if(!tmpw || !t) return; tmpw = GTK_BIN(tmpw)->child; gtk_label_set_text(GTK_LABEL(tmpw), mklabel(t)); } int noupdate = 0; void setvals(texture *t) { struct textures_t *l; if(!t) return; noupdate = 1; gtk_adjustment_set_value(GTK_ADJUSTMENT(amountscale), t->amount); gtk_adjustment_set_value(GTK_ADJUSTMENT(scalescale), t->oscale); gtk_adjustment_set_value(GTK_ADJUSTMENT(scalexscale), t->scale.x); gtk_adjustment_set_value(GTK_ADJUSTMENT(scaleyscale), t->scale.y); gtk_adjustment_set_value(GTK_ADJUSTMENT(scalezscale), t->scale.z); gtk_adjustment_set_value(GTK_ADJUSTMENT(rotxscale), t->rotate.x); gtk_adjustment_set_value(GTK_ADJUSTMENT(rotyscale), t->rotate.y); gtk_adjustment_set_value(GTK_ADJUSTMENT(rotzscale), t->rotate.z); gtk_adjustment_set_value(GTK_ADJUSTMENT(posxscale), t->translate.x); gtk_adjustment_set_value(GTK_ADJUSTMENT(posyscale), t->translate.y); gtk_adjustment_set_value(GTK_ADJUSTMENT(poszscale), t->translate.z); gtk_adjustment_set_value(GTK_ADJUSTMENT(turbulencescale), t->turbulence.x); gtk_adjustment_set_value(GTK_ADJUSTMENT(expscale), t->exp); drawcolor1(NULL); drawcolor2(NULL); l = textures; while(l->s) { if(l->n == t->type) { gtk_option_menu_set_history(GTK_OPTION_MENU(texturemenu), l->index); break; } l++; } gtk_option_menu_set_history(GTK_OPTION_MENU(typemenu), t->majtype); noupdate = 0; } void selectitem(GtkWidget *wg, GtkWidget *p) { setvals(currenttexture()); } void addtexture(void) { GtkWidget *item; int n = s.com.numtexture; if(n == MAXTEXTUREPEROBJ-1) return; setdefaults(&s.com.texture[n]); item = gtk_list_item_new_with_label(mklabel(&s.com.texture[n])); gtk_object_set_data (GTK_OBJECT(item), "texture", &s.com.texture[n]); gtk_container_add(GTK_CONTAINER(texturelist), item); gtk_widget_show(item); gtk_list_select_child(GTK_LIST(texturelist), item); s.com.numtexture++; restartrender(); } void duptexture(void) { GtkWidget *item; texture *t = currenttexture(); int n = s.com.numtexture; if(n == MAXTEXTUREPEROBJ-1) return; if(!t) return; memcpy(&s.com.texture[n], t, sizeof(texture)); item = gtk_list_item_new_with_label(mklabel(&s.com.texture[n])); gtk_object_set_data (GTK_OBJECT(item), "texture", &s.com.texture[n]); gtk_container_add(GTK_CONTAINER(texturelist), item); gtk_widget_show(item); gtk_list_select_child(GTK_LIST(texturelist), item); s.com.numtexture++; restartrender(); } void rebuildlist(void) { GtkWidget *item; int n; for(n = 0; n < s.com.numtexture; n++) { if(s.com.numtexture && (s.com.texture[n].majtype < 0)) { int i; for(i = n; i < s.com.numtexture-1; i++) memcpy(&s.com.texture[i],&s.com.texture[i+1],sizeof(texture)); s.com.numtexture--; n--; } } for(n = 0; n < s.com.numtexture; n++) { item = gtk_list_item_new_with_label(mklabel(&s.com.texture[n])); gtk_object_set_data (GTK_OBJECT(item), "texture", &s.com.texture[n]); gtk_container_add(GTK_CONTAINER(texturelist), item); gtk_widget_show(item); } restartrender(); } void sphere_reset(void) { s.com.numtexture = 3; setdefaults(&s.com.texture[0]); setdefaults(&s.com.texture[1]); setdefaults(&s.com.texture[2]); s.com.texture[1].majtype = 2; vset(&s.com.texture[1].color1, 1,1,1); vset(&s.com.texture[1].translate, -15,-15,-15); s.com.texture[2].majtype = 2; vset(&s.com.texture[2].color1, 0,0.4,0.4); vset(&s.com.texture[2].translate, 15,15,-15); gtk_list_clear_items(GTK_LIST(texturelist), 0, -1); rebuildlist(); restartrender(); } void deltexture(void) { texture *t; GtkWidget *tmpw; tmpw = currentitem(texturelist); if(!tmpw) return; t = currenttexture(); if(!t) return; t->majtype = -1; gtk_widget_destroy(tmpw); } void loadit(char *fn) { FILE *f; char line[1024]; int i; texture *t; s.com.numtexture = 0; f = fopen(fn, "rt"); while(!feof(f)) { if(!fgets(line, 1023, f)) break; i = s.com.numtexture; t = &s.com.texture[i]; setdefaults(t); sscanf(line, "%d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf\n", &t->majtype, &t->type, &t->color1.x,&t->color1.y,&t->color1.z,&t->color1.w, &t->color2.x,&t->color2.y,&t->color2.z,&t->color2.w, &t->oscale, &t->turbulence.x, &t->amount, &t->exp, &t->scale.x,&t->scale.y,&t->scale.z, &t->rotate.x,&t->rotate.y,&t->rotate.z, &t->translate.x,&t->translate.y,&t->translate.z); s.com.numtexture++; } fclose(f); } void loadpreset_ok(GtkWidget *w, GtkFileSelection *fs) { char *fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)); gtk_widget_hide(GTK_WIDGET(fs)); gtk_list_clear_items(GTK_LIST(texturelist), 0, -1); loadit(fn); rebuildlist(); restartrender(); } void saveit(char *fn) { int i; FILE *f; f = fopen(fn, "wt"); for(i = 0; i < s.com.numtexture; i++) { texture *t = &s.com.texture[i]; if(t->majtype < 0) continue; fprintf(f, "%d %d %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", t->majtype, t->type, t->color1.x,t->color1.y,t->color1.z,t->color1.w, t->color2.x,t->color2.y,t->color2.z,t->color2.w, t->oscale, t->turbulence.x, t->amount, t->exp, t->scale.x,t->scale.y,t->scale.z, t->rotate.x,t->rotate.y,t->rotate.z, t->translate.x,t->translate.y,t->translate.z); } fclose(f); } void savepreset_ok(GtkWidget *w, GtkFileSelection *fs) { char *fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)); gtk_widget_hide(GTK_WIDGET(fs)); saveit(fn); } void loadpreset(void) { fileselect(0); } void savepreset(void) { fileselect(1); } void fileselect(int action) { static GtkWidget *windows[2] = {NULL,NULL}; char *titles[] = { N_("Open file"), N_("Save file") }; void *handlers[] = { loadpreset_ok, savepreset_ok }; if(!windows[action]) { windows[action] = gtk_file_selection_new( gettext(titles[action])); gtk_window_set_position (GTK_WINDOW (windows[action]), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (windows[action]), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy), &windows[action]); gtk_signal_connect (GTK_OBJECT (windows[action]), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide), &windows[action]); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (windows[action])->ok_button), "clicked", GTK_SIGNAL_FUNC(handlers[action]), windows[action]); gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION (windows[action])->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (windows[action])); gimp_help_connect_help_accel (windows[action], gimp_standard_help_func, "filters/spheredesigner.html"); } gtk_widget_show (windows[action]); } void initworld(void) { int i; memset(&world, 0, sizeof(world)); s.com.type = SPHERE; s.a.x = s.a.y = s.a.z = 0.0; s.r = 4.0; memcpy(&world.obj[0], &s, sizeof(s)); world.numobj = 1; world.obj[0].com.numtexture = 0; world.obj[0].com.numnormal = 0; for(i = 0; i < s.com.numtexture; i++) { common *c = &s.com; common *d = &world.obj[0].com; texture *t = &c->texture[i]; if((t->amount <= 0.0) || (t->majtype < 0)) continue; if(t->majtype == 0) { /* Normal texture */ if(t->type == PHONG) { memcpy(&t->phongcolor, &t->color1, sizeof(t->color1)); t->phongsize = t->oscale / 25.0; } memcpy(&d->texture[d->numtexture],t,sizeof(texture)); vmul(&d->texture[d->numtexture].scale, d->texture[d->numtexture].oscale); d->numtexture++; } else if(t->majtype == 1) { /* Bumpmap */ memcpy(&d->normal[d->numnormal],t,sizeof(texture)); vmul(&d->normal[d->numnormal].scale, d->texture[d->numnormal].oscale); d->numnormal++; } else if(t->majtype == 2) { /* Lightsource */ light l; vcopy(&l.a, &t->translate); vcopy(&l.color, &t->color1); vmul(&l.color, t->amount); memcpy(&world.light[world.numlight], &l, sizeof(l)); world.numlight++; } } world.quality = 5; world.flags |= SMARTAMBIENT; world.smartambient = 40.0; } void drawit(gpointer data) { if(!drawarea) return; gdk_draw_rgb_image(drawarea->window, drawarea->style->white_gc, 0, 0, PREVIEWSIZE, PREVIEWSIZE, GDK_RGB_DITHER_MAX, data, PREVIEWSIZE*3); } static gint expose_event (GtkWidget *widget, GdkEventExpose *event) { drawit(img); return FALSE; } gint restartrender(void) { if(running) { running = 2; return 0; } render(); return 0; } void destroy_window(GtkWidget *widget, GtkWidget **window) { *window = NULL; } void selecttexture(GtkWidget *wg, gpointer data) { texture *t; int n = (long)data; if(noupdate) return; t = currenttexture(); if(!t) return; t->type = n; relabel(); restartrender(); } void selecttype(GtkWidget *wg, gpointer data) { texture *t; long n = (long)data; if(noupdate) return; t = currenttexture(); if(!t) return; t->majtype = n; relabel(); restartrender(); } void getscales(GtkWidget *wg, gpointer data) { double f; texture *t; if(noupdate) return; t = currenttexture(); if(!t) return; t->amount = GTK_ADJUSTMENT(amountscale)->value; t->exp = GTK_ADJUSTMENT(expscale)->value; f = GTK_ADJUSTMENT(turbulencescale)->value; vset(&t->turbulence, f,f,f); t->oscale = GTK_ADJUSTMENT(scalescale)->value; t->scale.x = GTK_ADJUSTMENT(scalexscale)->value; t->scale.y = GTK_ADJUSTMENT(scaleyscale)->value; t->scale.z = GTK_ADJUSTMENT(scalezscale)->value; t->rotate.x = GTK_ADJUSTMENT(rotxscale)->value; t->rotate.y = GTK_ADJUSTMENT(rotyscale)->value; t->rotate.z = GTK_ADJUSTMENT(rotzscale)->value; t->translate.x = GTK_ADJUSTMENT(posxscale)->value; t->translate.y = GTK_ADJUSTMENT(posyscale)->value; t->translate.z = GTK_ADJUSTMENT(poszscale)->value; } void mktexturemenu(GtkWidget *texturemenu_menu) { GtkWidget *item; struct textures_t *t; t = textures; while(t->s) { item = gtk_menu_item_new_with_label ( gettext(t->s)); gtk_widget_show(item); gtk_menu_append (GTK_MENU (texturemenu_menu), item); gtk_signal_connect (GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(selecttexture), (void *)t->n); t++; } } void drawcolor1(GtkWidget *w) { static GtkWidget *lastw = NULL; int x, y; guchar buf[COLORBUTTONWIDTH*3]; texture *t = currenttexture(); if(w) lastw = w; else w = lastw; if(!w) return; if(!t) return; for(x = 0; x < COLORBUTTONWIDTH; x++) { buf[x*3+0] = t->color1.x * 255.0; buf[x*3+1] = t->color1.y * 255.0; buf[x*3+2] = t->color1.z * 255.0; } for(y = 0; y < COLORBUTTONHEIGHT; y++) gtk_preview_draw_row (GTK_PREVIEW(w), buf, 0, y, COLORBUTTONWIDTH); gtk_widget_draw(w, NULL); } void drawcolor2(GtkWidget *w) { static GtkWidget *lastw = NULL; int x, y; guchar buf[COLORBUTTONWIDTH*3]; texture *t = currenttexture(); if(w) lastw = w; else w = lastw; if(!w) return; if(!t) return; for(x = 0; x < COLORBUTTONWIDTH; x++) { buf[x*3+0] = t->color2.x * 255.0; buf[x*3+1] = t->color2.y * 255.0; buf[x*3+2] = t->color2.z * 255.0; } for(y = 0; y < COLORBUTTONHEIGHT; y++) gtk_preview_draw_row (GTK_PREVIEW(w), buf, 0, y, COLORBUTTONWIDTH); gtk_widget_draw(w, NULL); } void selectcolor1_ok(GtkWidget *w, gpointer d) { texture *t = currenttexture(); GtkWidget *win = (GtkWidget *)d; gdouble tmpcol[4]; if(!t) return; gtk_color_selection_get_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (win)->colorsel), tmpcol); t->color1.x = tmpcol[0]; t->color1.y = tmpcol[1]; t->color1.z = tmpcol[2]; t->color1.w = tmpcol[3]; drawcolor1(NULL); gtk_widget_destroy(win); } void selectcolor2_ok(GtkWidget *w, gpointer d) { texture *t = currenttexture(); GtkWidget *win = (GtkWidget *)d; gdouble tmpcol[4]; if(!t) return; gtk_color_selection_get_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (win)->colorsel), tmpcol); t->color2.x = tmpcol[0]; t->color2.y = tmpcol[1]; t->color2.z = tmpcol[2]; t->color2.w = tmpcol[3]; drawcolor2(NULL); gtk_widget_destroy(win); } void selectcolor1(vector *col) { static GtkWidget *window = NULL; gdouble tmpcol[4]; texture *t = currenttexture(); if(!t) return; if(window) { gtk_widget_show(window); gdk_window_raise(window->window); return; } window = gtk_color_selection_dialog_new( _("Color Selection Dialog")); gtk_color_selection_set_opacity (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG (window)->colorsel), TRUE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); tmpcol[0] = t->color1.x; tmpcol[1] = t->color1.y; tmpcol[2] = t->color1.z; tmpcol[3] = t->color1.w; gtk_color_selection_set_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(window)->colorsel), tmpcol); gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(window)->ok_button), "clicked", GTK_SIGNAL_FUNC(selectcolor1_ok), window); gtk_signal_connect_object(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(window)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); gtk_widget_show (window); } void selectcolor2(vector *col) { static GtkWidget *window = NULL; gdouble tmpcol[4]; texture *t = currenttexture(); if(!t) return; if(window) { gtk_widget_show(window); gdk_window_raise(window->window); return; } window = gtk_color_selection_dialog_new( _("Color Selection Dialog")); gtk_color_selection_set_opacity (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG (window)->colorsel), TRUE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); tmpcol[0] = t->color2.x; tmpcol[1] = t->color2.y; tmpcol[2] = t->color2.z; tmpcol[3] = t->color2.w; gtk_color_selection_set_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(window)->colorsel), tmpcol); gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(window)->ok_button), "clicked", GTK_SIGNAL_FUNC(selectcolor2_ok), window); gtk_signal_connect_object(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(window)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); gtk_widget_show (window); } int do_run = 0; void sphere_ok(GtkWidget *widget, gpointer data) { running = -1; do_run = 1; gtk_widget_hide (GTK_WIDGET (data)); gtk_main_quit(); } void sphere_cancel(GtkWidget *widget, gpointer data) { gtk_widget_hide (GTK_WIDGET (data)); gtk_main_quit(); } GtkWidget* makewindow (void) { GtkWidget *window; GtkWidget *table1; GtkWidget *frame2; GtkWidget *frame3; GtkWidget *viewport2; GtkWidget *hbox1; GtkWidget *addbutton; GtkWidget *dupbutton; GtkWidget *delbutton; GtkWidget *loadbutton; GtkWidget *savebutton; GtkWidget *okbutton; GtkWidget *cancelbutton; GtkWidget *resetbutton; GtkWidget *frame4; GtkWidget *table2; GtkWidget *label2; GtkWidget *label3; GtkWidget *label4; GtkWidget *hbox3; GtkWidget *colorbutton1; GtkWidget *colorbutton2; GtkWidget *label5; GtkWidget *label6; GtkWidget *item; GtkWidget *label7; GtkWidget *label8; GtkWidget *updatebutton; GtkWidget *label1; GtkWidget *_scalescale; GtkWidget *_rotscale; GtkWidget *_turbulencescale; GtkWidget *_amountscale; GtkWidget *_expscale; GtkWidget *tmpw; window = gimp_dialog_new (_("Sphere Designer"), "spheredesigner", gimp_standard_help_func, "filters/spheredesigner.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("OK"), sphere_ok, NULL, NULL, &okbutton, TRUE, FALSE, _("Reset"), sphere_reset, NULL, NULL, &resetbutton, FALSE, FALSE, _("Cancel"), sphere_cancel, NULL, NULL, &cancelbutton, FALSE, TRUE, NULL); gtk_object_set_data (GTK_OBJECT (window), "window", window); gtk_object_set_data (GTK_OBJECT (window), "okbutton", okbutton); gtk_object_set_data (GTK_OBJECT (window), "cancelbutton", cancelbutton); gtk_object_set_data (GTK_OBJECT (window), "resetbutton", cancelbutton); table1 = gtk_table_new (3, 3, FALSE); gtk_object_set_data (GTK_OBJECT (window), "table1", table1); gtk_widget_show (table1); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (window)->vbox), table1); gtk_table_set_row_spacings (GTK_TABLE (table1), 4); gtk_table_set_col_spacings (GTK_TABLE (table1), 4); gtk_container_set_border_width (GTK_CONTAINER (table1), 6); frame2 = gtk_frame_new (_("Preview")); gtk_object_set_data (GTK_OBJECT (window), "frame2", frame2); gtk_widget_show (frame2); gtk_table_attach (GTK_TABLE (table1), frame2, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); drawarea = gtk_drawing_area_new (); gtk_object_set_data (GTK_OBJECT (window), "drawarea", drawarea); gtk_widget_show (drawarea); gtk_container_add (GTK_CONTAINER (frame2), drawarea); gtk_widget_set_usize (drawarea, PREVIEWSIZE, PREVIEWSIZE); gtk_signal_connect (GTK_OBJECT (drawarea), "expose_event", (GtkSignalFunc) expose_event, NULL); updatebutton = gtk_button_new_with_label ( _("Update")); gtk_object_set_data (GTK_OBJECT (window), "updatebutton", updatebutton); gtk_widget_show (updatebutton); gtk_table_attach (GTK_TABLE (table1), updatebutton, 0, 1, 1, 2, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT(updatebutton), "clicked", GTK_SIGNAL_FUNC(restartrender), NULL); frame3 = gtk_frame_new ( _("Textures")); gtk_object_set_data (GTK_OBJECT (window), "frame3", frame3); gtk_widget_show (frame3); gtk_table_attach (GTK_TABLE (table1), frame3, 1, 2, 0, 2, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); viewport2 = gtk_viewport_new (NULL, NULL); gtk_object_set_data (GTK_OBJECT (window), "viewport2", viewport2); gtk_widget_set_usize (viewport2, 150, -1); gtk_widget_show (viewport2); gtk_container_add (GTK_CONTAINER (frame3), viewport2); texturelist = gtk_list_new (); gtk_object_set_data (GTK_OBJECT (window), "texturelist", texturelist); gtk_widget_show (texturelist); gtk_container_add (GTK_CONTAINER (viewport2), texturelist); gtk_signal_connect (GTK_OBJECT(texturelist), "selection_changed", GTK_SIGNAL_FUNC(selectitem), texturelist); hbox1 = gtk_hbox_new (TRUE, 0); gtk_object_set_data (GTK_OBJECT (window), "hbox1", hbox1); gtk_widget_show (hbox1); gtk_table_attach (GTK_TABLE (table1), hbox1, 1, 2, 2, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); addbutton = gtk_button_new_with_label ( _("Add")); gtk_object_set_data (GTK_OBJECT (window), "addbutton", addbutton); gtk_widget_show (addbutton); gtk_box_pack_start (GTK_BOX (hbox1), addbutton, TRUE, TRUE, 0); gtk_signal_connect_object (GTK_OBJECT (addbutton), "clicked", GTK_SIGNAL_FUNC (addtexture), NULL); dupbutton = gtk_button_new_with_label ( _("Dup")); gtk_object_set_data (GTK_OBJECT (window), "dupbutton", dupbutton); gtk_widget_show (dupbutton); gtk_box_pack_start (GTK_BOX (hbox1), dupbutton, TRUE, TRUE, 0); gtk_signal_connect_object (GTK_OBJECT (dupbutton), "clicked", GTK_SIGNAL_FUNC (duptexture), NULL); delbutton = gtk_button_new_with_label ( _("Del")); gtk_object_set_data (GTK_OBJECT (window), "delbutton", delbutton); gtk_widget_show (delbutton); gtk_box_pack_start (GTK_BOX (hbox1), delbutton, TRUE, TRUE, 0); gtk_signal_connect_object (GTK_OBJECT (delbutton), "clicked", GTK_SIGNAL_FUNC (deltexture), NULL); hbox1 = gtk_hbox_new (TRUE, 0); gtk_object_set_data (GTK_OBJECT (window), "hbox1", hbox1); gtk_widget_show (hbox1); gtk_table_attach (GTK_TABLE (table1), hbox1, 0, 1, 2, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); loadbutton = gtk_button_new_with_label ( _("Load")); gtk_object_set_data (GTK_OBJECT (window), "loadbutton", loadbutton); gtk_widget_show (loadbutton); gtk_box_pack_start (GTK_BOX (hbox1), loadbutton, TRUE, TRUE, 0); gtk_signal_connect_object (GTK_OBJECT (loadbutton), "clicked", GTK_SIGNAL_FUNC (loadpreset), NULL); savebutton = gtk_button_new_with_label ( _("Save")); gtk_object_set_data (GTK_OBJECT (window), "savebutton", savebutton); gtk_widget_show (savebutton); gtk_box_pack_start (GTK_BOX (hbox1), savebutton, TRUE, TRUE, 0); gtk_signal_connect_object (GTK_OBJECT (savebutton), "clicked", GTK_SIGNAL_FUNC (savepreset), NULL); frame4 = gtk_frame_new ( _("Texture Properties")); gtk_object_set_data (GTK_OBJECT (window), "frame4", frame4); gtk_widget_show (frame4); gtk_table_attach (GTK_TABLE (table1), frame4, 2, 3, 0, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); table2 = gtk_table_new (6, 4, FALSE); gtk_object_set_data (GTK_OBJECT (window), "table2", table2); gtk_widget_show (table2); gtk_container_add (GTK_CONTAINER (frame4), table2); gtk_container_set_border_width (GTK_CONTAINER (table2), 5); gtk_table_set_col_spacings (GTK_TABLE (table2), 4); gtk_table_set_row_spacings (GTK_TABLE (table2), 2); label2 = gtk_label_new ( _("Type:")); gtk_object_set_data (GTK_OBJECT (window), "label2", label2); gtk_widget_show (label2); gtk_table_attach (GTK_TABLE (table2), label2, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5); label3 = gtk_label_new ( _("Texture:")); gtk_object_set_data (GTK_OBJECT (window), "label3", label3); gtk_widget_show (label3); gtk_table_attach (GTK_TABLE (table2), label3, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_misc_set_alignment (GTK_MISC (label3), 1.0, 0.5); label4 = gtk_label_new ( _("Colors:")); gtk_object_set_data (GTK_OBJECT (window), "label4", label4); gtk_widget_show (label4); gtk_table_attach (GTK_TABLE (table2), label4, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label4), 1.0, 0.5); hbox3 = gtk_hbox_new (FALSE, 0); gtk_object_set_data (GTK_OBJECT (window), "hbox3", hbox3); gtk_widget_show (hbox3); gtk_table_attach (GTK_TABLE (table2), hbox3, 1, 2, 2, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); colorbutton1 = gtk_button_new(); gtk_object_set_data (GTK_OBJECT (window), "colorbutton1", colorbutton1); gtk_widget_show (colorbutton1); gtk_box_pack_start (GTK_BOX (hbox3), colorbutton1, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (colorbutton1), "clicked", (GtkSignalFunc)selectcolor1, NULL); tmpw = gtk_preview_new(GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (tmpw), COLORBUTTONWIDTH, COLORBUTTONHEIGHT); gtk_container_add (GTK_CONTAINER (colorbutton1), tmpw); gtk_widget_show(tmpw); drawcolor1(tmpw); colorbutton2 = gtk_button_new(); gtk_object_set_data (GTK_OBJECT (window), "colorbutton2", colorbutton2); gtk_widget_show (colorbutton2); gtk_box_pack_start (GTK_BOX (hbox3), colorbutton2, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (colorbutton2), "clicked", (GtkSignalFunc)selectcolor2, NULL); tmpw = gtk_preview_new(GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (tmpw), COLORBUTTONWIDTH, COLORBUTTONHEIGHT); gtk_container_add (GTK_CONTAINER (colorbutton2), tmpw); gtk_widget_show(tmpw); drawcolor2(tmpw); label5 = gtk_label_new ( _("Scale:")); gtk_object_set_data (GTK_OBJECT (window), "label5", label5); gtk_widget_show (label5); gtk_table_attach (GTK_TABLE (table2), label5, 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label5), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (scalescale = gtk_adjustment_new (1.0, 0.0, 5.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_signal_connect(GTK_OBJECT(scalescale), "value_changed", (GtkSignalFunc)getscales, NULL); label5 = gtk_label_new ( _("Turbulence:")); gtk_object_set_data (GTK_OBJECT (window), "label5", label5); gtk_widget_show (label5); gtk_table_attach (GTK_TABLE (table2), label5, 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label5), 1.0, 1.0); _turbulencescale = gtk_hscale_new (GTK_ADJUSTMENT (turbulencescale = gtk_adjustment_new (0.0, 0.0, 5.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_turbulencescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_turbulencescale", _turbulencescale); gtk_widget_show (_turbulencescale); gtk_table_attach (GTK_TABLE (table2), _turbulencescale, 1, 2, 4, 5, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0); gtk_scale_set_digits (GTK_SCALE (_turbulencescale), 2); gtk_signal_connect(GTK_OBJECT(turbulencescale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Scale X:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (scalexscale = gtk_adjustment_new (1.0, 0.0, 5.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(scalexscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Scale Y:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (scaleyscale = gtk_adjustment_new (1.0, 0.0, 5.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 3, 4, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(scaleyscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Scale Z:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (scalezscale = gtk_adjustment_new (1.0, 0.0, 5.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 3, 4, 2, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(scalezscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Rotate X:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _rotscale = gtk_hscale_new (GTK_ADJUSTMENT (rotxscale = gtk_adjustment_new (1.0, 0.0, 360.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_rotscale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_rotscale", _rotscale); gtk_scale_set_digits (GTK_SCALE (_rotscale), 2); gtk_widget_show (_rotscale); gtk_table_attach (GTK_TABLE (table2), _rotscale, 3, 4, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(rotxscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Rotate Y:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 4, 5, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _rotscale = gtk_hscale_new (GTK_ADJUSTMENT (rotyscale = gtk_adjustment_new (1.0, 0.0, 360.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_rotscale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_rotscale", _rotscale); gtk_scale_set_digits (GTK_SCALE (_rotscale), 2); gtk_widget_show (_rotscale); gtk_table_attach (GTK_TABLE (table2), _rotscale, 3, 4, 4, 5, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(rotyscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Rotate Z:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 2, 3, 5, 6, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _rotscale = gtk_hscale_new (GTK_ADJUSTMENT (rotzscale = gtk_adjustment_new (1.0, 0.0, 360.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_rotscale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_rotscale", _rotscale); gtk_scale_set_digits (GTK_SCALE (_rotscale), 2); gtk_widget_show (_rotscale); gtk_table_attach (GTK_TABLE (table2), _rotscale, 3, 4, 5, 6, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(rotzscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Pos X:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 5, 6, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (posxscale = gtk_adjustment_new (0.0, -20.0, 20.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 6, 7, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(posxscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Pos Y:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 5, 6, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (posyscale = gtk_adjustment_new (1.0, -20.0, 20.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 6, 7, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(posyscale), "value_changed", (GtkSignalFunc)getscales, NULL); label6 = gtk_label_new ( _("Pos Z:")); gtk_object_set_data (GTK_OBJECT (window), "label6", label6); gtk_widget_show (label6); gtk_table_attach (GTK_TABLE (table2), label6, 5, 6, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 1.0, 1.0); _scalescale = gtk_hscale_new (GTK_ADJUSTMENT (poszscale = gtk_adjustment_new (1.0, -20.0, 20.1, 0.1, 0.1, 0.1))); gtk_widget_set_usize(_scalescale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_scalescale", _scalescale); gtk_scale_set_digits (GTK_SCALE (_scalescale), 2); gtk_widget_show (_scalescale); gtk_table_attach (GTK_TABLE (table2), _scalescale, 6, 7, 2, 3, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND, 0, 0); gtk_signal_connect(GTK_OBJECT(poszscale), "value_changed", (GtkSignalFunc)getscales, NULL); typemenu = gtk_option_menu_new (); gtk_object_set_data (GTK_OBJECT (window), "typemenu", typemenu); gtk_widget_show (typemenu); gtk_table_attach (GTK_TABLE (table2), typemenu, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_EXPAND, 0, 0); typemenu_menu = gtk_menu_new (); item = gtk_menu_item_new_with_label ( _("Texture")); gtk_widget_show (item); gtk_signal_connect (GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(selecttype), NULL); gtk_menu_append (GTK_MENU (typemenu_menu), item); item = gtk_menu_item_new_with_label ( _("Bump")); gtk_widget_show (item); gtk_signal_connect (GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(selecttype), (long *)1); gtk_menu_append (GTK_MENU (typemenu_menu), item); item = gtk_menu_item_new_with_label ( _("Light")); gtk_widget_show (item); gtk_signal_connect (GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(selecttype), (long *)2); gtk_menu_append (GTK_MENU (typemenu_menu), item); gtk_option_menu_set_menu (GTK_OPTION_MENU (typemenu), typemenu_menu); texturemenu = gtk_option_menu_new (); gtk_object_set_data (GTK_OBJECT (window), "texturemenu", texturemenu); gtk_widget_show (texturemenu); gtk_table_attach (GTK_TABLE (table2), texturemenu, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); texturemenu_menu = gtk_menu_new (); mktexturemenu (texturemenu_menu); gtk_option_menu_set_menu (GTK_OPTION_MENU (texturemenu), texturemenu_menu); label7 = gtk_label_new ( _("Amount:")); gtk_object_set_data (GTK_OBJECT (window), "label7", label7); gtk_widget_show (label7); gtk_table_attach (GTK_TABLE (table2), label7, 0, 1, 5, 6, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label7), 1.0, 1.0); _amountscale = gtk_hscale_new (GTK_ADJUSTMENT (amountscale = gtk_adjustment_new (1.0, 0, 1.01, .01, .01, .01))); gtk_widget_set_usize(_amountscale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_amountscale", _amountscale); gtk_widget_show (_amountscale); gtk_table_attach (GTK_TABLE (table2), _amountscale, 1, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_scale_set_digits (GTK_SCALE (_amountscale), 2); gtk_signal_connect(GTK_OBJECT(amountscale), "value_changed", (GtkSignalFunc)getscales, NULL); label8 = gtk_label_new ( _("Exp:")); gtk_object_set_data (GTK_OBJECT (window), "label8", label8); gtk_widget_show (label8); gtk_table_attach (GTK_TABLE (table2), label8, 0, 1, 6, 7, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_misc_set_alignment (GTK_MISC (label8), 1.0, 1.0); _expscale = gtk_hscale_new (GTK_ADJUSTMENT (expscale = gtk_adjustment_new (1.0, 0, 1.01, .01, .01, .01))); gtk_widget_set_usize(_expscale, 100, -1); gtk_object_set_data (GTK_OBJECT (window), "_expscale", _expscale); gtk_widget_show (_expscale); gtk_table_attach (GTK_TABLE (table2), _expscale, 1, 2, 6, 7, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, (GtkAttachOptions) GTK_EXPAND | GTK_FILL, 0, 0); gtk_scale_set_digits (GTK_SCALE (_expscale), 2); gtk_signal_connect(GTK_OBJECT(expscale), "value_changed", (GtkSignalFunc)getscales, NULL); label1 = gtk_label_new ( _("by Vidar Madsen\nSeptember 1999")); gtk_object_set_data (GTK_OBJECT (window), "label1", label1); gtk_widget_show (label1); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), label1, TRUE, TRUE, 0); gtk_widget_show (window); return window; } unsigned char pixelval(double v) { v += 0.5; if(v < 0.0) return 0; if(v > 255.0) return 255; return v; } void render(void) { int x, y, p; ray r; vector col; int hit; int tx, ty; guchar *dest_row; int bpp; r.v1.z = -10.0; r.v2.z = 0.0; running = 2; tx = PREVIEWSIZE; ty = PREVIEWSIZE; bpp = 3; while(running > 0) { if(running == 2) { running = 1; initworld(); } if(world.obj[0].com.numtexture == 0) break; for(y = 0; y < ty; y++) { dest_row = img + y*PREVIEWSIZE*3; for(x = 0; x < tx; x++) { int g, gridsize = 16; g = ((x/gridsize+y/gridsize)%2)*60+100; r.v1.x = r.v2.x = 8.5 * (x / (float)(tx-1) - 0.5); r.v1.y = r.v2.y = 8.5 * (y / (float)(ty-1) - 0.5); p = x*bpp; hit = traceray(&r, &col, 10, 1.0); if(col.w < 0.0) col.w = 0.0; else if(col.w > 1.0) col.w = 1.0; dest_row[p+0] = pixelval(255 * col.x) * col.w + g * (1.0 - col.w); dest_row[p+1] = pixelval(255 * col.y) * col.w + g * (1.0 - col.w); dest_row[p+2] = pixelval(255 * col.z) * col.w + g * (1.0 - col.w); if(running != 1) { break; } } drawit(img); while(gtk_events_pending()) gtk_main_iteration(); if(running != 1) { break; } } if(running == 1) break; if(running == -1) break; } running = 0; drawit(img); } void realrender(GimpDrawable *drawable) { int x, y, alpha; ray r; vector rcol; int tx, ty; gint x1, y1, x2, y2; guchar *dest; int bpp; GimpPixelRgn pr, dpr; guchar *buffer, *ibuffer; if(running > 0) return; /* Fixme: abort preview-render instead! */ initworld(); r.v1.z = -10.0; r.v2.z = 0.0; alpha = gimp_drawable_has_alpha(drawable->id); gimp_pixel_rgn_init(&pr, drawable, 0, 0, gimp_drawable_width(drawable->id), gimp_drawable_height(drawable->id), FALSE, FALSE); gimp_pixel_rgn_init(&dpr, drawable, 0, 0, gimp_drawable_width(drawable->id), gimp_drawable_height(drawable->id), TRUE, TRUE); gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); bpp = gimp_drawable_bpp(drawable->id); buffer = g_malloc((x2 - x1) * 4); ibuffer = g_malloc((x2 - x1) * 4); tx = x2 - x1; ty = y2 - y1; gimp_progress_init ( _("Rendering...")); for(y = 0; y < ty; y++) { dest = buffer; for(x = 0; x < tx; x++) { r.v1.x = r.v2.x = 8.1 * (x / (float)(tx-1) - 0.5); r.v1.y = r.v2.y = 8.1 * (y / (float)(ty-1) - 0.5); traceray(&r, &rcol, 10, 1.0); dest[0] = pixelval(255 * rcol.x); dest[1] = pixelval(255 * rcol.y); dest[2] = pixelval(255 * rcol.z); dest[3] = pixelval(255 * rcol.w); dest += 4; } gimp_pixel_rgn_get_row(&pr, ibuffer, x1, y1 + y, x2 - x1); for(x = 0; x < (x2-x1); x++) { int k, dx = x * 4, sx = x * bpp; float a = buffer[dx+3] / 255.0; for(k = 0; k < bpp; k++) { ibuffer[sx+k] = buffer[dx+k] * a + ibuffer[sx+k] * (1.0 - a); } } gimp_pixel_rgn_set_row(&dpr, ibuffer, x1, y1 + y, x2 - x1); gimp_progress_update((double)y / (double)ty); } g_free(buffer); g_free(ibuffer); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1)); } static void query (void) { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } }; static gint nargs = sizeof (args) / sizeof (args[0]); gimp_install_procedure ("plug_in_spheredesigner", "Renders textures spheres", "This plugin can be used to create textured and/or bumpmapped spheres, and uses a small lightweight raytracer to perform the task with good quality", "Vidar Madsen", "Vidar Madsen", "1999", N_("/Filters/Render/Sphere Designer..."), "RGB*, GRAY*", GIMP_PLUGIN, nargs, 0, args, NULL); } int sphere_main(GimpDrawable *drawable) { initworld (); gimp_ui_init ("spheredesigner", TRUE); memset (img, 0, PREVIEWSIZE*PREVIEWSIZE*3); makewindow (); if (!s.com.numtexture) sphere_reset (); else rebuildlist (); drawit (img); gtk_main (); gdk_flush (); return do_run; } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpDrawable *drawable; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; run_mode = param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; drawable = gimp_drawable_get (param[2].data.d_drawable); switch(run_mode) { case GIMP_RUN_INTERACTIVE: INIT_I18N_UI(); s.com.numtexture = 0; gimp_get_data(PLUG_IN_NAME, &s); if(!sphere_main(drawable)) { gimp_drawable_detach(drawable); return; } break; case GIMP_RUN_WITH_LAST_VALS: INIT_I18N(); s.com.numtexture = 0; gimp_get_data(PLUG_IN_NAME, &s); if(s.com.numtexture == 0) { gimp_drawable_detach(drawable); return; } break; case GIMP_RUN_NONINTERACTIVE: default: /* Not implementet yet... */ gimp_drawable_detach(drawable); return; } gimp_set_data(PLUG_IN_NAME, &s, sizeof(s)); realrender(drawable); gimp_displays_flush (); *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; gimp_drawable_detach (drawable); } MAIN ()