// // Handle rendering of landscape // // Copyright (C) J. Belson 2001.12.06 // // $Author: jon $ : $Date: 2003/10/11 18:11:00 $ // #include #include #include "colour.h" #include "handler_land.h" #include "hf_skydome.h" #include "handler_sky.h" #include "perlin2.h" #include "point2d.h" #include "procedural.h" #include "renderer_flat_int.h" //#include "texture.h" #include "texture_rough.h" #include "triangle3d.h" using namespace std; /** * Constructor */ handler_land::handler_land(int w, int h, const render_info& info) : handler_base(w, h, info) { my_settings->get_float(WAVE_HEIGHT, &wave_height); my_settings->get_float(WAVE_SCALE, &wave_scale); my_settings->get_float(WATER_REFLECTIVITY, &water_reflectivity); my_settings->get_float(WATER_LEVEL, &water_level); wave_scale += 0.5; wave_x.set_seed(345); wave_y.set_seed(765); wave_z.set_seed(23452); wave_x.set_freq_scale(wave_scale/2); wave_y.set_freq_scale(wave_scale/2); wave_z.set_freq_scale(wave_scale/2); num_tri = 0; call_count = 0; float i, j, k; my_settings->get_float3(LIGHT_RAY_VECTOR, &i, &j, &k); light = vector3d(i, j, k); hf_sky = new hfield(HF_SKYDOME); sky = new handler_sky(w, h, info); textset_noise ts; ts.seed = std::clock(); ts.persistence = 0.5; text = new texture_rough(ts); // Lousy attempt at rock strata //textset ts2; //text = new texture_strata(ts2); first = true; map = 0; } /** * Destructor */ handler_land::~handler_land() { delete text; delete hf_sky; delete sky; } /** * Calculate land height for specified point */ float handler_land::get_height(float x, float y) { call_count++; float height = land->get_height(x, y); if (height < -0.01) { height = -0.01; } return height; } /** * Pass quad patch through render pipeline */ void handler_land::process_patch( const point3d& p1, const point3d& p2, const point3d& p3, const point3d& p4) { point3d points[4]; points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4; // Our triangle patch... triangle3d triangle[2] = { triangle3d(0, 1, 2), triangle3d(0, 2, 3)}; // Decide what's sea and what isn't for (int tri=0; tri<2; tri++) { if (check_sea_level(triangle[tri], points)) { triangle[tri].set_flag(eSEA); } else { triangle[tri].clear_flag(eSEA); } } // For each triangle pair // XXX kludged to render each triangle pair as a quad for (int tri=0; tri<1 /*2*/; tri++) { //triangle[tri].calculate_normal(points); #if 0 // Skip backfacing triangles... if (backface_cull) { point3d p = triangle[tri].find_centre(points); point3d vp = viewpoint; vector3d view = vp - p; // Vector from point to camera vector3d normal = triangle[tri].normal; // Normal at triangle if (check_backface(normal, view)) { triangle[tri].set_flag(eINVISIBLE); continue; } } #endif if (triangle[tri].get_flag(eSEA)) { // Perturb surface normals to create a wave effect point3d p_tmp[3]; p_tmp[0] = points[triangle[tri].point[0]]; p_tmp[1] = points[triangle[tri].point[1]]; p_tmp[2] = points[triangle[tri].point[2]]; triangle3d tri_tmp = triangle[tri]; make_waves(p_tmp, 3); tri_tmp.calculate_normal(p_tmp); //triangle[tri].calculate_normal(points); triangle[tri].normal = tri_tmp.normal; triangle[1].normal = tri_tmp.normal; if (reflections) { // Figure out what we're reflecting point3d p = triangle[tri].find_centre(points); point3d p2 = triangle[1].find_centre(points); p = point3d((p.x + p2.x)/2.0, (p.y + p2.y)/2.0, (p.z + p2.z)/2.0); point3d vp = viewpoint; vector3d view = vp - p; // Vector from point to camera vector3d normal = triangle[tri].normal; // Normal at triangle vector3d reflection = get_reflection(normal, view); // Reflection vector view.normalise(); normal.normalise(); reflection.normalise(); // Check for intersection with ground bool found; found = land->trace_path(p, reflection); fcolour col; if (/*0 &&*/ found) { // Found an intersection point, figure out the reflected colour // Calculate surface normal at reflected point normal = land->get_normal(p.x, p.z); if (textures) { col = text->colour(p.x*200, p.z*200, 0, &normal); } else { col = colour::get_colour_from_height(p.y); } col = col*water_reflectivity; triangle[tri].set_colour(col); triangle[tri].normal = normal; triangle[1].set_colour(col); triangle[1].normal = normal; } else { // No intersection with ground, check for intersection with sky if (map) { // Find reflection in environmental map col = map->get_colour(reflection); triangle[tri].set_colour(col); triangle[1].set_colour(col); } else { // Trace ray to skydome found = hf_sky->trace_path(p, reflection); if (found) { col = sky->get_colour(p); col = col*water_reflectivity; triangle[tri].set_colour(col); triangle[1].set_colour(col); } else { triangle[tri].set_colour(water_colour); triangle[1].set_colour(water_colour); } } } } else { triangle[tri].set_colour(water_colour); triangle[1].set_colour(water_colour); } } else { triangle[tri].calculate_normal(points); triangle[1].normal = triangle[tri].normal; // Skip backfacing triangles... if (backface_cull) { point3d p = triangle[tri].find_centre(points); point3d vp = viewpoint; vector3d view = vp - p; // Vector from point to camera vector3d normal = triangle[tri].normal; // Normal at triangle if (check_backface(normal, view)) { triangle[tri].set_flag(eINVISIBLE); triangle[1].set_flag(eINVISIBLE); break; } } fcolour col; point3d p = triangle[tri].find_centre(points); point3d p2 = triangle[1].find_centre(points); p = point3d((p.x + p2.x)/2.0, (p.y + p2.y)/2.0, (p.z + p2.z)/2.0); if (textures) { col = text->colour(p.x*200, p.z*200, /*0*/ p.y, &triangle[tri].normal); col.clamp(); } else { col = colour::get_colour_from_height(p.y); } triangle[tri].set_colour(col); triangle[1].set_colour(col); } if (shadows) { // Trace straight line from current triangle in direction of light source, // checking for intersections with ground point3d p = triangle[tri].find_centre(points); point3d p2 = triangle[1].find_centre(points); p = point3d((p.x + p2.x)/2.0, (p.y + p2.y)/2.0, (p.z + p2.z)/2.0); //p.y += 0.1; bool found = land->trace_path(p, -light); if (found) { triangle[tri].set_flag(eSHADOW); triangle[1].set_flag(eSHADOW); } else { triangle[tri].clear_flag(eSHADOW); triangle[1].clear_flag(eSHADOW); } } } vector3d& v1 = triangle[0].normal; vector3d& v2 = triangle[1].normal; if (v1.i != v2.i && v1.j != v2.j && v1.k != v2.k) { //std::cout << "NORMALS NOT SAME" << std::endl; } triangle[1].normal = triangle[0].normal; // Translate/rotate transform(points, 4); // Do perspective transform... point2d pnt2d[4]; perspective(points, pnt2d, 4); transform_normals(triangle, 2); // Render the strip... vector3d dummy[4]; rinfo.rend->setup( triangle, pnt2d, dummy, 2, rinfo.fbuffer, rinfo.bytes_per_row, scene_width, scene_height, rinfo.zb); rinfo.rend->do_render(); num_tri+=2; // Aaarrggghhhh!!!!!! if ((num_tri%100) == 0 && rinfo.prog) { rinfo.prog->update_status(); if (rinfo.prog->cancelled()) { set_cancelled(); } } } /** * Decide if a triangle is a part of the sea * @return true if all points below sea level */ bool handler_land::check_sea_level(triangle3d& tri, const point3d* pnt3d) { bool sea = true; for (int i=0; i<3; i++) { if (pnt3d[tri.point[i]].y > 0.01) { sea = false; break; } } return sea; } /** * Apply water ripples to array of points */ void handler_land::make_waves(point3d* pnt3d, int num_points) { for (int idx=0; idxset_message("Rendering land..."); printf("Rendering land..."); } // Transform the light source for rendering. // Keep untransformed light for shadow calculations vector3d temp = camera*light; rinfo.rend->set_light(temp); int seed; my_settings->get_int(PREVIEW_SEED, &seed); land->seed_rand(seed); int TERRAIN_WIDTH = land->get_width(); float h1 = get_height(-TERRAIN_WIDTH/2, -TERRAIN_WIDTH/2 + TERRAIN_WIDTH); float h2 = get_height(-TERRAIN_WIDTH/2 + TERRAIN_WIDTH, -TERRAIN_WIDTH/2 + TERRAIN_WIDTH); float h3 = get_height(-TERRAIN_WIDTH/2 + TERRAIN_WIDTH, -TERRAIN_WIDTH/2); float h4 = get_height(-TERRAIN_WIDTH/2, -TERRAIN_WIDTH/2); subdivide(-TERRAIN_WIDTH/2, -TERRAIN_WIDTH/2, TERRAIN_WIDTH, h1, h2, h3, h4); printf("call_count = %d\n", call_count); printf("num_tri = %d\n", num_tri); }