// // Base class for scene item handling // // Copyright (C) J. Belson 2001.12.06 // // $Author: jon $ : $Date: 2003/05/30 17:50:43 $ // #include #include "handler_base.h" static int num_clipped; /** * Constructor */ handler_base::handler_base(int w, int h, const render_info& info) : cancelled(false), rinfo(info) { my_settings = settings::get_instance(); scene_width = w; scene_height = h; threshold = 0.1; threshold = 0.03; // Set up viewing frustrum fov = 60.0/360 * 2*M_PI; view_frustrum = frustrum(fov, 4, 640); distance = DEFAULT_DISTANCE; textures = false; shadows = false; reflections = false; atmospherics = false; my_settings->get_colour(WATER_COLOUR, &water_colour); float i, j, k; my_settings->get_float3(LIGHT_RAY_VECTOR, &i, &j, &k); light = vector3d(i, j, k); my_settings->get_bool(RENDER_BACKFACE_CULL, &backface_cull); my_settings->get_bool(RENDER_FRUSTRUM_CULL, &frustrum_cull); num_clipped = 0; } /** * Destructor */ handler_base::~handler_base() { std::cout << "Num. clipped polygons = " << num_clipped << std::endl; } /** * Allow the recursive rendering operation to be cancelled */ void handler_base::set_cancelled(void) { cancelled = true; } /** * Recursively subdivide according to camera position. * Polygon vertex heights laid out as follows: * * h4 h8 h3 * * * h9 h5 h7 * * * h1 h6 h2 */ void handler_base::subdivide(float x, float y, float size, float h1, float h2, float h3, float h4) { // If user has selected cancel, bail out of recursion asap if (cancelled) { return; } if (frustrum_cull) { // XXX Doesn't work too well when looking backwards point3d p[] = { point3d(x, h1, y + size), point3d(x + size, h2, y + size), point3d(x + size, h3, y), point3d(x, h4, y) }; // Transform to eye coordinates transform(p, 4); if (!view_frustrum.test_patch(p)) { num_clipped++; return; } } // Vector from viewpoint to centre of quad float centre_x = x+size/2; float centre_y = y+size/2; float centre_h = get_height(centre_x, centre_y); vector3d vec = point3d(centre_x, centre_h, centre_y) - viewpoint; float distance = vec.magnitude(); // Maximum size of quad (diagonal) is sqrt(2)*width // XXX simple approximation, should improve on this float size_x = (x + 1.4*size)/distance - x/distance; float size_y = (y + 1.4*size)/distance - y/distance; float transformed_size = (size_x < size_y) ? size_y : size_x; if (distance > 3 /*15*/ && transformed_size > threshold) { // Further subdivision required float half_size = size/2; float h5 = centre_h; float h6 = get_height(x + half_size, y + size); float h7 = get_height(x + size, y + half_size); float h8 = get_height(x + half_size, y); float h9 = get_height(x, y + half_size); subdivide(x, y, half_size, h9, h5, h8, h4), subdivide(x + half_size, y, half_size, h5, h7, h3, h8); subdivide(x + half_size, y + half_size, half_size, h6, h2, h7, h5); subdivide(x, y + half_size, half_size, h1, h6, h5, h9); } else { process_patch( point3d(x, h4, y), point3d(x+size, h3, y), point3d(x+size, h2, y + size), point3d(x, h1, y + size) ); } } /** * Set camera position and orientation */ void handler_base::set_camera(const point3d& p, const matrix& m) { viewpoint = p; camera = m; } /** * Enable/disable various rendering options */ void handler_base::set_textures(bool b) { textures = b; } void handler_base::set_shadows(bool b) { shadows = b; } void handler_base::set_reflections(bool b) { reflections = b; } void handler_base::set_atmospherics(bool b) { atmospherics = b; } void handler_base::set_heightfield(hfield*) { ; } void handler_base::set_threshold(float thresh) { threshold = thresh; } void handler_base::set_progress(progress*) { ; } /** * Set distance to projection plane used in perspective projection */ void handler_base::set_distance(float dist) { distance = dist; } /** * Calculate the reflection of vector '-v' from a surface with normal 'n' */ vector3d handler_base::get_reflection(const vector3d& n, const vector3d& v) { float f = n.dot_product(v); f = f * 2.0; vector3d r = n * f; r = r - v; return r; } /** * Determine if this polygon is facing us * @param n Normal of triangle * @param v Viewing vector * @return true if triangle is facing away from us (ie. to be culled) */ bool handler_base::check_backface(const vector3d& n, const vector3d& v) { return (n.dot_product(v) < 0) ? true : false; } /** * Check of point lies within 90 degree viewing frustrum * Negative z goes into the screen. * @return true if within frustrum (ie. visible) */ bool handler_base::check_frustrum(const point3d& p) { return view_frustrum.test_point(p); bool b = false; static const float fov = 45.0/360 * 2*M_PI; float k = 1.0/std::tan(fov/2.0); if (p.z <= k*p.x && k*p.x <= -p.z && p.y >= p.z && -p.z >= p.y) { b = true; } return b; } /** * Apply camera transform to array of points */ void handler_base::transform(point3d* points, int num_points) { float x, y, z; // XXX @todo Do translation via matrix? x = viewpoint.x; y = viewpoint.y; z = viewpoint.z; for (int i=0; i point2d assignment operator points2d[i] = point2d(p); } // Calculate values required to centre/scale the image float offset_x = scene_width/2.0; float offset_y = scene_height/2.0; float scale_x = -float(scene_width)/distance; // -ve? oh dear float scale_y = float(scene_height)/distance; // Apply viewing transform for (int i=0; i