1
/*************************************************/
2
/* Compute a preview image and preview wireframe */
3
/*************************************************/
9
#include <libgimp/gimp.h>
10
#include <libgimpmath/gimpmath.h>
12
#include "lighting_main.h"
13
#include "lighting_ui.h"
14
#include "lighting_image.h"
15
#include "lighting_apply.h"
16
#include "lighting_shade.h"
18
#include "lighting_preview.h"
20
#define LIGHT_SYMBOL_SIZE 8
22
gint handle_xpos = 0, handle_ypos = 0;
24
BackBuffer backbuf = { 0, 0, 0, 0, NULL };
26
/* g_free()'ed on exit */
27
gdouble *xpostab = NULL;
28
gdouble *ypostab = NULL;
30
static gint xpostab_size = -1; /* if preview size change, do realloc */
31
static gint ypostab_size = -1;
33
gboolean light_hit = FALSE;
34
gboolean left_button_pressed = FALSE;
35
static guint preview_update_timer = 0;
41
interactive_preview_timer_callback ( gpointer data );
44
compute_preview (gint startx, gint starty, gint w, gint h)
46
gint xcnt, ycnt, f1, f2;
47
gdouble imagex, imagey;
50
GimpRGB lightcheck, darkcheck;
52
get_ray_func ray_func;
54
if (xpostab_size != w)
65
xpostab = g_new (gdouble, w);
69
if (ypostab_size != h)
80
ypostab = g_new (gdouble, h);
84
for (xcnt = 0; xcnt < w; xcnt++)
85
xpostab[xcnt] = (gdouble) width *((gdouble) xcnt / (gdouble) w);
86
for (ycnt = 0; ycnt < h; ycnt++)
87
ypostab[ycnt] = (gdouble) height *((gdouble) ycnt / (gdouble) h);
89
precompute_init (width, height);
91
gimp_rgba_set (&lightcheck,
92
GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT,
94
gimp_rgba_set (&darkcheck, GIMP_CHECK_DARK, GIMP_CHECK_DARK,
95
GIMP_CHECK_DARK, 1.0);
97
if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
99
gimp_pixel_rgn_init (&bump_region,
100
gimp_drawable_get (mapvals.bumpmap_id),
101
0, 0, width, height, FALSE, FALSE);
106
if (mapvals.previewquality)
107
ray_func = get_ray_color;
109
ray_func = get_ray_color_no_bilinear;
111
if (mapvals.env_mapped == TRUE && mapvals.envmap_id != -1)
113
env_width = gimp_drawable_width (mapvals.envmap_id);
114
env_height = gimp_drawable_height (mapvals.envmap_id);
116
gimp_pixel_rgn_init (&env_region,
117
gimp_drawable_get (mapvals.envmap_id), 0,
118
0, env_width, env_height, FALSE, FALSE);
120
if (mapvals.previewquality)
121
ray_func = get_ray_color_ref;
123
ray_func = get_ray_color_no_bilinear_ref;
126
for (ycnt = 0; ycnt < PREVIEW_HEIGHT; ycnt++)
128
for (xcnt = 0; xcnt < PREVIEW_WIDTH; xcnt++)
130
if ((ycnt >= starty && ycnt < (starty + h)) &&
131
(xcnt >= startx && xcnt < (startx + w)))
133
imagex = xpostab[xcnt - startx];
134
imagey = ypostab[ycnt - starty];
135
pos = int_to_posf (imagex, imagey);
137
if (mapvals.bump_mapped == TRUE &&
138
mapvals.bumpmap_id != -1 &&
141
pos_to_float (pos.x, pos.y, &imagex, &imagey);
142
precompute_normals (0, width, RINT (imagey));
145
color = (*ray_func) (&pos);
149
f1 = ((xcnt % 32) < 16);
150
f2 = ((ycnt % 32) < 16);
158
gimp_rgb_composite (&color,
160
GIMP_RGB_COMPOSITE_BEHIND);
167
gimp_rgb_composite (&color,
169
GIMP_RGB_COMPOSITE_BEHIND);
173
gimp_rgb_get_uchar (&color,
174
preview_rgb_data + index,
175
preview_rgb_data + index +
177
preview_rgb_data + index +
184
preview_rgb_data[index++] = 200;
185
preview_rgb_data[index++] = 200;
186
preview_rgb_data[index++] = 200;
193
compute_preview_rectangle (gint * xp, gint * yp, gint * wid, gint * heig)
199
w = (PREVIEW_WIDTH - 50.0);
200
h = (gdouble) height *(w / (gdouble) width);
202
x = (PREVIEW_WIDTH - w) / 2.0;
203
y = (PREVIEW_HEIGHT - h) / 2.0;
207
h = (PREVIEW_HEIGHT - 50.0);
208
w = (gdouble) width *(h / (gdouble) height);
209
x = (PREVIEW_WIDTH - w) / 2.0;
210
y = (PREVIEW_HEIGHT - h) / 2.0;
218
/*************************************************/
219
/* Check if the given position is within the */
220
/* light marker. Return TRUE if so, FALSE if not */
221
/*************************************************/
224
check_handle_hit (gint xpos, gint ypos)
227
gint k = mapvals.light_selected;
229
dx = handle_xpos - xpos;
230
dy = handle_ypos - ypos;
233
if (mapvals.lightsource[k].type == POINT_LIGHT ||
234
mapvals.lightsource[k].type == DIRECTIONAL_LIGHT)
236
r = sqrt (dx * dx + dy * dy) + 0.5;
249
/****************************************/
250
/* Draw a light symbol */
251
/****************************************/
257
gdouble dxpos, dypos;
258
gint startx, starty, pw, ph;
259
GimpVector3 viewpoint;
260
GimpVector3 light_position;
261
gint k = mapvals.light_selected;
264
gfloat delta_x = 0.0;
265
gfloat delta_y = 0.0;
267
/* calculate handle position */
268
compute_preview_rectangle (&startx, &starty, &pw, &ph);
269
switch (mapvals.lightsource[k].type)
275
/* swap z to reverse light position */
276
viewpoint = mapvals.viewpoint;
277
viewpoint.z = -viewpoint.z;
278
light_position = mapvals.lightsource[k].position;
279
gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
280
&viewpoint, &light_position);
281
handle_xpos = (gint) (dxpos + 0.5);
282
handle_ypos = (gint) (dypos + 0.5);
284
case DIRECTIONAL_LIGHT:
285
light_position.x = light_position.y = 0.5;
286
light_position.z = 0;
287
viewpoint.z = -viewpoint.z;
288
gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
289
&viewpoint, &light_position);
290
length = PREVIEW_HEIGHT / 4;
291
delta_x = mapvals.lightsource[k].direction.x * length;
292
delta_y = mapvals.lightsource[k].direction.y * length;
293
handle_xpos = dxpos + delta_x;
294
handle_ypos = dypos + delta_y;
298
gdk_gc_set_function (gc, GDK_COPY);
300
if (mapvals.lightsource[k].type != NO_LIGHT)
304
/* Restore background if it has been saved */
305
/* ======================================= */
307
if (backbuf.image != NULL)
309
gdk_gc_set_function (gc, GDK_COPY);
310
gdk_draw_image (previewarea->window, gc,
311
backbuf.image, 0, 0, backbuf.x,
312
backbuf.y, backbuf.w, backbuf.h);
313
g_object_unref (backbuf.image);
314
backbuf.image = NULL;
317
/* calculate backbuffer */
318
switch (mapvals.lightsource[k].type)
321
backbuf.x = handle_xpos - LIGHT_SYMBOL_SIZE / 2;
322
backbuf.y = handle_ypos - LIGHT_SYMBOL_SIZE / 2;
323
backbuf.w = LIGHT_SYMBOL_SIZE;
324
backbuf.h = LIGHT_SYMBOL_SIZE;
326
case DIRECTIONAL_LIGHT:
328
backbuf.x = handle_xpos;
330
backbuf.x = startx + pw/2;
332
backbuf.y = handle_ypos;
334
backbuf.y = starty + ph/2;
335
backbuf.x -= LIGHT_SYMBOL_SIZE/2;
336
backbuf.y -= LIGHT_SYMBOL_SIZE/2;
337
backbuf.w = fabs(delta_x) + LIGHT_SYMBOL_SIZE;
338
backbuf.h = fabs(delta_y) + LIGHT_SYMBOL_SIZE;
341
backbuf.x = handle_xpos - LIGHT_SYMBOL_SIZE / 2;
342
backbuf.y = handle_ypos - LIGHT_SYMBOL_SIZE / 2;
343
backbuf.w = LIGHT_SYMBOL_SIZE;
344
backbuf.h = LIGHT_SYMBOL_SIZE;
350
/* Save background */
351
/* =============== */
352
if ((backbuf.x >= 0) &&
353
(backbuf.x <= PREVIEW_WIDTH) &&
354
(backbuf.y >= 0) && (backbuf.y <= PREVIEW_HEIGHT))
356
/* clip coordinates to preview widget sizes */
357
if ((backbuf.x + backbuf.w) > PREVIEW_WIDTH)
358
backbuf.w = (PREVIEW_WIDTH - backbuf.x);
360
if ((backbuf.y + backbuf.h) > PREVIEW_HEIGHT)
361
backbuf.h = (PREVIEW_HEIGHT - backbuf.y);
363
backbuf.image = gdk_drawable_get_image (previewarea->window,
364
backbuf.x, backbuf.y,
365
backbuf.w, backbuf.h);
371
gdk_gc_set_rgb_bg_color (gc, &color);
374
color.green = 0x4000;
376
gdk_gc_set_rgb_fg_color (gc, &color);
378
/* draw circle at light position */
379
switch (mapvals.lightsource[k].type)
383
gdk_draw_arc (previewarea->window, gc, TRUE,
384
handle_xpos - LIGHT_SYMBOL_SIZE / 2,
385
handle_ypos - LIGHT_SYMBOL_SIZE / 2,
387
LIGHT_SYMBOL_SIZE, 0, 360 * 64);
389
case DIRECTIONAL_LIGHT:
390
gdk_draw_arc (previewarea->window, gc, TRUE,
391
handle_xpos - LIGHT_SYMBOL_SIZE / 2,
392
handle_ypos - LIGHT_SYMBOL_SIZE / 2,
394
LIGHT_SYMBOL_SIZE, 0, 360 * 64);
395
gdk_draw_line (previewarea->window, gc,
396
handle_xpos, handle_ypos, startx+pw/2 , starty + ph/2);
405
/*************************************************/
406
/* Update light position given new screen coords */
407
/*************************************************/
410
update_light (gint xpos, gint ypos)
412
gint startx, starty, pw, ph;
414
gint k = mapvals.light_selected;
416
compute_preview_rectangle (&startx, &starty, &pw, &ph);
418
vp = mapvals.viewpoint;
421
switch (mapvals.lightsource[k].type)
427
gimp_vector_2d_to_3d (startx,
431
xpos, ypos, &vp, &mapvals.lightsource[k].position);
433
case DIRECTIONAL_LIGHT:
434
gimp_vector_2d_to_3d (startx,
438
xpos, ypos, &vp, &mapvals.lightsource[k].direction);
444
/******************************************************************/
445
/* Draw preview image. if DoCompute is TRUE then recompute image. */
446
/******************************************************************/
449
draw_preview_image (gboolean recompute)
451
gint startx, starty, pw, ph;
457
gdk_gc_set_rgb_bg_color (gc, &color);
460
color.green = 0xFFFF;
462
gdk_gc_set_rgb_fg_color (gc, &color);
464
gdk_gc_set_function (gc, GDK_COPY);
466
compute_preview_rectangle (&startx, &starty, &pw, &ph);
470
GdkDisplay *display = gtk_widget_get_display (previewarea);
473
cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
475
gdk_window_set_cursor (previewarea->window, cursor);
476
gdk_cursor_unref (cursor);
478
compute_preview (startx, starty, pw, ph);
479
cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
480
gdk_window_set_cursor (previewarea->window, cursor);
481
gdk_cursor_unref (cursor);
484
/* if we recompute, clear backbuf, so we don't
485
* restore the wrong bitmap */
486
if (backbuf.image != NULL)
488
g_object_unref (backbuf.image);
489
backbuf.image = NULL;
493
gdk_draw_rgb_image (previewarea->window, gc,
494
0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT,
495
GDK_RGB_DITHER_MAX, preview_rgb_data,
498
/* draw symbols if enabled in UI */
499
if (mapvals.interactive_preview)
505
/******************************/
506
/* Preview area event handler */
507
/******************************/
510
preview_events (GtkWidget *area,
517
/* Is this the first exposure? */
518
/* =========================== */
521
gc = gdk_gc_new (area->window);
522
draw_preview_image (TRUE);
525
draw_preview_image (FALSE);
527
case GDK_ENTER_NOTIFY:
529
case GDK_LEAVE_NOTIFY:
531
case GDK_BUTTON_PRESS:
532
light_hit = check_handle_hit (event->button.x, event->button.y);
533
left_button_pressed = TRUE;
535
case GDK_BUTTON_RELEASE:
536
left_button_pressed = FALSE;
538
case GDK_MOTION_NOTIFY:
539
if (left_button_pressed == TRUE &&
541
mapvals.interactive_preview == TRUE )
544
interactive_preview_callback(NULL);
545
update_light (event->motion.x, event->motion.y);
556
interactive_preview_callback (GtkWidget *widget)
558
if ( preview_update_timer != 0)
560
g_source_remove ( preview_update_timer );
562
/* start new timer */
563
preview_update_timer = g_timeout_add (100,
564
interactive_preview_timer_callback, NULL);
568
interactive_preview_timer_callback ( gpointer data )
570
gint k = mapvals.light_selected;
572
mapvals.update_enabled = FALSE; /* disable apply_settings() */
574
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_x),
575
mapvals.lightsource[k].position.x);
576
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_y),
577
mapvals.lightsource[k].position.y);
578
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_z),
579
mapvals.lightsource[k].position.z);
580
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_x),
581
mapvals.lightsource[k].direction.x);
582
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_y),
583
mapvals.lightsource[k].direction.y);
584
gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_z),
585
mapvals.lightsource[k].direction.z);
587
mapvals.update_enabled = TRUE;
589
draw_preview_image (TRUE);
591
preview_update_timer = 0;