/* * GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 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. ;-) */ #include "config.h" #include #include #include #include #include #include "libgimp/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-spheredesigner" #define PLUG_IN_BINARY "sphere-designer" #define PLUG_IN_ROLE "gimp-sphere-designer" #define RESPONSE_RESET 1 #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 (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); const 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 }; enum { TYPE, TEXTURE, NUM_COLUMNS }; /* World-flags */ #define SMARTAMBIENT 0x00000001 /* Object-flags */ #define NOSHADOW 0x00000001 /* Texture-flags */ #define GRADIENT 0x00000001 typedef struct { gshort xsize, ysize; guchar *rgb; } image; typedef struct { gshort numcol; gdouble pos[MAXCOLPERGRADIENT]; GimpVector4 color[MAXCOLPERGRADIENT]; } gradient; typedef struct { gint majtype; gint type; gulong flags; GimpVector4 color1, color2; gradient gradient; GimpVector4 ambient, diffuse; gdouble oscale; GimpVector4 scale, translate, rotate; image image; GimpVector4 reflection; GimpVector4 refraction; GimpVector4 transparent; gdouble ior; GimpVector4 phongcolor; gdouble phongsize; gdouble amount; gdouble exp; GimpVector4 turbulence; } texture; typedef struct { gshort type; gdouble density; GimpVector4 color; gdouble turbulence; } atmos; typedef struct { gshort type; gulong flags; gshort numtexture; texture texture[MAXTEXTUREPEROBJ]; gshort numnormal; texture normal[MAXNORMALPEROBJ]; } common; typedef struct { common com; GimpVector4 a, b, c; } triangle; typedef struct { common com; GimpVector4 a; gdouble b, r; } disc; typedef struct { common com; GimpVector4 a; gdouble r; } sphere; typedef struct { common com; GimpVector4 a, b, c; } cylinder; typedef struct { common com; GimpVector4 a; gdouble b; } plane; typedef struct { common com; GimpVector4 color; GimpVector4 a; } light; typedef struct { GimpVector4 v1, v2; gshort inside; gdouble ior; } ray; typedef union { common com; triangle tri; disc disc; plane plane; sphere sphere; cylinder cylinder; } object; struct world_t { gint numobj; object obj[MAXOBJECT]; gint numlight; light light[MAXLIGHT]; gint numtexture; texture texture[MAXTEXTURE]; gulong flags; gshort quality; gdouble smartambient; gshort numatmos; atmos atmos[MAXATMOS]; }; struct camera_t { GimpVector4 location, lookat, up, right; short type; double fov, tilt; }; static GtkWidget *drawarea = NULL; static guchar *img; static gint img_stride; static cairo_surface_t *buffer; static guint idle_id = 0; static sphere s; struct textures_t { gint index; gchar *s; glong n; }; static 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 } }; static inline void vset (GimpVector4 *v, gdouble a, gdouble b, gdouble c); static void restartrender (void); static void drawcolor1 (GtkWidget *widget); static void drawcolor2 (GtkWidget *widget); static gboolean render (void); static void realrender (gint32 drawable_ID); static void fileselect (GtkFileChooserAction action, GtkWidget *parent); static gint traceray (ray *r, GimpVector4 *col, gint level, gdouble imp); static gdouble turbulence (gdouble *point, gdouble lofreq, gdouble hifreq); #define COLORBUTTONWIDTH 30 #define COLORBUTTONHEIGHT 20 static GtkTreeView *texturelist = NULL; static GtkObject *scalexscale, *scaleyscale, *scalezscale; static GtkObject *rotxscale, *rotyscale, *rotzscale; static GtkObject *posxscale, *posyscale, *poszscale; static GtkObject *scalescale; static GtkObject *turbulencescale; static GtkObject *amountscale; static GtkObject *expscale; static GtkWidget *typemenu; static GtkWidget *texturemenu; #define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) #define B 256 static gint p[B + B + 2]; static gdouble g[B + B + 2][3]; static gboolean start = TRUE; static GRand *gr; static void init (void) { gint i, j, k; gdouble v[3], s; /* Create an array of random gradient vectors uniformly on the unit sphere */ gr = g_rand_new (); g_rand_set_seed (gr, 1); /* Use static seed, to get reproducible results */ for (i = 0; i < B; i++) { do { /* Choose uniformly in a cube */ for (j = 0; j < 3; j++) v[j] = g_rand_double_range (gr, -1, 1); 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 = g_rand_int_range (gr, 0, 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]; } g_rand_free (gr); } #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.; static gdouble noise3 (gdouble * vec) { gint bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; gdouble rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v; gint i, j; if (start) { start = FALSE; 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 */ } static double turbulence (gdouble * point, gdouble lofreq, gdouble hifreq) { gdouble 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 */ } static struct world_t world; static inline void vcopy (GimpVector4 *a, GimpVector4 *b) { *a = *b; } static inline void vcross (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b) { r->x = a->y * b->z - a->z * b->y; r->y = -(a->x * b->z - a->z * b->x); r->z = a->x * b->y - a->y * b->x; } static inline gdouble vdot (GimpVector4 *a, GimpVector4 *b) { return a->x * b->x + a->y * b->y + a->z * b->z; } static inline gdouble vdist (GimpVector4 *a, GimpVector4 *b) { gdouble 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); } static inline gdouble vdist2 (GimpVector4 *a, GimpVector4 *b) { gdouble x, y, z; x = a->x - b->x; y = a->y - b->y; z = a->z - b->z; return x * x + y * y + z * z; } static inline gdouble vlen (GimpVector4 *a) { return sqrt (a->x * a->x + a->y * a->y + a->z * a->z); } static inline void vnorm (GimpVector4 *a, gdouble v) { gdouble d; d = vlen (a); a->x *= v / d; a->y *= v / d; a->z *= v / d; } static inline void vrotate (GimpVector4 *axis, gdouble ang, GimpVector4 *vector) { gdouble rad = ang / 180.0 * G_PI; gdouble ax = vector->x; gdouble ay = vector->y; gdouble az = vector->z; gdouble x = axis->x; gdouble y = axis->y; gdouble z = axis->z; gdouble c = cos (rad); gdouble s = sin (rad); gdouble c1 = 1.0 - c; gdouble xx = c1 * x * x; gdouble yy = c1 * y * y; gdouble zz = c1 * z * z; gdouble xy = c1 * x * y; gdouble xz = c1 * x * z; gdouble yz = c1 * y * z; gdouble sx = s * x; gdouble sy = s * y; gdouble 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; } static inline void vset (GimpVector4 *v, gdouble a, gdouble b, gdouble c) { v->x = a; v->y = b; v->z = c; v->w = 1.0; } static inline void vcset (GimpVector4 *v, gdouble a, gdouble b, gdouble c, gdouble d) { v->x = a; v->y = b; v->z = c; v->w = d; } static inline void vvrotate (GimpVector4 *p, GimpVector4 *rot) { GimpVector4 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); } } static inline void vsub (GimpVector4 *a, GimpVector4 *b) { a->x -= b->x; a->y -= b->y; a->z -= b->z; a->w -= b->w; } static inline void vadd (GimpVector4 *a, GimpVector4 *b) { a->x += b->x; a->y += b->y; a->z += b->z; a->w += b->w; } static inline void vneg (GimpVector4 *a) { a->x = -a->x; a->y = -a->y; a->z = -a->z; a->w = -a->w; } static inline void vmul (GimpVector4 *v, gdouble a) { v->x *= a; v->y *= a; v->z *= a; v->w *= a; } static inline void vvmul (GimpVector4 *a, GimpVector4 *b) { a->x *= b->x; a->y *= b->y; a->z *= b->z; a->w *= b->w; } static inline void vvdiv (GimpVector4 *a, GimpVector4 *b) { a->x /= b->x; a->y /= b->y; a->z /= b->z; } static void vmix (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b, gdouble v) { gdouble 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; } static double vmax (GimpVector4 *a) { gdouble 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; } #if 0 static void vavg (GimpVector4 * a) { gdouble s; s = (a->x + a->y + a->z) / 3.0; a->x = a->y = a->z = s; } #endif static void trianglenormal (GimpVector4 * n, gdouble *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); } static gdouble checkdisc (ray * r, disc * disc) { GimpVector4 p, *v = &disc->a; gdouble t, d2; gdouble 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; d2 = vdist2 (&p, v); if (d2 > disc->r * disc->r) t = 0.0; return t; } static gdouble checksphere (ray * r, sphere * sphere) { GimpVector4 cendir, rdir; gdouble dirproj, cdlensq; gdouble linear, constant, rsq, quadratic, discriminant; gdouble 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; */ } } static gdouble checkcylinder (ray * r, cylinder * cylinder) { /* FIXME */ return 0.0; } static gdouble checkplane (ray * r, plane * plane) { GimpVector4 *v = &plane->a; gdouble t; gdouble 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; } static gdouble checktri (ray * r, triangle * tri) { GimpVector4 ed1, ed2; GimpVector4 tvec, pvec, qvec; gdouble det, idet, t, u, v; GimpVector4 *orig, dir; orig = &r->v1; dir = r->v2; 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; } static void transformpoint (GimpVector4 * p, texture * t) { gdouble 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; } } static void checker (GimpVector4 *q, GimpVector4 *col, texture *t) { gint c = 0; GimpVector4 p; 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 - (gint) p.x) < 0.5) c ^= 1; if ((p.y - (gint) p.y) < 0.5) c ^= 1; if ((p.z - (gint) p.z) < 0.5) c ^= 1; *col = (c) ? t->color1 : t->color2; } static void gradcolor (GimpVector4 *col, gradient *t, gdouble val) { gint i; gdouble d; GimpVector4 tmpcol; val = CLAMP (val, 0.0, 1.0); for (i = 0; i < t->numcol; i++) { if (t->pos[i] == val) { *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; } } g_printerr ("Error in gradient!\n"); vset (col, 0, 1, 0); } static void marble (GimpVector4 *q, GimpVector4 *col, texture *t) { gdouble f; GimpVector4 p; 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); } static void lizard (GimpVector4 *q, GimpVector4 *col, texture *t) { gdouble f; GimpVector4 p; 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); } static void wood (GimpVector4 *q, GimpVector4 *col, texture *t) { gdouble f; GimpVector4 p; 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); } static void spiral (GimpVector4 *q, GimpVector4 *col, texture *t) { gdouble f; GimpVector4 p; 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); } static void spots (GimpVector4 *q, GimpVector4 *col, texture *t) { gdouble f; GimpVector4 p, r; p = *q; transformpoint (&p, t); p.x += 10000.0; p.y += 10000.0; p.z += 10000.0; vset (&r, (gint) (p.x + 0.5), (gint) (p.y + 0.5), (gint) (p.z + 0.5)); f = vdist (&p, &r); f = cos (f * G_PI); f = CLAMP (f, 0.0, 1.0); f = pow (f, t->exp); if (t->flags & GRADIENT) gradcolor (col, &t->gradient, f); else vmix (col, &t->color1, &t->color2, f); } static void perlin (GimpVector4 * q, GimpVector4 * col, texture * t) { gdouble f, point[3]; GimpVector4 p; 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); } static void imagepixel (GimpVector4 * q, GimpVector4 * col, texture * t) { GimpVector4 p; gint x, y; guchar *rgb; 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); } static void objcolor (GimpVector4 *col, GimpVector4 *p, common *obj) { gint i; texture *t; GimpVector4 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: g_printerr ("Warning: unknown texture %d\n", t->type); break; } vmul (&tmpcol, t->amount); vadd (col, &tmpcol); } if (!i) { g_printerr ("Warning: object %p has no textures\n", obj); } } static void objnormal (GimpVector4 *res, common *obj, GimpVector4 *p) { gint 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: g_error ("objnormal(): Unsupported object type!?\n"); } vnorm (res, 1.0); for (i = 0; i < obj->numnormal; i++) { gint k; GimpVector4 tmpcol[6]; GimpVector4 q[6], nres; texture *t = &obj->normal[i]; gdouble 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: g_printerr ("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 */ static void calclight (GimpVector4 * col, GimpVector4 * point, common * obj) { gint i, j; ray r; gdouble b, a; GimpVector4 lcol; GimpVector4 norm; GimpVector4 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); 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; } static void calcphong (common * obj, ray * r2, GimpVector4 * col) { gint i, j; ray r; gdouble b; GimpVector4 lcol; GimpVector4 norm; GimpVector4 pcol; gdouble 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); if (traceray (&r, NULL, -1, 1.0)) 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); } } } static int traceray (ray * r, GimpVector4 * col, gint level, gdouble imp) { gint i, b = -1; gdouble t = -1.0, min = 0.0; common *obj, *bobj = NULL; gint hits = 0; GimpVector4 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: g_error ("Illegal object!!\n"); } if (t <= 0.0) continue; if (!(obj->flags & NOSHADOW) && (level == -1)) { return i + 1; } hits++; if ((!bobj) || (t < min)) { min = t; b = i; 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) { gdouble ambient = 0.3 * exp (-min / world.smartambient); GimpVector4 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))) { GimpVector4 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)) { GimpVector4 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)) { GimpVector4 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)) { GimpVector4 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)) { GimpVector4 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++) { GimpVector4 tmpcol; if (world.atmos[i].type == FOG) { gdouble 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; } static 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; } static gchar * mklabel (texture * t) { struct textures_t *l; static gchar 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, ""); 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; } static texture * currenttexture (void) { GtkTreeSelection *sel; GtkTreeIter iter; texture *t = NULL; sel = gtk_tree_view_get_selection (texturelist); if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { gtk_tree_model_get (gtk_tree_view_get_model (texturelist), &iter, TEXTURE, &t, -1); } return t; } static void relabel (void) { GtkTreeModel *model; GtkTreeSelection *sel; GtkTreeIter iter; texture *t = NULL; sel = gtk_tree_view_get_selection (texturelist); if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { model = gtk_tree_view_get_model (texturelist); gtk_tree_model_get (model, &iter, TEXTURE, &t, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, TYPE, mklabel (t), -1); } } static gboolean noupdate = FALSE; static void setvals (texture *t) { struct textures_t *l; if (!t) return; noupdate = TRUE; 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) { gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (texturemenu), l->index); break; } l++; } gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (typemenu), t->majtype); noupdate = FALSE; } static void selectitem (GtkTreeSelection *treeselection, gpointer data) { setvals (currenttexture ()); } static void addtexture (void) { GtkListStore *list_store; GtkTreeIter iter; gint n = s.com.numtexture; if (n == MAXTEXTUREPEROBJ - 1) return; setdefaults (&s.com.texture[n]); list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, TYPE, mklabel (&s.com.texture[n]), TEXTURE, &s.com.texture[n], -1); gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist), &iter); s.com.numtexture++; restartrender (); } static void duptexture (void) { GtkListStore *list_store; GtkTreeIter iter; texture *t = currenttexture (); gint n = s.com.numtexture; if (n == MAXTEXTUREPEROBJ - 1) return; if (!t) return; s.com.texture[n] = *t; list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, TYPE, mklabel (&s.com.texture[n]), TEXTURE, &s.com.texture[n], -1); gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist), &iter); s.com.numtexture++; restartrender (); } static void rebuildlist (void) { GtkListStore *list_store; GtkTreeSelection *sel; GtkTreeIter iter; gint n; sel = gtk_tree_view_get_selection (texturelist); for (n = 0; n < s.com.numtexture; n++) { if (s.com.numtexture && (s.com.texture[n].majtype < 0)) { gint i; for (i = n; i < s.com.numtexture - 1; i++) s.com.texture[i] = s.com.texture[i + 1]; s.com.numtexture--; n--; } } list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)); for (n = 0; n < s.com.numtexture; n++) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, TYPE, mklabel (&s.com.texture[n]), TEXTURE, &s.com.texture[n], -1); } if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) gtk_tree_selection_select_iter (sel, &iter); restartrender (); } static void deltexture (void) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreeIter iter; texture *t = NULL; sel = gtk_tree_view_get_selection (texturelist); if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { model = gtk_tree_view_get_model (texturelist); gtk_tree_model_get (model, &iter, TEXTURE, &t, -1); t->majtype = -1; gtk_list_store_remove (GTK_LIST_STORE (model), &iter); } restartrender (); } static void loadit (const gchar * fn) { FILE *f; gchar endbuf[21 * (G_ASCII_DTOSTR_BUF_SIZE + 1)]; gchar *end = endbuf; gchar line[1024]; gchar fmt_str[16]; gint i; texture *t; gint majtype, type; f = g_fopen (fn, "rt"); if (! f) { g_message (_("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (fn), g_strerror (errno)); return; } if (2 != fscanf (f, "%d %d", &majtype, &type) || majtype < 0 || majtype > 2) { g_message (_("File '%s' is not a valid save file."), gimp_filename_to_utf8 (fn)); fclose (f); return; } rewind (f); s.com.numtexture = 0; snprintf (fmt_str, sizeof (fmt_str), "%%d %%d %%%" G_GSIZE_FORMAT "s", sizeof (endbuf) - 1); while (!feof (f)) { if (!fgets (line, 1023, f)) break; i = s.com.numtexture; t = &s.com.texture[i]; setdefaults (t); if (sscanf (line, fmt_str, &t->majtype, &t->type, end) != 3) t->color1.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color1.y = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color1.z = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color1.w = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color2.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color2.y = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color2.z = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->color2.w = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->oscale = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->turbulence.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->amount = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->exp = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->scale.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->scale.y = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->scale.z = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->rotate.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->rotate.y = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->rotate.z = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->translate.x = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->translate.y = g_ascii_strtod (end, &end); if (end && errno != ERANGE) t->translate.z = g_ascii_strtod (end, &end); s.com.numtexture++; } fclose (f); } static void loadpreset_response (GtkWidget *dialog, gint response_id, gpointer data) { if (response_id == GTK_RESPONSE_OK) { GtkTreeModel *model = gtk_tree_view_get_model (texturelist); gchar *name; name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); gtk_list_store_clear (GTK_LIST_STORE (model)); loadit (name); g_free (name); rebuildlist (); } gtk_widget_hide (dialog); } static void saveit (const gchar *fn) { gint i; FILE *f; gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; f = g_fopen (fn, "wt"); if (!f) { g_message (_("Could not open '%s' for writing: %s"), gimp_filename_to_utf8 (fn), g_strerror (errno)); return; } for (i = 0; i < s.com.numtexture; i++) { texture *t = &s.com.texture[i]; if (t->majtype < 0) continue; fprintf (f, "%d %d", t->majtype, t->type); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.y)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.z)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.w)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.y)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.z)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.w)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->oscale)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->turbulence.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->amount)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->exp)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.y)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.z)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.y)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.z)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.x)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.y)); fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.z)); fprintf (f, "\n"); } fclose (f); } static void savepreset_response (GtkWidget *dialog, gint response_id, gpointer data) { if (response_id == GTK_RESPONSE_OK) { gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); saveit (name); g_free (name); } gtk_widget_hide (dialog); } static void loadpreset (GtkWidget *widget, GtkWidget *parent) { fileselect (GTK_FILE_CHOOSER_ACTION_OPEN, parent); } static void savepreset (GtkWidget *widget, GtkWidget *parent) { fileselect (GTK_FILE_CHOOSER_ACTION_SAVE, parent); } static void fileselect (GtkFileChooserAction action, GtkWidget *parent) { static GtkWidget *windows[2] = { NULL, NULL }; gchar *titles[] = { N_("Open File"), N_("Save File") }; void *handlers[] = { loadpreset_response, savepreset_response }; if (! windows[action]) { GtkWidget *dialog = windows[action] = gtk_file_chooser_dialog_new (gettext (titles[action]), GTK_WINDOW (parent), action, _("_Cancel"), GTK_RESPONSE_CANCEL, action == GTK_FILE_CHOOSER_ACTION_OPEN ? _("_Open") : _("_Save"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); if (action == GTK_FILE_CHOOSER_ACTION_SAVE) gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &windows[action]); g_signal_connect (dialog, "response", G_CALLBACK (handlers[action]), NULL); } gtk_window_present (GTK_WINDOW (windows[action])); } static void initworld (void) { gint 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; /* not: world.obj[0] = s; * s is a sphere so error C2115: '=' : incompatible types */ 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) { t->phongcolor = t->color1; t->phongsize = t->oscale / 25.0; } d->texture[d->numtexture] = *t; vmul (&d->texture[d->numtexture].scale, d->texture[d->numtexture].oscale); d->numtexture++; } else if (t->majtype == 1) { /* Bumpmap */ d->normal[d->numnormal] = *t; 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); world.light[world.numlight] = l; world.numlight++; } } world.quality = 5; world.flags |= SMARTAMBIENT; world.smartambient = 40.0; } static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event) { cairo_t *cr; cr = gdk_cairo_create (gtk_widget_get_window (widget)); gdk_cairo_region (cr, event->region); cairo_clip (cr); cairo_set_source_surface (cr, buffer, 0.0, 0.0); cairo_paint (cr); cairo_destroy (cr); return TRUE; } static void restartrender (void) { if (idle_id) g_source_remove (idle_id); idle_id = g_idle_add ((GSourceFunc) render, NULL); } static void selecttexture (GtkWidget *widget, gpointer data) { texture *t; if (noupdate) return; t = currenttexture (); if (!t) return; gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->type); relabel (); restartrender (); } static void selecttype (GtkWidget *widget, gpointer data) { texture *t; if (noupdate) return; t = currenttexture (); if (!t) return; gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->majtype); relabel (); restartrender (); } static void getscales (GtkWidget *widget, gpointer data) { gdouble f; texture *t; if (noupdate) return; t = currenttexture (); if (!t) return; t->amount = gtk_adjustment_get_value (GTK_ADJUSTMENT (amountscale)); t->exp = gtk_adjustment_get_value (GTK_ADJUSTMENT (expscale)); f = gtk_adjustment_get_value (GTK_ADJUSTMENT (turbulencescale)); vset (&t->turbulence, f, f, f); t->oscale = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalescale)); t->scale.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalexscale)); t->scale.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (scaleyscale)); t->scale.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalezscale)); t->rotate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotxscale)); t->rotate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotyscale)); t->rotate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotzscale)); t->translate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (posxscale)); t->translate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (posyscale)); t->translate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (poszscale)); restartrender (); } static void color1_changed (GimpColorButton *button) { texture *t = currenttexture (); if (t) { GimpRGB color; gimp_color_button_get_color (button, &color); t->color1.x = color.r; t->color1.y = color.g; t->color1.z = color.b; t->color1.w = color.a; restartrender (); } } static void color2_changed (GimpColorButton *button) { texture *t = currenttexture (); if (t) { GimpRGB color; gimp_color_button_get_color (button, &color); t->color2.x = color.r; t->color2.y = color.g; t->color2.z = color.b; t->color2.w = color.a; restartrender (); } } static void drawcolor1 (GtkWidget *w) { static GtkWidget *lastw = NULL; GimpRGB color; texture *t = currenttexture (); if (w) lastw = w; else w = lastw; if (!w) return; if (!t) return; gimp_rgba_set (&color, t->color1.x, t->color1.y, t->color1.z, t->color1.w); gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color); } static void drawcolor2 (GtkWidget *w) { static GtkWidget *lastw = NULL; GimpRGB color; texture *t = currenttexture (); if (w) lastw = w; else w = lastw; if (!w) return; if (!t) return; gimp_rgba_set (&color, t->color2.x, t->color2.y, t->color2.z, t->color2.w); gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color); } static gboolean do_run = FALSE; static void sphere_response (GtkWidget *widget, gint response_id, gpointer data) { switch (response_id) { case RESPONSE_RESET: 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_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (texturelist))); rebuildlist (); break; case GTK_RESPONSE_OK: if (idle_id) { g_source_remove (idle_id); idle_id = 0; } do_run = TRUE; default: gtk_widget_hide (widget); gtk_main_quit (); break; } } static GtkWidget * makewindow (void) { GtkListStore *store; GtkTreeViewColumn *col; GtkTreeSelection *selection; GtkWidget *window; GtkWidget *main_hbox; GtkWidget *main_vbox; GtkWidget *table; GtkWidget *frame; GtkWidget *scrolled; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *button; GtkWidget *list; GimpRGB rgb = { 0, 0, 0, 0 }; window = gimp_dialog_new (_("Sphere Designer"), PLUG_IN_ROLE, NULL, 0, gimp_standard_help_func, PLUG_IN_PROC, _("_Reset"), RESPONSE_RESET, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (window), RESPONSE_RESET, GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gimp_window_set_transient (GTK_WINDOW (window)); g_signal_connect (window, "response", G_CALLBACK (sphere_response), NULL); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0); gtk_widget_show (main_hbox); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); drawarea = gtk_drawing_area_new (); gtk_container_add (GTK_CONTAINER (frame), drawarea); gtk_widget_set_size_request (drawarea, PREVIEWSIZE, PREVIEWSIZE); gtk_widget_show (drawarea); g_signal_connect (drawarea, "expose-event", G_CALLBACK (expose_event), NULL); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE); gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); button = gtk_button_new_with_mnemonic (_("_Open")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); g_signal_connect (button, "clicked", G_CALLBACK (loadpreset), window); button = gtk_button_new_with_mnemonic (_("_Save")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); g_signal_connect (button, "clicked", G_CALLBACK (savepreset), window); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_end (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0); gtk_widget_show (vbox); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); gtk_widget_show (scrolled); store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); texturelist = GTK_TREE_VIEW (list); selection = gtk_tree_view_get_selection (texturelist); gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); g_signal_connect (selection, "changed", G_CALLBACK (selectitem), NULL); gtk_widget_set_size_request (list, -1, 150); gtk_container_add (GTK_CONTAINER (scrolled), list); gtk_widget_show (list); col = gtk_tree_view_column_new_with_attributes (_("Layers"), gtk_cell_renderer_text_new (), "text", TYPE, NULL); gtk_tree_view_append_column (texturelist, col); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); button = gtk_button_new_with_mnemonic (_("_New")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); g_signal_connect_swapped (button, "clicked", G_CALLBACK (addtexture), NULL); gtk_widget_show (button); button = gtk_button_new_with_mnemonic (_("D_uplicate")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); g_signal_connect_swapped (button, "clicked", G_CALLBACK (duptexture), NULL); gtk_widget_show (button); button = gtk_button_new_with_mnemonic (_("_Delete")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); g_signal_connect_swapped (button, "clicked", G_CALLBACK (deltexture), NULL); gtk_widget_show (button); main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, FALSE, 0); gtk_widget_show (main_hbox); frame = gimp_frame_new (_("Properties")); gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); table = gtk_table_new (7, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 2); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); typemenu = gimp_int_combo_box_new (_("Texture"), 0, _("Bump"), 1, _("Light"), 2, NULL); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (typemenu), 0, G_CALLBACK (selecttype), NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Type:"), 0.0, 0.5, typemenu, 2, FALSE); texturemenu = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL); { struct textures_t *t; for (t = textures; t->s; t++) gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (texturemenu), GIMP_INT_STORE_VALUE, t->n, GIMP_INT_STORE_LABEL, gettext (t->s), -1); } gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (texturemenu), 0, G_CALLBACK (selecttexture), NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Texture:"), 0.0, 0.5, texturemenu, 2, FALSE); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, _("Colors:"), 0.0, 0.5, hbox, 2, FALSE); button = gimp_color_button_new (_("Color Selection Dialog"), COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb, GIMP_COLOR_AREA_FLAT); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); drawcolor1 (button); g_signal_connect (button, "color-changed", G_CALLBACK (color1_changed), NULL); button = gimp_color_button_new (_("Color Selection Dialog"), COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb, GIMP_COLOR_AREA_FLAT); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); drawcolor2 (button); g_signal_connect (button, "color-changed", G_CALLBACK (color2_changed), NULL); scalescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Scale:"), 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (scalescale, "value-changed", G_CALLBACK (getscales), NULL); turbulencescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Turbulence:"), 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (turbulencescale, "value-changed", G_CALLBACK (getscales), NULL); amountscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Amount:"), 100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (amountscale, "value-changed", G_CALLBACK (getscales), NULL); expscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Exp.:"), 100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (expscale, "value-changed", G_CALLBACK (getscales), NULL); frame = gimp_frame_new (_("Transformations")); gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); table = gtk_table_new (9, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 2); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12); gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); scalexscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Scale X:"), 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (scalexscale, "value-changed", G_CALLBACK (getscales), NULL); scaleyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, _("Scale Y:"), 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (scaleyscale, "value-changed", G_CALLBACK (getscales), NULL); scalezscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Scale Z:"), 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (scalezscale, "value-changed", G_CALLBACK (getscales), NULL); rotxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Rotate X:"), 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (rotxscale, "value-changed", G_CALLBACK (getscales), NULL); rotyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Rotate Y:"), 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (rotyscale, "value-changed", G_CALLBACK (getscales), NULL); rotzscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Rotate Z:"), 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (rotzscale, "value-changed", G_CALLBACK (getscales), NULL); posxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Position X:"), 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (posxscale, "value-changed", G_CALLBACK (getscales), NULL); posyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 7, _("Position Y:"), 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (posyscale, "value-changed", G_CALLBACK (getscales), NULL); poszscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 8, _("Position Z:"), 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1, TRUE, 0.0, 0.0, NULL, NULL); g_signal_connect (poszscale, "value-changed", G_CALLBACK (getscales), NULL); gtk_widget_show (window); return window; } static guchar pixelval (gdouble v) { v += 0.5; if (v < 0.0) return 0; if (v > 255.0) return 255; return v; } static gboolean render (void) { GimpVector4 col; guchar *dest_row; ray r; gint x, y, p; gint tx = PREVIEWSIZE; gint ty = PREVIEWSIZE; gint bpp = 4; idle_id = 0; initworld (); r.v1.z = -10.0; r.v2.z = 0.0; if (world.obj[0].com.numtexture > 0) { cairo_surface_flush (buffer); for (y = 0; y < ty; y++) { dest_row = img + y * img_stride; for (x = 0; x < tx; x++) { gint 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; 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; GIMP_CAIRO_RGB24_SET_PIXEL ((dest_row + p), pixelval (255 * col.x) * col.w + g * (1.0 - col.w), pixelval (255 * col.y) * col.w + g * (1.0 - col.w), pixelval (255 * col.z) * col.w + g * (1.0 - col.w)); } } cairo_surface_mark_dirty (buffer); } gtk_widget_queue_draw (drawarea); return FALSE; } static void realrender (gint32 drawable_ID) { GeglBuffer *src_buffer; GeglBuffer *dest_buffer; const Babl *format; gint x, y; ray r; GimpVector4 rcol; gint width, height; gint x1, y1; guchar *dest; gint bpp; guchar *buffer, *ibuffer; initworld (); r.v1.z = -10.0; r.v2.z = 0.0; if (! gimp_drawable_mask_intersect (drawable_ID, &x1, &y1, &width, &height)) return; src_buffer = gimp_drawable_get_buffer (drawable_ID); dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID); if (gimp_drawable_is_rgb (drawable_ID)) { if (gimp_drawable_has_alpha (drawable_ID)) format = babl_format ("R'G'B'A u8"); else format = babl_format ("R'G'B' u8"); } else { if (gimp_drawable_has_alpha (drawable_ID)) format = babl_format ("Y'A u8"); else format = babl_format ("Y' u8"); } bpp = babl_format_get_bytes_per_pixel (format); buffer = g_malloc (width * 4); ibuffer = g_malloc (width * 4); gimp_progress_init (_("Rendering sphere")); for (y = 0; y < height; y++) { dest = buffer; for (x = 0; x < width; x++) { r.v1.x = r.v2.x = 8.1 * (x / (float) (width - 1) - 0.5); r.v1.y = r.v2.y = 8.1 * (y / (float) (height - 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; } gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 1.0, format, ibuffer, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); for (x = 0; x < width; x++) { gint k, dx = x * 4, sx = x * bpp; gfloat 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); } } gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 0, format, ibuffer, GEGL_AUTO_ROWSTRIDE); gimp_progress_update ((gdouble) y / (gdouble) height); } gimp_progress_update (1.0); g_free (buffer); g_free (ibuffer); g_object_unref (src_buffer); g_object_unref (dest_buffer); gimp_drawable_merge_shadow (drawable_ID, TRUE); gimp_drawable_update (drawable_ID, x1, y1, width, height); } static void query (void) { static const GimpParamDef args[] = { { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } }; gimp_install_procedure (PLUG_IN_PROC, N_("Create an image of a textured sphere"), "This plug-in 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_("Sphere _Designer..."), "RGB*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); gimp_plugin_menu_register (PLUG_IN_PROC, "/Filters/Render"); } static gboolean sphere_main (gint32 drawable_ID) { gimp_ui_init (PLUG_IN_BINARY, TRUE); img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, PREVIEWSIZE); img = g_malloc0 (img_stride * PREVIEWSIZE); buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24, PREVIEWSIZE, PREVIEWSIZE, img_stride); makewindow (); if (s.com.numtexture == 0) { /* Setup and use default list */ sphere_response (NULL, RESPONSE_RESET, NULL); } else { /* Reuse the list from a previous invocation */ rebuildlist (); } gtk_main (); cairo_surface_destroy (buffer); g_free (img); return do_run; } static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpRunMode run_mode; gint32 drawable_ID; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint x, y, w, h; INIT_I18N (); gegl_init (NULL, NULL); *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; run_mode = param[0].data.d_int32; drawable_ID = param[2].data.d_drawable; if (! gimp_drawable_mask_intersect (drawable_ID, &x, &y, &w, &h)) { g_message (_("Region selected for plug-in is empty")); return; } switch (run_mode) { case GIMP_RUN_INTERACTIVE: s.com.numtexture = 0; gimp_get_data (PLUG_IN_PROC, &s); if (! sphere_main (drawable_ID)) return; break; case GIMP_RUN_WITH_LAST_VALS: s.com.numtexture = 0; gimp_get_data (PLUG_IN_PROC, &s); if (s.com.numtexture == 0) return; break; case GIMP_RUN_NONINTERACTIVE: default: /* Not implemented yet... */ return; } gimp_set_data (PLUG_IN_PROC, &s, sizeof (s)); realrender (drawable_ID); gimp_displays_flush (); values[0].data.d_status = status; } MAIN ()