/** * gimp_vector_3d_to_2d: * @sx: the abscissa of the upper-left screen rectangle. * @sy: the ordinate of the upper-left screen rectangle. * @w: the width of the screen rectangle. * @h: the height of the screen rectangle. * @x: the abscissa of the point in the screen rectangle to map (return value). * @y: the ordinate of the point in the screen rectangle to map (return value). * @vp: position of the observer. * @p: the 3D point to project to the plane. * * Convert the given 3D point to 2D (project it onto the viewing * plane, (sx, sy, 0) - (sx + w, sy + h, 0). The input is assumed to * be in the unit square (0, 0, z) - (1, 1, z). The viewpoint of the * observer is passed in vp. * * This is basically the opposite of gimp_vector_2d_to_3d(). **/ void gimp_vector_3d_to_2d (gint sx, gint sy, gint w, gint h, gdouble *x, gdouble *y, const GimpVector3 *vp, const GimpVector3 *p) { gdouble t; GimpVector3 dir; gimp_vector3_sub (&dir, p, vp); gimp_vector3_normalize (&dir); if (dir.z != 0.0) { t = (-1.0 * vp->z) / dir.z; *x = (gdouble) sx + ((vp->x + t * dir.x) * (gdouble) w); *y = (gdouble) sy + ((vp->y + t * dir.y) * (gdouble) h); } else { *x = (gdouble) sx + (p->x * (gdouble) w); *y = (gdouble) sy + (p->y * (gdouble) h); } }
void compute_bounding_box (void) { GimpVector3 p1, p2; gdouble t; GimpVector3 dir; p1 = mapvals.position; p1.x -= (mapvals.radius + 0.01); p1.y -= (mapvals.radius + 0.01); p2 = mapvals.position; p2.x += (mapvals.radius + 0.01); p2.y += (mapvals.radius + 0.01); gimp_vector3_sub (&dir, &p1, &mapvals.viewpoint); gimp_vector3_normalize (&dir); if (dir.z != 0.0) { t = (-1.0 * mapvals.viewpoint.z) / dir.z; p1.x = (mapvals.viewpoint.x + t * dir.x); p1.y = (mapvals.viewpoint.y + t * dir.y); } gimp_vector3_sub (&dir, &p2, &mapvals.viewpoint); gimp_vector3_normalize (&dir); if (dir.z != 0.0) { t = (-1.0 * mapvals.viewpoint.z) / dir.z; p2.x = (mapvals.viewpoint.x + t * dir.x); p2.y = (mapvals.viewpoint.y + t * dir.y); } bx1 = p1.x; by1 = p1.y; bx2 = p2.x; by2 = p2.y; }
GimpRGB get_ray_color_plane (GimpVector3 *pos) { GimpRGB color = background; static gint inside = FALSE; static GimpVector3 ray, spos; static gdouble vx, vy; /* Construct a line from our VP to the point */ /* ========================================= */ gimp_vector3_sub (&ray, pos, &mapvals.viewpoint); gimp_vector3_normalize (&ray); /* Check for intersection. This is a quasi ray-tracer. */ /* =================================================== */ if (plane_intersect (&ray, &mapvals.viewpoint, &spos, &vx, &vy) == TRUE) { color = get_image_color (vx, vy, &inside); if (color.a != 0.0 && inside == TRUE && mapvals.lightsource.type != NO_LIGHT) { /* Compute shading at this point */ /* ============================= */ color = phong_shade (&spos, &mapvals.viewpoint, &mapvals.normal, &mapvals.lightsource.position, &color, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color); } } if (mapvals.transparent_background == FALSE && color.a < 1.0) { gimp_rgb_composite (&color, &background, GIMP_RGB_COMPOSITE_BEHIND); } return color; }
static GimpVector3 compute_reflected_ray (GimpVector3 *normal, GimpVector3 *view) { GimpVector3 ref; gdouble nl; nl = 2.0 * gimp_vector3_inner_product (normal, view); ref = *normal; gimp_vector3_mul (&ref, nl); gimp_vector3_sub (&ref, &ref, view); return ref; }
static gint sphere_intersect (GimpVector3 *dir, GimpVector3 *viewp, GimpVector3 *spos1, GimpVector3 *spos2) { static gdouble alpha, beta, tau, s1, s2, tmp; static GimpVector3 t; gimp_vector3_sub (&t, &mapvals.position, viewp); alpha = gimp_vector3_inner_product (dir, &t); beta = gimp_vector3_inner_product (&t, &t); tau = alpha * alpha - beta + mapvals.radius * mapvals.radius; if (tau >= 0.0) { tau = sqrt (tau); s1 = alpha + tau; s2 = alpha - tau; if (s2 < s1) { tmp = s1; s1 = s2; s2 = tmp; } spos1->x = viewp->x + s1 * dir->x; spos1->y = viewp->y + s1 * dir->y; spos1->z = viewp->z + s1 * dir->z; spos2->x = viewp->x + s2 * dir->x; spos2->y = viewp->y + s2 * dir->y; spos2->z = viewp->z + s2 * dir->z; return TRUE; } return FALSE; }
GimpRGB get_ray_color_box (GimpVector3 *pos) { GimpVector3 lvp, ldir, vp, p, dir, ns, nn; GimpRGB color, color2; gfloat m[16]; gint i; FaceIntersectInfo face_intersect[2]; color = background; vp = mapvals.viewpoint; p = *pos; /* Translate viewpoint so that the box has its origin */ /* at its lower left corner. */ /* ================================================== */ vp.x = vp.x - mapvals.position.x; vp.y = vp.y - mapvals.position.y; vp.z = vp.z - mapvals.position.z; p.x = p.x - mapvals.position.x; p.y = p.y - mapvals.position.y; p.z = p.z - mapvals.position.z; /* Compute direction */ /* ================= */ gimp_vector3_sub (&dir, &p, &vp); gimp_vector3_normalize (&dir); /* Compute inverse of rotation matrix and apply it to */ /* the viewpoint and direction. This transforms the */ /* observer into the local coordinate system of the box */ /* ==================================================== */ memcpy (m, rotmat, sizeof (gfloat) * 16); transpose_mat (m); vecmulmat (&lvp, &vp, m); vecmulmat (&ldir, &dir, m); /* Ok. Now the observer is in the space where the box is located */ /* with its lower left corner at the origin and its axis aligned */ /* to the cartesian basis. Check if the transformed ray hits it. */ /* ============================================================= */ face_intersect[0].t = 1000000.0; face_intersect[1].t = 1000000.0; if (intersect_box (mapvals.scale, lvp, ldir, face_intersect) == TRUE) { /* We've hit the box. Transform the hit points and */ /* normals back into the world coordinate system */ /* =============================================== */ for (i = 0; i < 2; i++) { vecmulmat (&ns, &face_intersect[i].s, rotmat); vecmulmat (&nn, &face_intersect[i].n, rotmat); ns.x = ns.x + mapvals.position.x; ns.y = ns.y + mapvals.position.y; ns.z = ns.z + mapvals.position.z; face_intersect[i].s = ns; face_intersect[i].n = nn; } color = get_box_image_color (face_intersect[0].face, face_intersect[0].u, face_intersect[0].v); /* Check for total transparency... */ /* =============================== */ if (color.a < 1.0) { /* Hey, we can see through here! */ /* Lets see what's on the other side.. */ /* =================================== */ color = phong_shade (&face_intersect[0].s, &mapvals.viewpoint, &face_intersect[0].n, &mapvals.lightsource.position, &color, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color); color2 = get_box_image_color (face_intersect[1].face, face_intersect[1].u, face_intersect[1].v); /* Make the normal point inwards */ /* ============================= */ gimp_vector3_mul (&face_intersect[1].n, -1.0); color2 = phong_shade (&face_intersect[1].s, &mapvals.viewpoint, &face_intersect[1].n, &mapvals.lightsource.position, &color2, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color2); if (mapvals.transparent_background == FALSE && color2.a < 1.0) { gimp_rgb_composite (&color2, &background, GIMP_RGB_COMPOSITE_BEHIND); } /* Compute a mix of the first and second colors */ /* ============================================ */ gimp_rgb_composite (&color, &color2, GIMP_RGB_COMPOSITE_NORMAL); gimp_rgb_clamp (&color); } else if (color.a != 0.0 && mapvals.lightsource.type != NO_LIGHT) { color = phong_shade (&face_intersect[0].s, &mapvals.viewpoint, &face_intersect[0].n, &mapvals.lightsource.position, &color, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color); } } else { if (mapvals.transparent_background == TRUE) gimp_rgb_set_alpha (&color, 0.0); } return color; }
GimpRGB get_ray_color_sphere (GimpVector3 *pos) { GimpRGB color = background; static GimpRGB color2; static gint inside = FALSE; static GimpVector3 normal, ray, spos1, spos2; static gdouble vx, vy; /* Check if ray is within the bounding box */ /* ======================================= */ if (pos->x<bx1 || pos->x>bx2 || pos->y<by1 || pos->y>by2) return color; /* Construct a line from our VP to the point */ /* ========================================= */ gimp_vector3_sub (&ray, pos, &mapvals.viewpoint); gimp_vector3_normalize (&ray); /* Check for intersection. This is a quasi ray-tracer. */ /* =================================================== */ if (sphere_intersect (&ray, &mapvals.viewpoint, &spos1, &spos2) == TRUE) { /* Compute spherical to rectangular mapping */ /* ======================================== */ gimp_vector3_sub (&normal, &spos1, &mapvals.position); gimp_vector3_normalize (&normal); sphere_to_image (&normal, &vx, &vy); color = get_image_color (vx, vy, &inside); /* Check for total transparency... */ /* =============================== */ if (color.a < 1.0) { /* Hey, we can see through here! */ /* Lets see what's on the other side.. */ /* =================================== */ color = phong_shade (&spos1, &mapvals.viewpoint, &normal, &mapvals.lightsource.position, &color, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color); gimp_vector3_sub (&normal, &spos2, &mapvals.position); gimp_vector3_normalize (&normal); sphere_to_image (&normal, &vx, &vy); color2 = get_image_color (vx, vy, &inside); /* Make the normal point inwards */ /* ============================= */ gimp_vector3_mul (&normal, -1.0); color2 = phong_shade (&spos2, &mapvals.viewpoint, &normal, &mapvals.lightsource.position, &color2, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color2); /* Compute a mix of the first and second colors */ /* ============================================ */ gimp_rgb_composite (&color, &color2, GIMP_RGB_COMPOSITE_NORMAL); gimp_rgb_clamp (&color); } else if (color.a != 0.0 && inside == TRUE && mapvals.lightsource.type != NO_LIGHT) { /* Compute shading at this point */ /* ============================= */ color = phong_shade (&spos1, &mapvals.viewpoint, &normal, &mapvals.lightsource.position, &color, &mapvals.lightsource.color, mapvals.lightsource.type); gimp_rgb_clamp (&color); } } if (mapvals.transparent_background == FALSE && color.a < 1.0) { gimp_rgb_composite (&color, &background, GIMP_RGB_COMPOSITE_BEHIND); } return color; }
static GimpRGB phong_shade (GimpVector3 *pos, GimpVector3 *viewpoint, GimpVector3 *normal, GimpVector3 *light, GimpRGB *diff_col, GimpRGB *spec_col, gint type) { GimpRGB ambientcolor, diffusecolor, specularcolor; gdouble NL, RV, dist; GimpVector3 L, NN, V, N; /* Compute ambient intensity */ /* ========================= */ N = *normal; ambientcolor = *diff_col; gimp_rgb_multiply (&ambientcolor, mapvals.material.ambient_int); /* Compute (N*L) term of Phong's equation */ /* ====================================== */ if (type == POINT_LIGHT) gimp_vector3_sub (&L, light, pos); else L = *light; dist = gimp_vector3_length (&L); if (dist != 0.0) gimp_vector3_mul (&L, 1.0 / dist); NL = 2.0 * gimp_vector3_inner_product (&N, &L); if (NL >= 0.0) { /* Compute (R*V)^alpha term of Phong's equation */ /* ============================================ */ gimp_vector3_sub (&V, viewpoint, pos); gimp_vector3_normalize (&V); gimp_vector3_mul (&N, NL); gimp_vector3_sub (&NN, &N, &L); RV = gimp_vector3_inner_product (&NN, &V); RV = pow (RV, mapvals.material.highlight); /* Compute diffuse and specular intensity contribution */ /* =================================================== */ diffusecolor = *diff_col; gimp_rgb_multiply (&diffusecolor, mapvals.material.diffuse_ref); gimp_rgb_multiply (&diffusecolor, NL); specularcolor = *spec_col; gimp_rgb_multiply (&specularcolor, mapvals.material.specular_ref); gimp_rgb_multiply (&specularcolor, RV); gimp_rgb_add (&diffusecolor, &specularcolor); gimp_rgb_multiply (&diffusecolor, mapvals.material.diffuse_int); gimp_rgb_clamp (&diffusecolor); gimp_rgb_add (&ambientcolor, &diffusecolor); } return ambientcolor; }
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 = RINT (x1); linetab[n].y1 = RINT (y1); linetab[n].x2 = RINT (x2); linetab[n].y2 = RINT (y2); 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 = RINT (x1); linetab[n].y1 = RINT (y1); linetab[n].x2 = RINT (x2); linetab[n].y2 = RINT (y2); 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; }
GimpRGB get_ray_color_no_bilinear_ref (GimpVector3 *position) { GimpRGB color_sum; GimpRGB color_int; GimpRGB light_color; GimpRGB color, env_color; gint x; gdouble xf, yf; GimpVector3 normal, *p, v, r; gint k; gdouble tmpval; pos_to_float (position->x, position->y, &xf, &yf); x = RINT (xf); if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1) normal = mapvals.planenormal; else normal = vertex_normals[1][(gint) RINT (xf)]; gimp_vector3_normalize (&normal); if (mapvals.transparent_background && heights[1][x] == 0) { gimp_rgb_set_alpha (&color_sum, 0.0); } else { color = peek (RINT (xf), RINT (yf)); color_sum = color; gimp_rgb_multiply (&color_sum, mapvals.material.ambient_int); for (k = 0; k < NUM_LIGHTS; k++) { p = &mapvals.lightsource[k].direction; if (!mapvals.lightsource[k].active || mapvals.lightsource[k].type == NO_LIGHT) continue; else if (mapvals.lightsource[k].type == POINT_LIGHT) p = &mapvals.lightsource[k].position; color_int = mapvals.lightsource[k].color; gimp_rgb_multiply (&color_int, mapvals.lightsource[k].intensity); light_color = phong_shade (position, &mapvals.viewpoint, &normal, p, &color, &color_int, mapvals.lightsource[0].type); } gimp_vector3_sub (&v, &mapvals.viewpoint, position); gimp_vector3_normalize (&v); r = compute_reflected_ray (&normal, &v); /* Get color in the direction of r */ /* =============================== */ sphere_to_image (&r, &xf, &yf); env_color = peek_env_map (RINT (env_width * xf), RINT (env_height * yf)); tmpval = mapvals.material.diffuse_int; mapvals.material.diffuse_int = 0.; light_color = phong_shade (position, &mapvals.viewpoint, &normal, &r, &color, &env_color, DIRECTIONAL_LIGHT); mapvals.material.diffuse_int = tmpval; gimp_rgb_add (&color_sum, &light_color); } gimp_rgb_clamp (&color_sum); return color_sum; }
static GimpRGB phong_shade (GimpVector3 *position, GimpVector3 *viewpoint, GimpVector3 *normal, GimpVector3 *lightposition, GimpRGB *diff_col, GimpRGB *light_col, LightType light_type) { GimpRGB diffuse_color, specular_color; gdouble nl, rv, dist; GimpVector3 l, v, n, lnormal, h; /* Compute ambient intensity */ /* ========================= */ n = *normal; /* Compute (N*L) term of Phong's equation */ /* ====================================== */ if (light_type == POINT_LIGHT) gimp_vector3_sub (&l, lightposition, position); else { l = *lightposition; gimp_vector3_normalize (&l); } dist = gimp_vector3_length (&l); if (dist != 0.0) gimp_vector3_mul (&l, 1.0 / dist); nl = MAX (0., 2.0 * gimp_vector3_inner_product (&n, &l)); lnormal = l; gimp_vector3_normalize (&lnormal); if (nl >= 0.0) { /* Compute (R*V)^alpha term of Phong's equation */ /* ============================================ */ gimp_vector3_sub (&v, viewpoint, position); gimp_vector3_normalize (&v); gimp_vector3_add (&h, &lnormal, &v); gimp_vector3_normalize (&h); rv = MAX (0.01, gimp_vector3_inner_product (&n, &h)); rv = pow (rv, mapvals.material.highlight); rv *= nl; /* Compute diffuse and specular intensity contribution */ /* =================================================== */ diffuse_color = *light_col; gimp_rgb_multiply (&diffuse_color, mapvals.material.diffuse_int); diffuse_color.r *= diff_col->r; diffuse_color.g *= diff_col->g; diffuse_color.b *= diff_col->b; gimp_rgb_multiply (&diffuse_color, nl); specular_color = *light_col; if (mapvals.material.metallic) /* for metals, specular color = diffuse color */ { specular_color.r *= diff_col->r; specular_color.g *= diff_col->g; specular_color.b *= diff_col->b; } gimp_rgb_multiply (&specular_color, mapvals.material.specular_ref); gimp_rgb_multiply (&specular_color, rv); gimp_rgb_add (&diffuse_color, &specular_color); gimp_rgb_clamp (&diffuse_color); } gimp_rgb_clamp (&diffuse_color); return diffuse_color; }