1 /*
2  * GIMP - The GNU Image Manipulation Program
3  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * SphereDesigner v0.4 - creates textured spheres
21  * by Vidar Madsen <vidar@prosalg.no>
22  *
23  * Status: Last updated 1999-09-11
24  *
25  * Known issues:
26  * - Might crash if you click OK or Cancel before first preview is rendered
27  * - Phong might look weird with transparent textures
28  *
29  * Todo:
30  * - Saving / Loading of presets needs an overhaul
31  * - Antialiasing
32  * - Global controls: Gamma, ++
33  * - Beautification of GUI
34  * - Clean up messy source (lots of Glade remnants)
35  * - (Probably more. ;-)
36  */
37 
38 #include "config.h"
39 
40 #include <string.h>
41 #include <errno.h>
42 
43 #include <glib/gstdio.h>
44 
45 #include <libgimp/gimp.h>
46 #include <libgimp/gimpui.h>
47 
48 #include "libgimp/stdplugins-intl.h"
49 
50 
51 #define PLUG_IN_PROC   "plug-in-spheredesigner"
52 #define PLUG_IN_BINARY "sphere-designer"
53 #define PLUG_IN_ROLE   "gimp-sphere-designer"
54 
55 #define RESPONSE_RESET 1
56 
57 #define PREVIEWSIZE 150
58 
59 /* These must be adjusted as more functionality is added */
60 #define MAXOBJECT 5
61 #define MAXLIGHT 5
62 #define MAXTEXTURE 20
63 #define MAXTEXTUREPEROBJ 20
64 #define MAXNORMAL 20
65 #define MAXNORMALPEROBJ 20
66 #define MAXATMOS 1
67 #define MAXCOLPERGRADIENT 5
68 
69 static void query (void);
70 static void run   (const gchar      *name,
71                    gint              nparams,
72                    const GimpParam  *param,
73                    gint             *nreturn_vals,
74                    GimpParam       **return_vals);
75 
76 const GimpPlugInInfo PLUG_IN_INFO =
77 {
78   NULL,   /* init_proc  */
79   NULL,   /* quit_proc  */
80   query,  /* query_proc */
81   run,    /* run_proc   */
82 };
83 
84 enum
85 {
86   TRIANGLE,
87   DISC,
88   PLANE,
89   SPHERE,
90   CYLINDER,
91   LIGHT
92 };
93 
94 enum
95 {
96   SOLID,
97   CHECKER,
98   MARBLE,
99   LIZARD,
100   IMAGE,
101   PHONG,
102   REFLECTION,
103   REFRACTION,
104   PERLIN,
105   WOOD,
106   TRANSPARENT,
107   SPIRAL,
108   SPOTS,
109   SMOKE
110 };
111 
112 enum
113 {
114   PERSPECTIVE,
115   ORTHOGONAL,
116   FISHEYE
117 };
118 
119 enum
120 {
121   FOG
122 };
123 
124 enum
125 {
126   TYPE,
127   TEXTURE,
128   NUM_COLUMNS
129 };
130 
131 
132 /* World-flags */
133 #define SMARTAMBIENT 0x00000001
134 
135 /* Object-flags */
136 #define NOSHADOW   0x00000001
137 
138 /* Texture-flags */
139 #define GRADIENT   0x00000001
140 
141 typedef struct
142 {
143   gshort  xsize, ysize;
144   guchar *rgb;
145 } image;
146 
147 typedef struct
148 {
149   gshort        numcol;
150   gdouble       pos[MAXCOLPERGRADIENT];
151   GimpVector4   color[MAXCOLPERGRADIENT];
152 } gradient;
153 
154 typedef struct
155 {
156   gint          majtype;
157   gint          type;
158   gulong        flags;
159   GimpVector4   color1, color2;
160   gradient      gradient;
161   GimpVector4   ambient, diffuse;
162   gdouble       oscale;
163   GimpVector4   scale, translate, rotate;
164   image         image;
165   GimpVector4   reflection;
166   GimpVector4   refraction;
167   GimpVector4   transparent;
168   gdouble       ior;
169   GimpVector4   phongcolor;
170   gdouble       phongsize;
171   gdouble       amount;
172   gdouble       exp;
173   GimpVector4   turbulence;
174 } texture;
175 
176 typedef struct
177 {
178   gshort  type;
179   gdouble density;
180   GimpVector4  color;
181   gdouble turbulence;
182 } atmos;
183 
184 typedef struct
185 {
186   gshort  type;
187   gulong  flags;
188   gshort  numtexture;
189   texture texture[MAXTEXTUREPEROBJ];
190   gshort  numnormal;
191   texture normal[MAXNORMALPEROBJ];
192 } common;
193 
194 typedef struct
195 {
196   common com;
197   GimpVector4 a, b, c;
198 } triangle;
199 
200 typedef struct
201 {
202   common        com;
203   GimpVector4   a;
204   gdouble       b, r;
205 } disc;
206 
207 typedef struct
208 {
209   common        com;
210   GimpVector4   a;
211   gdouble       r;
212 } sphere;
213 
214 typedef struct
215 {
216   common        com;
217   GimpVector4   a, b, c;
218 } cylinder;
219 
220 typedef struct
221 {
222   common        com;
223   GimpVector4   a;
224   gdouble       b;
225 } plane;
226 
227 typedef struct
228 {
229   common        com;
230   GimpVector4   color;
231   GimpVector4   a;
232 } light;
233 
234 typedef struct
235 {
236   GimpVector4   v1, v2;
237   gshort        inside;
238   gdouble       ior;
239 } ray;
240 
241 typedef union
242 {
243   common   com;
244   triangle tri;
245   disc     disc;
246   plane    plane;
247   sphere   sphere;
248   cylinder cylinder;
249 } object;
250 
251 
252 struct world_t
253 {
254   gint    numobj;
255   object  obj[MAXOBJECT];
256   gint    numlight;
257   light   light[MAXLIGHT];
258   gint    numtexture;
259   texture texture[MAXTEXTURE];
260   gulong  flags;
261   gshort  quality;
262   gdouble smartambient;
263   gshort  numatmos;
264   atmos   atmos[MAXATMOS];
265 };
266 
267 struct camera_t
268 {
269   GimpVector4 location, lookat, up, right;
270   short  type;
271   double fov, tilt;
272 };
273 
274 static GtkWidget *drawarea = NULL;
275 
276 static guchar          *img;
277 static gint             img_stride;
278 static cairo_surface_t *buffer;
279 
280 static guint  idle_id = 0;
281 
282 static sphere s;
283 
284 struct textures_t
285 {
286   gint   index;
287   gchar *s;
288   glong  n;
289 };
290 
291 static struct textures_t textures[] =
292 {
293   { 0, N_("Solid"),   SOLID   },
294   { 1, N_("Checker"), CHECKER },
295   { 2, N_("Marble"),  MARBLE  },
296   { 3, N_("Lizard"),  LIZARD  },
297   { 4, N_("Phong"),   PHONG   },
298   { 5, N_("Noise"),   PERLIN  },
299   { 6, N_("Wood"),    WOOD    },
300   { 7, N_("Spiral"),  SPIRAL  },
301   { 8, N_("Spots"),   SPOTS   },
302   { 0, NULL,          0       }
303 };
304 
305 static inline void vset        (GimpVector4          *v,
306                                 gdouble               a,
307                                 gdouble               b,
308                                 gdouble               c);
309 static void      restartrender (void);
310 static void      drawcolor1    (GtkWidget            *widget);
311 static void      drawcolor2    (GtkWidget            *widget);
312 static gboolean  render        (void);
313 static void      realrender    (gint32                drawable_ID);
314 static void      fileselect    (GtkFileChooserAction  action,
315                                 GtkWidget            *parent);
316 static gint      traceray      (ray                  *r,
317                                 GimpVector4          *col,
318                                 gint                  level,
319                                 gdouble               imp);
320 static gdouble   turbulence    (gdouble              *point,
321                                 gdouble               lofreq,
322                                 gdouble               hifreq);
323 
324 
325 #define COLORBUTTONWIDTH  30
326 #define COLORBUTTONHEIGHT 20
327 
328 static GtkTreeView *texturelist = NULL;
329 
330 static GtkObject *scalexscale, *scaleyscale, *scalezscale;
331 static GtkObject *rotxscale, *rotyscale, *rotzscale;
332 static GtkObject *posxscale, *posyscale, *poszscale;
333 static GtkObject *scalescale;
334 static GtkObject *turbulencescale;
335 static GtkObject *amountscale;
336 static GtkObject *expscale;
337 static GtkWidget *typemenu;
338 static GtkWidget *texturemenu;
339 
340 #define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])
341 
342 #define B 256
343 
344 static gint      p[B + B + 2];
345 static gdouble   g[B + B + 2][3];
346 static gboolean  start = TRUE;
347 static GRand    *gr;
348 
349 
350 static void
init(void)351 init (void)
352 {
353   gint i, j, k;
354   gdouble v[3], s;
355 
356   /* Create an array of random gradient vectors uniformly on the unit sphere */
357 
358   gr = g_rand_new ();
359   g_rand_set_seed (gr, 1);    /* Use static seed, to get reproducible results */
360 
361   for (i = 0; i < B; i++)
362     {
363       do
364         {                     /* Choose uniformly in a cube */
365           for (j = 0; j < 3; j++)
366             v[j] = g_rand_double_range (gr, -1, 1);
367           s = DOT (v, v);
368         }
369       while (s > 1.0);        /* If not in sphere try again */
370       s = sqrt (s);
371       for (j = 0; j < 3; j++) /* Else normalize */
372         g[i][j] = v[j] / s;
373     }
374 
375 /* Create a pseudorandom permutation of [1..B] */
376 
377   for (i = 0; i < B; i++)
378     p[i] = i;
379   for (i = B; i > 0; i -= 2)
380     {
381       k = p[i];
382       p[i] = p[j = g_rand_int_range (gr, 0, B)];
383       p[j] = k;
384     }
385 
386   /* Extend g and p arrays to allow for faster indexing */
387 
388   for (i = 0; i < B + 2; i++)
389     {
390       p[B + i] = p[i];
391       for (j = 0; j < 3; j++)
392         g[B + i][j] = g[i][j];
393     }
394   g_rand_free (gr);
395 }
396 
397 #define setup(i,b0,b1,r0,r1) \
398         t = vec[i] + 10000.; \
399         b0 = ((int)t) & (B-1); \
400         b1 = (b0+1) & (B-1); \
401         r0 = t - (int)t; \
402         r1 = r0 - 1.;
403 
404 
405 static gdouble
noise3(gdouble * vec)406 noise3 (gdouble * vec)
407 {
408   gint    bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
409   gdouble rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v;
410   gint    i, j;
411 
412   if (start)
413     {
414       start = FALSE;
415       init ();
416     }
417 
418   setup (0, bx0, bx1, rx0, rx1);
419   setup (1, by0, by1, ry0, ry1);
420   setup (2, bz0, bz1, rz0, rz1);
421 
422   i = p[bx0];
423   j = p[bx1];
424 
425   b00 = p[i + by0];
426   b10 = p[j + by0];
427   b01 = p[i + by1];
428   b11 = p[j + by1];
429 
430 #define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
431 
432 #define surve(t) ( t * t * (3. - 2. * t) )
433 
434 #define lerp(t, a, b) ( a + t * (b - a) )
435 
436   sx = surve (rx0);
437   sy = surve (ry0);
438   sz = surve (rz0);
439 
440 
441   q = g[b00 + bz0];
442   u = at (rx0, ry0, rz0);
443   q = g[b10 + bz0];
444   v = at (rx1, ry0, rz0);
445   a = lerp (sx, u, v);
446 
447   q = g[b01 + bz0];
448   u = at (rx0, ry1, rz0);
449   q = g[b11 + bz0];
450   v = at (rx1, ry1, rz0);
451   b = lerp (sx, u, v);
452 
453   c = lerp (sy, a, b);          /* interpolate in y at lo x */
454 
455   q = g[b00 + bz1];
456   u = at (rx0, ry0, rz1);
457   q = g[b10 + bz1];
458   v = at (rx1, ry0, rz1);
459   a = lerp (sx, u, v);
460 
461   q = g[b01 + bz1];
462   u = at (rx0, ry1, rz1);
463   q = g[b11 + bz1];
464   v = at (rx1, ry1, rz1);
465   b = lerp (sx, u, v);
466 
467   d = lerp (sy, a, b);          /* interpolate in y at hi x */
468 
469   return 1.5 * lerp (sz, c, d); /* interpolate in z */
470 }
471 
472 static double
turbulence(gdouble * point,gdouble lofreq,gdouble hifreq)473 turbulence (gdouble * point, gdouble lofreq, gdouble hifreq)
474 {
475   gdouble freq, t, p[3];
476 
477   p[0] = point[0] + 123.456;
478   p[1] = point[1] + 234.567;
479   p[2] = point[2] + 345.678;
480 
481   t = 0;
482   for (freq = lofreq; freq < hifreq; freq *= 2.)
483     {
484       t += noise3 (p) / freq;
485       p[0] *= 2.;
486       p[1] *= 2.;
487       p[2] *= 2.;
488     }
489   return t - 0.3;               /* readjust to make mean value = 0.0 */
490 }
491 
492 static struct world_t  world;
493 
494 static inline void
vcopy(GimpVector4 * a,GimpVector4 * b)495 vcopy (GimpVector4 *a, GimpVector4 *b)
496 {
497   *a = *b;
498 }
499 
500 static inline void
vcross(GimpVector4 * r,GimpVector4 * a,GimpVector4 * b)501 vcross (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b)
502 {
503   r->x = a->y * b->z - a->z * b->y;
504   r->y = -(a->x * b->z - a->z * b->x);
505   r->z = a->x * b->y - a->y * b->x;
506 }
507 
508 static inline gdouble
vdot(GimpVector4 * a,GimpVector4 * b)509 vdot (GimpVector4 *a, GimpVector4 *b)
510 {
511   return a->x * b->x + a->y * b->y + a->z * b->z;
512 }
513 
514 static inline gdouble
vdist(GimpVector4 * a,GimpVector4 * b)515 vdist (GimpVector4 *a, GimpVector4 *b)
516 {
517   gdouble x, y, z;
518 
519   x = a->x - b->x;
520   y = a->y - b->y;
521   z = a->z - b->z;
522 
523   return sqrt (x * x + y * y + z * z);
524 }
525 
526 static inline gdouble
vdist2(GimpVector4 * a,GimpVector4 * b)527 vdist2 (GimpVector4 *a, GimpVector4 *b)
528 {
529   gdouble x, y, z;
530 
531   x = a->x - b->x;
532   y = a->y - b->y;
533   z = a->z - b->z;
534 
535   return x * x + y * y + z * z;
536 }
537 
538 static inline gdouble
vlen(GimpVector4 * a)539 vlen (GimpVector4 *a)
540 {
541   return sqrt (a->x * a->x + a->y * a->y + a->z * a->z);
542 }
543 
544 static inline void
vnorm(GimpVector4 * a,gdouble v)545 vnorm (GimpVector4 *a, gdouble v)
546 {
547   gdouble d;
548 
549   d = vlen (a);
550   a->x *= v / d;
551   a->y *= v / d;
552   a->z *= v / d;
553 }
554 
555 static inline void
vrotate(GimpVector4 * axis,gdouble ang,GimpVector4 * vector)556 vrotate (GimpVector4 *axis, gdouble ang, GimpVector4 *vector)
557 {
558   gdouble rad = ang / 180.0 * G_PI;
559   gdouble ax  = vector->x;
560   gdouble ay  = vector->y;
561   gdouble az  = vector->z;
562   gdouble x   = axis->x;
563   gdouble y   = axis->y;
564   gdouble z   = axis->z;
565   gdouble c   = cos (rad);
566   gdouble s   = sin (rad);
567   gdouble c1  = 1.0 - c;
568   gdouble xx  = c1 * x * x;
569   gdouble yy  = c1 * y * y;
570   gdouble zz  = c1 * z * z;
571   gdouble xy  = c1 * x * y;
572   gdouble xz  = c1 * x * z;
573   gdouble yz  = c1 * y * z;
574   gdouble sx  = s * x;
575   gdouble sy  = s * y;
576   gdouble sz  = s * z;
577 
578   vector->x = (xx + c) * ax + (xy + sz) * ay + (xz - sy) * az;
579   vector->y = (xy - sz) * ax + (yy + c) * ay + (yz + sx) * az;
580   vector->z = (xz + sy) * ax + (yz - sx) * ay + (zz + c) * az;
581 }
582 
583 static inline void
vset(GimpVector4 * v,gdouble a,gdouble b,gdouble c)584 vset (GimpVector4 *v, gdouble a, gdouble b, gdouble c)
585 {
586   v->x = a;
587   v->y = b;
588   v->z = c;
589   v->w = 1.0;
590 }
591 
592 static inline void
vcset(GimpVector4 * v,gdouble a,gdouble b,gdouble c,gdouble d)593 vcset (GimpVector4 *v, gdouble a, gdouble b, gdouble c, gdouble d)
594 {
595   v->x = a;
596   v->y = b;
597   v->z = c;
598   v->w = d;
599 }
600 
601 static inline void
vvrotate(GimpVector4 * p,GimpVector4 * rot)602 vvrotate (GimpVector4 *p, GimpVector4 *rot)
603 {
604   GimpVector4 axis;
605 
606   if (rot->x != 0.0)
607     {
608       vset (&axis, 1, 0, 0);
609       vrotate (&axis, rot->x, p);
610     }
611   if (rot->y != 0.0)
612     {
613       vset (&axis, 0, 1, 0);
614       vrotate (&axis, rot->y, p);
615     }
616   if (rot->z != 0.0)
617     {
618       vset (&axis, 0, 0, 1);
619       vrotate (&axis, rot->z, p);
620     }
621 }
622 
623 static inline void
vsub(GimpVector4 * a,GimpVector4 * b)624 vsub (GimpVector4 *a, GimpVector4 *b)
625 {
626   a->x -= b->x;
627   a->y -= b->y;
628   a->z -= b->z;
629   a->w -= b->w;
630 }
631 
632 static inline void
vadd(GimpVector4 * a,GimpVector4 * b)633 vadd (GimpVector4 *a, GimpVector4 *b)
634 {
635   a->x += b->x;
636   a->y += b->y;
637   a->z += b->z;
638   a->w += b->w;
639 }
640 
641 static inline void
vneg(GimpVector4 * a)642 vneg (GimpVector4 *a)
643 {
644   a->x = -a->x;
645   a->y = -a->y;
646   a->z = -a->z;
647   a->w = -a->w;
648 }
649 
650 static inline void
vmul(GimpVector4 * v,gdouble a)651 vmul (GimpVector4 *v, gdouble a)
652 {
653   v->x *= a;
654   v->y *= a;
655   v->z *= a;
656   v->w *= a;
657 }
658 
659 static inline void
vvmul(GimpVector4 * a,GimpVector4 * b)660 vvmul (GimpVector4 *a, GimpVector4 *b)
661 {
662   a->x *= b->x;
663   a->y *= b->y;
664   a->z *= b->z;
665   a->w *= b->w;
666 }
667 
668 static inline void
vvdiv(GimpVector4 * a,GimpVector4 * b)669 vvdiv (GimpVector4 *a, GimpVector4 *b)
670 {
671   a->x /= b->x;
672   a->y /= b->y;
673   a->z /= b->z;
674 }
675 
676 static void
vmix(GimpVector4 * r,GimpVector4 * a,GimpVector4 * b,gdouble v)677 vmix (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b, gdouble v)
678 {
679   gdouble i = 1.0 - v;
680 
681   r->x = a->x * v + b->x * i;
682   r->y = a->y * v + b->y * i;
683   r->z = a->z * v + b->z * i;
684   r->w = a->w * v + b->w * i;
685 }
686 
687 static double
vmax(GimpVector4 * a)688 vmax (GimpVector4 *a)
689 {
690   gdouble max = fabs (a->x);
691 
692   if (fabs (a->y) > max)
693     max = fabs (a->y);
694   if (fabs (a->z) > max)
695     max = fabs (a->z);
696   if (fabs (a->w) > max)
697     max = fabs (a->w);
698 
699   return max;
700 }
701 
702 #if 0
703 static void
704 vavg (GimpVector4 * a)
705 {
706   gdouble s;
707 
708   s = (a->x + a->y + a->z) / 3.0;
709   a->x = a->y = a->z = s;
710 }
711 #endif
712 
713 static void
trianglenormal(GimpVector4 * n,gdouble * t,triangle * tri)714 trianglenormal (GimpVector4 * n, gdouble *t, triangle * tri)
715 {
716   triangle tmp;
717   vcopy (&tmp.b, &tri->b);
718   vcopy (&tmp.c, &tri->c);
719   vsub (&tmp.b, &tri->a);
720   vsub (&tmp.c, &tri->a);
721   vset (&tmp.a, 0, 0, 0);
722   vcross (n, &tmp.b, &tmp.c);
723   if (t)
724     *t = vdot (&tmp.b, &tmp.c);
725 }
726 
727 static gdouble
checkdisc(ray * r,disc * disc)728 checkdisc (ray * r, disc * disc)
729 {
730   GimpVector4 p, *v = &disc->a;
731   gdouble t, d2;
732   gdouble i, j, k;
733 
734   i = r->v2.x - r->v1.x;
735   j = r->v2.y - r->v1.y;
736   k = r->v2.z - r->v1.z;
737 
738   t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - disc->b) /
739     (v->x * i + v->y * j + v->z * k);
740 
741   p.x = r->v1.x + i * t;
742   p.y = r->v1.y + j * t;
743   p.z = r->v1.z + k * t;
744 
745   d2 = vdist2 (&p, v);
746 
747   if (d2 > disc->r * disc->r)
748     t = 0.0;
749 
750   return t;
751 }
752 
753 static gdouble
checksphere(ray * r,sphere * sphere)754 checksphere (ray * r, sphere * sphere)
755 {
756   GimpVector4 cendir, rdir;
757   gdouble dirproj, cdlensq;
758   gdouble linear, constant, rsq, quadratic, discriminant;
759   gdouble smallzero, solmin, solmax, tolerance = 0.001;
760 
761   vcopy (&rdir, &r->v2);
762   vsub (&rdir, &r->v1);
763 
764   rsq = sphere->r * sphere->r;
765 
766   vcopy (&cendir, &r->v1);
767   vsub (&cendir, &sphere->a);
768   dirproj = vdot (&rdir, &cendir);
769   cdlensq = vdot (&cendir, &cendir);
770 
771   if ((cdlensq >= rsq) && (dirproj > 0.0))
772     return 0.0;
773 
774   linear = 2.0 * dirproj;
775   constant = cdlensq - rsq;
776   quadratic = vdot (&rdir, &rdir);
777 
778   smallzero = (constant / linear);
779   if ((smallzero < tolerance) && (smallzero > -tolerance))
780     {
781       solmin = -linear / quadratic;
782 
783       if (solmin > tolerance)
784         {
785           return solmin;
786           /*
787            *hits = solmin;
788            return 1;
789            */
790         }
791       else
792         return 0.0;
793     }
794   discriminant = linear * linear - 4.0 * quadratic * constant;
795   if (discriminant < 0.0)
796     return 0.0;
797   quadratic *= 2.0;
798   discriminant = sqrt (discriminant);
799   solmax = (-linear + discriminant) / (quadratic);
800   solmin = (-linear - discriminant) / (quadratic);
801 
802   if (solmax < tolerance)
803     return 0.0;
804 
805   if (solmin < tolerance)
806     {
807       return solmax;
808       /*
809        * hits = solmax;
810        * return 1;
811        */
812     }
813   else
814     {
815       return solmin;
816       /*
817        * hits++ = solmin;
818        * hits = solmax;
819        * return 2;
820        */
821     }
822 }
823 
824 static gdouble
checkcylinder(ray * r,cylinder * cylinder)825 checkcylinder (ray * r, cylinder * cylinder)
826 {
827   /* FIXME */
828   return 0.0;
829 }
830 
831 
832 static gdouble
checkplane(ray * r,plane * plane)833 checkplane (ray * r, plane * plane)
834 {
835   GimpVector4 *v = &plane->a;
836   gdouble t;
837   gdouble i, j, k;
838 
839   i = r->v2.x - r->v1.x;
840   j = r->v2.y - r->v1.y;
841   k = r->v2.z - r->v1.z;
842 
843   t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - plane->b) /
844     (v->x * i + v->y * j + v->z * k);
845 
846   return t;
847 }
848 
849 static gdouble
checktri(ray * r,triangle * tri)850 checktri (ray * r, triangle * tri)
851 {
852   GimpVector4  ed1, ed2;
853   GimpVector4  tvec, pvec, qvec;
854   gdouble det, idet, t, u, v;
855   GimpVector4 *orig, dir;
856 
857   orig = &r->v1;
858   dir = r->v2;
859   vsub (&dir, orig);
860 
861   ed1.x = tri->c.x - tri->a.x;
862   ed1.y = tri->c.y - tri->a.y;
863   ed1.z = tri->c.z - tri->a.z;
864   ed2.x = tri->b.x - tri->a.x;
865   ed2.y = tri->b.y - tri->a.y;
866   ed2.z = tri->b.z - tri->a.z;
867   vcross (&pvec, &dir, &ed2);
868   det = vdot (&ed1, &pvec);
869 
870   idet = 1.0 / det;
871 
872   tvec.x = orig->x;
873   tvec.y = orig->y;
874   tvec.z = orig->z;
875   vsub (&tvec, &tri->a);
876   u = vdot (&tvec, &pvec) * idet;
877 
878   if (u < 0.0)
879     return 0;
880   if (u > 1.0)
881     return 0;
882 
883   vcross (&qvec, &tvec, &ed1);
884   v = vdot (&dir, &qvec) * idet;
885 
886   if ((v < 0.0) || (u + v > 1.0))
887     return 0;
888 
889   t = vdot (&ed2, &qvec) * idet;
890 
891   return t;
892 }
893 
894 static void
transformpoint(GimpVector4 * p,texture * t)895 transformpoint (GimpVector4 * p, texture * t)
896 {
897   gdouble point[3], f;
898 
899   if ((t->rotate.x != 0.0) || (t->rotate.y != 0.0) || (t->rotate.z != 0.0))
900     vvrotate (p, &t->rotate);
901   vvdiv (p, &t->scale);
902 
903   vsub (p, &t->translate);
904 
905   if ((t->turbulence.x != 0.0) || (t->turbulence.y != 0.0) ||
906       (t->turbulence.z != 0.0))
907     {
908       point[0] = p->x;
909       point[1] = p->y;
910       point[2] = p->z;
911       f = turbulence (point, 1, 256);
912       p->x += t->turbulence.x * f;
913       p->y += t->turbulence.y * f;
914       p->z += t->turbulence.z * f;
915     }
916 }
917 
918 static void
checker(GimpVector4 * q,GimpVector4 * col,texture * t)919 checker (GimpVector4 *q, GimpVector4 *col, texture *t)
920 {
921   gint   c = 0;
922   GimpVector4 p;
923 
924   p = *q;
925   transformpoint (&p, t);
926 
927   vmul (&p, 0.25);
928 
929   p.x += 0.00001;
930   p.y += 0.00001;
931   p.z += 0.00001;
932 
933   if (p.x < 0.0)
934     p.x = 0.5 - p.x;
935   if (p.y < 0.0)
936     p.y = 0.5 - p.y;
937   if (p.z < 0.0)
938     p.z = 0.5 - p.z;
939 
940   if ((p.x - (gint) p.x) < 0.5)
941     c ^= 1;
942   if ((p.y - (gint) p.y) < 0.5)
943     c ^= 1;
944   if ((p.z - (gint) p.z) < 0.5)
945     c ^= 1;
946 
947   *col = (c) ? t->color1 : t->color2;
948 }
949 
950 static void
gradcolor(GimpVector4 * col,gradient * t,gdouble val)951 gradcolor (GimpVector4 *col, gradient *t, gdouble val)
952 {
953   gint    i;
954   gdouble d;
955   GimpVector4  tmpcol;
956 
957   val = CLAMP (val, 0.0, 1.0);
958 
959   for (i = 0; i < t->numcol; i++)
960     {
961       if (t->pos[i] == val)
962         {
963           *col = t->color[i];
964           return;
965         }
966       if (t->pos[i] > val)
967         {
968           d = (val - t->pos[i - 1]) / (t->pos[i] - t->pos[i - 1]);
969           vcopy (&tmpcol, &t->color[i]);
970           vmul (&tmpcol, d);
971           vcopy (col, &tmpcol);
972           vcopy (&tmpcol, &t->color[i - 1]);
973           vmul (&tmpcol, 1.0 - d);
974           vadd (col, &tmpcol);
975           return;
976         }
977     }
978   g_printerr ("Error in gradient!\n");
979   vset (col, 0, 1, 0);
980 }
981 
982 static void
marble(GimpVector4 * q,GimpVector4 * col,texture * t)983 marble (GimpVector4 *q, GimpVector4 *col, texture *t)
984 {
985   gdouble f;
986   GimpVector4 p;
987 
988   p = *q;
989   transformpoint (&p, t);
990 
991   f = sin (p.x * 4) / 2 + 0.5;
992   f = pow (f, t->exp);
993 
994   if (t->flags & GRADIENT)
995     gradcolor (col, &t->gradient, f);
996   else
997     vmix (col, &t->color1, &t->color2, f);
998 }
999 
1000 static void
lizard(GimpVector4 * q,GimpVector4 * col,texture * t)1001 lizard (GimpVector4 *q, GimpVector4 *col, texture *t)
1002 {
1003   gdouble f;
1004   GimpVector4 p;
1005 
1006   p = *q;
1007   transformpoint (&p, t);
1008 
1009   f = fabs (sin (p.x * 4));
1010   f += fabs (sin (p.y * 4));
1011   f += fabs (sin (p.z * 4));
1012   f /= 3.0;
1013   f = pow (f, t->exp);
1014 
1015   if (t->flags & GRADIENT)
1016     gradcolor (col, &t->gradient, f);
1017   else
1018     vmix (col, &t->color1, &t->color2, f);
1019 }
1020 
1021 static void
wood(GimpVector4 * q,GimpVector4 * col,texture * t)1022 wood (GimpVector4 *q, GimpVector4 *col, texture *t)
1023 {
1024   gdouble f;
1025   GimpVector4 p;
1026 
1027   p = *q;
1028   transformpoint (&p, t);
1029 
1030   f = fabs (p.x);
1031   f = f - (int) f;
1032 
1033   f = pow (f, t->exp);
1034 
1035   if (t->flags & GRADIENT)
1036     gradcolor (col, &t->gradient, f);
1037   else
1038     vmix (col, &t->color1, &t->color2, f);
1039 }
1040 
1041 static void
spiral(GimpVector4 * q,GimpVector4 * col,texture * t)1042 spiral (GimpVector4 *q, GimpVector4 *col, texture *t)
1043 {
1044   gdouble f;
1045   GimpVector4 p;
1046 
1047   p = *q;
1048   transformpoint (&p, t);
1049 
1050   f = fabs (atan2 (p.x, p.z) / G_PI / 2 + p.y + 99999);
1051   f = f - (int) f;
1052 
1053   f = pow (f, t->exp);
1054 
1055   if (t->flags & GRADIENT)
1056     gradcolor (col, &t->gradient, f);
1057   else
1058     vmix (col, &t->color1, &t->color2, f);
1059 }
1060 
1061 static void
spots(GimpVector4 * q,GimpVector4 * col,texture * t)1062 spots (GimpVector4 *q, GimpVector4 *col, texture *t)
1063 {
1064   gdouble f;
1065   GimpVector4 p, r;
1066 
1067   p = *q;
1068   transformpoint (&p, t);
1069 
1070   p.x += 10000.0;
1071   p.y += 10000.0;
1072   p.z += 10000.0;
1073 
1074   vset (&r, (gint) (p.x + 0.5), (gint) (p.y + 0.5), (gint) (p.z + 0.5));
1075   f = vdist (&p, &r);
1076   f = cos (f * G_PI);
1077   f = CLAMP (f, 0.0, 1.0);
1078   f = pow (f, t->exp);
1079 
1080   if (t->flags & GRADIENT)
1081     gradcolor (col, &t->gradient, f);
1082   else
1083     vmix (col, &t->color1, &t->color2, f);
1084 }
1085 
1086 static void
perlin(GimpVector4 * q,GimpVector4 * col,texture * t)1087 perlin (GimpVector4 * q, GimpVector4 * col, texture * t)
1088 {
1089   gdouble f, point[3];
1090   GimpVector4  p;
1091 
1092   p = *q;
1093   transformpoint (&p, t);
1094 
1095   point[0] = p.x;
1096   point[1] = p.y;
1097   point[2] = p.z;
1098 
1099   f = turbulence (point, 1, 256) * 0.3 + 0.5;
1100 
1101   f = pow (f, t->exp);
1102 
1103   if (t->flags & GRADIENT)
1104     gradcolor (col, &t->gradient, f);
1105   else
1106     vmix (col, &t->color1, &t->color2, f);
1107 }
1108 
1109 static void
imagepixel(GimpVector4 * q,GimpVector4 * col,texture * t)1110 imagepixel (GimpVector4 * q, GimpVector4 * col, texture * t)
1111 {
1112   GimpVector4 p;
1113   gint x, y;
1114   guchar *rgb;
1115 
1116   p = *q;
1117   transformpoint (&p, t);
1118 
1119   x = (p.x * t->image.xsize);
1120   y = (p.y * t->image.ysize);
1121 
1122   x = (x % t->image.xsize + t->image.xsize) % t->image.xsize;
1123   y = (y % t->image.ysize + t->image.ysize) % t->image.ysize;
1124 
1125   rgb = &t->image.rgb[x * 3 + (t->image.ysize - 1 - y) * t->image.xsize * 3];
1126   vset (col, rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0);
1127 }
1128 
1129 static void
objcolor(GimpVector4 * col,GimpVector4 * p,common * obj)1130 objcolor (GimpVector4 *col, GimpVector4 *p, common *obj)
1131 {
1132   gint     i;
1133   texture *t;
1134   GimpVector4   tmpcol;
1135 
1136   vcset (col, 0, 0, 0, 0);
1137 
1138   for (i = 0; i < obj->numtexture; i++)
1139     {
1140       t = &obj->texture[i];
1141 
1142       if (world.quality < 1)
1143         {
1144           vadd (col, &t->color1);
1145           continue;
1146         }
1147 
1148       vset (&tmpcol, 0, 0, 0);
1149       switch (t->type)
1150         {
1151         case SOLID:
1152           vcopy (&tmpcol, &t->color1);
1153           break;
1154         case CHECKER:
1155           checker (p, &tmpcol, t);
1156           break;
1157         case MARBLE:
1158           marble (p, &tmpcol, t);
1159           break;
1160         case LIZARD:
1161           lizard (p, &tmpcol, t);
1162           break;
1163         case PERLIN:
1164           perlin (p, &tmpcol, t);
1165           break;
1166         case WOOD:
1167           wood (p, &tmpcol, t);
1168           break;
1169         case SPIRAL:
1170           spiral (p, &tmpcol, t);
1171           break;
1172         case SPOTS:
1173           spots (p, &tmpcol, t);
1174           break;
1175         case IMAGE:
1176           imagepixel (p, &tmpcol, t);
1177           break;
1178         case PHONG:
1179         case REFRACTION:
1180         case REFLECTION:
1181         case TRANSPARENT:
1182         case SMOKE:
1183           /* Silently ignore non-color textures */
1184           continue;
1185           break;
1186         default:
1187           g_printerr ("Warning: unknown texture %d\n", t->type);
1188           break;
1189         }
1190       vmul (&tmpcol, t->amount);
1191       vadd (col, &tmpcol);
1192     }
1193   if (!i)
1194     {
1195       g_printerr ("Warning: object %p has no textures\n", obj);
1196     }
1197 }
1198 
1199 static void
objnormal(GimpVector4 * res,common * obj,GimpVector4 * p)1200 objnormal (GimpVector4 *res, common *obj, GimpVector4 *p)
1201 {
1202   gint i;
1203 
1204   switch (obj->type)
1205     {
1206     case TRIANGLE:
1207       trianglenormal (res, NULL, (triangle *) obj);
1208       break;
1209     case DISC:
1210       vcopy (res, &((disc *) obj)->a);
1211       break;
1212     case PLANE:
1213       vcopy (res, &((plane *) obj)->a);
1214       break;
1215     case SPHERE:
1216       vcopy (res, &((sphere *) obj)->a);
1217       vsub (res, p);
1218       break;
1219     case CYLINDER:
1220       vset (res, 1, 1, 1);      /* fixme */
1221       break;
1222     default:
1223       g_error ("objnormal(): Unsupported object type!?\n");
1224     }
1225   vnorm (res, 1.0);
1226 
1227   for (i = 0; i < obj->numnormal; i++)
1228     {
1229       gint     k;
1230       GimpVector4   tmpcol[6];
1231       GimpVector4   q[6], nres;
1232       texture *t = &obj->normal[i];
1233       gdouble  nstep = 0.1;
1234 
1235       vset (&nres, 0, 0, 0);
1236       for (k = 0; k < 6; k++)
1237         {
1238           vcopy (&q[k], p);
1239         }
1240       q[0].x += nstep;
1241       q[1].x -= nstep;
1242       q[2].y += nstep;
1243       q[3].y -= nstep;
1244       q[4].z += nstep;
1245       q[5].z -= nstep;
1246 
1247       switch (t->type)
1248         {
1249         case MARBLE:
1250           for (k = 0; k < 6; k++)
1251             marble (&q[k], &tmpcol[k], t);
1252           break;
1253         case LIZARD:
1254           for (k = 0; k < 6; k++)
1255             lizard (&q[k], &tmpcol[k], t);
1256           break;
1257         case PERLIN:
1258           for (k = 0; k < 6; k++)
1259             perlin (&q[k], &tmpcol[k], t);
1260           break;
1261         case WOOD:
1262           for (k = 0; k < 6; k++)
1263             wood (&q[k], &tmpcol[k], t);
1264           break;
1265         case SPIRAL:
1266           for (k = 0; k < 6; k++)
1267             spiral (&q[k], &tmpcol[k], t);
1268           break;
1269         case SPOTS:
1270           for (k = 0; k < 6; k++)
1271             spots (&q[k], &tmpcol[k], t);
1272           break;
1273         case IMAGE:
1274           for (k = 0; k < 6; k++)
1275             imagepixel (&q[k], &tmpcol[k], t);
1276           break;
1277         case CHECKER:
1278         case SOLID:
1279         case PHONG:
1280         case REFRACTION:
1281         case REFLECTION:
1282         case TRANSPARENT:
1283         case SMOKE:
1284           continue;
1285           break;
1286         default:
1287           g_printerr ("Warning: unknown texture %d\n", t->type);
1288           break;
1289         }
1290 
1291       nres.x = tmpcol[0].x - tmpcol[1].x;
1292       nres.y = tmpcol[2].x - tmpcol[3].x;
1293       nres.z = tmpcol[4].x - tmpcol[5].x;
1294       vadd (&nres, res);
1295       vnorm (&nres, 1.0);
1296       vmul (&nres, t->amount);
1297       vadd (res, &nres);
1298       vnorm (res, 1.0);
1299     }
1300 }
1301 
1302 /*
1303    Quality:
1304    0 = Color only
1305    1 = Textures
1306    2 = Light + Normals
1307    3 = Shadows
1308    4 = Phong
1309    5 = Reflection + Refraction
1310  */
1311 
1312 static void
calclight(GimpVector4 * col,GimpVector4 * point,common * obj)1313 calclight (GimpVector4 * col, GimpVector4 * point, common * obj)
1314 {
1315   gint i, j;
1316   ray r;
1317   gdouble b, a;
1318   GimpVector4 lcol;
1319   GimpVector4 norm;
1320   GimpVector4 pcol;
1321 
1322   vcset (col, 0, 0, 0, 0);
1323 
1324   objcolor (&pcol, point, obj);
1325   a = pcol.w;
1326 
1327   if (world.quality < 2)
1328     {
1329       vcopy (col, &pcol);
1330       return;
1331     }
1332 
1333   for (i = 0; i < obj->numtexture; i++)
1334     {
1335       if (obj->texture[i].type == PHONG)
1336         continue;
1337       if (obj->texture[i].type == REFLECTION)
1338         continue;
1339       if (obj->texture[i].type == REFRACTION)
1340         continue;
1341       if (obj->texture[i].type == TRANSPARENT)
1342         continue;
1343       if (obj->texture[i].type == SMOKE)
1344         continue;
1345       vcopy (&lcol, &pcol);
1346       vvmul (&lcol, &obj->texture[i].ambient);
1347       vadd (col, &lcol);
1348     }
1349 
1350   objnormal (&norm, obj, point);
1351   vnorm (&norm, 1.0);
1352 
1353   r.inside = -1;
1354   r.ior = 1.0;
1355 
1356   for (i = 0; i < world.numlight; i++)
1357     {
1358       vcopy (&r.v1, point);
1359       vcopy (&r.v2, &world.light[i].a);
1360       vmix (&r.v1, &r.v1, &r.v2, 0.9999);
1361 
1362       vsub (&r.v1, &r.v2);
1363       vnorm (&r.v1, 1.0);
1364       b = vdot (&r.v1, &norm);
1365 
1366       if (b < 0.0)
1367         continue;
1368 
1369       for (j = 0; j < obj->numtexture; j++)
1370         {
1371           if (obj->texture[j].type == PHONG)
1372             continue;
1373           if (obj->texture[j].type == REFLECTION)
1374             continue;
1375           if (obj->texture[j].type == REFRACTION)
1376             continue;
1377           if (obj->texture[j].type == TRANSPARENT)
1378             continue;
1379           if (obj->texture[j].type == SMOKE)
1380             continue;
1381           vcopy (&lcol, &pcol);
1382           vvmul (&lcol, &world.light[i].color);
1383           vvmul (&lcol, &obj->texture[j].diffuse);
1384           vmul (&lcol, b);
1385           vadd (col, &lcol);
1386         }
1387     }
1388   col->w = a;
1389 }
1390 
1391 static void
calcphong(common * obj,ray * r2,GimpVector4 * col)1392 calcphong (common * obj, ray * r2, GimpVector4 * col)
1393 {
1394   gint    i, j;
1395   ray     r;
1396   gdouble b;
1397   GimpVector4  lcol;
1398   GimpVector4  norm;
1399   GimpVector4  pcol;
1400   gdouble ps;
1401 
1402   vcopy (&pcol, col);
1403 
1404   vcopy (&norm, &r2->v2);
1405   vsub (&norm, &r2->v1);
1406   vnorm (&norm, 1.0);
1407 
1408   r.inside = -1;
1409   r.ior = 1.0;
1410 
1411   for (i = 0; i < world.numlight; i++)
1412     {
1413       vcopy (&r.v1, &r2->v1);
1414       vcopy (&r.v2, &world.light[i].a);
1415       vmix (&r.v1, &r.v1, &r.v2, 0.9999);
1416 
1417       if (traceray (&r, NULL, -1, 1.0))
1418         continue;
1419 
1420       /* OK, light is visible */
1421 
1422       vsub (&r.v1, &r.v2);
1423       vnorm (&r.v1, 1.0);
1424       b = -vdot (&r.v1, &norm);
1425 
1426       for (j = 0; j < obj->numtexture; j++)
1427         {
1428           if (obj->texture[j].type != PHONG)
1429             continue;
1430 
1431           ps = obj->texture[j].phongsize;
1432 
1433           if (b < (1 - ps))
1434             continue;
1435           ps = (b - (1 - ps)) / ps;
1436 
1437           vcopy (&lcol, &obj->texture[j].phongcolor);
1438           vvmul (&lcol, &world.light[i].color);
1439           vmul (&lcol, ps);
1440           vadd (col, &lcol);
1441         }
1442     }
1443 }
1444 
1445 static int
traceray(ray * r,GimpVector4 * col,gint level,gdouble imp)1446 traceray (ray * r, GimpVector4 * col, gint level, gdouble imp)
1447 {
1448   gint     i, b = -1;
1449   gdouble  t = -1.0, min = 0.0;
1450   common  *obj, *bobj = NULL;
1451   gint     hits = 0;
1452   GimpVector4   p;
1453 
1454   if ((level == 0) || (imp < 0.005))
1455     {
1456       vset (col, 0, 1, 0);
1457       return 0;
1458     }
1459 
1460   for (i = 0; i < world.numobj; i++)
1461     {
1462       obj = (common *) & world.obj[i];
1463       switch (obj->type)
1464         {
1465         case TRIANGLE:
1466           t = checktri (r, (triangle *) & world.obj[i]);
1467           break;
1468         case DISC:
1469           t = checkdisc (r, (disc *) & world.obj[i]);
1470           break;
1471         case PLANE:
1472           t = checkplane (r, (plane *) & world.obj[i]);
1473           break;
1474         case SPHERE:
1475           t = checksphere (r, (sphere *) & world.obj[i]);
1476           break;
1477         case CYLINDER:
1478           t = checkcylinder (r, (cylinder *) & world.obj[i]);
1479           break;
1480         default:
1481           g_error ("Illegal object!!\n");
1482         }
1483       if (t <= 0.0)
1484         continue;
1485 
1486       if (!(obj->flags & NOSHADOW) && (level == -1))
1487         {
1488           return i + 1;
1489         }
1490 
1491       hits++;
1492       if ((!bobj) || (t < min))
1493         {
1494 
1495           min = t;
1496           b = i;
1497           bobj = obj;
1498         }
1499     }
1500   if (level == -1)
1501     return 0;
1502 
1503   if (bobj)
1504     {
1505       p.x = r->v1.x + (r->v2.x - r->v1.x) * min;
1506       p.y = r->v1.y + (r->v2.y - r->v1.y) * min;
1507       p.z = r->v1.z + (r->v2.z - r->v1.z) * min;
1508 
1509       calclight (col, &p, bobj);
1510 
1511       if (world.flags & SMARTAMBIENT)
1512         {
1513           gdouble ambient = 0.3 * exp (-min / world.smartambient);
1514           GimpVector4 lcol;
1515           objcolor (&lcol, &p, bobj);
1516           vmul (&lcol, ambient);
1517           vadd (col, &lcol);
1518         }
1519 
1520       for (i = 0; i < bobj->numtexture; i++)
1521         {
1522 
1523           if ((world.quality >= 4)
1524               && ((bobj->texture[i].type == REFLECTION)
1525                   || (bobj->texture[i].type == PHONG)))
1526             {
1527 
1528               GimpVector4 refcol, norm, ocol;
1529               ray ref;
1530 
1531               objcolor (&ocol, &p, bobj);
1532 
1533               vcopy (&ref.v1, &p);
1534               vcopy (&ref.v2, &r->v1);
1535               ref.inside = r->inside;
1536               ref.ior = r->ior;
1537 
1538               vmix (&ref.v1, &ref.v1, &ref.v2, 0.9999); /* push it a tad */
1539 
1540               vsub (&ref.v2, &p);
1541               objnormal (&norm, bobj, &p);
1542               vnorm (&norm, 1.0);
1543               vrotate (&norm, 180.0, &ref.v2);
1544 
1545               vmul (&norm, -0.0001);    /* push it a tad */
1546               vadd (&ref.v1, &norm);
1547 
1548               vnorm (&ref.v2, 1.0);
1549               vadd (&ref.v2, &p);
1550 
1551               if ((world.quality >= 5)
1552                   && (bobj->texture[i].type == REFLECTION))
1553                 {
1554                   traceray (&ref, &refcol, level - 1,
1555                             imp * vmax (&bobj->texture[i].reflection));
1556                   vvmul (&refcol, &bobj->texture[i].reflection);
1557                   refcol.w = ocol.w;
1558                   vadd (col, &refcol);
1559                 }
1560               if (bobj->texture[i].type == PHONG)
1561                 {
1562                   vcset (&refcol, 0, 0, 0, 0);
1563                   calcphong (bobj, &ref, &refcol);
1564                   refcol.w = ocol.w;
1565                   vadd (col, &refcol);
1566                 }
1567 
1568             }
1569 
1570           if ((world.quality >= 5) && (col->w < 1.0))
1571             {
1572               GimpVector4 refcol;
1573               ray ref;
1574 
1575               vcopy (&ref.v1, &p);
1576               vcopy (&ref.v2, &p);
1577               vsub (&ref.v2, &r->v1);
1578               vnorm (&ref.v2, 1.0);
1579               vadd (&ref.v2, &p);
1580 
1581               vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */
1582               traceray (&ref, &refcol, level - 1, imp * (1.0 - col->w));
1583               vmul (&refcol, (1.0 - col->w));
1584               vadd (col, &refcol);
1585             }
1586 
1587           if ((world.quality >= 5) && (bobj->texture[i].type == TRANSPARENT))
1588             {
1589               GimpVector4 refcol;
1590               ray ref;
1591 
1592               vcopy (&ref.v1, &p);
1593               vcopy (&ref.v2, &p);
1594               vsub (&ref.v2, &r->v1);
1595               vnorm (&ref.v2, 1.0);
1596               vadd (&ref.v2, &p);
1597 
1598               vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */
1599 
1600               traceray (&ref, &refcol, level - 1,
1601                         imp * vmax (&bobj->texture[i].transparent));
1602               vvmul (&refcol, &bobj->texture[i].transparent);
1603 
1604               vadd (col, &refcol);
1605             }
1606 
1607           if ((world.quality >= 5) && (bobj->texture[i].type == SMOKE))
1608             {
1609               GimpVector4 smcol, raydir, norm;
1610               double tran;
1611               ray ref;
1612 
1613               vcopy (&ref.v1, &p);
1614               vcopy (&ref.v2, &p);
1615               vsub (&ref.v2, &r->v1);
1616               vnorm (&ref.v2, 1.0);
1617               vadd (&ref.v2, &p);
1618 
1619               objnormal (&norm, bobj, &p);
1620               vcopy (&raydir, &r->v2);
1621               vsub (&raydir, &r->v1);
1622               vnorm (&raydir, 1.0);
1623               tran = vdot (&norm, &raydir);
1624               if (tran < 0.0)
1625                 continue;
1626               tran *= tran;
1627               vcopy (&smcol, &bobj->texture[i].color1);
1628               vmul (&smcol, tran);
1629               vadd (col, &smcol);
1630             }
1631 
1632           if ((world.quality >= 5) && (bobj->texture[i].type == REFRACTION))
1633             {
1634               GimpVector4 refcol, norm, tmpv;
1635               ray ref;
1636               double c1, c2, n1, n2, n;
1637 
1638               vcopy (&ref.v1, &p);
1639               vcopy (&ref.v2, &p);
1640               vsub (&ref.v2, &r->v1);
1641               vadd (&ref.v2, &r->v2);
1642 
1643               vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */
1644 
1645               vsub (&ref.v2, &p);
1646               objnormal (&norm, bobj, &p);
1647 
1648               if (r->inside == b)
1649                 {
1650                   ref.inside = -1;
1651                   ref.ior = 1.0;
1652                 }
1653               else
1654                 {
1655                   ref.inside = b;
1656                   ref.ior = bobj->texture[i].ior;
1657                 }
1658 
1659               c1 = vdot (&norm, &ref.v2);
1660 
1661               if (ref.inside < 0)
1662                 c1 = -c1;
1663 
1664               n1 = r->ior;      /* IOR of current media  */
1665               n2 = ref.ior;     /* IOR of new media  */
1666               n = n1 / n2;
1667               c2 = 1.0 - n * n * (1.0 - c1 * c1);
1668 
1669               if (c2 < 0.0)
1670                 {
1671                   /* FIXME: Internal reflection should occur */
1672                   c2 = sqrt (-c2);
1673 
1674                 }
1675               else
1676                 {
1677                   c2 = sqrt (c2);
1678                 }
1679 
1680               vmul (&ref.v2, n);
1681               vcopy (&tmpv, &norm);
1682               vmul (&tmpv, n * c1 - c2);
1683               vadd (&ref.v2, &tmpv);
1684 
1685               vnorm (&ref.v2, 1.0);
1686               vadd (&ref.v2, &p);
1687 
1688               traceray (&ref, &refcol, level - 1,
1689                         imp * vmax (&bobj->texture[i].refraction));
1690 
1691               vvmul (&refcol, &bobj->texture[i].refraction);
1692               vadd (col, &refcol);
1693             }
1694         }
1695     }
1696   else
1697     {
1698       vcset (col, 0, 0, 0, 0);
1699       min = 10000.0;
1700       vcset (&p, 0, 0, 0, 0);
1701     }
1702 
1703   for (i = 0; i < world.numatmos; i++)
1704     {
1705       GimpVector4 tmpcol;
1706       if (world.atmos[i].type == FOG)
1707         {
1708           gdouble v, pt[3];
1709           pt[0] = p.x;
1710           pt[1] = p.y;
1711           pt[2] = p.z;
1712           if ((v = world.atmos[i].turbulence) > 0.0)
1713             v = turbulence (pt, 1, 256) * world.atmos[i].turbulence;
1714           v = exp (-(min + v) / world.atmos[i].density);
1715           vmul (col, v);
1716           vcopy (&tmpcol, &world.atmos[i].color);
1717           vmul (&tmpcol, 1.0 - v);
1718           vadd (col, &tmpcol);
1719         }
1720     }
1721 
1722   return hits;
1723 }
1724 
1725 static void
setdefaults(texture * t)1726 setdefaults (texture * t)
1727 {
1728   memset (t, 0, sizeof (texture));
1729   t->type = SOLID;
1730   vcset (&t->color1, 1, 1, 1, 1);
1731   vcset (&t->color2, 0, 0, 0, 1);
1732   vcset (&t->diffuse, 1, 1, 1, 1);
1733   vcset (&t->ambient, 0, 0, 0, 1);
1734   vset (&t->scale, 1, 1, 1);
1735   vset (&t->rotate, 0, 0, 0);
1736   vset (&t->translate, 0, 0, 0);
1737   t->oscale = 1.0;
1738   t->amount = 1.0;
1739   t->exp = 1.0;
1740 }
1741 
1742 static gchar *
mklabel(texture * t)1743 mklabel (texture * t)
1744 {
1745   struct textures_t *l;
1746   static gchar tmps[100];
1747 
1748   if (t->majtype == 0)
1749     strcpy (tmps, _("Texture"));
1750   else if (t->majtype == 1)
1751     strcpy (tmps, _("Bumpmap"));
1752   else if (t->majtype == 2)
1753     strcpy (tmps, _("Light"));
1754   else
1755     strcpy (tmps, "<unknown>");
1756   if ((t->majtype == 0) || (t->majtype == 1))
1757     {
1758       strcat (tmps, " / ");
1759       l = textures;
1760       while (l->s)
1761         {
1762           if (t->type == l->n)
1763             {
1764               strcat (tmps, gettext (l->s));
1765               break;
1766             }
1767           l++;
1768         }
1769     }
1770   return tmps;
1771 }
1772 
1773 static texture *
currenttexture(void)1774 currenttexture (void)
1775 {
1776   GtkTreeSelection *sel;
1777   GtkTreeIter       iter;
1778   texture          *t = NULL;
1779 
1780   sel = gtk_tree_view_get_selection (texturelist);
1781 
1782   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
1783     {
1784       gtk_tree_model_get (gtk_tree_view_get_model (texturelist), &iter,
1785                           TEXTURE, &t,
1786                           -1);
1787     }
1788 
1789   return t;
1790 }
1791 
1792 static void
relabel(void)1793 relabel (void)
1794 {
1795   GtkTreeModel     *model;
1796   GtkTreeSelection *sel;
1797   GtkTreeIter       iter;
1798   texture          *t = NULL;
1799 
1800   sel = gtk_tree_view_get_selection (texturelist);
1801 
1802   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
1803     {
1804       model = gtk_tree_view_get_model (texturelist);
1805 
1806       gtk_tree_model_get (model, &iter,
1807                           TEXTURE, &t,
1808                           -1);
1809       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1810                           TYPE, mklabel (t),
1811                           -1);
1812     }
1813 }
1814 
1815 static gboolean noupdate = FALSE;
1816 
1817 static void
setvals(texture * t)1818 setvals (texture *t)
1819 {
1820   struct textures_t *l;
1821 
1822   if (!t)
1823     return;
1824 
1825   noupdate = TRUE;
1826   gtk_adjustment_set_value (GTK_ADJUSTMENT (amountscale), t->amount);
1827 
1828   gtk_adjustment_set_value (GTK_ADJUSTMENT (scalescale), t->oscale);
1829 
1830   gtk_adjustment_set_value (GTK_ADJUSTMENT (scalexscale), t->scale.x);
1831   gtk_adjustment_set_value (GTK_ADJUSTMENT (scaleyscale), t->scale.y);
1832   gtk_adjustment_set_value (GTK_ADJUSTMENT (scalezscale), t->scale.z);
1833 
1834   gtk_adjustment_set_value (GTK_ADJUSTMENT (rotxscale), t->rotate.x);
1835   gtk_adjustment_set_value (GTK_ADJUSTMENT (rotyscale), t->rotate.y);
1836   gtk_adjustment_set_value (GTK_ADJUSTMENT (rotzscale), t->rotate.z);
1837 
1838   gtk_adjustment_set_value (GTK_ADJUSTMENT (posxscale), t->translate.x);
1839   gtk_adjustment_set_value (GTK_ADJUSTMENT (posyscale), t->translate.y);
1840   gtk_adjustment_set_value (GTK_ADJUSTMENT (poszscale), t->translate.z);
1841 
1842   gtk_adjustment_set_value (GTK_ADJUSTMENT (turbulencescale),
1843                             t->turbulence.x);
1844   gtk_adjustment_set_value (GTK_ADJUSTMENT (expscale), t->exp);
1845 
1846   drawcolor1 (NULL);
1847   drawcolor2 (NULL);
1848 
1849   l = textures;
1850   while (l->s)
1851     {
1852       if (l->n == t->type)
1853         {
1854           gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (texturemenu),
1855                                          l->index);
1856           break;
1857         }
1858       l++;
1859     }
1860 
1861   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (typemenu), t->majtype);
1862 
1863   noupdate = FALSE;
1864 }
1865 
1866 static void
selectitem(GtkTreeSelection * treeselection,gpointer data)1867 selectitem (GtkTreeSelection *treeselection,
1868             gpointer          data)
1869 {
1870   setvals (currenttexture ());
1871 }
1872 
1873 static void
addtexture(void)1874 addtexture (void)
1875 {
1876   GtkListStore *list_store;
1877   GtkTreeIter   iter;
1878   gint          n = s.com.numtexture;
1879 
1880   if (n == MAXTEXTUREPEROBJ - 1)
1881     return;
1882 
1883   setdefaults (&s.com.texture[n]);
1884 
1885   list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
1886 
1887   gtk_list_store_append (list_store, &iter);
1888   gtk_list_store_set (list_store, &iter,
1889                       TYPE,    mklabel (&s.com.texture[n]),
1890                       TEXTURE, &s.com.texture[n],
1891                       -1);
1892   gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
1893                                   &iter);
1894 
1895   s.com.numtexture++;
1896 
1897   restartrender ();
1898 }
1899 
1900 static void
duptexture(void)1901 duptexture (void)
1902 {
1903   GtkListStore *list_store;
1904   GtkTreeIter   iter;
1905   texture      *t = currenttexture ();
1906   gint          n = s.com.numtexture;
1907 
1908   if (n == MAXTEXTUREPEROBJ - 1)
1909     return;
1910   if (!t)
1911     return;
1912 
1913   s.com.texture[n] = *t;
1914 
1915   list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
1916 
1917   gtk_list_store_append (list_store, &iter);
1918   gtk_list_store_set (list_store, &iter,
1919                       TYPE,    mklabel (&s.com.texture[n]),
1920                       TEXTURE, &s.com.texture[n],
1921                       -1);
1922   gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
1923                                   &iter);
1924 
1925   s.com.numtexture++;
1926 
1927   restartrender ();
1928 }
1929 
1930 static void
rebuildlist(void)1931 rebuildlist (void)
1932 {
1933   GtkListStore     *list_store;
1934   GtkTreeSelection *sel;
1935   GtkTreeIter       iter;
1936   gint              n;
1937 
1938   sel = gtk_tree_view_get_selection (texturelist);
1939 
1940   for (n = 0; n < s.com.numtexture; n++)
1941     {
1942       if (s.com.numtexture && (s.com.texture[n].majtype < 0))
1943         {
1944           gint i;
1945 
1946           for (i = n; i < s.com.numtexture - 1; i++)
1947             s.com.texture[i] = s.com.texture[i + 1];
1948 
1949           s.com.numtexture--;
1950           n--;
1951         }
1952     }
1953 
1954   list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
1955 
1956   for (n = 0; n < s.com.numtexture; n++)
1957     {
1958       gtk_list_store_append (list_store, &iter);
1959       gtk_list_store_set (list_store, &iter,
1960                           TYPE,    mklabel (&s.com.texture[n]),
1961                           TEXTURE, &s.com.texture[n],
1962                           -1);
1963     }
1964 
1965   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
1966     gtk_tree_selection_select_iter (sel, &iter);
1967 
1968   restartrender ();
1969 }
1970 
1971 static void
deltexture(void)1972 deltexture (void)
1973 {
1974   GtkTreeSelection *sel;
1975   GtkTreeModel     *model;
1976   GtkTreeIter       iter;
1977   texture          *t = NULL;
1978 
1979   sel = gtk_tree_view_get_selection (texturelist);
1980 
1981   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
1982     {
1983       model = gtk_tree_view_get_model (texturelist);
1984 
1985       gtk_tree_model_get (model, &iter,
1986                           TEXTURE, &t,
1987                           -1);
1988       t->majtype = -1;
1989       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1990     }
1991 
1992   restartrender ();
1993 }
1994 
1995 static void
loadit(const gchar * fn)1996 loadit (const gchar * fn)
1997 {
1998   FILE    *f;
1999   gchar    endbuf[21 * (G_ASCII_DTOSTR_BUF_SIZE + 1)];
2000   gchar   *end = endbuf;
2001   gchar    line[1024];
2002   gchar    fmt_str[16];
2003   gint     i;
2004   texture *t;
2005   gint     majtype, type;
2006 
2007   f = g_fopen (fn, "rt");
2008   if (! f)
2009     {
2010       g_message (_("Could not open '%s' for reading: %s"),
2011                  gimp_filename_to_utf8 (fn), g_strerror (errno));
2012       return;
2013     }
2014 
2015   if (2 != fscanf (f, "%d %d", &majtype, &type) || majtype < 0 || majtype > 2)
2016     {
2017       g_message (_("File '%s' is not a valid save file."),
2018                  gimp_filename_to_utf8 (fn));
2019       fclose (f);
2020       return;
2021     }
2022 
2023   rewind (f);
2024 
2025   s.com.numtexture = 0;
2026 
2027   snprintf (fmt_str, sizeof (fmt_str), "%%d %%d %%%" G_GSIZE_FORMAT "s", sizeof (endbuf) - 1);
2028 
2029   while (!feof (f))
2030     {
2031 
2032       if (!fgets (line, 1023, f))
2033         break;
2034 
2035       i = s.com.numtexture;
2036       t = &s.com.texture[i];
2037       setdefaults (t);
2038 
2039       if (sscanf (line, fmt_str, &t->majtype, &t->type, end) != 3)
2040         t->color1.x = g_ascii_strtod (end, &end);
2041       if (end && errno != ERANGE)
2042         t->color1.y = g_ascii_strtod (end, &end);
2043       if (end && errno != ERANGE)
2044         t->color1.z = g_ascii_strtod (end, &end);
2045       if (end && errno != ERANGE)
2046         t->color1.w = g_ascii_strtod (end, &end);
2047       if (end && errno != ERANGE)
2048         t->color2.x = g_ascii_strtod (end, &end);
2049       if (end && errno != ERANGE)
2050         t->color2.y = g_ascii_strtod (end, &end);
2051       if (end && errno != ERANGE)
2052         t->color2.z = g_ascii_strtod (end, &end);
2053       if (end && errno != ERANGE)
2054         t->color2.w = g_ascii_strtod (end, &end);
2055       if (end && errno != ERANGE)
2056         t->oscale = g_ascii_strtod (end, &end);
2057       if (end && errno != ERANGE)
2058         t->turbulence.x = g_ascii_strtod (end, &end);
2059       if (end && errno != ERANGE)
2060         t->amount = g_ascii_strtod (end, &end);
2061       if (end && errno != ERANGE)
2062         t->exp = g_ascii_strtod (end, &end);
2063       if (end && errno != ERANGE)
2064         t->scale.x = g_ascii_strtod (end, &end);
2065       if (end && errno != ERANGE)
2066         t->scale.y = g_ascii_strtod (end, &end);
2067       if (end && errno != ERANGE)
2068         t->scale.z = g_ascii_strtod (end, &end);
2069       if (end && errno != ERANGE)
2070         t->rotate.x = g_ascii_strtod (end, &end);
2071       if (end && errno != ERANGE)
2072         t->rotate.y = g_ascii_strtod (end, &end);
2073       if (end && errno != ERANGE)
2074         t->rotate.z = g_ascii_strtod (end, &end);
2075       if (end && errno != ERANGE)
2076         t->translate.x = g_ascii_strtod (end, &end);
2077       if (end && errno != ERANGE)
2078         t->translate.y = g_ascii_strtod (end, &end);
2079       if (end && errno != ERANGE)
2080         t->translate.z = g_ascii_strtod (end, &end);
2081 
2082       s.com.numtexture++;
2083     }
2084 
2085   fclose (f);
2086 }
2087 
2088 static void
loadpreset_response(GtkWidget * dialog,gint response_id,gpointer data)2089 loadpreset_response (GtkWidget *dialog,
2090                      gint       response_id,
2091                      gpointer   data)
2092 {
2093   if (response_id == GTK_RESPONSE_OK)
2094     {
2095       GtkTreeModel *model = gtk_tree_view_get_model (texturelist);
2096       gchar        *name;
2097 
2098       name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2099 
2100       gtk_list_store_clear (GTK_LIST_STORE (model));
2101 
2102       loadit (name);
2103       g_free (name);
2104 
2105       rebuildlist ();
2106     }
2107 
2108   gtk_widget_hide (dialog);
2109 }
2110 
2111 static void
saveit(const gchar * fn)2112 saveit (const gchar *fn)
2113 {
2114   gint   i;
2115   FILE  *f;
2116   gchar  buf[G_ASCII_DTOSTR_BUF_SIZE];
2117 
2118   f = g_fopen (fn, "wt");
2119   if (!f)
2120     {
2121       g_message (_("Could not open '%s' for writing: %s"),
2122                  gimp_filename_to_utf8 (fn), g_strerror (errno));
2123       return;
2124     }
2125 
2126   for (i = 0; i < s.com.numtexture; i++)
2127     {
2128       texture *t = &s.com.texture[i];
2129 
2130       if (t->majtype < 0)
2131         continue;
2132 
2133       fprintf (f, "%d %d", t->majtype, t->type);
2134       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.x));
2135       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.y));
2136       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.z));
2137       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.w));
2138       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.x));
2139       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.y));
2140       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.z));
2141       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.w));
2142       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->oscale));
2143       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->turbulence.x));
2144       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->amount));
2145       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->exp));
2146       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.x));
2147       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.y));
2148       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.z));
2149       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.x));
2150       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.y));
2151       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.z));
2152       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.x));
2153       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.y));
2154       fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.z));
2155       fprintf (f, "\n");
2156     }
2157 
2158   fclose (f);
2159 }
2160 
2161 static void
savepreset_response(GtkWidget * dialog,gint response_id,gpointer data)2162 savepreset_response (GtkWidget *dialog,
2163                      gint       response_id,
2164                      gpointer   data)
2165 {
2166   if (response_id == GTK_RESPONSE_OK)
2167     {
2168       gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2169 
2170       saveit (name);
2171       g_free (name);
2172     }
2173 
2174   gtk_widget_hide (dialog);
2175 }
2176 
2177 static void
loadpreset(GtkWidget * widget,GtkWidget * parent)2178 loadpreset (GtkWidget *widget,
2179             GtkWidget *parent)
2180 {
2181   fileselect (GTK_FILE_CHOOSER_ACTION_OPEN, parent);
2182 }
2183 
2184 static void
savepreset(GtkWidget * widget,GtkWidget * parent)2185 savepreset (GtkWidget *widget,
2186             GtkWidget *parent)
2187 {
2188   fileselect (GTK_FILE_CHOOSER_ACTION_SAVE, parent);
2189 }
2190 
2191 static void
fileselect(GtkFileChooserAction action,GtkWidget * parent)2192 fileselect (GtkFileChooserAction  action,
2193             GtkWidget            *parent)
2194 {
2195   static GtkWidget *windows[2] = { NULL, NULL };
2196 
2197   gchar *titles[]   = { N_("Open File"), N_("Save File") };
2198   void  *handlers[] = { loadpreset_response,   savepreset_response };
2199 
2200   if (! windows[action])
2201     {
2202       GtkWidget *dialog = windows[action] =
2203         gtk_file_chooser_dialog_new (gettext (titles[action]),
2204                                      GTK_WINDOW (parent),
2205                                      action,
2206 
2207                                      _("_Cancel"), GTK_RESPONSE_CANCEL,
2208 
2209                                      action == GTK_FILE_CHOOSER_ACTION_OPEN ?
2210                                      _("_Open") : _("_Save"),
2211                                      GTK_RESPONSE_OK,
2212 
2213                                      NULL);
2214 
2215       gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
2216                                                GTK_RESPONSE_OK,
2217                                                GTK_RESPONSE_CANCEL,
2218                                                -1);
2219 
2220       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2221 
2222       if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
2223         gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
2224                                                         TRUE);
2225 
2226       g_signal_connect (dialog, "destroy",
2227                         G_CALLBACK (gtk_widget_destroyed),
2228                         &windows[action]);
2229       g_signal_connect (dialog, "response",
2230                         G_CALLBACK (handlers[action]),
2231                         NULL);
2232     }
2233 
2234   gtk_window_present (GTK_WINDOW (windows[action]));
2235 }
2236 
2237 static void
initworld(void)2238 initworld (void)
2239 {
2240   gint i;
2241 
2242   memset (&world, 0, sizeof (world));
2243 
2244   s.com.type = SPHERE;
2245   s.a.x = s.a.y = s.a.z = 0.0;
2246   s.r = 4.0;
2247 
2248   /* not: world.obj[0] = s;
2249    * s is a sphere so error C2115: '=' : incompatible types
2250    */
2251   memcpy (&world.obj[0], &s, sizeof (s));
2252   world.numobj = 1;
2253 
2254   world.obj[0].com.numtexture = 0;
2255   world.obj[0].com.numnormal = 0;
2256 
2257   for (i = 0; i < s.com.numtexture; i++)
2258     {
2259       common *c = &s.com;
2260       common *d = &world.obj[0].com;
2261       texture *t = &c->texture[i];
2262       if ((t->amount <= 0.0) || (t->majtype < 0))
2263         continue;
2264       if (t->majtype == 0)
2265         {                       /* Normal texture */
2266           if (t->type == PHONG)
2267             {
2268               t->phongcolor = t->color1;
2269               t->phongsize = t->oscale / 25.0;
2270             }
2271           d->texture[d->numtexture] = *t;
2272           vmul (&d->texture[d->numtexture].scale,
2273                 d->texture[d->numtexture].oscale);
2274           d->numtexture++;
2275         }
2276       else if (t->majtype == 1)
2277         {                       /* Bumpmap */
2278           d->normal[d->numnormal] = *t;
2279           vmul (&d->normal[d->numnormal].scale,
2280                 d->texture[d->numnormal].oscale);
2281           d->numnormal++;
2282         }
2283       else if (t->majtype == 2)
2284         {                       /* Lightsource */
2285           light l;
2286           vcopy (&l.a, &t->translate);
2287           vcopy (&l.color, &t->color1);
2288           vmul (&l.color, t->amount);
2289           world.light[world.numlight] = l;
2290           world.numlight++;
2291         }
2292     }
2293 
2294   world.quality = 5;
2295 
2296   world.flags |= SMARTAMBIENT;
2297   world.smartambient = 40.0;
2298 }
2299 
2300 static gboolean
expose_event(GtkWidget * widget,GdkEventExpose * event)2301 expose_event (GtkWidget      *widget,
2302               GdkEventExpose *event)
2303 {
2304   cairo_t *cr;
2305 
2306   cr = gdk_cairo_create (gtk_widget_get_window (widget));
2307 
2308   gdk_cairo_region (cr, event->region);
2309   cairo_clip (cr);
2310 
2311   cairo_set_source_surface (cr, buffer, 0.0, 0.0);
2312 
2313   cairo_paint (cr);
2314 
2315   cairo_destroy (cr);
2316 
2317   return TRUE;
2318 }
2319 
2320 static void
restartrender(void)2321 restartrender (void)
2322 {
2323   if (idle_id)
2324     g_source_remove (idle_id);
2325 
2326   idle_id = g_idle_add ((GSourceFunc) render, NULL);
2327 }
2328 
2329 static void
selecttexture(GtkWidget * widget,gpointer data)2330 selecttexture (GtkWidget *widget,
2331                gpointer   data)
2332 {
2333   texture *t;
2334 
2335   if (noupdate)
2336     return;
2337 
2338   t = currenttexture ();
2339   if (!t)
2340     return;
2341 
2342   gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->type);
2343 
2344   relabel ();
2345   restartrender ();
2346 }
2347 
2348 static void
selecttype(GtkWidget * widget,gpointer data)2349 selecttype (GtkWidget *widget,
2350             gpointer   data)
2351 {
2352   texture *t;
2353 
2354   if (noupdate)
2355     return;
2356 
2357   t = currenttexture ();
2358   if (!t)
2359     return;
2360 
2361   gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->majtype);
2362 
2363   relabel ();
2364   restartrender ();
2365 }
2366 
2367 static void
getscales(GtkWidget * widget,gpointer data)2368 getscales (GtkWidget *widget,
2369            gpointer   data)
2370 {
2371   gdouble f;
2372   texture *t;
2373 
2374   if (noupdate)
2375     return;
2376 
2377   t = currenttexture ();
2378   if (!t)
2379     return;
2380 
2381   t->amount = gtk_adjustment_get_value (GTK_ADJUSTMENT (amountscale));
2382   t->exp = gtk_adjustment_get_value (GTK_ADJUSTMENT (expscale));
2383 
2384   f = gtk_adjustment_get_value (GTK_ADJUSTMENT (turbulencescale));
2385   vset (&t->turbulence, f, f, f);
2386 
2387   t->oscale = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalescale));
2388 
2389   t->scale.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalexscale));
2390   t->scale.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (scaleyscale));
2391   t->scale.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalezscale));
2392 
2393   t->rotate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotxscale));
2394   t->rotate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotyscale));
2395   t->rotate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotzscale));
2396 
2397   t->translate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (posxscale));
2398   t->translate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (posyscale));
2399   t->translate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (poszscale));
2400 
2401   restartrender ();
2402 }
2403 
2404 
2405 static void
color1_changed(GimpColorButton * button)2406 color1_changed (GimpColorButton *button)
2407 {
2408   texture *t = currenttexture ();
2409 
2410   if (t)
2411     {
2412       GimpRGB color;
2413 
2414       gimp_color_button_get_color (button, &color);
2415 
2416       t->color1.x = color.r;
2417       t->color1.y = color.g;
2418       t->color1.z = color.b;
2419       t->color1.w = color.a;
2420 
2421       restartrender ();
2422     }
2423 }
2424 
2425 static void
color2_changed(GimpColorButton * button)2426 color2_changed (GimpColorButton *button)
2427 {
2428   texture *t = currenttexture ();
2429 
2430   if (t)
2431     {
2432       GimpRGB color;
2433 
2434       gimp_color_button_get_color (button, &color);
2435 
2436       t->color2.x = color.r;
2437       t->color2.y = color.g;
2438       t->color2.z = color.b;
2439       t->color2.w = color.a;
2440 
2441       restartrender ();
2442     }
2443 }
2444 
2445 static void
drawcolor1(GtkWidget * w)2446 drawcolor1 (GtkWidget *w)
2447 {
2448   static GtkWidget *lastw = NULL;
2449 
2450   GimpRGB  color;
2451   texture *t = currenttexture ();
2452 
2453   if (w)
2454     lastw = w;
2455   else
2456     w = lastw;
2457 
2458   if (!w)
2459     return;
2460   if (!t)
2461     return;
2462 
2463   gimp_rgba_set (&color,
2464                  t->color1.x, t->color1.y, t->color1.z, t->color1.w);
2465 
2466   gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color);
2467 }
2468 
2469 static void
drawcolor2(GtkWidget * w)2470 drawcolor2 (GtkWidget *w)
2471 {
2472   static GtkWidget *lastw = NULL;
2473 
2474   GimpRGB  color;
2475   texture *t = currenttexture ();
2476 
2477   if (w)
2478     lastw = w;
2479   else
2480     w = lastw;
2481 
2482   if (!w)
2483     return;
2484   if (!t)
2485     return;
2486 
2487   gimp_rgba_set (&color,
2488                  t->color2.x, t->color2.y, t->color2.z, t->color2.w);
2489 
2490   gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color);
2491 }
2492 
2493 static gboolean do_run = FALSE;
2494 
2495 static void
sphere_response(GtkWidget * widget,gint response_id,gpointer data)2496 sphere_response (GtkWidget *widget,
2497                  gint       response_id,
2498                  gpointer   data)
2499 {
2500   switch (response_id)
2501     {
2502     case RESPONSE_RESET:
2503       s.com.numtexture = 3;
2504 
2505       setdefaults (&s.com.texture[0]);
2506       setdefaults (&s.com.texture[1]);
2507       setdefaults (&s.com.texture[2]);
2508 
2509       s.com.texture[1].majtype = 2;
2510       vset (&s.com.texture[1].color1, 1, 1, 1);
2511       vset (&s.com.texture[1].translate, -15, -15, -15);
2512 
2513       s.com.texture[2].majtype = 2;
2514       vset (&s.com.texture[2].color1, 0, 0.4, 0.4);
2515       vset (&s.com.texture[2].translate, 15, 15, -15);
2516 
2517       gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)));
2518 
2519       rebuildlist ();
2520       break;
2521 
2522     case GTK_RESPONSE_OK:
2523       if (idle_id)
2524         {
2525           g_source_remove (idle_id);
2526           idle_id = 0;
2527         }
2528 
2529       do_run = TRUE;
2530 
2531     default:
2532       gtk_widget_hide (widget);
2533       gtk_main_quit ();
2534       break;
2535     }
2536 }
2537 
2538 static GtkWidget *
makewindow(void)2539 makewindow (void)
2540 {
2541   GtkListStore      *store;
2542   GtkTreeViewColumn *col;
2543   GtkTreeSelection  *selection;
2544   GtkWidget  *window;
2545   GtkWidget  *main_hbox;
2546   GtkWidget  *main_vbox;
2547   GtkWidget  *table;
2548   GtkWidget  *frame;
2549   GtkWidget  *scrolled;
2550   GtkWidget  *hbox;
2551   GtkWidget  *vbox;
2552   GtkWidget  *button;
2553   GtkWidget  *list;
2554   GimpRGB     rgb = { 0, 0, 0, 0 };
2555 
2556   window = gimp_dialog_new (_("Sphere Designer"), PLUG_IN_ROLE,
2557                             NULL, 0,
2558                             gimp_standard_help_func, PLUG_IN_PROC,
2559 
2560                             _("_Reset"),  RESPONSE_RESET,
2561                             _("_Cancel"), GTK_RESPONSE_CANCEL,
2562                             _("_OK"),     GTK_RESPONSE_OK,
2563 
2564                             NULL);
2565 
2566   gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
2567                                            RESPONSE_RESET,
2568                                            GTK_RESPONSE_OK,
2569                                            GTK_RESPONSE_CANCEL,
2570                                            -1);
2571 
2572   gimp_window_set_transient (GTK_WINDOW (window));
2573 
2574   g_signal_connect (window, "response",
2575                     G_CALLBACK (sphere_response),
2576                     NULL);
2577 
2578   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
2579   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
2580   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
2581                       main_vbox, TRUE, TRUE, 0);
2582   gtk_widget_show (main_vbox);
2583 
2584   main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
2585   gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0);
2586   gtk_widget_show (main_hbox);
2587 
2588   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
2589   gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
2590   gtk_widget_show (vbox);
2591 
2592   frame = gtk_frame_new (NULL);
2593   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2594   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
2595   gtk_widget_show (frame);
2596 
2597   drawarea = gtk_drawing_area_new ();
2598   gtk_container_add (GTK_CONTAINER (frame), drawarea);
2599   gtk_widget_set_size_request (drawarea, PREVIEWSIZE, PREVIEWSIZE);
2600   gtk_widget_show (drawarea);
2601 
2602   g_signal_connect (drawarea, "expose-event",
2603                     G_CALLBACK (expose_event), NULL);
2604 
2605   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2606   gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
2607   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2608   gtk_widget_show (hbox);
2609 
2610   button = gtk_button_new_with_mnemonic (_("_Open"));
2611   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2612   gtk_widget_show (button);
2613 
2614   g_signal_connect (button, "clicked",
2615                     G_CALLBACK (loadpreset),
2616                     window);
2617 
2618   button = gtk_button_new_with_mnemonic (_("_Save"));
2619   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2620   gtk_widget_show (button);
2621 
2622   g_signal_connect (button, "clicked",
2623                     G_CALLBACK (savepreset),
2624                     window);
2625 
2626   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
2627   gtk_box_pack_end (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
2628   gtk_widget_show (vbox);
2629 
2630   scrolled = gtk_scrolled_window_new (NULL, NULL);
2631   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
2632                                        GTK_SHADOW_IN);
2633   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
2634                                   GTK_POLICY_NEVER,
2635                                   GTK_POLICY_AUTOMATIC);
2636   gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
2637   gtk_widget_show (scrolled);
2638 
2639   store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
2640   list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
2641   g_object_unref (store);
2642 
2643   texturelist = GTK_TREE_VIEW (list);
2644 
2645   selection = gtk_tree_view_get_selection (texturelist);
2646 
2647   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2648 
2649   g_signal_connect (selection, "changed",
2650                     G_CALLBACK (selectitem),
2651                     NULL);
2652 
2653   gtk_widget_set_size_request (list, -1, 150);
2654   gtk_container_add (GTK_CONTAINER (scrolled), list);
2655   gtk_widget_show (list);
2656 
2657   col = gtk_tree_view_column_new_with_attributes (_("Layers"),
2658                                                   gtk_cell_renderer_text_new (),
2659                                                   "text", TYPE,
2660                                                   NULL);
2661   gtk_tree_view_append_column (texturelist, col);
2662 
2663   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2664   gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
2665   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2666   gtk_widget_show (hbox);
2667 
2668   button = gtk_button_new_with_mnemonic (_("_New"));
2669   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2670   g_signal_connect_swapped (button, "clicked",
2671                             G_CALLBACK (addtexture), NULL);
2672   gtk_widget_show (button);
2673 
2674   button = gtk_button_new_with_mnemonic (_("D_uplicate"));
2675   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2676   g_signal_connect_swapped (button, "clicked",
2677                             G_CALLBACK (duptexture), NULL);
2678   gtk_widget_show (button);
2679 
2680   button = gtk_button_new_with_mnemonic (_("_Delete"));
2681   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2682   g_signal_connect_swapped (button, "clicked",
2683                             G_CALLBACK (deltexture), NULL);
2684   gtk_widget_show (button);
2685 
2686   main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
2687   gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, FALSE, 0);
2688   gtk_widget_show (main_hbox);
2689 
2690   frame = gimp_frame_new (_("Properties"));
2691   gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
2692   gtk_widget_show (frame);
2693 
2694   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2695   gtk_container_add (GTK_CONTAINER (frame), vbox);
2696   gtk_widget_show (vbox);
2697 
2698   table = gtk_table_new (7, 3, FALSE);
2699   gtk_table_set_col_spacings (GTK_TABLE (table), 2);
2700   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
2701   gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
2702   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2703   gtk_widget_show (table);
2704 
2705   typemenu = gimp_int_combo_box_new (_("Texture"), 0,
2706                                      _("Bump"),    1,
2707                                      _("Light"),   2,
2708                                      NULL);
2709   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (typemenu), 0,
2710                               G_CALLBACK (selecttype),
2711                               NULL);
2712 
2713   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
2714                              _("Type:"), 0.0, 0.5,
2715                              typemenu, 2, FALSE);
2716 
2717   texturemenu = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
2718   {
2719     struct textures_t *t;
2720 
2721     for (t = textures; t->s; t++)
2722       gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (texturemenu),
2723                                  GIMP_INT_STORE_VALUE, t->n,
2724                                  GIMP_INT_STORE_LABEL, gettext (t->s),
2725                                  -1);
2726   }
2727 
2728   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (texturemenu), 0,
2729                               G_CALLBACK (selecttexture),
2730                               NULL);
2731 
2732   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
2733                              _("Texture:"), 0.0, 0.5,
2734                              texturemenu, 2, FALSE);
2735 
2736   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
2737   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
2738                              _("Colors:"), 0.0, 0.5,
2739                              hbox, 2, FALSE);
2740 
2741   button = gimp_color_button_new (_("Color Selection Dialog"),
2742                                   COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb,
2743                                   GIMP_COLOR_AREA_FLAT);
2744   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2745   gtk_widget_show (button);
2746   drawcolor1 (button);
2747 
2748   g_signal_connect (button, "color-changed",
2749                     G_CALLBACK (color1_changed),
2750                     NULL);
2751 
2752   button = gimp_color_button_new (_("Color Selection Dialog"),
2753                                   COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb,
2754                                   GIMP_COLOR_AREA_FLAT);
2755   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
2756   gtk_widget_show (button);
2757   drawcolor2 (button);
2758 
2759   g_signal_connect (button, "color-changed",
2760                     G_CALLBACK (color2_changed),
2761                     NULL);
2762 
2763   scalescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Scale:"),
2764                                      100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1,
2765                                      TRUE, 0.0, 0.0, NULL, NULL);
2766   g_signal_connect (scalescale, "value-changed",
2767                     G_CALLBACK (getscales),
2768                     NULL);
2769 
2770   turbulencescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
2771                                           _("Turbulence:"),
2772                                           100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1,
2773                                           TRUE, 0.0, 0.0, NULL, NULL);
2774   g_signal_connect (turbulencescale, "value-changed",
2775                     G_CALLBACK (getscales),
2776                     NULL);
2777 
2778   amountscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Amount:"),
2779                                        100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2,
2780                                        TRUE, 0.0, 0.0, NULL, NULL);
2781   g_signal_connect (amountscale, "value-changed",
2782                     G_CALLBACK (getscales),
2783                     NULL);
2784 
2785   expscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Exp.:"),
2786                                    100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2,
2787                                    TRUE, 0.0, 0.0, NULL, NULL);
2788   g_signal_connect (expscale, "value-changed",
2789                     G_CALLBACK (getscales),
2790                     NULL);
2791 
2792   frame = gimp_frame_new (_("Transformations"));
2793   gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
2794   gtk_widget_show (frame);
2795 
2796   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2797   gtk_container_add (GTK_CONTAINER (frame), vbox);
2798   gtk_widget_show (vbox);
2799 
2800   table = gtk_table_new (9, 3, FALSE);
2801   gtk_table_set_col_spacings (GTK_TABLE (table), 2);
2802   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
2803   gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
2804   gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
2805   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2806   gtk_widget_show (table);
2807 
2808   scalexscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Scale X:"),
2809                                       100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
2810                                       TRUE, 0.0, 0.0, NULL, NULL);
2811   g_signal_connect (scalexscale, "value-changed",
2812                     G_CALLBACK (getscales),
2813                     NULL);
2814 
2815   scaleyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, _("Scale Y:"),
2816                                       100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
2817                                       TRUE, 0.0, 0.0, NULL, NULL);
2818   g_signal_connect (scaleyscale, "value-changed",
2819                     G_CALLBACK (getscales),
2820                     NULL);
2821   scalezscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Scale Z:"),
2822                                       100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
2823                                       TRUE, 0.0, 0.0, NULL, NULL);
2824   g_signal_connect (scalezscale, "value-changed",
2825                     G_CALLBACK (getscales),
2826                     NULL);
2827 
2828   rotxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Rotate X:"),
2829                                     100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
2830                                     TRUE, 0.0, 0.0, NULL, NULL);
2831   g_signal_connect (rotxscale, "value-changed",
2832                     G_CALLBACK (getscales),
2833                     NULL);
2834 
2835   rotyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Rotate Y:"),
2836                                     100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
2837                                     TRUE, 0.0, 0.0, NULL, NULL);
2838   g_signal_connect (rotyscale, "value-changed",
2839                     G_CALLBACK (getscales),
2840                     NULL);
2841 
2842   rotzscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Rotate Z:"),
2843                                     100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
2844                                     TRUE, 0.0, 0.0, NULL, NULL);
2845   g_signal_connect (rotzscale, "value-changed",
2846                     G_CALLBACK (getscales),
2847                     NULL);
2848 
2849   posxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Position X:"),
2850                                     100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
2851                                     TRUE, 0.0, 0.0, NULL, NULL);
2852   g_signal_connect (posxscale, "value-changed",
2853                     G_CALLBACK (getscales),
2854                     NULL);
2855 
2856   posyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 7, _("Position Y:"),
2857                                     100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
2858                                     TRUE, 0.0, 0.0, NULL, NULL);
2859   g_signal_connect (posyscale, "value-changed",
2860                     G_CALLBACK (getscales),
2861                     NULL);
2862 
2863   poszscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 8, _("Position Z:"),
2864                                     100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
2865                                     TRUE, 0.0, 0.0, NULL, NULL);
2866   g_signal_connect (poszscale, "value-changed",
2867                     G_CALLBACK (getscales),
2868                     NULL);
2869 
2870   gtk_widget_show (window);
2871 
2872   return window;
2873 }
2874 
2875 static guchar
pixelval(gdouble v)2876 pixelval (gdouble v)
2877 {
2878   v += 0.5;
2879   if (v < 0.0)
2880     return 0;
2881   if (v > 255.0)
2882     return 255;
2883   return v;
2884 }
2885 
2886 static gboolean
render(void)2887 render (void)
2888 {
2889   GimpVector4  col;
2890   guchar *dest_row;
2891   ray     r;
2892   gint    x, y, p;
2893   gint    tx  = PREVIEWSIZE;
2894   gint    ty  = PREVIEWSIZE;
2895   gint    bpp = 4;
2896 
2897   idle_id = 0;
2898 
2899   initworld ();
2900 
2901   r.v1.z = -10.0;
2902   r.v2.z = 0.0;
2903 
2904   if (world.obj[0].com.numtexture > 0)
2905     {
2906       cairo_surface_flush (buffer);
2907 
2908       for (y = 0; y < ty; y++)
2909         {
2910           dest_row = img + y * img_stride;
2911 
2912           for (x = 0; x < tx; x++)
2913             {
2914               gint g, gridsize = 16;
2915 
2916               g = ((x / gridsize + y / gridsize) % 2) * 60 + 100;
2917 
2918               r.v1.x = r.v2.x = 8.5 * (x / (float) (tx - 1) - 0.5);
2919               r.v1.y = r.v2.y = 8.5 * (y / (float) (ty - 1) - 0.5);
2920 
2921               p = x * bpp;
2922 
2923               traceray (&r, &col, 10, 1.0);
2924 
2925               if (col.w < 0.0)
2926                 col.w = 0.0;
2927               else if (col.w > 1.0)
2928                 col.w = 1.0;
2929 
2930               GIMP_CAIRO_RGB24_SET_PIXEL ((dest_row + p),
2931                 pixelval (255 * col.x) * col.w + g * (1.0 - col.w),
2932                 pixelval (255 * col.y) * col.w + g * (1.0 - col.w),
2933                 pixelval (255 * col.z) * col.w + g * (1.0 - col.w));
2934              }
2935         }
2936 
2937       cairo_surface_mark_dirty (buffer);
2938     }
2939 
2940   gtk_widget_queue_draw (drawarea);
2941 
2942   return FALSE;
2943 }
2944 
2945 static void
realrender(gint32 drawable_ID)2946 realrender (gint32 drawable_ID)
2947 {
2948   GeglBuffer  *src_buffer;
2949   GeglBuffer  *dest_buffer;
2950   const Babl  *format;
2951   gint         x, y;
2952   ray          r;
2953   GimpVector4  rcol;
2954   gint         width, height;
2955   gint         x1, y1;
2956   guchar      *dest;
2957   gint         bpp;
2958   guchar      *buffer, *ibuffer;
2959 
2960   initworld ();
2961 
2962   r.v1.z = -10.0;
2963   r.v2.z = 0.0;
2964 
2965   if (! gimp_drawable_mask_intersect (drawable_ID,
2966                                       &x1, &y1, &width, &height))
2967     return;
2968 
2969   src_buffer  = gimp_drawable_get_buffer (drawable_ID);
2970   dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
2971 
2972   if (gimp_drawable_is_rgb (drawable_ID))
2973     {
2974       if (gimp_drawable_has_alpha (drawable_ID))
2975         format = babl_format ("R'G'B'A u8");
2976       else
2977         format = babl_format ("R'G'B' u8");
2978     }
2979   else
2980     {
2981       if (gimp_drawable_has_alpha (drawable_ID))
2982         format = babl_format ("Y'A u8");
2983       else
2984         format = babl_format ("Y' u8");
2985     }
2986 
2987   bpp = babl_format_get_bytes_per_pixel (format);
2988 
2989   buffer  = g_malloc (width * 4);
2990   ibuffer = g_malloc (width * 4);
2991 
2992   gimp_progress_init (_("Rendering sphere"));
2993 
2994   for (y = 0; y < height; y++)
2995     {
2996       dest = buffer;
2997       for (x = 0; x < width; x++)
2998         {
2999           r.v1.x = r.v2.x = 8.1 * (x / (float) (width - 1) - 0.5);
3000           r.v1.y = r.v2.y = 8.1 * (y / (float) (height - 1) - 0.5);
3001 
3002           traceray (&r, &rcol, 10, 1.0);
3003           dest[0] = pixelval (255 * rcol.x);
3004           dest[1] = pixelval (255 * rcol.y);
3005           dest[2] = pixelval (255 * rcol.z);
3006           dest[3] = pixelval (255 * rcol.w);
3007           dest += 4;
3008         }
3009 
3010       gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 1.0,
3011                        format, ibuffer,
3012                        GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
3013 
3014       for (x = 0; x < width; x++)
3015         {
3016           gint   k, dx = x * 4, sx = x * bpp;
3017           gfloat a     = buffer[dx + 3] / 255.0;
3018 
3019           for (k = 0; k < bpp; k++)
3020             {
3021               ibuffer[sx + k] =
3022                 buffer[dx + k] * a + ibuffer[sx + k] * (1.0 - a);
3023             }
3024         }
3025 
3026       gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 0,
3027                        format, ibuffer,
3028                        GEGL_AUTO_ROWSTRIDE);
3029 
3030       gimp_progress_update ((gdouble) y / (gdouble) height);
3031     }
3032 
3033   gimp_progress_update (1.0);
3034   g_free (buffer);
3035   g_free (ibuffer);
3036 
3037   g_object_unref (src_buffer);
3038   g_object_unref (dest_buffer);
3039 
3040   gimp_drawable_merge_shadow (drawable_ID, TRUE);
3041   gimp_drawable_update (drawable_ID, x1, y1, width, height);
3042 }
3043 
3044 static void
query(void)3045 query (void)
3046 {
3047   static const GimpParamDef args[] =
3048   {
3049     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
3050     { GIMP_PDB_IMAGE,    "image",    "Input image (unused)"         },
3051     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"               }
3052   };
3053 
3054   gimp_install_procedure (PLUG_IN_PROC,
3055                           N_("Create an image of a textured sphere"),
3056                           "This plug-in can be used to create textured and/or "
3057                           "bumpmapped spheres, and uses a small lightweight "
3058                           "raytracer to perform the task with good quality",
3059                           "Vidar Madsen",
3060                           "Vidar Madsen",
3061                           "1999",
3062                           N_("Sphere _Designer..."),
3063                           "RGB*, GRAY*",
3064                           GIMP_PLUGIN,
3065                           G_N_ELEMENTS (args), 0,
3066                           args, NULL);
3067 
3068   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render");
3069 }
3070 
3071 static gboolean
sphere_main(gint32 drawable_ID)3072 sphere_main (gint32 drawable_ID)
3073 {
3074   gimp_ui_init (PLUG_IN_BINARY, TRUE);
3075 
3076   img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, PREVIEWSIZE);
3077   img = g_malloc0 (img_stride * PREVIEWSIZE);
3078 
3079   buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24,
3080                                                 PREVIEWSIZE,
3081                                                 PREVIEWSIZE,
3082                                                 img_stride);
3083 
3084   makewindow ();
3085 
3086   if (s.com.numtexture == 0)
3087     {
3088       /* Setup and use default list */
3089       sphere_response (NULL, RESPONSE_RESET, NULL);
3090     }
3091   else
3092     {
3093       /* Reuse the list from a previous invocation */
3094       rebuildlist ();
3095     }
3096 
3097   gtk_main ();
3098 
3099   cairo_surface_destroy (buffer);
3100   g_free (img);
3101 
3102   return do_run;
3103 }
3104 
3105 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)3106 run (const gchar      *name,
3107      gint              nparams,
3108      const GimpParam  *param,
3109      gint             *nreturn_vals,
3110      GimpParam       **return_vals)
3111 {
3112   static GimpParam   values[1];
3113   GimpRunMode        run_mode;
3114   gint32             drawable_ID;
3115   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
3116   gint               x, y, w, h;
3117 
3118   INIT_I18N ();
3119   gegl_init (NULL, NULL);
3120 
3121   *nreturn_vals = 1;
3122   *return_vals  = values;
3123 
3124   values[0].type          = GIMP_PDB_STATUS;
3125   values[0].data.d_status = status;
3126 
3127   run_mode    = param[0].data.d_int32;
3128   drawable_ID = param[2].data.d_drawable;
3129 
3130   if (! gimp_drawable_mask_intersect (drawable_ID, &x, &y, &w, &h))
3131     {
3132       g_message (_("Region selected for plug-in is empty"));
3133       return;
3134     }
3135 
3136   switch (run_mode)
3137     {
3138     case GIMP_RUN_INTERACTIVE:
3139       s.com.numtexture = 0;
3140       gimp_get_data (PLUG_IN_PROC, &s);
3141       if (! sphere_main (drawable_ID))
3142         return;
3143       break;
3144 
3145     case GIMP_RUN_WITH_LAST_VALS:
3146       s.com.numtexture = 0;
3147       gimp_get_data (PLUG_IN_PROC, &s);
3148       if (s.com.numtexture == 0)
3149         return;
3150       break;
3151 
3152     case GIMP_RUN_NONINTERACTIVE:
3153     default:
3154       /* Not implemented yet... */
3155       return;
3156     }
3157 
3158   gimp_set_data (PLUG_IN_PROC, &s, sizeof (s));
3159 
3160   realrender (drawable_ID);
3161   gimp_displays_flush ();
3162 
3163   values[0].data.d_status = status;
3164 }
3165 
3166 MAIN ()
3167