diff options
Diffstat (limited to 'plug-ins/map-object/map-object-preview.c')
-rw-r--r-- | plug-ins/map-object/map-object-preview.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/plug-ins/map-object/map-object-preview.c b/plug-ins/map-object/map-object-preview.c new file mode 100644 index 0000000..afc29b4 --- /dev/null +++ b/plug-ins/map-object/map-object-preview.c @@ -0,0 +1,745 @@ +/*************************************************/ +/* Compute a preview image and preview wireframe */ +/*************************************************/ + +#include "config.h" + +#include <gtk/gtk.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "map-object-main.h" +#include "map-object-ui.h" +#include "map-object-image.h" +#include "map-object-apply.h" +#include "map-object-shade.h" +#include "map-object-preview.h" + + +gdouble mat[3][4]; +gint lightx, lighty; + +/* Protos */ +/* ====== */ + +static void compute_preview (gint x, + gint y, + gint w, + gint h, + gint pw, + gint ph); +static void draw_light_marker (cairo_t *cr, + gint xpos, + gint ypos); +static void draw_line (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph, + gdouble cx1, + gdouble cy1, + gdouble cx2, + gdouble cy2, + GimpVector3 a, + GimpVector3 b); +static void draw_wireframe (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph); +static void draw_preview_wireframe (cairo_t *cr); +static void draw_wireframe_plane (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph); +static void draw_wireframe_sphere (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph); +static void draw_wireframe_box (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph); +static void draw_wireframe_cylinder (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph); + +/**************************************************************/ +/* Computes a preview of the rectangle starting at (x,y) with */ +/* dimensions (w,h), placing the result in preview_RGB_data. */ +/**************************************************************/ + +static void +compute_preview (gint x, + gint y, + gint w, + gint h, + gint pw, + gint ph) +{ + gdouble xpostab[PREVIEW_WIDTH]; + gdouble ypostab[PREVIEW_HEIGHT]; + gdouble realw; + gdouble realh; + GimpVector3 p1, p2; + GimpRGB color; + GimpRGB lightcheck, darkcheck; + gint xcnt, ycnt, f1, f2; + guchar r, g, b; + glong index = 0; + + init_compute (); + + if (! preview_surface) + return; + + p1 = int_to_pos (x, y); + p2 = int_to_pos (x + w, y + h); + + /* First, compute the linear mapping (x,y,x+w,y+h) to (0,0,pw,ph) */ + /* ============================================================== */ + + realw = (p2.x - p1.x); + realh = (p2.y - p1.y); + + for (xcnt = 0; xcnt < pw; xcnt++) + xpostab[xcnt] = p1.x + realw * ((gdouble) xcnt / (gdouble) pw); + + for (ycnt = 0; ycnt < ph; ycnt++) + ypostab[ycnt] = p1.y + realh * ((gdouble) ycnt / (gdouble) ph); + + /* Compute preview using the offset tables */ + /* ======================================= */ + + if (mapvals.transparent_background == TRUE) + { + gimp_rgba_set (&background, 0.0, 0.0, 0.0, 0.0); + } + else + { + gimp_context_get_background (&background); + gimp_rgb_set_alpha (&background, 1.0); + } + + gimp_rgba_set (&lightcheck, + GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, 1.0); + gimp_rgba_set (&darkcheck, + GIMP_CHECK_DARK, GIMP_CHECK_DARK, GIMP_CHECK_DARK, 1.0); + gimp_vector3_set (&p2, -1.0, -1.0, 0.0); + + cairo_surface_flush (preview_surface); + + for (ycnt = 0; ycnt < ph; ycnt++) + { + index = ycnt * preview_rgb_stride; + for (xcnt = 0; xcnt < pw; xcnt++) + { + p1.x = xpostab[xcnt]; + p1.y = ypostab[ycnt]; + + p2 = p1; + color = (* get_ray_color) (&p1); + + if (color.a < 1.0) + { + f1 = ((xcnt % 32) < 16); + f2 = ((ycnt % 32) < 16); + f1 = f1 ^ f2; + + if (f1) + { + if (color.a == 0.0) + color = lightcheck; + else + gimp_rgb_composite (&color, &lightcheck, + GIMP_RGB_COMPOSITE_BEHIND); + } + else + { + if (color.a == 0.0) + color = darkcheck; + else + gimp_rgb_composite (&color, &darkcheck, + GIMP_RGB_COMPOSITE_BEHIND); + } + } + + gimp_rgb_get_uchar (&color, &r, &g, &b); + GIMP_CAIRO_RGB24_SET_PIXEL((preview_rgb_data + index), r, g, b); + index += 4; + } + } + cairo_surface_mark_dirty (preview_surface); +} + +/*************************************************/ +/* Check if the given position is within the */ +/* light marker. Return TRUE if so, FALSE if not */ +/*************************************************/ + +gint +check_light_hit (gint xpos, + gint ypos) +{ + gdouble dx, dy, r; + + if (mapvals.lightsource.type == POINT_LIGHT) + { + dx = (gdouble) lightx - xpos; + dy = (gdouble) lighty - ypos; + r = sqrt (dx * dx + dy * dy) + 0.5; + + if ((gint) r > 7) + return FALSE; + else + return TRUE; + } + + return FALSE; +} + +/****************************************/ +/* Draw a marker to show light position */ +/****************************************/ + +static void +draw_light_marker (cairo_t *cr, + gint xpos, + gint ypos) +{ + GdkColor color; + + if (mapvals.lightsource.type != POINT_LIGHT) + return; + + cairo_set_line_width (cr, 1.0); + + color.red = 0x0; + color.green = 0x4000; + color.blue = 0xFFFF; + gdk_cairo_set_source_color (cr, &color); + + lightx = xpos; + lighty = ypos; + + cairo_arc (cr, lightx, lighty, 7, 0, 2 * G_PI); + cairo_fill (cr); +} + +static void +draw_lights (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + gdouble dxpos, dypos; + gint xpos, ypos; + + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &dxpos, &dypos, &mapvals.viewpoint, + &mapvals.lightsource.position); + xpos = RINT (dxpos); + ypos = RINT (dypos); + + if (xpos >= 0 && xpos <= PREVIEW_WIDTH && + ypos >= 0 && ypos <= PREVIEW_HEIGHT) + { + draw_light_marker (cr, xpos, ypos); + } +} + +/*************************************************/ +/* Update light position given new screen coords */ +/*************************************************/ + +void +update_light (gint xpos, + gint ypos) +{ + gint startx, starty, pw, ph; + + pw = PREVIEW_WIDTH * mapvals.zoom; + ph = PREVIEW_HEIGHT * mapvals.zoom; + startx = (PREVIEW_WIDTH - pw) / 2; + starty = (PREVIEW_HEIGHT - ph) / 2; + + gimp_vector_2d_to_3d (startx, starty, pw, ph, xpos, ypos, + &mapvals.viewpoint, &mapvals.lightsource.position); + + gtk_widget_queue_draw (previewarea); +} + +/**************************/ +/* Compute preview image. */ +/**************************/ + +void +compute_preview_image (void) +{ + GdkDisplay *display = gtk_widget_get_display (previewarea); + GdkCursor *cursor; + gint pw, ph; + + pw = PREVIEW_WIDTH * mapvals.zoom; + ph = PREVIEW_HEIGHT * mapvals.zoom; + + cursor = gdk_cursor_new_for_display (display, GDK_WATCH); + gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor); + gdk_cursor_unref (cursor); + + compute_preview (0, 0, width - 1, height - 1, pw, ph); + + cursor = gdk_cursor_new_for_display (display, GDK_HAND2); + gdk_window_set_cursor(gtk_widget_get_window (previewarea), cursor); + gdk_cursor_unref (cursor); +} + +gboolean +preview_expose (GtkWidget *widget, + GdkEventExpose *eevent) +{ + gint startx, starty, pw, ph; + cairo_t *cr; + + cr = gdk_cairo_create (eevent->window); + + pw = PREVIEW_WIDTH * mapvals.zoom; + ph = PREVIEW_HEIGHT * mapvals.zoom; + startx = (PREVIEW_WIDTH - pw) / 2; + starty = (PREVIEW_HEIGHT - ph) / 2; + + cairo_set_source_surface (cr, preview_surface, startx, starty); + cairo_rectangle (cr, startx, starty, pw, ph); + cairo_clip (cr); + + cairo_paint (cr); + + cairo_reset_clip (cr); + + if (mapvals.showgrid) + draw_preview_wireframe (cr); + + cairo_reset_clip (cr); + draw_lights (cr, startx, starty, pw, ph); + + cairo_destroy (cr); + + return FALSE; +} + +/**************************/ +/* Draw preview wireframe */ +/**************************/ + +void +draw_preview_wireframe (cairo_t *cr) +{ + gint startx, starty, pw, ph; + + pw = PREVIEW_WIDTH * mapvals.zoom; + ph = PREVIEW_HEIGHT * mapvals.zoom; + startx = (PREVIEW_WIDTH - pw) / 2; + starty = (PREVIEW_HEIGHT - ph) / 2; + + draw_wireframe (cr, startx, starty, pw, ph); +} + +/****************************/ +/* Draw a wireframe preview */ +/****************************/ + +void +draw_wireframe (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + switch (mapvals.maptype) + { + case MAP_PLANE: + draw_wireframe_plane (cr, startx, starty, pw, ph); + break; + case MAP_SPHERE: + draw_wireframe_sphere (cr, startx, starty, pw, ph); + break; + case MAP_BOX: + draw_wireframe_box (cr, startx, starty, pw, ph); + break; + case MAP_CYLINDER: + draw_wireframe_cylinder (cr, startx, starty, pw, ph); + break; + } +} + +static void +draw_wireframe_plane (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + GimpVector3 v1, v2, a, b, c, d, dir1, dir2; + gint cnt; + gdouble x1, y1, x2, y2, fac; + + cairo_rectangle (cr, startx, starty, pw, ph); + cairo_clip (cr); + + /* Find rotated box corners */ + /* ======================== */ + + gimp_vector3_set (&v1, 0.5, 0.0, 0.0); + gimp_vector3_set (&v2, 0.0, 0.5, 0.0); + + gimp_vector3_rotate (&v1, + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + + gimp_vector3_rotate (&v2, + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + + dir1 = v1; gimp_vector3_normalize (&dir1); + dir2 = v2; gimp_vector3_normalize (&dir2); + + fac = 1.0 / (gdouble) WIRESIZE; + + gimp_vector3_mul (&dir1, fac); + gimp_vector3_mul (&dir2, fac); + + gimp_vector3_add (&a, &mapvals.position, &v1); + gimp_vector3_sub (&b, &a, &v2); + gimp_vector3_add (&a, &a, &v2); + gimp_vector3_sub (&d, &mapvals.position, &v1); + gimp_vector3_sub (&d, &d, &v2); + + c = b; + + for (cnt = 0; cnt <= WIRESIZE; cnt++) + { + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x1, &y1, &mapvals.viewpoint, &a); + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x2, &y2, &mapvals.viewpoint, &b); + + cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5); + cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5); + + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x1, &y1, &mapvals.viewpoint, &c); + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x2, &y2, &mapvals.viewpoint, &d); + + cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5); + cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5); + + gimp_vector3_sub (&a, &a, &dir1); + gimp_vector3_sub (&b, &b, &dir1); + gimp_vector3_add (&c, &c, &dir2); + gimp_vector3_add (&d, &d, &dir2); + } + + cairo_set_line_width (cr, 3.0); + cairo_stroke_preserve (cr); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); +} + +static void +draw_wireframe_sphere (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + GimpVector3 p[2 * (WIRESIZE + 5)]; + gint cnt, cnt2; + gdouble x1, y1, x2, y2, twopifac; + + cairo_rectangle (cr, startx, starty, pw, ph); + cairo_clip (cr); + + /* Compute wireframe points */ + /* ======================== */ + + twopifac = (2.0 * G_PI) / WIRESIZE; + + for (cnt = 0; cnt < WIRESIZE; cnt++) + { + p[cnt].x = mapvals.radius * cos ((gdouble) cnt * twopifac); + p[cnt].y = 0.0; + p[cnt].z = mapvals.radius * sin ((gdouble) cnt * twopifac); + gimp_vector3_rotate (&p[cnt], + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position); + } + + p[cnt] = p[0]; + + for (cnt = WIRESIZE + 1; cnt < 2 * WIRESIZE + 1; cnt++) + { + p[cnt].x = mapvals.radius * cos ((gdouble) (cnt-(WIRESIZE+1))*twopifac); + p[cnt].y = mapvals.radius * sin ((gdouble) (cnt-(WIRESIZE+1))*twopifac); + p[cnt].z = 0.0; + gimp_vector3_rotate (&p[cnt], + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position); + } + + p[cnt] = p[WIRESIZE+1]; + cnt++; + cnt2 = cnt; + + /* Find rotated axis */ + /* ================= */ + + gimp_vector3_set (&p[cnt], 0.0, -0.35, 0.0); + gimp_vector3_rotate (&p[cnt], + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + p[cnt+1] = mapvals.position; + + gimp_vector3_set (&p[cnt+2], 0.0, 0.0, -0.35); + gimp_vector3_rotate (&p[cnt+2], + gimp_deg_to_rad (mapvals.alpha), + gimp_deg_to_rad (mapvals.beta), + gimp_deg_to_rad (mapvals.gamma)); + p[cnt+3] = mapvals.position; + + p[cnt + 4] = p[cnt]; + gimp_vector3_mul (&p[cnt + 4], -1.0); + p[cnt + 5] = p[cnt + 1]; + + gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position); + gimp_vector3_add (&p[cnt + 2], &p[cnt + 2], &mapvals.position); + gimp_vector3_add (&p[cnt + 4], &p[cnt + 4], &mapvals.position); + + /* Draw the circles (equator and zero meridian) */ + /* ============================================ */ + + for (cnt = 0; cnt < cnt2 - 1; cnt++) + { + if (p[cnt].z > mapvals.position.z && p[cnt + 1].z > mapvals.position.z) + { + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x1, &y1, &mapvals.viewpoint, &p[cnt]); + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x2, &y2, &mapvals.viewpoint, &p[cnt + 1]); + + cairo_move_to (cr, (gint) (x1 + 0.5) + 0.5, (gint) (y1 + 0.5) + 0.5); + cairo_line_to (cr, (gint) (x2 + 0.5) + 0.5, (gint) (y2 + 0.5) + 0.5); + } + } + + /* Draw the axis (pole to pole and center to zero meridian) */ + /* ======================================================== */ + + for (cnt = 0; cnt < 3; cnt++) + { + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x1, &y1, &mapvals.viewpoint, &p[cnt2]); + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x2, &y2, &mapvals.viewpoint, &p[cnt2 + 1]); + + cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5); + cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5); + + cnt2 += 2; + } + + cairo_set_line_width (cr, 3.0); + cairo_stroke_preserve (cr); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); +} + +static void +draw_line (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph, + gdouble cx1, + gdouble cy1, + gdouble cx2, + gdouble cy2, + GimpVector3 a, + GimpVector3 b) +{ + gdouble x1, y1, x2, y2; + + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x1, &y1, &mapvals.viewpoint, &a); + gimp_vector_3d_to_2d (startx, starty, pw, ph, + &x2, &y2, &mapvals.viewpoint, &b); + + cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5); + cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5); +} + +static void +draw_wireframe_box (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + GimpVector3 p[8], tmp, scale; + gint i; + gdouble cx1, cy1, cx2, cy2; + + cairo_rectangle (cr, startx, starty, pw, ph); + cairo_clip (cr); + + /* Compute wireframe points */ + /* ======================== */ + + init_compute (); + + scale = mapvals.scale; + gimp_vector3_mul (&scale, 0.5); + + gimp_vector3_set (&p[0], -scale.x, -scale.y, scale.z); + gimp_vector3_set (&p[1], scale.x, -scale.y, scale.z); + gimp_vector3_set (&p[2], scale.x, scale.y, scale.z); + gimp_vector3_set (&p[3], -scale.x, scale.y, scale.z); + + gimp_vector3_set (&p[4], -scale.x, -scale.y, -scale.z); + gimp_vector3_set (&p[5], scale.x, -scale.y, -scale.z); + gimp_vector3_set (&p[6], scale.x, scale.y, -scale.z); + gimp_vector3_set (&p[7], -scale.x, scale.y, -scale.z); + + /* Rotate and translate points */ + /* =========================== */ + + for (i = 0; i < 8; i++) + { + vecmulmat (&tmp, &p[i], rotmat); + gimp_vector3_add (&p[i], &tmp, &mapvals.position); + } + + /* Draw the box */ + /* ============ */ + + cx1 = (gdouble) startx; + cy1 = (gdouble) starty; + cx2 = cx1 + (gdouble) pw; + cy2 = cy1 + (gdouble) ph; + + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[1]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[2]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[3]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[0]); + + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[4],p[5]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[5],p[6]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[6],p[7]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[4]); + + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[4]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[5]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[6]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[7]); + + cairo_set_line_width (cr, 3.0); + cairo_stroke_preserve (cr); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); +} + +static void +draw_wireframe_cylinder (cairo_t *cr, + gint startx, + gint starty, + gint pw, + gint ph) +{ + GimpVector3 p[2*8], a, axis, scale; + gint i; + gdouble cx1, cy1, cx2, cy2; + gfloat m[16], l, angle; + + cairo_rectangle (cr, startx, starty, pw, ph); + cairo_clip (cr); + + /* Compute wireframe points */ + /* ======================== */ + + init_compute (); + + scale = mapvals.scale; + gimp_vector3_mul (&scale, 0.5); + + l = mapvals.cylinder_length / 2.0; + angle = 0; + + gimp_vector3_set (&axis, 0.0, 1.0, 0.0); + + for (i = 0; i < 8; i++) + { + rotatemat (angle, &axis, m); + + gimp_vector3_set (&a, mapvals.cylinder_radius, 0.0, 0.0); + + vecmulmat (&p[i], &a, m); + + p[i+8] = p[i]; + + p[i].y += l; + p[i+8].y -= l; + + angle += 360.0 / 8.0; + } + + /* Rotate and translate points */ + /* =========================== */ + + for (i = 0; i < 16; i++) + { + vecmulmat (&a, &p[i], rotmat); + gimp_vector3_add (&p[i], &a, &mapvals.position); + } + + /* Draw the box */ + /* ============ */ + + cx1 = (gdouble) startx; + cy1 = (gdouble) starty; + cx2 = cx1 + (gdouble) pw; + cy2 = cy1 + (gdouble) ph; + + for (i = 0; i < 7; i++) + { + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+1]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i+8],p[i+9]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+8]); + } + + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[0]); + draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[15],p[8]); + + cairo_set_line_width (cr, 3.0); + cairo_stroke_preserve (cr); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); +} |