/*************************************************/ /* Compute a preview image and preview wireframe */ /*************************************************/ #include #include #include #include "arcball.h" #include "mapobject_main.h" #include "mapobject_ui.h" #include "mapobject_image.h" #include "mapobject_apply.h" #include "mapobject_shade.h" #include "mapobject_preview.h" line linetab[WIRESIZE*2+8]; gdouble mat[3][4]; gint lightx,lighty; BackBuffer backbuf={0,0,0,0,NULL}; /* Protos */ /* ====== */ static void draw_light_marker (gint xpos, gint ypos); static void clear_light_marker (void); static gint draw_line (gint n, gint startx, gint starty, gint pw, gint ph, gdouble cx1, gdouble cy1, gdouble cx2, gdouble cy2, GimpVector3 a, GimpVector3 b); static void draw_wireframe_plane (gint startx, gint starty, gint pw, gint ph); static void draw_wireframe_sphere (gint startx, gint starty, gint pw, gint ph); static void draw_wireframe_box (gint startx, gint starty, gint pw, gint ph); static void draw_wireframe_cylinder (gint startx, gint starty, gint pw, gint ph); /*************************************************/ /* Quick and dirty (and slow) line clip routine. */ /* The function returns FALSE if the line isn't */ /* visible according to the limits given. */ /*************************************************/ static gboolean clip_line (gdouble *x1, gdouble *y1, gdouble *x2, gdouble *y2, gdouble minx, gdouble miny, gdouble maxx, gdouble maxy) { gdouble tmp; g_assert (x1!=NULL); g_assert (y1!=NULL); g_assert (x2!=NULL); g_assert (y2!=NULL); /* First, check if line is visible at all */ /* ====================================== */ if (*x1maxx && *x2>maxx) return(FALSE); if (*y1maxy && *y2>maxy) return(FALSE); /* Check for intersection with the four edges. Sort on x first. */ /* ============================================================ */ if (*x2<*x1) { tmp=*x1; *x1=*x2; *x2=tmp; tmp=*y1; *y1=*y2; *y2=tmp; } if (*x1maxx) { if (*y1<*y2) *y2=*y2-(*x2-maxx)*((*y2-*y1)/(*x2-*x1)); else *y2=*y2+(*x2-maxx)*((*y1-*y2)/(*x2-*x1)); *x2=maxx; } if (*y1maxy) { *x1=*x1+(*y1-maxy)*((*x2-*x1)/(*y1-*y2)); *y1=maxy; } if (*y2>maxy) { *x2=*x2-(*y2-maxy)*((*x2-*x1)/(*y2-*y1)); *y2=maxy; } return TRUE; } /**************************************************************/ /* Computes a preview of the rectangle starting at (x,y) with */ /* dimensions (w,h), placing the result in preview_RGB_data. */ /**************************************************************/ void compute_preview (gint x, gint y, gint w, gint h, gint pw, gint ph) { gdouble xpostab[PREVIEW_WIDTH],ypostab[PREVIEW_HEIGHT],realw,realh; GimpVector3 p1,p2; GckRGB color,lightcheck,darkcheck,temp; guchar r,g,b; gint xcnt,ycnt,f1,f2; glong index=0; init_compute(); 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;xcnt7) return(FALSE); else return(TRUE); } return(FALSE); } /****************************************/ /* Draw a marker to show light position */ /****************************************/ static void draw_light_marker (gint xpos, gint ypos) { gck_gc_set_foreground(visinfo,gc,0,50,255); gck_gc_set_background(visinfo,gc,0,0,0); gdk_gc_set_function(gc,GDK_COPY); if (mapvals.lightsource.type==POINT_LIGHT) { lightx=xpos; lighty=ypos; /* Save background */ /* =============== */ backbuf.x=lightx-7; backbuf.y=lighty-7; backbuf.w=14; backbuf.h=14; /* X doesn't like images that's outside a window, make sure */ /* we get the backbuffer image from within the boundaries */ /* ======================================================== */ if (backbuf.x<0) backbuf.x=0; else if ((backbuf.x+backbuf.w)>PREVIEW_WIDTH) backbuf.w=(PREVIEW_WIDTH-backbuf.x); if (backbuf.y<0) backbuf.y=0; else if ((backbuf.y+backbuf.h)>PREVIEW_HEIGHT) backbuf.h=(PREVIEW_WIDTH-backbuf.y); backbuf.image=gdk_image_get(previewarea->window,backbuf.x,backbuf.y,backbuf.w,backbuf.h); gdk_draw_arc(previewarea->window,gc,TRUE,lightx-7,lighty-7,14,14,0,360*64); } } static void clear_light_marker (void) { /* Restore background if it has been saved */ /* ======================================= */ if (backbuf.image!=NULL) { gck_gc_set_foreground(visinfo,gc,255,255,255); gck_gc_set_background(visinfo,gc,0,0,0); gdk_gc_set_function(gc,GDK_COPY); gdk_draw_image(previewarea->window,gc,backbuf.image,0,0,backbuf.x,backbuf.y, backbuf.w,backbuf.h); gdk_image_destroy(backbuf.image); backbuf.image=NULL; } } static void draw_lights (gint startx, gint starty, gint pw, gint ph) { gdouble dxpos,dypos; gint xpos,ypos; clear_light_marker(); gimp_vector_3d_to_2d(startx,starty,pw,ph,&dxpos,&dypos,&mapvals.viewpoint, &mapvals.lightsource.position); xpos=(gint)(dxpos+0.5); ypos=(gint)(dypos+0.5); if (xpos>=0 && xpos<=PREVIEW_WIDTH && ypos>=0 && ypos<=PREVIEW_HEIGHT) draw_light_marker(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.preview_zoom_factor; ph=PREVIEW_HEIGHT >> mapvals.preview_zoom_factor; startx=(PREVIEW_WIDTH-pw)>>1; starty=(PREVIEW_HEIGHT-ph)>>1; gimp_vector_2d_to_3d(startx,starty,pw,ph,xpos,ypos,&mapvals.viewpoint,&mapvals.lightsource.position); draw_lights(startx,starty,pw,ph); } /******************************************************************/ /* Draw preview image. if DoCompute is TRUE then recompute image. */ /******************************************************************/ void draw_preview_image (gint docompute) { gint startx, starty, pw, ph; gck_gc_set_foreground (visinfo, gc, 255, 255, 255); gck_gc_set_background (visinfo, gc, 0, 0, 0); gdk_gc_set_function (gc, GDK_COPY); linetab[0].x1 = -1; pw = PREVIEW_WIDTH >> mapvals.preview_zoom_factor; ph = PREVIEW_HEIGHT >> mapvals.preview_zoom_factor; startx = (PREVIEW_WIDTH - pw) >> 1; starty = (PREVIEW_HEIGHT - ph) >> 1; if (docompute == TRUE) { GdkCursor *newcursor; newcursor = gdk_cursor_new (GDK_WATCH); gdk_window_set_cursor(previewarea->window, newcursor); gdk_cursor_destroy (newcursor); gdk_flush(); compute_preview (0, 0, width - 1, height - 1, pw, ph); newcursor = gdk_cursor_new (GDK_HAND2); gdk_window_set_cursor(previewarea->window, newcursor); gdk_cursor_destroy (newcursor); gdk_flush(); clear_light_marker (); } if (pw != PREVIEW_WIDTH) gdk_window_clear (previewarea->window); gdk_draw_image (previewarea->window, gc, image, 0, 0, startx, starty, pw, ph); draw_lights (startx, starty, pw, ph); } /**************************/ /* Draw preview wireframe */ /**************************/ void draw_preview_wireframe (void) { gint startx,starty,pw,ph; gck_gc_set_foreground(visinfo,gc,255,255,255); gck_gc_set_background(visinfo,gc,0,0,0); gdk_gc_set_function(gc,GDK_INVERT); pw=PREVIEW_WIDTH >> mapvals.preview_zoom_factor; ph=PREVIEW_HEIGHT >> mapvals.preview_zoom_factor; startx=(PREVIEW_WIDTH-pw)>>1; starty=(PREVIEW_HEIGHT-ph)>>1; clear_wireframe(); draw_wireframe(startx,starty,pw,ph); } /****************************/ /* Draw a wireframe preview */ /****************************/ void draw_wireframe (gint startx, gint starty, gint pw, gint ph) { switch (mapvals.maptype) { case MAP_PLANE: draw_wireframe_plane(startx,starty,pw,ph); break; case MAP_SPHERE: draw_wireframe_sphere(startx,starty,pw,ph); break; case MAP_BOX: draw_wireframe_box(startx,starty,pw,ph); break; case MAP_CYLINDER: draw_wireframe_cylinder(startx,starty,pw,ph); break; } } static void draw_wireframe_plane (gint startx, gint starty, gint pw, gint ph) { GimpVector3 v1,v2,a,b,c,d,dir1,dir2; gint cnt,n=0; gdouble x1,y1,x2,y2,cx1,cy1,cx2,cy2,fac; /* 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; cx1=(gdouble)startx; cy1=(gdouble)starty; cx2=cx1+(gdouble)pw; cy2=cy1+(gdouble)ph; 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); if (clip_line(&x1,&y1,&x2,&y2,cx1,cy1,cx2,cy2)==TRUE) { linetab[n].x1=(gint)(x1+0.5); linetab[n].y1=(gint)(y1+0.5); linetab[n].x2=(gint)(x2+0.5); linetab[n].y2=(gint)(y2+0.5); linetab[n].linewidth=1; linetab[n].linestyle=GDK_LINE_SOLID; gdk_gc_set_line_attributes(gc,linetab[n].linewidth,linetab[n].linestyle,GDK_CAP_NOT_LAST,GDK_JOIN_MITER); gdk_draw_line(previewarea->window,gc,linetab[n].x1,linetab[n].y1,linetab[n].x2,linetab[n].y2); n++; } 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); if (clip_line(&x1,&y1,&x2,&y2,cx1,cy1,cx2,cy2)==TRUE) { linetab[n].x1=(gint)(x1+0.5); linetab[n].y1=(gint)(y1+0.5); linetab[n].x2=(gint)(x2+0.5); linetab[n].y2=(gint)(y2+0.5); linetab[n].linewidth=1; linetab[n].linestyle=GDK_LINE_SOLID; gdk_gc_set_line_attributes(gc,linetab[n].linewidth,linetab[n].linestyle,GDK_CAP_NOT_LAST,GDK_JOIN_MITER); gdk_draw_line(previewarea->window,gc,linetab[n].x1,linetab[n].y1,linetab[n].x2,linetab[n].y2); n++; } gimp_vector3_sub(&a,&a,&dir1); gimp_vector3_sub(&b,&b,&dir1); gimp_vector3_add(&c,&c,&dir2); gimp_vector3_add(&d,&d,&dir2); } /* Mark end of lines */ /* ================= */ linetab[n].x1=-1; } static void draw_wireframe_sphere (gint startx, gint starty, gint pw, gint ph) { GimpVector3 p[2*(WIRESIZE+5)]; gint cnt,cnt2,n=0; gdouble x1,y1,x2,y2,twopifac,cx1,cy1,cx2,cy2; /* Compute wireframe points */ /* ======================== */ twopifac=(2.0*G_PI)/WIRESIZE; for (cnt=0;cntmapvals.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]); if (clip_line(&x1,&y1,&x2,&y2,cx1,cy1,cx2,cy2)==TRUE) { linetab[n].x1=(gint)(x1+0.5); linetab[n].y1=(gint)(y1+0.5); linetab[n].x2=(gint)(x2+0.5); linetab[n].y2=(gint)(y2+0.5); linetab[n].linewidth=3; linetab[n].linestyle=GDK_LINE_SOLID; gdk_gc_set_line_attributes(gc,linetab[n].linewidth,linetab[n].linestyle,GDK_CAP_NOT_LAST,GDK_JOIN_MITER); gdk_draw_line(previewarea->window,gc,linetab[n].x1,linetab[n].y1,linetab[n].x2,linetab[n].y2); n++; } } } /* 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]); if (clip_line(&x1,&y1,&x2,&y2,cx1,cy1,cx2,cy2)==TRUE) { linetab[n].x1=(gint)(x1+0.5); linetab[n].y1=(gint)(y1+0.5); linetab[n].x2=(gint)(x2+0.5); linetab[n].y2=(gint)(y2+0.5); if (p[cnt2].zwindow,gc,linetab[n].x1,linetab[n].y1,linetab[n].x2,linetab[n].y2); n++; } cnt2+=2; } /* Mark end of lines */ /* ================= */ linetab[n].x1=-1; } static gint draw_line (gint n, 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; gint i = n; 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); if (clip_line(&x1,&y1,&x2,&y2,cx1,cy1,cx2,cy2)==TRUE) { linetab[i].x1=(gint)(x1+0.5); linetab[i].y1=(gint)(y1+0.5); linetab[i].x2=(gint)(x2+0.5); linetab[i].y2=(gint)(y2+0.5); linetab[i].linewidth=3; linetab[i].linestyle=GDK_LINE_SOLID; gdk_gc_set_line_attributes(gc,linetab[i].linewidth,linetab[i].linestyle, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); gdk_draw_line(previewarea->window,gc,linetab[i].x1,linetab[i].y1, linetab[i].x2,linetab[i].y2); i++; } return(i); } static void draw_wireframe_box (gint startx, gint starty, gint pw, gint ph) { GimpVector3 p[8], tmp, scale; gint n=0,i; gdouble cx1,cy1,cx2,cy2; /* 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; n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[1]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[2]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[3]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[0]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[4],p[5]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[5],p[6]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[6],p[7]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[4]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[4]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[5]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[6]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[7]); /* Mark end of lines */ /* ================= */ linetab[n].x1=-1; } static void draw_wireframe_cylinder (gint startx, gint starty, gint pw, gint ph) { GimpVector3 p[2*8], a, axis, scale; gint n=0,i; gdouble cx1,cy1,cx2,cy2; gfloat m[16], l, angle; /* 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++) { n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+1]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i+8],p[i+9]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+8]); } n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[0]); n = draw_line(n, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[15],p[8]); /* Mark end of lines */ /* ================= */ linetab[n].x1=-1; } void clear_wireframe (void) { gint n=0; while (linetab[n].x1!=-1) { gdk_gc_set_line_attributes(gc,linetab[n].linewidth,linetab[n].linestyle,GDK_CAP_NOT_LAST,GDK_JOIN_MITER); gdk_draw_line(previewarea->window,gc,linetab[n].x1,linetab[n].y1, linetab[n].x2,linetab[n].y2); n++; } }