summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/sphere-designer.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/common/sphere-designer.c3166
1 files changed, 3166 insertions, 0 deletions
diff --git a/plug-ins/common/sphere-designer.c b/plug-ins/common/sphere-designer.c
new file mode 100644
index 0000000..624e6ff
--- /dev/null
+++ b/plug-ins/common/sphere-designer.c
@@ -0,0 +1,3166 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * SphereDesigner v0.4 - creates textured spheres
+ * by Vidar Madsen <vidar@prosalg.no>
+ *
+ * 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 <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#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, "<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;
+}
+
+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, "<Image>/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 ()