diff options
Diffstat (limited to 'dev/a6-harold/ground.cc')
-rw-r--r-- | dev/a6-harold/ground.cc | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/dev/a6-harold/ground.cc b/dev/a6-harold/ground.cc new file mode 100644 index 0000000..51d4d92 --- /dev/null +++ b/dev/a6-harold/ground.cc @@ -0,0 +1,283 @@ +/** CSci-4611 Assignment 6: Harold + */ + +#include "ground.h" + + +Ground::Ground() : diffuse_ramp_(GL_CLAMP_TO_EDGE), + specular_ramp_(GL_CLAMP_TO_EDGE), light_pos_(30,30,30) +{ + +} + +Ground::~Ground() { + +} + +Mesh* Ground::mesh_ptr() { return &ground_mesh_; } + + +void Ground::Init(const std::vector<std::string> &search_path) { + // init ground geometry, a simple grid is used. if it is running too slow, + // you can turn down the resolution by decreasing nx and ny, but this will + // make the hills look more jaggy. + const int nx = 150; + const int ny = 150; + const float size = 100.0; + std::vector<Point3> verts; + std::vector<Vector3> norms; + for (int j = 0; j <= ny; j++) { + for (int i = 0; i <= nx; i++) { + float x = size*(float)j/nx - size/2.0f; + float y = size*(float)i/ny - size/2.0f; + verts.push_back(Point3(x, 0, y)); + norms.push_back(Vector3(0,1,0)); + } + } + std::vector<unsigned int> indices; + for (int j = 0; j < ny; j++) { + for (int i = 0; i < nx; i++) { + // L\ triangle + indices.push_back((i+0)+(j+0)*(nx+1)); + indices.push_back((i+1)+(j+0)*(nx+1)); + indices.push_back((i+0)+(j+1)*(nx+1)); + // \7 triangle + indices.push_back((i+1)+(j+0)*(nx+1)); + indices.push_back((i+1)+(j+1)*(nx+1)); + indices.push_back((i+0)+(j+1)*(nx+1)); + } + } + ground_mesh_.SetIndices(indices); + ground_mesh_.SetVertices(verts); + ground_mesh_.SetNormals(norms); + ground_mesh_.UpdateGPUMemory(); + ground_edge_mesh_.CreateFromMesh(ground_mesh_); + + + // load textures and shaders + diffuse_ramp_.InitFromFile(Platform::FindFile("toonDiffuse.png", search_path)); + specular_ramp_.InitFromFile(Platform::FindFile("toonSpecular.png", search_path)); + + artsy_shaderprog_.AddVertexShaderFromFile(Platform::FindFile("artsy.vert", search_path)); + artsy_shaderprog_.AddFragmentShaderFromFile(Platform::FindFile("artsy.frag", search_path)); + artsy_shaderprog_.LinkProgram(); + + outline_shaderprog_.AddVertexShaderFromFile(Platform::FindFile("outline.vert", search_path)); + outline_shaderprog_.AddFragmentShaderFromFile(Platform::FindFile("outline.frag", search_path)); + outline_shaderprog_.LinkProgram(); +} + + + + +// Projects a 2D normalized screen point (e.g., the mouse position in normalized +// device coordinates) to a 3D point on the ground. Returns true and sets ground_point +// to be equal to the result if the conversion is successful. Returns false if +// the screen point does not project onto the ground. +bool Ground::ScreenPtHitsGround(const Matrix4 &view_matrix, const Matrix4 &proj_matrix, + const Point2 &normalized_screen_pt, Point3 *ground_point) +{ + Matrix4 camera_matrix = view_matrix.Inverse(); + Point3 eye = camera_matrix.ColumnToPoint3(3); + + Point3 pt3d = GfxMath::ScreenToNearPlane(view_matrix, proj_matrix, normalized_screen_pt); + Ray ray(eye, (pt3d - eye).ToUnit()); + float i_time; + int i_tri; + return ray.FastIntersectMesh(&ground_mesh_, &i_time, ground_point, &i_tri); +} + + + + +/** This implements the "h" term used in the equations described in section 4.5 of the + paper. Three arguments are needed: + 1. projection_plane_normal: We need to know where the projection plane is in 3-space + Since a plane can be defined by a point within the plane and a normal, we use + this normal together with the 3rd argument to the function to define the projection + plane described in the paper. + 2. silhouette_curve: As described in the paper, the silhouette curve is a 3D version + of the curve the user draws with the mouse. It is formed by projecting the + original 2D screen-space curve onto the 3D projection plane. + 3. closest_pt_in_plane: As described in the paper, this is the closest point within + the projection plane to the vertex of the mesh that we want to modify. In other + words, it is the perpendicular projection of the vertex we want to modify onto + the projection plane. + */ +float hfunc(const Vector3 projection_plane_normal, const std::vector<Point3> &silhouette_curve, const Point3 &closest_pt_in_plane) { + // define the y axis for a "plane space" coordinate system as a world space vector + Vector3 plane_y = Vector3(0,1,0); + // define the x axis for a "plane space" coordinate system as a world space vector + Vector3 plane_x = plane_y.Cross(projection_plane_normal).ToUnit(); + // define the origin for a "plane space" coordinate system as the first point in the curve + Point3 origin = silhouette_curve[0]; + + // loop over line segments in the curve, find the one that lies over the point by + // comparing the "plane space" x value for the start and end of the line segment + // to the "plane space" x value for the closest point to the vertex that lies + // in the projection plane. + float x_target = (closest_pt_in_plane - origin).Dot(plane_x); + for (int i=1; i<silhouette_curve.size(); i++) { + float x_start = (silhouette_curve[(size_t)i-1] - origin).Dot(plane_x); + float x_end = (silhouette_curve[i] - origin).Dot(plane_x); + if ((x_start <= x_target) && (x_target <= x_end)) { + float alpha = (x_target - x_start) / (x_end - x_start); + float y_curve = silhouette_curve[(size_t)i-1][1] + alpha*(silhouette_curve[i][1] - silhouette_curve[(size_t)i-1][1]); + return y_curve - closest_pt_in_plane[1]; + } + else if ((x_end <= x_target) && (x_target <= x_start)) { + float alpha = (x_target - x_end) / (x_start - x_end); + float y_curve = silhouette_curve[i][1] + alpha*(silhouette_curve[(size_t)i-1][1] - silhouette_curve[i][1]); + return y_curve - closest_pt_in_plane[1]; + } + } + + // here return 0 because the point does not lie under the curve. + return 0.0; +} + + + + +/// Modifies the vertices of the ground mesh to create a hill or valley based +/// on the input stroke. The 2D path of the stroke on the screen is passed +/// in, this is the centerline of the stroke mesh that is actually drawn on +/// the screen while the user is drawing. +void Ground::ReshapeGround(const Matrix4 &view_matrix, const Matrix4 &proj_matrix, + const std::vector<Point2> &stroke2d) +{ + // TODO: Deform the 3D ground mesh according to the algorithm described in the + // Cohen et al. Harold paper. + + // You might need the eye point and the look vector, these can be determined + // from the view matrix as follows: + Matrix4 camera_matrix = view_matrix.Inverse(); + Point3 eye = camera_matrix.ColumnToPoint3(3); + Vector3 look = -camera_matrix.ColumnToVector3(2); + + + + // There are 3 major steps to the algorithm, outlined here: + + // 1. Define a plane to project the stroke onto. The first and last points + // of the stroke are guaranteed to project onto the ground plane. The plane + // should pass through these two points on the ground. The plane should also + // have a normal vector that points toward the camera and is parallel to the + // ground plane. + + + + + + // 2. Project the 2D stroke into 3D so that it lies on the "projection plane" + // defined in step 1. + + + + + + // 3. Loop through all of the vertices of the ground mesh, and adjust the + // height of each based on the equations in section 4.5 of the paper, also + // repeated in the assignment handout. The equations rely upon a function + // h(), and we have implemented that for you as hfunc() defined above in + // this file. The basic structure of the loop you will need is here: + std::vector<Point3> new_verts; + for (int i=0; i<ground_mesh_.num_vertices(); i++) { + Point3 P = ground_mesh_.read_vertex_data(i); // original vertex + + // adjust P according to equations... + + + + + + new_verts.push_back(P); + } + ground_mesh_.SetVertices(new_verts); + ground_mesh_.CalcPerVertexNormals(); + ground_mesh_.UpdateGPUMemory(); + ground_edge_mesh_.CreateFromMesh(ground_mesh_); +} + + + + +/// Draws the ground mesh with toon shading +void Ground::Draw(const Matrix4 &view_matrix, const Matrix4 &proj_matrix, const Color &ground_color) { + // Lighting parameters + Color Ia(1.0f, 1.0f, 1.0f, 1.0f); + Color Id(1.0f, 1.0f, 1.0f, 1.0f); + Color Is(1.0f, 1.0f, 1.0f, 1.0f); + + // Material parameters + Color ka = ground_color; + Color kd(0.4f, 0.4f, 0.4f, 1.0f); + Color ks(0.6f, 0.6f, 0.6f, 1.0f); + float s = 50.0f; + + // Precompute matrices needed in the shader + Matrix4 model_matrix; // identity + Matrix4 modelview_matrix = view_matrix * model_matrix; + Matrix4 normal_matrix = modelview_matrix.Inverse().Transpose(); + Point3 light_in_eye_space = view_matrix * light_pos_; + + // Make sure the default option to only draw front facing triangles is set + glEnable(GL_CULL_FACE); + + + // Draw the ground using the artsy shader + artsy_shaderprog_.UseProgram(); + artsy_shaderprog_.SetUniform("modelViewMatrix", modelview_matrix); + artsy_shaderprog_.SetUniform("normalMatrix", normal_matrix); + artsy_shaderprog_.SetUniform("projectionMatrix", proj_matrix); + artsy_shaderprog_.SetUniform("ka", ka); + artsy_shaderprog_.SetUniform("kd", kd); + artsy_shaderprog_.SetUniform("ks", ks); + artsy_shaderprog_.SetUniform("s", s); + artsy_shaderprog_.SetUniform("lightPosition", light_in_eye_space); + artsy_shaderprog_.SetUniform("Ia", Ia); + artsy_shaderprog_.SetUniform("Id", Id); + artsy_shaderprog_.SetUniform("Is", Is); + artsy_shaderprog_.BindTexture("diffuseRamp", diffuse_ramp_); + artsy_shaderprog_.BindTexture("specularRamp", specular_ramp_); + ground_mesh_.Draw(); + artsy_shaderprog_.StopProgram(); + + // And, draw silhouette edges for the ground using the outline shader + glDisable(GL_CULL_FACE); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1,1); + static const float thickness = 0.2f; + outline_shaderprog_.UseProgram(); + outline_shaderprog_.SetUniform("modelViewMatrix", modelview_matrix); + outline_shaderprog_.SetUniform("normalMatrix", normal_matrix); + outline_shaderprog_.SetUniform("projectionMatrix", proj_matrix); + outline_shaderprog_.SetUniform("thickness", thickness); + ground_edge_mesh_.Draw(); + outline_shaderprog_.StopProgram(); + + + + // This can be useful for debugging, but it is extremely slow to draw. + // Before uncommenting this, it's recommended to turn down the resolution + // of the ground mesh by adjusting the nx and ny constants inside Init(). + /** + // draw lines around each triangle + for (int t=0; t<ground_mesh_.num_triangles(); t++) { + std::vector<unsigned int> indices = ground_mesh_.triangle_vertices(t); + std::vector<Point3> loop; + loop.push_back(ground_mesh_.vertex(indices[0])); + loop.push_back(ground_mesh_.vertex(indices[1])); + loop.push_back(ground_mesh_.vertex(indices[2])); + qs_.DrawLines(model_matrix, view_matrix, proj_matrix, Color(0.7,0.7,0.7), loop, QuickShapes::LinesType::LINE_LOOP, 0.01); + } + + // draw normals + for (int i=0; i<ground_mesh_.num_vertices(); i++) { + Point3 p1 = ground_mesh_.vertex(i); + Point3 p2 = p1 + 0.5*ground_mesh_.normal(i); + qs_.DrawLineSegment(model_matrix, view_matrix, proj_matrix, Color(0.7,0.7,0.7), p1, p2, 0.01); + } + **/ +} + |