├── cmake ├── eigen3.cmake ├── openmp.cmake ├── gl3.cmake ├── libutgraphicsutil.cmake ├── gcc.cmake ├── warning.cmake ├── glm.cmake └── env.cmake ├── lib └── utgraphicsutil │ ├── portable_gl.h │ ├── jpegio.h │ ├── image.h │ ├── debuggl.cc │ ├── material.h │ ├── jpegio.cc │ └── debuggl.h ├── src ├── shaders │ ├── sun.vert │ ├── rain.frag │ ├── cube_fragment.shad │ ├── sun.frag │ ├── ocean.vert │ ├── sun.tes │ ├── rain.vert │ ├── sun.geom │ ├── sky.vert │ ├── sun.tcs │ ├── cube_vertex.shad │ ├── default.vert │ ├── terrain.vert │ ├── sky.frag │ ├── ocean.geom │ ├── boat.geom │ ├── default.geom │ ├── ocean.tcs │ ├── boat.frag │ ├── default.frag │ ├── boat.vert │ ├── ocean.frag │ ├── terrain.geom │ ├── ocean.tes │ └── terrain.frag ├── procedure_geometry.h ├── CMakeLists.txt ├── texture_to_render.h ├── rain_render.h ├── procedure_geometry.cc ├── config.h ├── terrain_render.h ├── perlin.hpp ├── texture_to_render.cc ├── rain_render.cc ├── gui.h ├── util.hpp ├── render_pass.h ├── gui.cc ├── main.cc ├── render_pass.cc └── terrain_render.cc ├── CMakeLists.txt ├── README.md ├── LICENSE └── assets └── rowboat.obj /cmake/eigen3.cmake: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(Eigen3 QUIET) 2 | INCLUDE_DIRECTORIES(SYSTEM ${EIGEN3_INCLUDE_DIR}) 3 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/portable_gl.h: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include 3 | #else 4 | #include 5 | #endif 6 | -------------------------------------------------------------------------------- /src/shaders/sun.vert: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | in vec4 vertex_position; 3 | void main() 4 | { 5 | gl_Position = vertex_position; 6 | } 7 | )zzz" 8 | -------------------------------------------------------------------------------- /src/shaders/rain.frag: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | flat in vec3 off; 4 | out vec4 fragment_color; 5 | 6 | void main() { 7 | vec3 color = vec3(0.77f, 0.88f, 0.96f); 8 | fragment_color = vec4(color, 0.7f); 9 | } 10 | )zzz" 11 | -------------------------------------------------------------------------------- /src/shaders/cube_fragment.shad: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | smooth in vec3 eye_direction; 3 | uniform samplerCube skybox; 4 | out vec4 fragment_color; 5 | 6 | void main() 7 | { 8 | fragment_color = texture(skybox, eye_direction); 9 | } 10 | )zzz" 11 | -------------------------------------------------------------------------------- /src/shaders/sun.frag: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | uniform float time_of_day; 3 | out vec4 fragment_color; 4 | void main() 5 | { 6 | // TODO: maybe scale the color based on time of day? 7 | fragment_color = vec4(0.98, 0.83, 0.25, 1.0); 8 | } 9 | )zzz" 10 | -------------------------------------------------------------------------------- /src/shaders/ocean.vert: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | in vec4 vertex_position; 4 | in vec3 offset; 5 | out vec3 off; 6 | 7 | void main() { 8 | vec3 pos = vertex_position.xyz; 9 | pos.x += offset.x; 10 | pos.z += offset.z; 11 | gl_Position = vec4(pos, 1.0f); 12 | off = offset; 13 | } 14 | )zzz" 15 | -------------------------------------------------------------------------------- /src/procedure_geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCEDURE_GEOMETRY_H 2 | #define PROCEDURE_GEOMETRY_H 3 | 4 | #include 5 | #include 6 | 7 | struct LineMesh; 8 | 9 | void create_floor(std::vector &floor_vertices, 10 | std::vector &floor_faces); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/shaders/sun.tes: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | layout (triangles) in; 3 | void main() 4 | { 5 | int n = 0; 6 | vec4 accum = vec4(0.0f); 7 | for (n = 0; n < 3; n++) { 8 | accum += gl_TessCoord[n] * gl_in[n].gl_Position; 9 | } 10 | gl_Position = vec4(2.0f * normalize(accum.xyz), 1.0); 11 | } 12 | )zzz" 13 | -------------------------------------------------------------------------------- /cmake/openmp.cmake: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(OpenMP QUIET) 2 | if(OPENMP_FOUND) 3 | message(STATUS "OPENMP FOUND") 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 6 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 7 | endif() 8 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/jpegio.h: -------------------------------------------------------------------------------- 1 | #ifndef JPEGIO_H 2 | #define JPEGIO_H 3 | 4 | #include "image.h" 5 | #include 6 | #include 7 | 8 | bool SaveJPEG(const std::string &filename, int image_width, int image_height, 9 | const unsigned char *pixels); 10 | std::unique_ptr LoadJPEG(const std::string &file_name); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | SET(pwd ${CMAKE_CURRENT_LIST_DIR}) 2 | 3 | SET(src "") 4 | AUX_SOURCE_DIRECTORY(${pwd} src) 5 | add_executable(sea-of-thieves ${src}) 6 | message(STATUS "minecraft added ${src}") 7 | 8 | target_link_libraries(sea-of-thieves ${stdgl_libraries}) 9 | FIND_PACKAGE(JPEG REQUIRED) 10 | TARGET_LINK_LIBRARIES(sea-of-thieves ${JPEG_LIBRARIES}) 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3) 2 | project(GLSL) 3 | 4 | FILE(GLOB cmakes ${CMAKE_SOURCE_DIR}/cmake/*.cmake) 5 | FOREACH(cmake ${cmakes}) 6 | INCLUDE(${cmake}) 7 | ENDFOREACH(cmake) 8 | 9 | MESSAGE(STATUS "stdgl: ${stdgl_libraries}") 10 | 11 | ADD_SUBDIRECTORY(src) 12 | 13 | IF (EXISTS ${CMAKE_SOURCE_DIR}/sln/CMakeLists.txt) 14 | ADD_SUBDIRECTORY(sln) 15 | ENDIF() 16 | -------------------------------------------------------------------------------- /src/shaders/rain.vert: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | in vec4 vertex_position; 4 | in vec3 offset; 5 | uniform vec3 center_position; 6 | uniform mat4 projection; 7 | uniform mat4 view; 8 | flat out vec3 off; 9 | 10 | void main() { 11 | vec3 pos = vertex_position.xyz + offset; 12 | pos.xz += center_position.xz; 13 | gl_Position = projection * view * vec4(pos, 1.0f); 14 | off = offset; 15 | } 16 | )zzz" 17 | -------------------------------------------------------------------------------- /cmake/gl3.cmake: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(GLEW REQUIRED) 2 | INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS}) 3 | LINK_LIBRARIES(${GLEW_LIBRARIES}) 4 | 5 | FIND_PACKAGE(PkgConfig REQUIRED) 6 | pkg_search_module(GLFW3 REQUIRED glfw3) 7 | INCLUDE_DIRECTORIES(${GLFW3_INCLUDE_DIRS}) 8 | 9 | LIST(APPEND stdgl_libraries ${GLFW3_STATIC_LIBRARIES} ${GLEW_LIBRARIES}) 10 | 11 | message(STATUS "GLEW_LIBRARIES=${GLEW_LIBRARIES}") 12 | message(STATUS "GLFW_LIBRARIES=${GLFW3_STATIC_LIBRARIES}") 13 | -------------------------------------------------------------------------------- /src/shaders/sun.geom: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | layout (triangles) in; 3 | layout (triangle_strip, max_vertices = 3) out; 4 | uniform mat4 projection; 5 | uniform vec4 light_position; 6 | uniform mat4 view; 7 | void main() 8 | { 9 | int n = 0; 10 | for (n = 0; n < gl_in.length(); n++) { 11 | gl_Position = projection * view * vec4(gl_in[n].gl_Position.xyz + light_position.xyz, 1.0f); 12 | EmitVertex(); 13 | } 14 | EndPrimitive(); 15 | } 16 | )zzz" 17 | -------------------------------------------------------------------------------- /cmake/libutgraphicsutil.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/utgraphicsutil) 2 | AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/lib/utgraphicsutil libutgu_src) 3 | FIND_PACKAGE(JPEG REQUIRED) 4 | ADD_LIBRARY(utgraphicsutil STATIC ${libutgu_src}) 5 | TARGET_LINK_LIBRARIES(utgraphicsutil ${JPEG_LIBRARIES}) 6 | message("JPEG ${JPEG_INCLUDE_DIR}") 7 | TARGET_INCLUDE_DIRECTORIES(utgraphicsutil SYSTEM BEFORE PRIVATE ${JPEG_INCLUDE_DIR}) 8 | list(APPEND stdgl_libraries utgraphicsutil) 9 | -------------------------------------------------------------------------------- /cmake/gcc.cmake: -------------------------------------------------------------------------------- 1 | IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 2 | IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.2.0") 3 | IF (EXISTS /lusr/opt/gcc-5.2.0/bin/g++) 4 | SET(CMAKE_CXX_COMPILER /lusr/opt/gcc-5.2.0/bin/g++) 5 | SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-rpath,/lusr/opt/gcc-5.2.0/lib64/") 6 | ENDIF () 7 | ENDIF () 8 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") 9 | ENDIF () 10 | 11 | MESSAGE(STATUS "USING C++ Compiler ${CMAKE_CXX_COMPILER}") 12 | -------------------------------------------------------------------------------- /src/shaders/sky.vert: -------------------------------------------------------------------------------- 1 | R"zzz(#version 430 core 2 | uniform mat4 inverse_projection_view; 3 | smooth out vec3 eye_direction; 4 | void main() 5 | { 6 | vec2 position = vec2((gl_VertexID & 2) >> 1, 1 - (gl_VertexID & 1)) * 2.0 - 1.0; 7 | vec4 front = inverse_projection_view * vec4(position, -1.0, 1.0); 8 | vec4 back = inverse_projection_view * vec4(position, 1.0, 1.0); 9 | eye_direction = normalize(back.xyz / back.w - front.xyz / front.w); 10 | gl_Position = vec4(position, -1.0, 1.0); 11 | } 12 | )zzz" 13 | -------------------------------------------------------------------------------- /src/shaders/sun.tcs: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | layout (vertices = 3) out; 3 | // Range is 1 to 64 4 | const float InnerLevel = 4.0f; 5 | const float OuterLevel = 4.0f; 6 | void main() 7 | { 8 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 9 | 10 | if (gl_InvocationID == 0) { 11 | gl_TessLevelInner[0] = InnerLevel; 12 | gl_TessLevelOuter[0] = OuterLevel; 13 | gl_TessLevelOuter[1] = OuterLevel; 14 | gl_TessLevelOuter[2] = OuterLevel; 15 | } 16 | } 17 | )zzz" 18 | -------------------------------------------------------------------------------- /src/texture_to_render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class TextureToRender { 4 | public: 5 | TextureToRender(); 6 | ~TextureToRender(); 7 | TextureToRender(TextureToRender &&); 8 | TextureToRender &operator=(TextureToRender &&); 9 | void create(int width, int height); 10 | void bind(); 11 | void unbind(); 12 | const unsigned int *getTexture() const { return &tex_; } 13 | 14 | private: 15 | int w_, h_; 16 | unsigned int fb_ = -1; 17 | unsigned int tex_ = -1; 18 | unsigned int dep_ = -1; 19 | }; 20 | -------------------------------------------------------------------------------- /src/rain_render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render_pass.h" 4 | #include 5 | 6 | class RainRender { 7 | public: 8 | RainRender(size_t rows, size_t cols, std::vector uniforms); 9 | 10 | void update(bool draw); 11 | 12 | private: 13 | void move_particles(float time_delta); 14 | 15 | std::vector rain_points_; 16 | std::unique_ptr rain_pass_; 17 | std::chrono::high_resolution_clock::time_point prev = 18 | std::chrono::high_resolution_clock::now(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/shaders/cube_vertex.shad: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | in vec4 vertex_position; 3 | uniform mat4 inverse_projection_view; 4 | smooth out vec3 eye_direction; 5 | void main() 6 | { 7 | vec2 position = vec2((gl_VertexID & 2) >> 1, 1 - (gl_VertexID & 1)) * 2.0 - 1.0; 8 | vec4 front = inverse_projection_view * vec4(position, -1.0, 1.0); 9 | vec4 back = inverse_projection_view * vec4(position, 1.0, 1.0); 10 | eye_direction = back.xyz / back.w - front.xyz / front.w; 11 | gl_Position = vec4(position, -1.0, 1.0); 12 | } 13 | )zzz" 14 | -------------------------------------------------------------------------------- /cmake/warning.cmake: -------------------------------------------------------------------------------- 1 | # Skeleton code will have tons of unused variables 2 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder -Wno-unknown-pragmas -Wno-unused-variable") 3 | IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 4 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable") 5 | ENDIF () 6 | 7 | IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 8 | # Clang complains more about GLU and overloading functions 9 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations -Wno-overloaded-virtual") 10 | ENDIF () 11 | -------------------------------------------------------------------------------- /src/shaders/default.vert: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | uniform vec4 light_position; 4 | uniform vec3 camera_position; 5 | in vec4 vertex_position; 6 | in vec4 normal; 7 | in vec2 uv; 8 | out vec4 vs_light_direction; 9 | out vec4 vs_normal; 10 | out vec2 vs_uv; 11 | out vec4 vs_camera_direction; 12 | void main() { 13 | gl_Position = vertex_position; 14 | vs_light_direction = light_position - gl_Position; 15 | vs_camera_direction = vec4(camera_position, 1.0) - gl_Position; 16 | vs_normal = normal; 17 | vs_uv = uv; 18 | } 19 | )zzz" 20 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/image.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_H 2 | #define IMAGE_H 3 | 4 | #include 5 | 6 | struct Image { 7 | /* 8 | * Image data in GL_RGB sequence. 9 | * Notes: because of some funny alignment problem it's recommended to 10 | * transform the data into GL_RGBA format before calling 11 | * glTexSubImage2D if you want to use the data for texture mapping 12 | */ 13 | std::vector bytes; 14 | int width; 15 | int height; 16 | int stride; // Stores the actual number of bytes for a scan line, you can 17 | // ignore this for our current case. 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/procedure_geometry.cc: -------------------------------------------------------------------------------- 1 | #include "procedure_geometry.h" 2 | #include "config.h" 3 | void create_floor(std::vector &floor_vertices, 4 | std::vector &floor_faces) { 5 | floor_vertices.push_back(glm::vec4(kFloorXMin, kFloorY, kFloorZMax, 1.0f)); 6 | floor_vertices.push_back(glm::vec4(kFloorXMax, kFloorY, kFloorZMax, 1.0f)); 7 | floor_vertices.push_back(glm::vec4(kFloorXMax, kFloorY, kFloorZMin, 1.0f)); 8 | floor_vertices.push_back(glm::vec4(kFloorXMin, kFloorY, kFloorZMin, 1.0f)); 9 | floor_faces.push_back(glm::uvec3(0, 1, 2)); 10 | floor_faces.push_back(glm::uvec3(2, 3, 0)); 11 | } 12 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | /* 5 | * Global variables go here. 6 | */ 7 | 8 | const float kCylinderRadius = 0.25; 9 | const int kMaxBones = 128; 10 | /* 11 | * Extra credit: what would happen if you set kNear to 1e-5? How to solve it? 12 | */ 13 | const float kNear = 0.1f; 14 | const float kFar = 1000.0f; 15 | const float kFov = 45.0f; 16 | 17 | // Floor info. 18 | const float kFloorEps = 0.5 * (0.025 + 0.0175); 19 | const float kFloorXMin = -100.0f; 20 | const float kFloorXMax = 100.0f; 21 | const float kFloorZMin = -100.0f; 22 | const float kFloorZMax = 100.0f; 23 | const float kFloorY = -0.75617 - kFloorEps; 24 | 25 | const float kScrollSpeed = 64.0f; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenGL Ocean World 2 | 3 | ## Feature List (Planned) 4 | - Ocean water simulation following Tessendorf 5 | - Volumetric clouds 6 | - Screen-space reflections, refractions, Fresnel 7 | - Precipitation particle system 8 | - Boat mesh and buoyant force modeling 9 | 10 | ## Building 11 | ```shell 12 | mkdir build 13 | cd build 14 | cmake .. -DCMAKE_BUILD_TYPE=Release 15 | make && ./bin/sea-of-thieves 16 | ``` 17 | 18 | Note that OpenGL is required to be installed. Tested on Ubuntu 16.04, 17.10. 19 | 20 | ## Controls 21 | - Move with WASD 22 | - Turn with Mouse 23 | - Press the '+=' button to increment the day by 1 hour 24 | - Press the '1' (one) button to toggle stormy mode 25 | 26 | ## Authors 27 | Aaron Zou 28 | Calvin Ly 29 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/debuggl.cc: -------------------------------------------------------------------------------- 1 | #include "debuggl.h" 2 | #include 3 | #include 4 | 5 | const char *DebugGLErrorToString(int error) { 6 | switch (error) { 7 | case GL_NO_ERROR: 8 | return "GL_NO_ERROR"; 9 | break; 10 | case GL_INVALID_ENUM: 11 | return "GL_INVALID_ENUM"; 12 | break; 13 | case GL_INVALID_VALUE: 14 | return "GL_INVALID_VALUE"; 15 | break; 16 | case GL_INVALID_OPERATION: 17 | return "GL_INVALID_OPERATION"; 18 | break; 19 | case GL_OUT_OF_MEMORY: 20 | return "GL_OUT_OF_MEMORY"; 21 | break; 22 | default: 23 | return "Unknown Error"; 24 | break; 25 | } 26 | return "Unicorns Exist"; 27 | } 28 | 29 | void debugglTerminate() { glfwTerminate(); } 30 | -------------------------------------------------------------------------------- /src/shaders/terrain.vert: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | in vec4 vertex_position; 4 | in vec3 offset; 5 | in vec4 heightVec; 6 | in vec3 norm0; 7 | in vec3 norm1; 8 | in vec3 norm2; 9 | in vec3 norm3; 10 | out vec3 off; 11 | out vec3 norm; 12 | 13 | void main() { 14 | vec4 pos = vec4(vertex_position.xyz + offset, 1.0f); 15 | pos.y = vertex_position.y; 16 | if (vertex_position.xz == vec2(0, 0)) { 17 | pos.y += heightVec[0]; 18 | norm = norm0; 19 | } 20 | else if (vertex_position.xz == vec2(1.0f, 0)) { 21 | pos.y += heightVec[1]; 22 | norm = norm1; 23 | } 24 | else if (vertex_position.xz == vec2(0, 1.0f)) { 25 | pos.y += heightVec[2]; 26 | norm = norm2; 27 | } else { 28 | pos.y += heightVec[3]; 29 | norm = norm3; 30 | } 31 | gl_Position = pos; 32 | off = offset; 33 | } 34 | )zzz" 35 | -------------------------------------------------------------------------------- /src/shaders/sky.frag: -------------------------------------------------------------------------------- 1 | R"zzz(#version 430 core 2 | smooth in vec3 eye_direction; 3 | uniform bool is_raining; 4 | uniform float time_of_day; 5 | out vec4 fragment_color; 6 | 7 | vec4 calculateSkyColor() { 8 | if (is_raining) { 9 | vec4 light_gray = vec4(0.66f, 0.66f, 0.66f, 1.0f); 10 | vec4 dark_gray = vec4(0, 0, 0, 1.0f); 11 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 12 | return dark_gray; 13 | } else { 14 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 15 | return mix(light_gray, dark_gray, scale); 16 | } 17 | } else { 18 | vec4 dark = vec4(0.1f, 0.1f, 0.3f, 1.0f); 19 | vec4 bright_blue = vec4(0.53f, 0.81f, 0.92f, 1.0f); 20 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 21 | return dark; 22 | } else { 23 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 24 | return mix(bright_blue, dark, scale); 25 | } 26 | } 27 | } 28 | 29 | void main() 30 | { 31 | fragment_color = calculateSkyColor(); 32 | } 33 | )zzz" 34 | -------------------------------------------------------------------------------- /cmake/glm.cmake: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(glm 0.9.8.0 QUIET) 2 | IF (${glm_FOUND}) 3 | INCLUDE_DIRECTORIES(${glm_INCLUDE_DIR}) 4 | message(STATUS "Using System glm") 5 | ELSE (${glm_FOUND}) 6 | IF (EXISTS /usr/include/glm/gtx/extended_min_max.hpp) 7 | message(STATUS "glm was found in default location") 8 | ELSE () 9 | SET(expected_glm_dir ${CMAKE_SOURCE_DIR}/third-party/glm) 10 | IF (NOT EXISTS ${expected_glm_dir}/copying.txt) 11 | EXECUTE_PROCESS(COMMAND git clone -b 0.9.8.4 --single-branch --depth 1 https://github.com/g-truc/glm.git ${expected_glm_dir}) 12 | ENDIF() 13 | IF (NOT EXISTS ${expected_glm_dir}/glm/gtx/extended_min_max.hpp) 14 | EXECUTE_PROCESS(COMMAND rm -rf ${expected_glm_dir}) 15 | EXECUTE_PROCESS(COMMAND git clone -b 0.9.8.4 --single-branch --depth 1 https://github.com/g-truc/glm.git ${expected_glm_dir}) 16 | ENDIF() 17 | INCLUDE_DIRECTORIES(BEFORE SYSTEM ${expected_glm_dir}) 18 | message(STATUS "Using bundled glm at ${expected_glm_dir}") 19 | ENDIF () 20 | ENDIF (${glm_FOUND}) 21 | 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGLM_FORCE_SIZE_FUNC=1 -DGLM_FORCE_RADIANS=1") 23 | 24 | # vim: tw=78 25 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H 2 | #define MATERIAL_H 3 | 4 | #include "image.h" 5 | #include 6 | #include 7 | 8 | /* 9 | * PMD format groups faces according to their materials. 10 | * Each material was assigned to a continuous sequence of faces. 11 | * 12 | * A material consists of Phong shading model data and optionally a texture. 13 | * 14 | * Note: you need to bypass Phong shading model to get correct results if the 15 | * texture presented in a material, which can be done by checking the texture 16 | * color is zero or not in the shader. 17 | * 18 | * Alternatively in theory, you can also call textureSize in GLSL to check if 19 | * the texture size is non-zero. However this method doesn't work here... 20 | */ 21 | struct Material { 22 | // Phong shading model 23 | glm::vec4 diffuse, ambient, specular; 24 | float shininess; 25 | std::shared_ptr texture; // Texture for current material, can be null. 26 | 27 | size_t offset; // This material applies to faces starting from offset. 28 | size_t nfaces; // This material applies to nfaces faces. 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aaron Zou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/shaders/ocean.geom: -------------------------------------------------------------------------------- 1 | R"zzz(#version 430 core 2 | layout (triangles) in; 3 | layout (triangle_strip, max_vertices = 3) out; 4 | uniform mat4 projection; 5 | uniform mat4 model; 6 | uniform mat4 view; 7 | uniform vec4 light_position; 8 | uniform vec3 camera_position; 9 | in vec3 off[]; 10 | in vec3 normal[]; 11 | out vec4 face_normal; 12 | out vec4 light_direction; 13 | out vec4 camera_direction; 14 | out vec4 world_position; 15 | flat out vec3 offset; 16 | 17 | void emitPrimitive(vec4 position[3]) { 18 | int n = 0; 19 | for (n = 0; n < gl_in.length(); n++) { 20 | light_direction = vec4(normalize(light_position.xyz - position[n].xyz), 1.0f); 21 | camera_direction = vec4(normalize(camera_position - position[n].xyz), 1.0f); 22 | world_position = position[n]; 23 | gl_Position = projection * view * model * (position[n]); 24 | face_normal = vec4(normal[n], 0.0f); 25 | EmitVertex(); 26 | } 27 | EndPrimitive(); 28 | } 29 | 30 | void main() { 31 | vec3 offset = off[0]; 32 | vec4 position[3]; 33 | position[0] = gl_in[0].gl_Position; 34 | position[1] = gl_in[1].gl_Position; 35 | position[2] = gl_in[2].gl_Position; 36 | emitPrimitive(position); 37 | } 38 | )zzz" 39 | -------------------------------------------------------------------------------- /cmake/env.cmake: -------------------------------------------------------------------------------- 1 | # Directories 2 | LINK_DIRECTORIES("/usr/local/lib" "/opt/local/lib") 3 | INCLUDE_DIRECTORIES("/usr/local/include" "/opt/local/include") 4 | INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib) 5 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 6 | 7 | # Flags 8 | #set(CMAKE_CXX_FLAGS "--std=c++14 -g -fmax-errors=1") 9 | set(CMAKE_CXX_FLAGS "--std=c++14 -g") 10 | 11 | # Packages 12 | FIND_PACKAGE(OpenGL REQUIRED) 13 | INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIRS}) 14 | LINK_DIRECTORIES(${OPENGL_LIBRARY_DIRS}) 15 | ADD_DEFINITIONS(${OPENGL_DEFINITIONS}) 16 | 17 | MESSAGE(STATUS "OpenGL: ${OPENGL_LIBRARIES}") 18 | LIST(APPEND stdgl_libraries ${OPENGL_gl_LIBRARY}) 19 | 20 | if (APPLE) 21 | FIND_LIBRARY(COCOA_LIBRARY Cocoa REQUIRED) 22 | FIND_LIBRARY(IOKIT_LIBRARY IOKit REQUIRED) 23 | FIND_LIBRARY(CoreVideo_LIBRARY CoreVideo REQUIRED) 24 | LIST(APPEND stdgl_libraries iconv ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${CoreVideo_LIBRARY}) 25 | if (EXISTS /usr/local/opt/qt5) 26 | # Homebrew installs Qt5 (up to at least 5.9.1) in 27 | # /usr/local/qt5, ensure it can be found by CMake since 28 | # it is not in the default /usr/local prefix. 29 | list(APPEND CMAKE_PREFIX_PATH "/usr/local/opt/qt5") 30 | endif() 31 | endif(APPLE) 32 | -------------------------------------------------------------------------------- /src/shaders/boat.geom: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | layout (triangles) in; 4 | layout (triangle_strip, max_vertices = 3) out; 5 | uniform mat4 projection; 6 | uniform mat4 model; 7 | uniform mat4 view; 8 | uniform vec4 light_position; 9 | in vec4 vs_light_direction[]; 10 | in vec4 vs_camera_direction[]; 11 | in vec4 vs_normal[]; 12 | in vec2 vs_uv[]; 13 | in int id[]; 14 | out vec4 face_normal; 15 | out vec4 light_direction; 16 | out vec4 camera_direction; 17 | out vec4 world_position; 18 | out vec4 vertex_normal; 19 | out vec2 uv_coords; 20 | flat out int instanceID; 21 | void main() { 22 | int n = 0; 23 | vec3 a = gl_in[0].gl_Position.xyz; 24 | vec3 b = gl_in[1].gl_Position.xyz; 25 | vec3 c = gl_in[2].gl_Position.xyz; 26 | vec3 u = normalize(b - a); 27 | vec3 v = normalize(c - a); 28 | face_normal = normalize(vec4(normalize(cross(u, v)), 0.0)); 29 | for (n = 0; n < gl_in.length(); n++) { 30 | light_direction = normalize(vs_light_direction[n]); 31 | camera_direction = normalize(vs_camera_direction[n]); 32 | world_position = gl_in[n].gl_Position; 33 | vertex_normal = vs_normal[n]; 34 | uv_coords = vs_uv[n]; 35 | gl_Position = projection * view * model * gl_in[n].gl_Position; 36 | instanceID = id[n]; 37 | EmitVertex(); 38 | } 39 | EndPrimitive(); 40 | } 41 | )zzz" 42 | -------------------------------------------------------------------------------- /src/shaders/default.geom: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | layout (triangles) in; 4 | layout (triangle_strip, max_vertices = 3) out; 5 | uniform mat4 projection; 6 | uniform mat4 model; 7 | uniform mat4 view; 8 | uniform vec4 light_position; 9 | in vec4 vs_light_direction[]; 10 | in vec4 vs_camera_direction[]; 11 | in vec4 vs_normal[]; 12 | in vec2 vs_uv[]; 13 | in int id[]; 14 | out vec4 face_normal; 15 | out vec4 light_direction; 16 | out vec4 camera_direction; 17 | out vec4 world_position; 18 | out vec4 vertex_normal; 19 | out vec2 uv_coords; 20 | flat out int instanceID; 21 | void main() { 22 | int n = 0; 23 | vec3 a = gl_in[0].gl_Position.xyz; 24 | vec3 b = gl_in[1].gl_Position.xyz; 25 | vec3 c = gl_in[2].gl_Position.xyz; 26 | vec3 u = normalize(b - a); 27 | vec3 v = normalize(c - a); 28 | face_normal = normalize(vec4(normalize(cross(u, v)), 0.0)); 29 | for (n = 0; n < gl_in.length(); n++) { 30 | light_direction = normalize(vs_light_direction[n]); 31 | camera_direction = normalize(vs_camera_direction[n]); 32 | world_position = gl_in[n].gl_Position; 33 | vertex_normal = vs_normal[n]; 34 | uv_coords = vs_uv[n]; 35 | gl_Position = projection * view * model * gl_in[n].gl_Position; 36 | instanceID = id[n]; 37 | EmitVertex(); 38 | } 39 | EndPrimitive(); 40 | } 41 | )zzz" 42 | -------------------------------------------------------------------------------- /src/shaders/ocean.tcs: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | layout (vertices = 3) out; 3 | // Range is 1 to 64 4 | const float InnerLevel = 1.0f; 5 | const float OuterLevel = 1.0f; 6 | uniform vec3 center_position; 7 | uniform vec3 camera_position; 8 | in vec3 off[]; 9 | out vec3 offset[]; 10 | 11 | void main() { 12 | // Sets tesselation levels and pass-through offsets 13 | offset[gl_InvocationID] = off[gl_InvocationID]; 14 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 15 | float multiplier = 1.0f; 16 | vec2 vert_xz = off[gl_InvocationID].xz; 17 | float dist = distance(camera_position.xz, vert_xz); 18 | vec2 look = center_position.xz - camera_position.xz; 19 | vec2 to_current_point = vert_xz - camera_position.xz; 20 | if (dot(look, to_current_point) < 0.0f) { 21 | multiplier = 1.0f; 22 | } else if (dist <= 20.0f) { 23 | multiplier = 4.0f; //HERE: 16.0f; 24 | } else if (dist <= 40.0f) { 25 | multiplier = 2.0f; //HERE: 8.0f; 26 | } else if (dist <= 60.0f) { 27 | multiplier = 1.0f; //HERE: 4.0f; 28 | } else if (dist <= 70.0f) { 29 | multiplier = 1.0f; //HERE: 2.0f; 30 | } 31 | if (gl_InvocationID == 0) { 32 | gl_TessLevelInner[0] = InnerLevel * multiplier; 33 | gl_TessLevelOuter[0] = OuterLevel * multiplier; 34 | gl_TessLevelOuter[1] = OuterLevel * multiplier; 35 | gl_TessLevelOuter[2] = OuterLevel * multiplier; 36 | } 37 | } 38 | )zzz" 39 | -------------------------------------------------------------------------------- /src/terrain_render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render_pass.h" 4 | #include 5 | #include 6 | #include 7 | 8 | class TerrainRender { 9 | public: 10 | TerrainRender(size_t rows, size_t cols, std::vector uniforms); 11 | void renderVisible(glm::vec3 eye); 12 | bool isPositionLegal(const glm::vec3 &loc); 13 | float getWaveHeight(const glm::vec3 &loc); 14 | void setStartTime(std::chrono::high_resolution_clock::time_point t) { 15 | start_time_ = t; 16 | } 17 | float getTime() { 18 | auto end = std::chrono::high_resolution_clock::now(); 19 | return std::chrono::duration(end - start_time_).count(); 20 | } 21 | 22 | glm::vec3 getWaveNormal(const glm::vec3 &loc); 23 | void toggle_storm(bool is_raining); 24 | 25 | private: 26 | void updateInstanceOffsets(int x, int z); 27 | void updateWaveParams(); 28 | 29 | std::chrono::high_resolution_clock::time_point start_time_; 30 | size_t ticks_; 31 | size_t rows_; 32 | size_t cols_; 33 | int cached_x_; 34 | int cached_z_; 35 | std::unique_ptr terrain_pass_; 36 | std::unique_ptr ocean_pass_; 37 | std::vector instanceOffsets_; 38 | std::vector sortedOffsets_; 39 | std::vector heightVec_; 40 | std::vector norm0_; 41 | std::vector norm1_; 42 | std::vector norm2_; 43 | std::vector norm3_; 44 | }; 45 | -------------------------------------------------------------------------------- /src/shaders/boat.frag: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | uniform float time_of_day; 4 | in vec4 face_normal; 5 | in vec4 vertex_normal; 6 | in vec4 light_direction; 7 | in vec4 camera_direction; 8 | in vec2 uv_coords; 9 | layout(binding = 0) uniform sampler2D textureSampler; 10 | layout(location = 0) out vec4 fragment_color; 11 | 12 | float rand(vec2 co){ 13 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 14 | } 15 | void main() { 16 | 17 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 18 | fragment_color = vec4(0.2f, 0.2f, 0.2f, 1.0f); 19 | return; 20 | } 21 | 22 | vec3 texcolor = texture(textureSampler, uv_coords).xyz; 23 | float alpha = 1.0f; 24 | if (length(texcolor) == 0.0) { 25 | // Manually specified constants 26 | vec4 specular = vec4(0.1f, 0.1f, 0.1f, 1.0f); 27 | vec4 diffuse = vec4(0.625f, 0.32f, 0.175f, 1.0f); 28 | vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f); 29 | float shininess = 1.0f; 30 | 31 | vec3 color = diffuse.xyz; 32 | float dot_nl = dot(normalize(light_direction), normalize(vertex_normal)); 33 | dot_nl = clamp(dot_nl, 0.0, 1.0); 34 | vec4 spec = specular * pow(max(0.0, dot(reflect(-light_direction, vertex_normal), camera_direction)), shininess); 35 | color = clamp(dot_nl * color + vec3(ambient) + vec3(spec), 0.0, 1.0); 36 | fragment_color = vec4(color, alpha); 37 | } else { 38 | fragment_color = vec4(texcolor.rgb, alpha); 39 | } 40 | } 41 | )zzz" 42 | -------------------------------------------------------------------------------- /src/shaders/default.frag: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | in vec4 face_normal; 4 | in vec4 vertex_normal; 5 | in vec4 light_direction; 6 | in vec4 camera_direction; 7 | in vec2 uv_coords; 8 | uniform vec4 diffuse; 9 | uniform vec4 ambient; 10 | uniform vec4 specular; 11 | uniform float shininess; 12 | uniform float alpha; 13 | layout(binding = 0) uniform sampler2D textureSampler; 14 | layout(location = 0) out vec4 fragment_color; 15 | 16 | float rand(vec2 co){ 17 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 18 | } 19 | void main() { 20 | vec3 texcolor = texture(textureSampler, uv_coords).xyz; 21 | if (length(texcolor) == 0.0) { 22 | //vec3 color = vec3(0.0, 1.0, 0.0); 23 | //vec3 color = vec3(diffuse); 24 | vec3 color = vec3(diffuse); 25 | //vec2 randuv = vec2(rand(light_direction.xy), rand(light_direction.zw)); 26 | //vec3 color = vec3(diffuse) + texture(textureSampler, randuv).xyz; 27 | //vec3 color = texture(textureSampler, randuv).xyz; 28 | //vec3 color = vec3(diffuse) + vec3(randuv.x, randuv.y, 1.0); 29 | float dot_nl = dot(normalize(light_direction), normalize(vertex_normal)); 30 | dot_nl = clamp(dot_nl, 0.0, 1.0); 31 | vec4 spec = specular * pow(max(0.0, dot(reflect(-light_direction, vertex_normal), camera_direction)), shininess); 32 | color = clamp(dot_nl * color + vec3(ambient) + vec3(spec), 0.0, 1.0); 33 | fragment_color = vec4(color, alpha); 34 | } else { 35 | fragment_color = vec4(texcolor.rgb, alpha); 36 | } 37 | //fragment_color = vec4(0.2, 0.2, 0.2, 0.5); 38 | } 39 | )zzz" 40 | -------------------------------------------------------------------------------- /src/shaders/boat.vert: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | uniform vec4 light_position; 4 | uniform vec3 camera_position; 5 | uniform vec3 center_position; 6 | uniform vec3 prev_move; 7 | uniform vec3 boat_pos_normal; 8 | in vec4 vertex_position; 9 | in vec3 normal; 10 | in vec2 uv; 11 | out vec4 vs_light_direction; 12 | out vec4 vs_normal; 13 | out vec2 vs_uv; 14 | out int id; 15 | out vec4 vs_camera_direction; 16 | 17 | mat4 rotationMatrix(vec3 axis, float angle) { 18 | axis = normalize(axis); 19 | float s = sin(angle); 20 | float c = cos(angle); 21 | float oc = 1.0 - c; 22 | 23 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, 24 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, 25 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 26 | 0.0, 0.0, 0.0, 1.0); 27 | } 28 | 29 | vec3 rotate(vec3 v, vec3 axis, float angle) { 30 | mat4 m = rotationMatrix(axis, angle); 31 | return (m * vec4(v, 1.0)).xyz; 32 | } 33 | 34 | void main() { 35 | mat3 rot = mat3(1.0f); 36 | if (prev_move.xz != vec2(0.0f, 0.0f)) { 37 | vec2 look = normalize(prev_move.xz); 38 | rot[2] = vec3(look[0], 0.0f, look[1]); 39 | rot[0] = cross(rot[1], rot[2]); 40 | } 41 | vec3 pos = rot * vertex_position.xyz; 42 | 43 | // apply wave normal 44 | vec3 y_axis = vec3(0.0f, 1.0f, 0.0f); 45 | pos = rotate(pos, cross(y_axis, boat_pos_normal), -acos(dot(y_axis, boat_pos_normal)) * 0.3f); 46 | 47 | gl_Position = vec4(pos, 1.0f) + vec4(center_position, 0.0); 48 | vs_light_direction = light_position - gl_Position; 49 | vs_camera_direction = vec4(camera_position, 1.0) - gl_Position; 50 | vs_normal = vec4(normal, 0.0f); 51 | vs_uv = uv; 52 | id = 0; 53 | } 54 | )zzz" 55 | -------------------------------------------------------------------------------- /src/shaders/ocean.frag: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | uniform vec3 center_position; 4 | uniform vec3 camera_position; 5 | uniform float time_of_day; 6 | uniform bool is_raining; 7 | in vec4 face_normal; 8 | in vec4 light_direction; 9 | in vec4 world_position; 10 | in vec4 camera_direction; 11 | flat in vec3 offset; 12 | out vec4 fragment_color; 13 | 14 | const float fog_near_plane = 60.0f; 15 | const float fog_far_plane = 70.0f; 16 | const float transparency_scale = 20.0f; 17 | 18 | vec4 calculateSkyColor() { 19 | if (is_raining) { 20 | vec4 light_gray = vec4(0.66f, 0.66f, 0.66f, 1.0f); 21 | vec4 dark_gray = vec4(0, 0, 0, 1.0f); 22 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 23 | return dark_gray; 24 | } else { 25 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 26 | return mix(light_gray, dark_gray, scale); 27 | } 28 | } else { 29 | vec4 dark = vec4(0.1f, 0.1f, 0.3f, 1.0f); 30 | vec4 bright_blue = vec4(0.53f, 0.81f, 0.92f, 1.0f); 31 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 32 | return dark; 33 | } else { 34 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 35 | return mix(bright_blue, dark, scale); 36 | } 37 | } 38 | } 39 | 40 | vec4 processFog(in vec3 color) { 41 | float dist = distance(world_position.xz, camera_position.xz); 42 | float fog_coefficient = (fog_far_plane - dist) / (fog_far_plane - fog_near_plane); 43 | fog_coefficient = clamp(fog_coefficient, 0.0f, 1.0f); 44 | vec4 fog_color = calculateSkyColor(); 45 | float transparency = 0.9f; 46 | return mix(fog_color, vec4(color, transparency), fog_coefficient); 47 | } 48 | 49 | void main() { 50 | vec3 color = vec3(0.016f, 0.0825f, 0.408f); 51 | vec4 specular = vec4(1.0f, 1.0f, 1.0f, 1.0f); 52 | float shininess = 20.0f; 53 | vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f); 54 | float dot_nl = dot(normalize(light_direction), normalize(face_normal)); 55 | dot_nl = clamp(dot_nl, 0.0f, 1.0f); 56 | vec4 spec = specular * pow(clamp(dot(reflect(-light_direction.xyz, face_normal.xyz), camera_direction.xyz), 0.0f, 1.0f), shininess); 57 | color = clamp(dot_nl * color + vec3(ambient) + vec3(spec), 0.0f, 1.0f); 58 | fragment_color = processFog(color); 59 | } 60 | )zzz" 61 | -------------------------------------------------------------------------------- /src/perlin.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace perlin { 8 | float kPi = 3.1415926535897932384626433832795f; 9 | float kBlockSize = 1.0f; 10 | float kMaxHeight = 50.0f; 11 | 12 | float fade(float t) { 13 | return 6 * glm::pow(t, 5) - 15 * glm::pow(t, 4) + 10 * glm::pow(t, 3); 14 | } 15 | 16 | float random(const glm::vec2 &co) { 17 | return glm::fract(glm::sin(glm::dot(co, glm::vec2(12.9898, 78.233))) * 18 | 43758.5453); 19 | } 20 | 21 | glm::vec2 randUnitVec(const glm::vec2 &xz) { 22 | float angle = random(xz) * 2 * kPi; 23 | return glm::normalize(glm::vec2(glm::cos(angle), glm::sin(angle))); 24 | } 25 | 26 | float dotGridGradient(int ix, int iz, float x, float z) { 27 | // Generate seeded random unit gradient vector 28 | auto unit_gradient = randUnitVec(glm::vec2(ix, iz)); 29 | 30 | // Compute distance vector 31 | float dx = x - static_cast(ix); 32 | float dz = z - static_cast(iz); 33 | 34 | // Return dot product 35 | return (dx * unit_gradient[0] + dz * unit_gradient[1]); 36 | } 37 | 38 | // Compute Perlin noise for the given coordinates 39 | float perlin(float x, float z) { 40 | // Get coordinates of unit cell 41 | int x0 = std::floor(x); 42 | int x1 = x0 + 1; 43 | int z0 = std::floor(z); 44 | int z1 = z0 + 1; 45 | 46 | // Interpolation weights 47 | float wt_x = fade(x - static_cast(x0)); 48 | float wt_z = fade(z - static_cast(z0)); 49 | 50 | // Interpolate between grid point gradients 51 | float n0, n1, ix0, ix1, value; 52 | n0 = dotGridGradient(x0, z0, x, z); 53 | n1 = dotGridGradient(x1, z0, x, z); 54 | ix0 = glm::mix(n0, n1, wt_x); 55 | n0 = dotGridGradient(x0, z1, x, z); 56 | n1 = dotGridGradient(x1, z1, x, z); 57 | ix1 = glm::mix(n0, n1, wt_x); 58 | value = glm::mix(ix0, ix1, wt_z); 59 | 60 | return value; 61 | } 62 | 63 | float multipass_noise(float x, float z) { 64 | float sum = 0.0f; 65 | float amp = 8.0f / 15.0f; 66 | float freq_divisor = 1.0f; 67 | for (int n = 0; n < 3; n++) { 68 | sum += perlin(x * freq_divisor, z * freq_divisor) * amp; 69 | freq_divisor *= 2.0f; 70 | amp /= 2.0f; 71 | } 72 | return sum; 73 | } 74 | 75 | float getHeight(float x, float z) { 76 | return multipass_noise(x / kBlockSize / 20.0f, z / kBlockSize / 20.0f) * 77 | kMaxHeight * kBlockSize - 78 | 8.0f; 79 | } 80 | 81 | } /* namespace perlin */ 82 | -------------------------------------------------------------------------------- /src/shaders/terrain.geom: -------------------------------------------------------------------------------- 1 | R"zzz(#version 430 core 2 | layout (triangles) in; 3 | layout (triangle_strip, max_vertices = 3) out; 4 | uniform mat4 projection; 5 | uniform mat4 model; 6 | uniform mat4 view; 7 | uniform vec4 light_position; 8 | uniform vec3 camera_position; 9 | in vec3 off[]; 10 | in vec3 norm[]; 11 | out vec4 normal; 12 | out vec4 light_direction; 13 | out vec4 camera_direction; 14 | out vec4 world_position; 15 | flat out vec3 offset; 16 | 17 | const float kPi = 3.1415926535897932384626433832795f; 18 | 19 | float fade(float t) { 20 | return 6 * pow(t, 5) - 15 * pow(t, 4) + 10 * pow(t, 3); 21 | } 22 | 23 | float random(vec2 co) { 24 | return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453); 25 | } 26 | 27 | vec2 randUnitVec(vec2 xz) { 28 | float angle = random(xz) * 2 * kPi; 29 | return normalize(vec2(cos(angle), sin(angle))); 30 | } 31 | 32 | float dotGridGradient(int iu, int iv, float u, float v) { 33 | // Generate seeded random unit gradient vector 34 | vec2 unit_gradient = randUnitVec(vec2(iu, iv)); 35 | 36 | // Compute distance vector 37 | float du = u - float(iu); 38 | float dv = v - float(iv); 39 | 40 | // Return dot product 41 | return (du * unit_gradient[0] + dv * unit_gradient[1]); 42 | } 43 | 44 | float perlin(float u, float v) { 45 | // Get coordinates of unit cell 46 | int u0 = int(floor(u)); 47 | int u1 = u0 + 1; 48 | int v0 = int(floor(v)); 49 | int v1 = v0 + 1; 50 | 51 | // Interpolation weights 52 | float wt_u = fade(u - float(u0)); 53 | float wt_v = fade(v - float(v0)); 54 | 55 | // Interpolate between grid point gradients 56 | float n0, n1, iu0, iu1, value; 57 | n0 = dotGridGradient(u0, v0, u, v); 58 | n1 = dotGridGradient(u1, v0, u, v); 59 | iu0 = mix(n0, n1, wt_u); 60 | n0 = dotGridGradient(u0, v1, u, v); 61 | n1 = dotGridGradient(u1, v1, u, v); 62 | iu1 = mix(n0, n1, wt_u); 63 | value = mix(iu0, iu1, wt_v); 64 | 65 | return value; 66 | } 67 | 68 | void emitPrimitive(vec4 position[3]) { 69 | int n = 0; 70 | for (n = 0; n < gl_in.length(); n++) { 71 | light_direction = vec4(normalize(light_position.xyz - position[n].xyz), 1.0f); 72 | camera_direction = vec4(normalize(camera_position - position[n].xyz), 1.0f); 73 | world_position = position[n]; 74 | gl_Position = projection * view * model * (position[n]); 75 | vec3 perlin_normal = norm[n]; 76 | perlin_normal.y += perlin(world_position.x, world_position.z) * 0.8f; 77 | normal = vec4(normalize(perlin_normal), 0.0f); 78 | EmitVertex(); 79 | } 80 | EndPrimitive(); 81 | } 82 | 83 | void main() { 84 | vec3 offset = off[0]; 85 | vec4 position[3]; 86 | position[0] = gl_in[0].gl_Position; 87 | position[1] = gl_in[1].gl_Position; 88 | position[2] = gl_in[2].gl_Position; 89 | emitPrimitive(position); 90 | } 91 | )zzz" 92 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/jpegio.cc: -------------------------------------------------------------------------------- 1 | #include "jpegio.h" 2 | #include 3 | #include 4 | #include 5 | 6 | bool SaveJPEG(const std::string &filename, int image_width, int image_height, 7 | const unsigned char *pixels) { 8 | struct jpeg_compress_struct cinfo; 9 | struct jpeg_error_mgr jerr; 10 | FILE *outfile; 11 | JSAMPROW row_pointer[1]; 12 | int row_stride; 13 | 14 | cinfo.err = jpeg_std_error(&jerr); 15 | jpeg_create_compress(&cinfo); 16 | 17 | outfile = fopen(filename.c_str(), "wb"); 18 | if (outfile == NULL) 19 | return false; 20 | 21 | jpeg_stdio_dest(&cinfo, outfile); 22 | 23 | cinfo.image_width = image_width; 24 | cinfo.image_height = image_height; 25 | cinfo.input_components = 3; 26 | cinfo.in_color_space = JCS_RGB; 27 | jpeg_set_defaults(&cinfo); 28 | jpeg_set_quality(&cinfo, 100, (boolean) true); 29 | jpeg_start_compress(&cinfo, (boolean) true); 30 | 31 | row_stride = image_width * 3; 32 | 33 | while (cinfo.next_scanline < cinfo.image_height) { 34 | row_pointer[0] = const_cast( 35 | &pixels[(cinfo.image_height - 1 - cinfo.next_scanline) * row_stride]); 36 | jpeg_write_scanlines(&cinfo, row_pointer, 1); 37 | } 38 | 39 | jpeg_finish_compress(&cinfo); 40 | fclose(outfile); 41 | 42 | jpeg_destroy_compress(&cinfo); 43 | return true; 44 | } 45 | 46 | std::unique_ptr LoadJPEG(const std::string &file_name) { 47 | FILE *file = fopen(file_name.c_str(), "rb"); 48 | struct jpeg_decompress_struct info; 49 | struct jpeg_error_mgr err; 50 | 51 | info.err = jpeg_std_error(&err); 52 | jpeg_create_decompress(&info); 53 | 54 | if (file == NULL) { 55 | return nullptr; 56 | } 57 | auto image = std::make_unique(); 58 | jpeg_stdio_src(&info, file); 59 | jpeg_read_header(&info, (boolean) true); 60 | jpeg_start_decompress(&info); 61 | 62 | image->width = info.output_width; 63 | image->height = info.output_height; 64 | 65 | int channels = info.num_components; 66 | long size = image->width * image->height * 3; 67 | 68 | image->bytes.resize(size); 69 | 70 | int a = (channels > 2 ? 1 : 0); 71 | int b = (channels > 2 ? 2 : 0); 72 | std::vector scan_line(image->width * channels, 0); 73 | unsigned char *p1 = &scan_line[0]; 74 | unsigned char **p2 = &p1; 75 | unsigned char *out_scan_line = image->bytes.data(); 76 | while (info.output_scanline < info.output_height) { 77 | jpeg_read_scanlines(&info, p2, 1); 78 | for (int i = 0; i < image->width; ++i) { 79 | out_scan_line[3 * i] = scan_line[channels * i]; 80 | out_scan_line[3 * i + 1] = scan_line[channels * i + a]; 81 | out_scan_line[3 * i + 2] = scan_line[channels * i + b]; 82 | } 83 | out_scan_line += image->width * 3; 84 | } 85 | jpeg_finish_decompress(&info); 86 | fclose(file); 87 | return image; 88 | } 89 | -------------------------------------------------------------------------------- /src/texture_to_render.cc: -------------------------------------------------------------------------------- 1 | #include "texture_to_render.h" 2 | #include 3 | #include 4 | #include 5 | 6 | TextureToRender::TextureToRender() {} 7 | 8 | TextureToRender::~TextureToRender() { 9 | if (fb_ == static_cast(-1)) { 10 | return; 11 | } 12 | unbind(); 13 | glDeleteFramebuffers(1, &fb_); 14 | glDeleteTextures(1, &tex_); 15 | glDeleteRenderbuffers(1, &dep_); 16 | } 17 | 18 | TextureToRender::TextureToRender(TextureToRender &&other) { 19 | *this = std::move(other); 20 | } 21 | 22 | TextureToRender &TextureToRender::operator=(TextureToRender &&other) { 23 | if (this != &other) { 24 | // Free existing resources 25 | if (fb_ != static_cast(-1)) { 26 | unbind(); 27 | glDeleteFramebuffers(1, &fb_); 28 | glDeleteTextures(1, &tex_); 29 | glDeleteRenderbuffers(1, &dep_); 30 | } 31 | 32 | // Steal other's resources 33 | w_ = other.w_; 34 | h_ = other.h_; 35 | fb_ = other.fb_; 36 | tex_ = other.tex_; 37 | dep_ = other.dep_; 38 | 39 | // Ensure other's destructor doesn't free anything 40 | other.fb_ = -1; 41 | } 42 | return *this; 43 | } 44 | 45 | void TextureToRender::create(int width, int height) { 46 | w_ = width; 47 | h_ = height; 48 | // Create the framebuffer object backed by a texture 49 | glGenFramebuffers(1, &fb_); 50 | glGenTextures(1, &tex_); 51 | glGenRenderbuffers(1, &dep_); 52 | bind(); 53 | // Give empty image to OpenGL 54 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 55 | GL_UNSIGNED_BYTE, 0); 56 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 57 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 58 | 59 | // Configure framebuffer to use texture 60 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_, 0); 61 | GLenum draw_buffers[1] = {GL_COLOR_ATTACHMENT0}; 62 | glDrawBuffers(1, draw_buffers); 63 | 64 | // Attach renderbuffer 65 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); 66 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 67 | GL_RENDERBUFFER, dep_); 68 | 69 | // Check framebuffer creation success 70 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 71 | std::cerr << "Failed to create framebuffer object as render target" 72 | << std::endl; 73 | } 74 | unbind(); 75 | } 76 | 77 | void TextureToRender::bind() { 78 | // Bind the framebuffer object to GL_FRAMEBUFFER 79 | glBindFramebuffer(GL_FRAMEBUFFER, fb_); 80 | glBindTexture(GL_TEXTURE_2D, tex_); 81 | glBindRenderbuffer(GL_RENDERBUFFER, dep_); 82 | } 83 | 84 | void TextureToRender::unbind() { 85 | // Unbind current framebuffer object from the render target 86 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 87 | glBindTexture(GL_TEXTURE_2D, 0); 88 | glBindRenderbuffer(GL_RENDERBUFFER, 0); 89 | } 90 | -------------------------------------------------------------------------------- /src/rain_render.cc: -------------------------------------------------------------------------------- 1 | #include "rain_render.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | const float kRainSpeed = 50.0f; 8 | const float kRainDropLength = 0.5f; 9 | const float kResetHeight = 100.0f; 10 | const float kResetThreshold = 0.0f; 11 | 12 | const char *rain_vertex_shader = 13 | #include "shaders/rain.vert" 14 | ; 15 | 16 | const char *rain_fragment_shader = 17 | #include "shaders/rain.frag" 18 | ; 19 | 20 | const std::array line_vertices = { 21 | {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, kRainDropLength, 0.0f, 1.0f}}}; 22 | 23 | const std::array line_index = {{{0, 1}}}; 24 | 25 | RainRender::RainRender(size_t rows, size_t cols, 26 | std::vector uniforms) 27 | : rain_points_(rows * cols) { 28 | size_t index = 0; 29 | for (size_t i = 0; i < rows; i++) { 30 | for (size_t j = 0; j < cols; j++) { 31 | auto chance = fmodf(rand(), 100.0f); 32 | if (chance < 50.0f) { 33 | continue; 34 | } 35 | float height = 36 | fmodf(rand(), kResetHeight - kResetThreshold) + kResetThreshold; 37 | rain_points_[index++] = {float(i) - float(rows) / 2, height, 38 | float(j) - float(cols) / 2}; 39 | } 40 | } 41 | rain_points_.resize(index); 42 | 43 | auto rain_pass_input = RenderDataInput{}; 44 | rain_pass_input.assign(0, "vertex_position", line_vertices.data(), 45 | line_vertices.size(), 4, GL_FLOAT); 46 | rain_pass_input.assign(1, "offset", rain_points_.data(), rain_points_.size(), 47 | 3, GL_FLOAT, true); 48 | rain_pass_input.assignIndex(line_index.data(), line_index.size(), 2); 49 | auto rain_shaders = std::vector{ 50 | {rain_vertex_shader, nullptr, rain_fragment_shader}}; 51 | auto output = std::vector{{"fragment_color"}}; 52 | this->rain_pass_ = std::make_unique( 53 | -1, rain_pass_input, rain_shaders, uniforms, output); 54 | } 55 | 56 | void RainRender::move_particles(float time_delta) { 57 | for (size_t i = 0; i < rain_points_.size(); i++) { 58 | auto point = rain_points_.at(i); 59 | if (point.y < kResetThreshold) { 60 | point.y += kResetHeight - kResetThreshold; 61 | } else { 62 | point.y -= kRainSpeed * time_delta; 63 | } 64 | rain_points_[i] = point; 65 | } 66 | rain_pass_->updateVBO(1, rain_points_.data(), rain_points_.size()); 67 | } 68 | 69 | void RainRender::update(bool draw) { 70 | auto now = std::chrono::high_resolution_clock::now(); 71 | move_particles(std::chrono::duration(now - prev).count()); 72 | prev = now; 73 | if (draw) { 74 | glEnable(GL_LINE_SMOOTH); 75 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 76 | rain_pass_->setup(); 77 | glDrawElementsInstanced(GL_LINES, 2, GL_UNSIGNED_INT, 0, 78 | rain_points_.size()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/gui.h: -------------------------------------------------------------------------------- 1 | #ifndef SKINNING_GUI_H 2 | #define SKINNING_GUI_H 3 | 4 | #include "terrain_render.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct Mesh; 14 | 15 | /* 16 | * Hint: call glUniformMatrix4fv on thest pointers 17 | */ 18 | struct MatrixPointers { 19 | const float *projection, *model, *view, *inv_proj_view; 20 | }; 21 | 22 | class GUI { 23 | public: 24 | GUI(GLFWwindow *); 25 | ~GUI(); 26 | 27 | void keyCallback(int key, int scancode, int action, int mods); 28 | void mousePosCallback(double mouse_x, double mouse_y); 29 | void mouseButtonCallback(int button, int action, int mods); 30 | void mouseScrollCallback(double dx, double dy); 31 | void updateMatrices(); 32 | MatrixPointers getMatrixPointers() const; 33 | 34 | static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, 35 | int mods); 36 | static void MousePosCallback(GLFWwindow *window, double mouse_x, 37 | double mouse_y); 38 | static void MouseButtonCallback(GLFWwindow *window, int button, int action, 39 | int mods); 40 | static void MouseScrollCallback(GLFWwindow *window, double dx, double dy); 41 | 42 | const glm::vec3 &getCenter() const { return center_; } 43 | const glm::vec3 &getCamera() const { return eye_; } 44 | const float *getLightPositionPtr() const { return &light_position_[0]; } 45 | 46 | void updatePosition(); 47 | glm::vec3 getMoveVec(const glm::vec3 &input); 48 | TerrainRender *terrainRender = nullptr; 49 | const float kMaxTimeOfDay = 1440.0f; 50 | void incrementTimeOfDay(float f) { 51 | time_of_day_ = fmodf(time_of_day_ + f, kMaxTimeOfDay); 52 | } 53 | float &getTimeOfDay() { return time_of_day_; } 54 | int &isRaining() { return raining_; } 55 | 56 | glm::vec3 &getPreviousMoveVec() { return previous_move_; } 57 | 58 | private: 59 | GLFWwindow *window_; 60 | 61 | // Dimension state 62 | int window_width_, window_height_; 63 | 64 | bool drag_state_ = false; 65 | bool fps_mode_ = false; 66 | int current_button_ = -1; 67 | float roll_speed_ = M_PI / 64.0f; 68 | float last_x_ = 0.0f; 69 | float last_y_ = 0.0f; 70 | float current_x_ = 0.0f; 71 | float current_y_ = 0.0f; 72 | float camera_distance_ = 10.0; 73 | float pan_speed_ = 0.1f; 74 | float rotation_speed_ = 0.02f; 75 | float zoom_speed_ = 0.1f; 76 | float aspect_; 77 | 78 | glm::vec3 eye_ = glm::vec3(0.0f, 10.0f, camera_distance_); 79 | glm::vec3 up_ = glm::vec3(0.0f, 1.0f, 0.0f); 80 | glm::vec3 look_ = glm::vec3(0.0f, 0.0f, 1.0f); 81 | glm::vec3 tangent_ = glm::cross(look_, up_); 82 | glm::vec3 center_ = eye_ - camera_distance_ * look_; 83 | glm::mat3 orientation_ = glm::mat3(tangent_, up_, look_); 84 | glm::vec4 light_position_; 85 | float y_velocity_ = 0.0f; 86 | bool gravity_enabled_ = true; 87 | 88 | glm::mat4 view_matrix_ = glm::lookAt(eye_, center_, up_); 89 | glm::mat4 projection_matrix_; 90 | glm::mat4 model_matrix_ = glm::mat4(1.0f); 91 | glm::mat4 inv_proj_matrix_ = glm::inverse(projection_matrix_ * view_matrix_); 92 | std::unordered_map key_pressed_; 93 | 94 | bool captureWASDUPDOWN(int key, int action); 95 | float time_of_day_ = 9 * 60.0f; 96 | // each unit represents minutes of the day (so 24 * 60 = 1 day) 97 | int raining_ = false; 98 | glm::vec3 previous_move_{0.0f}; 99 | }; 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/shaders/ocean.tes: -------------------------------------------------------------------------------- 1 | R"zzz(#version 400 core 2 | layout (triangles) in; 3 | uniform int num_waves; 4 | uniform float time; 5 | uniform float amp[100]; 6 | uniform float freq[100]; 7 | uniform float phi[100]; 8 | uniform vec3 dir[100]; 9 | uniform float steepness; 10 | in vec3 offset[]; 11 | out vec3 off; 12 | out vec3 normal; 13 | 14 | const float kPi = 3.1415926535897932384626433832795f; 15 | 16 | float fade(float t) { 17 | return 6 * pow(t, 5) - 15 * pow(t, 4) + 10 * pow(t, 3); 18 | } 19 | 20 | float random(vec2 co) { 21 | return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453); 22 | } 23 | 24 | vec2 randUnitVec(vec2 xz) { 25 | float angle = random(xz) * 2 * kPi; 26 | return normalize(vec2(cos(angle), sin(angle))); 27 | } 28 | 29 | float dotGridGradient(int iu, int iv, float u, float v) { 30 | // Generate seeded random unit gradient vector 31 | vec2 unit_gradient = randUnitVec(vec2(iu, iv)); 32 | 33 | // Compute distance vector 34 | float du = u - float(iu); 35 | float dv = v - float(iv); 36 | 37 | // Return dot product 38 | return (du * unit_gradient[0] + dv * unit_gradient[1]); 39 | } 40 | 41 | float perlin(float u, float v) { 42 | // Get coordinates of unit cell 43 | int u0 = int(floor(u)); 44 | int u1 = u0 + 1; 45 | int v0 = int(floor(v)); 46 | int v1 = v0 + 1; 47 | 48 | // Interpolation weights 49 | float wt_u = fade(u - float(u0)); 50 | float wt_v = fade(v - float(v0)); 51 | 52 | // Interpolate between grid point gradients 53 | float n0, n1, iu0, iu1, value; 54 | n0 = dotGridGradient(u0, v0, u, v); 55 | n1 = dotGridGradient(u1, v0, u, v); 56 | iu0 = mix(n0, n1, wt_u); 57 | n0 = dotGridGradient(u0, v1, u, v); 58 | n1 = dotGridGradient(u1, v1, u, v); 59 | iu1 = mix(n0, n1, wt_u); 60 | value = mix(iu0, iu1, wt_v); 61 | 62 | return value; 63 | } 64 | 65 | // Bilinear interpolation 66 | vec4 interp(in vec4 corner0, in vec4 corner1, in vec4 corner2) { 67 | return corner0 * gl_TessCoord[0] + corner1 * gl_TessCoord[1] + corner2 * gl_TessCoord[2]; 68 | } 69 | 70 | // State of a given wave 71 | float calculateWaveHeight(in vec4 location, in float a, in float freq, in float phi, in vec3 direction) { 72 | return a * sin(dot(direction.xz, location.xz) * freq + time * phi); 73 | } 74 | 75 | // Derivative calculation 76 | vec3 calculateXZDeriv(in vec4 location, in float a, in float freq, in float phi, in vec3 direction) { 77 | vec3 deriv = vec3(0.0f); 78 | deriv.x -= freq * direction.x * a * cos(dot(direction.xz, location.xz) * freq + time * phi); 79 | deriv.z -= freq * direction.z * a * cos(dot(direction.xz, location.xz) * freq + time * phi); 80 | return deriv; 81 | } 82 | 83 | // Height function for Gerstner waves, returns contribution in x, y, z dims 84 | vec3 gerstnerHeight(in vec4 loc, in float amp, in float freq, in float phi, in vec3 dir) { 85 | vec3 part = vec3(0.0f); 86 | float q = steepness / (freq * amp * num_waves); 87 | part.xz = q * amp * dir.xz * cos(dot(freq * dir.xz, loc.xz) + phi * time); 88 | part.y = amp * sin(dot(freq * dir.xz, loc.xz) + phi * time); 89 | return part; 90 | } 91 | 92 | // Normal calculations for Gerstner waves 93 | vec3 gerstnerNormal(in vec3 wave, in float amp, in float freq, in float phi, in vec3 dir) { 94 | float q = steepness / (freq * amp * num_waves); 95 | float WA = freq * amp; 96 | float S = sin(dot(freq * dir, wave) + phi * time); 97 | float C = cos(dot(freq * dir, wave) + phi * time); 98 | vec3 normal = vec3(0.0f); 99 | normal.xz = -(dir.xz * WA * C); 100 | normal.y = -(q * WA * S); 101 | return normal; 102 | } 103 | 104 | void main() { 105 | off = offset[0]; 106 | vec4 loc = interp(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position); 107 | 108 | // Gerstner wave calculations 109 | vec4 wave = vec4(loc.x, 0.0f, loc.z, loc.w); 110 | for (int i = 0; i < num_waves; i++) { 111 | wave.xyz += gerstnerHeight(loc, amp[i], freq[i], phi[i], dir[i]); 112 | } 113 | vec3 norm = vec3(0.0f, 1.0f, 0.0f); 114 | for (int i = 0; i < num_waves; i++) { 115 | norm += gerstnerNormal(wave.xyz, amp[i], freq[i], phi[i], dir[i]); 116 | } 117 | norm.y += (perlin(wave.x, wave.z) * 0.8f); 118 | gl_Position = wave; 119 | normal = normalize(norm); 120 | } 121 | )zzz" 122 | -------------------------------------------------------------------------------- /lib/utgraphicsutil/debuggl.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGL_H 2 | #define DEBUGGL_H 3 | 4 | void debugglTerminate(); 5 | 6 | #define CHECK_SUCCESS(x) \ 7 | do { \ 8 | if (!(x)) { \ 9 | debugglTerminate(); \ 10 | exit(EXIT_FAILURE); \ 11 | } \ 12 | } while (0) 13 | 14 | #define CHECK_GL_SHADER_ERROR(id) \ 15 | do { \ 16 | GLint status = 0; \ 17 | GLint length = 0; \ 18 | glGetShaderiv(id, GL_COMPILE_STATUS, &status); \ 19 | glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length); \ 20 | if (status != GL_TRUE) { \ 21 | std::string log(length, 0); \ 22 | glGetShaderInfoLog(id, length, nullptr, &log[0]); \ 23 | std::cerr << __func__ << " Line :" << __LINE__ << " Status: " << status \ 24 | << " OpenGL Shader Error: Log = \n" \ 25 | << &log[0]; \ 26 | std::cerr << length << " bytes\n"; \ 27 | debugglTerminate(); \ 28 | exit(EXIT_FAILURE); \ 29 | } \ 30 | } while (0) 31 | 32 | #define CHECK_GL_PROGRAM_ERROR(id) \ 33 | do { \ 34 | GLint status = 0; \ 35 | GLint length = 0; \ 36 | glGetProgramiv(id, GL_LINK_STATUS, &status); \ 37 | glGetProgramiv(id, GL_INFO_LOG_LENGTH, &length); \ 38 | if (status != GL_TRUE) { \ 39 | std::string log(length, 0); \ 40 | glGetProgramInfoLog(id, length, nullptr, &log[0]); \ 41 | std::cerr << __func__ << " Line :" << __LINE__ \ 42 | << " OpenGL Program Error: Log = \n" \ 43 | << &log[0]; \ 44 | debugglTerminate(); \ 45 | exit(EXIT_FAILURE); \ 46 | } \ 47 | } while (0) 48 | 49 | #define CHECK_GL_ERROR(statement) \ 50 | do { \ 51 | { statement; } \ 52 | GLenum error = GL_NO_ERROR; \ 53 | if ((error = glGetError()) != GL_NO_ERROR) { \ 54 | std::cerr << __func__ << " Line :" << __LINE__ \ 55 | << " OpenGL Error: code = " << error \ 56 | << " description = " << DebugGLErrorToString(int(error)); \ 57 | debugglTerminate(); \ 58 | exit(EXIT_FAILURE); \ 59 | } \ 60 | } while (0) 61 | 62 | const char *DebugGLErrorToString(int error); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace util { 18 | 19 | struct Mesh { 20 | std::vector vertices; 21 | std::vector normals; 22 | std::vector vertex_indices; 23 | }; 24 | 25 | std::vector split(const std::string &str, char delim) { 26 | std::stringstream ss{str}; 27 | std::string item; 28 | std::vector tokens; 29 | while (getline(ss, item, delim)) { 30 | tokens.push_back(item); 31 | } 32 | return tokens; 33 | } 34 | 35 | // Load geometry from OBJ file 36 | Mesh LoadObj(const std::string &filename) { 37 | auto mesh = Mesh{}; 38 | std::cout << "Reading mesh from OBJ file" << std::endl; 39 | 40 | // Helper objects 41 | std::ifstream file{filename}; 42 | if (!file.is_open()) { 43 | throw std::invalid_argument("Failed to open file: " + filename); 44 | } 45 | auto line = std::string{}; 46 | auto type = std::string{}; 47 | auto vertex = glm::vec4{0.0, 0.0, 0.0, 1.0}; 48 | auto normals = std::vector{}; 49 | auto normal = glm::vec3{0.0, 0.0, 0.0}; 50 | auto vertex_index = glm::uvec3{0, 0, 0}; 51 | auto normal_index = glm::uvec3{0, 0, 0}; 52 | 53 | // Parse each line 54 | while (std::getline(file, line)) { 55 | auto stream = std::istringstream{line}; 56 | stream >> type; 57 | if (type == "v") { 58 | stream >> vertex.x >> vertex.y >> vertex.z; 59 | mesh.vertices.push_back(vertex); 60 | } else if (type == "f") { 61 | if (mesh.normals.size() == 0) { 62 | mesh.normals.resize(mesh.vertices.size()); 63 | } 64 | // Each entry specifies both vertex index and normal index 65 | for (int i = 0; i <= 2; i++) { 66 | std::string temp; 67 | stream >> temp; 68 | auto parts = split(temp, '/'); 69 | vertex_index[i] = std::stoi(parts.at(0)) - 1; 70 | mesh.normals[vertex_index[i]] = normals.at(std::stoi(parts.at(2)) - 1); 71 | // normal_index[i] = std::stoi(parts.at(2)) - 1; 72 | } 73 | mesh.vertex_indices.push_back(vertex_index); 74 | // mesh.normals.push_back(normals.at(normal_index[0])); 75 | } else if (type == "vn") { 76 | stream >> normal.x >> normal.y >> normal.z; 77 | normals.push_back(normal); 78 | } else if (type == "#") { 79 | // Skip comment 80 | continue; 81 | } else { 82 | std::cout << "Skipping unknown type: " << type << std::endl; 83 | } 84 | } 85 | 86 | // Print out some stats 87 | std::cout << mesh.vertices.size() << " vertices, " << mesh.normals.size() 88 | << " normals, " << mesh.vertex_indices.size() << " faces" 89 | << std::endl; 90 | 91 | std::cout << "Done reading mesh" << std::endl; 92 | return mesh; 93 | } 94 | 95 | std::array, 6> 96 | loadSkyboxImages(std::array paths) { 97 | auto images = std::array, 6>{}; 98 | for (size_t i = 0; i < 6; i++) { 99 | auto image = std::unique_ptr{}; 100 | if ((image = LoadJPEG(paths[i])) == nullptr) { 101 | std::cout << "Failed to load: " << paths[i] << std::endl; 102 | } 103 | images[i] = std::move(image); 104 | } 105 | return images; 106 | } 107 | 108 | void createCubemap(GLuint programID, std::array paths) { 109 | auto skybox = loadSkyboxImages(paths); 110 | std::cout << "Cubemap loaded!" << std::endl; 111 | GLuint texture = 0; 112 | CHECK_GL_ERROR(glGenTextures(1, &texture)); 113 | CHECK_GL_ERROR(glBindTexture(GL_TEXTURE_CUBE_MAP, texture)); 114 | CHECK_GL_ERROR(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, 115 | GL_CLAMP_TO_EDGE)); 116 | CHECK_GL_ERROR(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, 117 | GL_CLAMP_TO_EDGE)); 118 | CHECK_GL_ERROR( 119 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); 120 | CHECK_GL_ERROR( 121 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); 122 | for (size_t i = 0; i < 6; i++) { 123 | CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, 124 | skybox[i]->width, skybox[i]->height, 0, GL_RGB, 125 | GL_UNSIGNED_BYTE, 126 | static_cast(skybox[i]->bytes.data()))); 127 | } 128 | CHECK_GL_ERROR(glBindTexture(GL_TEXTURE_CUBE_MAP, 0)); 129 | GLuint location = 0; 130 | CHECK_GL_ERROR(location = glGetUniformLocation(programID, "skybox")); 131 | CHECK_GL_ERROR(glProgramUniform1i(programID, location, 1)); 132 | CHECK_GL_ERROR(glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)); 133 | CHECK_GL_ERROR(glBindTextureUnit(1, texture)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/shaders/terrain.frag: -------------------------------------------------------------------------------- 1 | R"zzz( 2 | #version 430 core 3 | uniform vec3 center_position; 4 | uniform vec3 camera_position; 5 | uniform float time_of_day; 6 | uniform bool is_raining; 7 | in vec4 normal; 8 | in vec4 light_direction; 9 | in vec4 world_position; 10 | in vec4 camera_direction; 11 | flat in vec3 offset; 12 | out vec4 fragment_color; 13 | 14 | const float kPi = 3.1415926535897932384626433832795f; 15 | const float fog_near_plane = 60.0f; 16 | const float fog_far_plane = 70.0f; 17 | 18 | float fade(float t) { 19 | return 6 * pow(t, 5) - 15 * pow(t, 4) + 10 * pow(t, 3); 20 | } 21 | 22 | vec4 calculateSkyColor() { 23 | if (is_raining) { 24 | vec4 light_gray = vec4(0.66f, 0.66f, 0.66f, 1.0f); 25 | vec4 dark_gray = vec4(0, 0, 0, 1.0f); 26 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 27 | return dark_gray; 28 | } else { 29 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 30 | return mix(light_gray, dark_gray, scale); 31 | } 32 | } else { 33 | vec4 dark = vec4(0.1f, 0.1f, 0.3f, 1.0f); 34 | vec4 bright_blue = vec4(0.53f, 0.81f, 0.92f, 1.0f); 35 | if (time_of_day < 300.0f || time_of_day > 1140.0f) { // Before 5 AM, After 7 PM 36 | return dark; 37 | } else { 38 | float scale = pow(abs(720.0f - time_of_day) / 420.0f, 2.0f); 39 | return mix(bright_blue, dark, scale); 40 | } 41 | } 42 | } 43 | 44 | vec4 processFog(in vec3 color) { 45 | float fog_coefficient = (fog_far_plane - distance(world_position.xz, camera_position.xz)) / (fog_far_plane - fog_near_plane); 46 | fog_coefficient = clamp(fog_coefficient, 0.0f, 1.0f); 47 | vec4 fog_color = calculateSkyColor(); 48 | return mix(fog_color, vec4(color, 1.0f), fog_coefficient); 49 | } 50 | 51 | float random(vec2 co) { 52 | return fract(sin(dot(co, vec2(12.9898,78.233))) * 43758.5453); 53 | } 54 | 55 | vec2 randUnitVec(vec2 xz) { 56 | float angle = random(xz) * 2 * kPi; 57 | return normalize(vec2(cos(angle), sin(angle))); 58 | } 59 | 60 | float dotGridGradient(int iu, int iv, float u, float v) { 61 | // Generate seeded random unit gradient vector 62 | vec2 unit_gradient = randUnitVec(vec2(iu, iv)); 63 | 64 | // Compute distance vector 65 | float du = u - float(iu); 66 | float dv = v - float(iv); 67 | 68 | // Return dot product 69 | return (du * unit_gradient[0] + dv * unit_gradient[1]); 70 | } 71 | 72 | float perlin(float u, float v) { 73 | // Get coordinates of unit cell 74 | int u0 = int(floor(u)); 75 | int u1 = u0 + 1; 76 | int v0 = int(floor(v)); 77 | int v1 = v0 + 1; 78 | 79 | // Interpolation weights 80 | float wt_u = fade(u - float(u0)); 81 | float wt_v = fade(v - float(v0)); 82 | 83 | // Interpolate between grid point gradients 84 | float n0, n1, iu0, iu1, value; 85 | n0 = dotGridGradient(u0, v0, u, v); 86 | n1 = dotGridGradient(u1, v0, u, v); 87 | iu0 = mix(n0, n1, wt_u); 88 | n0 = dotGridGradient(u0, v1, u, v); 89 | n1 = dotGridGradient(u1, v1, u, v); 90 | iu1 = mix(n0, n1, wt_u); 91 | value = mix(iu0, iu1, wt_v); 92 | 93 | return value; 94 | } 95 | 96 | float mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 97 | vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 98 | vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);} 99 | 100 | float noise(vec3 p){ 101 | vec3 a = floor(p); 102 | vec3 d = p - a; 103 | d = d * d * (3.0 - 2.0 * d); 104 | 105 | vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); 106 | vec4 k1 = perm(b.xyxy); 107 | vec4 k2 = perm(k1.xyxy + b.zzww); 108 | 109 | vec4 c = k2 + a.zzzz; 110 | vec4 k3 = perm(c); 111 | vec4 k4 = perm(c + 1.0); 112 | 113 | vec4 o1 = fract(k3 * (1.0 / 41.0)); 114 | vec4 o2 = fract(k4 * (1.0 / 41.0)); 115 | 116 | vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); 117 | vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); 118 | 119 | return o4.y * d.y + o4.x * (1.0 - d.y); 120 | } 121 | 122 | float multipass_noise(vec3 p) { 123 | float sum = 0.0f; 124 | float amp = 8.0f / 15.0f; 125 | float freq_divisor = 1.0f; 126 | for (int n = 0; n < 4; n++) { 127 | sum += noise(p * freq_divisor) * amp;//perlin(p.x * freq_divisor, p.z * freq_divisor) * amp; 128 | freq_divisor *= 2.0f; 129 | amp /= 2.0f; 130 | } 131 | return sum; 132 | } 133 | 134 | vec3 grass_texture() { 135 | float r = multipass_noise(world_position.xyz); 136 | r = clamp(r, 0.0f, 1.0f); 137 | vec3 green = vec3(0.1333f, 0.54f, 0.133f); 138 | vec3 dark_green = vec3(0.0f, 0.20f, 0.0f); 139 | return mix(green, dark_green, r); 140 | } 141 | 142 | void main() { 143 | vec3 color = vec3(0.0f, 0.0f, 0.0f); 144 | vec4 specular = vec4(0.0f, 0.0f, 0.0f, 1.0f); 145 | float shininess = 1.0f; 146 | if (world_position.y > 1.0f) { // GRASS 147 | color = grass_texture();//vec3(0.133f, 0.54f, 0.133f); 148 | } else { // DIRT 149 | color = vec3(0.341f, 0.231f, 0.047f); 150 | if (world_position.y > 0.0f) { 151 | color = mix(grass_texture(), color, 1.0f - world_position.y); 152 | } 153 | } 154 | 155 | vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f); 156 | float dot_nl = dot(normalize(light_direction), normalize(normal)); 157 | dot_nl = clamp(dot_nl, 0.0f, 1.0f); 158 | vec4 spec = specular * pow(clamp(dot(reflect(-light_direction.xyz, normal.xyz), camera_direction.xyz), 0.0f, 1.0f), shininess); 159 | color = clamp(dot_nl * color + vec3(ambient) + vec3(spec), 0.0f, 1.0f); 160 | if (world_position.y < 0.0f) { 161 | float coeff = clamp(world_position.y / -10.0f, 0.0f, 1.0f); 162 | vec3 water_color = vec3(0.1f, 0.1f, 0.3f); 163 | color = mix(color, water_color, coeff); 164 | } 165 | fragment_color = processFog(color); 166 | } 167 | )zzz" 168 | -------------------------------------------------------------------------------- /src/render_pass.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_PASS_H 2 | #define RENDER_PASS_H 3 | 4 | /* 5 | * For students: 6 | * 7 | * This file defines RenderPass class and its associated classes. 8 | * RenderPass is used to simplify multi-pass rendering code in the 9 | * reference solution. 10 | * 11 | * However, understanding this class is COMPLETELY OPTIONAL. 12 | * It's totally OK to ignore this class and cram a bunch of OpenGL 13 | * function calls in your solution. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /* 23 | * ShaderUniform: description of a uniform in a shader program. 24 | * name: name 25 | * binder: function to bind the uniform 26 | * data_source: function to get the data for the uniform 27 | */ 28 | struct ShaderUniform { 29 | std::string name; 30 | /* 31 | * binder 32 | * argument 0: the location of the uniform 33 | * argument 1: the data pointer returned by data_source function. 34 | */ 35 | std::function binder; 36 | /* 37 | * data_source: 38 | * return: the pointer to the uniform data 39 | * 40 | * Hint: DON'T DO THIS 41 | * data_source = []() -> void* { float value = 1.0f; return &f; }; 42 | * the value variable will become invalid after returning from 43 | * the lambda function 44 | */ 45 | std::function data_source; 46 | }; 47 | 48 | /* 49 | * RenderInputMeta: describe one buffer used in some RenderPass 50 | */ 51 | struct RenderInputMeta { 52 | int position = -1; 53 | std::string name; 54 | const void *data = nullptr; 55 | size_t nelements = 0; 56 | size_t element_length = 0; 57 | int element_type = 0; 58 | bool divisor = false; 59 | 60 | size_t 61 | getElementSize() const; // simple check: return 12 (3 * 4 bytes) for float3 62 | RenderInputMeta(); 63 | RenderInputMeta(int _position, const std::string &_name, const void *_data, 64 | size_t _nelements, size_t _element_length, int _element_type, 65 | bool _divisor); 66 | bool isInteger() const; 67 | }; 68 | 69 | /* 70 | * RenderDataInput: describe the complete set of buffers used in a RenderPass 71 | */ 72 | class RenderDataInput { 73 | public: 74 | RenderDataInput(); 75 | 76 | /* 77 | * assign: assign per-vertex attribute data 78 | * position: glVertexAttribPointer position 79 | * name: glBindAttribLocation name 80 | * nelements: number of elements 81 | * element_length: element dimension, e.g. for vec3 it's 3 82 | * element_type: GL_FLOAT or GL_UNSIGNED_INT 83 | */ 84 | void assign(int position, const std::string &name, const void *data, 85 | size_t nelements, size_t element_length, int element_type, 86 | bool divisor = false); 87 | /* 88 | * assign_index: assign the index buffer for vertices 89 | * This will bind the data to GL_ELEMENT_ARRAY_BUFFER 90 | * The element must be uvec3. 91 | */ 92 | void assignIndex(const void *data, size_t nelements, size_t element_length); 93 | /* 94 | * useMaterials: assign materials to the input data 95 | */ 96 | void useMaterials(const std::vector &); 97 | 98 | int getNBuffers() const { return int(meta_.size()); } 99 | RenderInputMeta getBufferMeta(int i) const { return meta_[i]; } 100 | bool hasIndex() const { return has_index_; } 101 | RenderInputMeta getIndexMeta() const { return index_meta_; } 102 | 103 | bool hasMaterial() const { return !materials_.empty(); } 104 | size_t getNMaterials() const { return materials_.size(); } 105 | const Material &getMaterial(size_t id) const { return materials_[id]; } 106 | Material &getMaterial(size_t id) { return materials_[id]; } 107 | 108 | private: 109 | std::vector meta_; 110 | std::vector materials_; 111 | RenderInputMeta index_meta_; 112 | bool has_index_ = false; 113 | }; 114 | 115 | class RenderPass { 116 | public: 117 | /* 118 | * Constructor 119 | * vao: the Vertex Array Object, pass -1 to create new 120 | * input: RenderDataInput object 121 | * shaders: array of shaders, leave the second as nullptr if no GS 122 | * present 123 | * uniforms: array of ShaderUniform objects 124 | * output: the FS output variable name. 125 | * RenderPass does not support render-to-texture or multi-target 126 | * rendering for now (and you also don't need it). 127 | */ 128 | RenderPass( 129 | int vao, // -1: create new VAO, otherwise use given VAO 130 | const RenderDataInput &input, 131 | const std::vector shaders, // Order: VS, GS, FS, TCS, TES 132 | const std::vector uniforms, 133 | const std::vector output // Order: 0, 1, 2... 134 | ); 135 | ~RenderPass(); 136 | 137 | unsigned getVAO() const { return unsigned(vao_); } 138 | void updateVBO(int position, const void *data, size_t nelement); 139 | void setup(); 140 | /* 141 | * Note: here we don't have an unified render() function, because the 142 | * reference solution renders with different primitives 143 | * 144 | * However you can freely trianglize everything and add an 145 | * render() function 146 | */ 147 | 148 | /* 149 | * renderWithMaterial: render a part of vertex buffer, after binding 150 | * corresponding uniforms for Phong shading. 151 | */ 152 | bool renderWithMaterial(int i); // return false if material id is invalid 153 | private: 154 | void initMaterialUniform(); 155 | void createMaterialTexture(); 156 | 157 | int vao_; 158 | RenderDataInput input_; 159 | std::vector uniforms_; 160 | std::vector> material_uniforms_; 161 | 162 | std::vector glbuffers_, unilocs_, malocs_; 163 | std::vector gltextures_, matexids_; 164 | unsigned sampler2d_; 165 | unsigned vs_ = 0, gs_ = 0, fs_ = 0, tcs_ = 0, tes_ = 0; 166 | unsigned sp_ = 0; 167 | 168 | static unsigned compileShader(const char *, int type); 169 | static std::map shader_cache_; 170 | 171 | static void bindUniforms(std::vector &uniforms, 172 | const std::vector &unilocs); 173 | }; 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /src/gui.cc: -------------------------------------------------------------------------------- 1 | #include "gui.h" 2 | #include "config.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using std::vector; 14 | 15 | // start ffmpeg telling it to expect raw rgba 720p-60hz frames 16 | // -i - tells it to read frames from stdin 17 | 18 | GUI::GUI(GLFWwindow *window) : window_(window) { 19 | glfwSetWindowUserPointer(window_, this); 20 | glfwSetKeyCallback(window_, KeyCallback); 21 | glfwSetCursorPosCallback(window_, MousePosCallback); 22 | glfwSetMouseButtonCallback(window_, MouseButtonCallback); 23 | glfwSetScrollCallback(window_, MouseScrollCallback); 24 | 25 | glfwGetWindowSize(window_, &window_width_, &window_height_); 26 | float aspect_ = static_cast(window_width_) / window_height_; 27 | projection_matrix_ = 28 | glm::perspective((float)(kFov * (M_PI / 180.0f)), aspect_, kNear, kFar); 29 | key_pressed_['W'] = false; 30 | key_pressed_['A'] = false; 31 | key_pressed_['S'] = false; 32 | key_pressed_['D'] = false; 33 | key_pressed_['u'] = false; 34 | key_pressed_['d'] = false; 35 | } 36 | 37 | GUI::~GUI() {} 38 | 39 | void GUI::keyCallback(int key, int scancode, int action, int mods) { 40 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 41 | glfwSetWindowShouldClose(window_, GL_TRUE); 42 | return; 43 | } 44 | 45 | // Save screenshot 46 | if (key == GLFW_KEY_J && action == GLFW_RELEASE) { 47 | // Read pixels from the framebuffer 48 | auto num_bytes = window_width_ * window_height_ * 3; 49 | auto pixels = vector(num_bytes); 50 | glReadPixels(0, 0, window_width_, window_height_, GL_RGB, GL_UNSIGNED_BYTE, 51 | pixels.data()); 52 | 53 | // Write image out to file 54 | SaveJPEG("screenshot.jpg", window_width_, window_height_, pixels.data()); 55 | std::cout << "Screenshot written out to \"screenshot.jpg\"" << std::endl; 56 | return; 57 | } 58 | 59 | // Toggle gravity 60 | if (key == GLFW_KEY_F && (mods & GLFW_MOD_CONTROL) && 61 | action == GLFW_RELEASE) { 62 | gravity_enabled_ = !gravity_enabled_; 63 | std::cout << "Gravity is: " << (gravity_enabled_ ? "on" : "off") 64 | << std::endl; 65 | } 66 | 67 | if (key == GLFW_KEY_S && (mods & GLFW_MOD_CONTROL) && 68 | action == GLFW_RELEASE) { 69 | // Control + S 70 | // Pass 71 | } 72 | 73 | if (key == GLFW_KEY_LEFT || key == GLFW_KEY_RIGHT) { 74 | // Left/Right Arrow Keys 75 | // Pass 76 | } else if (key == GLFW_KEY_C && action != GLFW_RELEASE) { 77 | fps_mode_ = !fps_mode_; 78 | } else if (key == GLFW_KEY_LEFT_BRACKET && action == GLFW_RELEASE) { 79 | // [ 80 | // Pass 81 | } else if (key == GLFW_KEY_RIGHT_BRACKET && action == GLFW_RELEASE) { 82 | // ] 83 | // Pass 84 | } else if (key == GLFW_KEY_T && action != GLFW_RELEASE) { 85 | // T 86 | // Pass 87 | } 88 | 89 | if (key == GLFW_KEY_EQUAL && action == GLFW_RELEASE) { 90 | incrementTimeOfDay(60); 91 | } 92 | 93 | if (key == GLFW_KEY_1 && action == GLFW_RELEASE) { 94 | raining_ = !raining_; 95 | terrainRender->toggle_storm(raining_); 96 | } 97 | 98 | if (mods == 0 && captureWASDUPDOWN(key, action)) 99 | return; 100 | } 101 | 102 | void GUI::mousePosCallback(double mouse_x, double mouse_y) { 103 | last_x_ = current_x_; 104 | last_y_ = current_y_; 105 | current_x_ = mouse_x; 106 | current_y_ = window_height_ - mouse_y; 107 | float delta_x = current_x_ - last_x_; 108 | float delta_y = current_y_ - last_y_; 109 | if (sqrt(delta_x * delta_x + delta_y * delta_y) < 1e-15) 110 | return; 111 | glm::vec3 mouse_direction = glm::normalize(glm::vec3(delta_x, delta_y, 0.0f)); 112 | glm::vec2 mouse_start = glm::vec2(last_x_, last_y_); 113 | glm::vec2 mouse_end = glm::vec2(current_x_, current_y_); 114 | glm::uvec4 viewport = glm::uvec4(0, 0, window_width_, window_height_); 115 | 116 | // Calculate rotation matrix 117 | auto temp_orientation = orientation_; 118 | temp_orientation[1] = glm::vec3{0.0, 1.0, 0.0}; 119 | glm::vec3 axis = 120 | glm::normalize(temp_orientation * 121 | glm::vec3(mouse_direction.y, -mouse_direction.x, 0.0f)); 122 | orientation_ = 123 | glm::mat3(glm::rotate(rotation_speed_, axis) * glm::mat4(orientation_)); 124 | 125 | // Set camera instance variables 126 | tangent_ = glm::column(orientation_, 0); 127 | up_ = glm::column(orientation_, 1); 128 | look_ = glm::column(orientation_, 2); 129 | } 130 | 131 | void GUI::mouseButtonCallback(int button, int action, int mods) { 132 | // Mouse press 133 | // Pass 134 | } 135 | 136 | void GUI::mouseScrollCallback(double dx, double dy) { 137 | // Mouse scroll 138 | // Pass 139 | } 140 | 141 | void GUI::updateMatrices() { 142 | // Compute our view, and projection matrices. 143 | if (fps_mode_) { 144 | center_ = eye_ + camera_distance_ * look_; 145 | view_matrix_ = glm::lookAt(eye_, center_, up_); 146 | } else { 147 | auto stabilized_center = center_; 148 | stabilized_center.y = 0.0f; 149 | eye_ = stabilized_center - camera_distance_ * look_; 150 | view_matrix_ = glm::lookAt(eye_, stabilized_center, up_); 151 | } 152 | 153 | light_position_ = glm::vec4(eye_, 1.0f); 154 | 155 | aspect_ = static_cast(window_width_) / window_height_; 156 | projection_matrix_ = 157 | glm::perspective((float)(kFov * (M_PI / 180.0f)), aspect_, kNear, kFar); 158 | model_matrix_ = glm::mat4(1.0f); 159 | inv_proj_matrix_ = glm::inverse(projection_matrix_ * view_matrix_); 160 | } 161 | 162 | MatrixPointers GUI::getMatrixPointers() const { 163 | MatrixPointers ret; 164 | ret.projection = &projection_matrix_[0][0]; 165 | ret.model = &model_matrix_[0][0]; 166 | ret.view = &view_matrix_[0][0]; 167 | ret.inv_proj_view = &inv_proj_matrix_[0][0]; 168 | return ret; 169 | } 170 | 171 | void GUI::updatePosition() { 172 | if (fps_mode_) { 173 | if (gravity_enabled_) { 174 | // Effects of gravity 175 | y_velocity_ -= 0.008f; 176 | auto new_pos = eye_ + glm::vec3{0, y_velocity_, 0}; 177 | 178 | // Check for ground collision 179 | if (!terrainRender->isPositionLegal(new_pos)) { 180 | y_velocity_ = 0.0f; 181 | } else { 182 | eye_ = new_pos; 183 | } 184 | } 185 | if (key_pressed_['W']) { 186 | glm::vec3 move_vec = getMoveVec(zoom_speed_ * look_); 187 | if (terrainRender->isPositionLegal(eye_ + move_vec)) { 188 | eye_ += move_vec; 189 | } 190 | } 191 | if (key_pressed_['S']) { 192 | glm::vec3 move_vec = getMoveVec(zoom_speed_ * look_); 193 | if (terrainRender->isPositionLegal(eye_ - move_vec)) { 194 | eye_ -= move_vec; 195 | } 196 | } 197 | if (key_pressed_['A']) { 198 | glm::vec3 move_vec = getMoveVec(pan_speed_ * tangent_); 199 | if (terrainRender->isPositionLegal(eye_ - move_vec)) { 200 | eye_ -= move_vec; 201 | } 202 | } 203 | if (key_pressed_['D']) { 204 | glm::vec3 move_vec = getMoveVec(pan_speed_ * tangent_); 205 | if (terrainRender->isPositionLegal(eye_ + move_vec)) { 206 | eye_ += move_vec; 207 | } 208 | } 209 | if (key_pressed_['u'] && !gravity_enabled_) { 210 | if (terrainRender->isPositionLegal(eye_ + pan_speed_ * up_)) { 211 | eye_ += pan_speed_ * up_; 212 | } 213 | } 214 | if (key_pressed_['d'] && !gravity_enabled_) { 215 | if (terrainRender->isPositionLegal(eye_ - pan_speed_ * up_)) { 216 | eye_ -= pan_speed_ * up_; 217 | } 218 | } 219 | } else { // Center focused 220 | glm::vec3 move_vec{0.0f}; 221 | if (gravity_enabled_) { 222 | // Effects of gravity 223 | y_velocity_ -= 0.008f; 224 | 225 | // Check for ground collision 226 | if (terrainRender->isPositionLegal(center_ + 227 | glm::vec3{0, y_velocity_, 0})) { 228 | move_vec = glm::vec3{0, y_velocity_, 0}; 229 | } else { 230 | y_velocity_ = 0.0f; 231 | } 232 | } 233 | 234 | if (key_pressed_['W']) { 235 | move_vec += getMoveVec(zoom_speed_ * look_); 236 | } 237 | if (key_pressed_['S']) { 238 | move_vec -= getMoveVec(zoom_speed_ * look_); 239 | } 240 | if (key_pressed_['A']) { 241 | move_vec -= getMoveVec(pan_speed_ * tangent_); 242 | } 243 | if (key_pressed_['D']) { 244 | move_vec += getMoveVec(pan_speed_ * tangent_); 245 | } 246 | auto new_center = center_ + move_vec; 247 | if (terrainRender->isPositionLegal(new_center)) { 248 | center_ += move_vec; 249 | } 250 | float waveHeight = terrainRender->getWaveHeight(new_center); 251 | if (center_.y < waveHeight) { 252 | center_.y = waveHeight; 253 | y_velocity_ = 0.0f; 254 | } 255 | if (move_vec.x != 0 || move_vec.z != 0) { 256 | previous_move_ = move_vec; 257 | } 258 | } 259 | } 260 | 261 | glm::vec3 GUI::getMoveVec(const glm::vec3 &input) { 262 | if (gravity_enabled_) { 263 | return glm::normalize(glm::vec3{input.x, 0, input.z}) * 0.2f; 264 | } else { 265 | return input; 266 | } 267 | } 268 | 269 | bool GUI::captureWASDUPDOWN(int key, int action) { 270 | if (fps_mode_) { 271 | // When under effect of gravity, only y-axis movement via jumping 272 | if (gravity_enabled_) { 273 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS && 274 | abs(y_velocity_) < 1e-7) { 275 | // Jumping behavior in Minecraft 276 | y_velocity_ += 0.2; 277 | } 278 | } 279 | } 280 | 281 | // Regular move forward/backward + strafe left/right 282 | if (key == GLFW_KEY_W) { 283 | if (action == GLFW_PRESS) { 284 | key_pressed_['W'] = true; 285 | } 286 | if (action == GLFW_RELEASE) { 287 | key_pressed_['W'] = false; 288 | } 289 | return true; 290 | } else if (key == GLFW_KEY_S) { 291 | if (action == GLFW_PRESS) { 292 | key_pressed_['S'] = true; 293 | } 294 | if (action == GLFW_RELEASE) { 295 | key_pressed_['S'] = false; 296 | } 297 | return true; 298 | } else if (key == GLFW_KEY_A) { 299 | if (action == GLFW_PRESS) { 300 | key_pressed_['A'] = true; 301 | } 302 | if (action == GLFW_RELEASE) { 303 | key_pressed_['A'] = false; 304 | } 305 | return true; 306 | } else if (key == GLFW_KEY_D) { 307 | if (action == GLFW_PRESS) { 308 | key_pressed_['D'] = true; 309 | } 310 | if (action == GLFW_RELEASE) { 311 | key_pressed_['D'] = false; 312 | } 313 | return true; 314 | } 315 | 316 | // Allow arbitrary y-axis (up/down) movement when gravity is disabled 317 | if (key == GLFW_KEY_DOWN) { 318 | if (action == GLFW_PRESS) { 319 | key_pressed_['d'] = true; 320 | } 321 | if (action == GLFW_RELEASE) { 322 | key_pressed_['d'] = false; 323 | } 324 | return true; 325 | } else if (key == GLFW_KEY_UP) { 326 | if (action == GLFW_PRESS) { 327 | key_pressed_['u'] = true; 328 | } 329 | if (action == GLFW_RELEASE) { 330 | key_pressed_['u'] = false; 331 | } 332 | return true; 333 | } 334 | 335 | return false; 336 | } 337 | 338 | // Delegrate to the actual GUI object. 339 | void GUI::KeyCallback(GLFWwindow *window, int key, int scancode, int action, 340 | int mods) { 341 | GUI *gui = (GUI *)glfwGetWindowUserPointer(window); 342 | gui->keyCallback(key, scancode, action, mods); 343 | } 344 | 345 | void GUI::MousePosCallback(GLFWwindow *window, double mouse_x, double mouse_y) { 346 | GUI *gui = (GUI *)glfwGetWindowUserPointer(window); 347 | gui->mousePosCallback(mouse_x, mouse_y); 348 | } 349 | 350 | void GUI::MouseButtonCallback(GLFWwindow *window, int button, int action, 351 | int mods) { 352 | GUI *gui = (GUI *)glfwGetWindowUserPointer(window); 353 | gui->mouseButtonCallback(button, action, mods); 354 | } 355 | 356 | void GUI::MouseScrollCallback(GLFWwindow *window, double dx, double dy) { 357 | GUI *gui = (GUI *)glfwGetWindowUserPointer(window); 358 | gui->mouseScrollCallback(dx, dy); 359 | } 360 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "config.h" 5 | #include "gui.h" 6 | #include "procedure_geometry.h" 7 | #include "rain_render.h" 8 | #include "render_pass.h" 9 | #include "terrain_render.h" 10 | #include "util.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int window_width = 1280; 28 | int window_height = 720; 29 | int height_map_rows = 150; 30 | int height_map_cols = 150; 31 | const std::string window_title = "Sea of Thieves"; 32 | const float SUN_RADIUS = 100.0f; 33 | 34 | const std::vector SUN_VERTICES = { 35 | {0.000f, 0.000f, 1.000f, 0.0f}, {0.894f, 0.000f, 0.447f, 0.0f}, 36 | {0.276f, 0.851f, 0.447f, 0.0f}, {-0.724f, 0.526f, 0.447f, 0.0f}, 37 | {-0.724f, -0.526f, 0.447f, 0.0f}, {0.276f, -0.851f, 0.447f, 0.0f}, 38 | {0.724f, 0.526f, -0.447f, 0.0f}, {-0.276f, 0.851f, -0.447f, 0.0f}, 39 | {-0.894f, 0.000f, -0.447f, 0.0f}, {-0.276f, -0.851f, -0.447f, 0.0f}, 40 | {0.724f, -0.526f, -0.447f, 0.0f}, {0.000f, 0.000f, -1.000f, 0.0f}}; 41 | 42 | const std::vector SUN_FACES = { 43 | {2, 1, 0}, {3, 2, 0}, {4, 3, 0}, {5, 4, 0}, {1, 5, 0}, 44 | {11, 6, 7}, {11, 7, 8}, {11, 8, 9}, {11, 9, 10}, {11, 10, 6}, 45 | {1, 2, 6}, {2, 3, 7}, {3, 4, 8}, {4, 5, 9}, {5, 1, 10}, 46 | {2, 7, 6}, {3, 8, 7}, {4, 9, 8}, {5, 10, 9}, {1, 6, 10}}; 47 | 48 | const char *vertex_shader = 49 | #include "shaders/default.vert" 50 | ; 51 | 52 | const char *boat_vertex_shader = 53 | #include "shaders/boat.vert" 54 | ; 55 | 56 | const char *boat_geometry_shader = 57 | #include "shaders/boat.geom" 58 | ; 59 | 60 | const char *boat_fragment_shader = 61 | #include "shaders/boat.frag" 62 | ; 63 | 64 | const char *sky_vertex_shader = 65 | #include "shaders/sky.vert" 66 | ; 67 | 68 | const char *geometry_shader = 69 | #include "shaders/default.geom" 70 | ; 71 | 72 | const char *fragment_shader = 73 | #include "shaders/default.frag" 74 | ; 75 | 76 | const char *sky_fragment_shader = 77 | #include "shaders/sky.frag" 78 | ; 79 | 80 | const char *sun_vertex_shader = 81 | #include "shaders/sun.vert" 82 | ; 83 | 84 | const char *sun_geometry_shader = 85 | #include "shaders/sun.geom" 86 | ; 87 | 88 | const char *sun_fragment_shader = 89 | #include "shaders/sun.frag" 90 | ; 91 | 92 | const char *sun_tcs_shader = 93 | #include "shaders/sun.tcs" 94 | ; 95 | 96 | const char *sun_tes_shader = 97 | #include "shaders/sun.tes" 98 | ; 99 | 100 | void ErrorCallback(int error, const char *description) { 101 | std::cerr << "GLFW Error: " << description << "\n"; 102 | } 103 | 104 | GLFWwindow *init_glefw() { 105 | if (!glfwInit()) 106 | exit(EXIT_FAILURE); 107 | glfwSetErrorCallback(ErrorCallback); 108 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 109 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); 110 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 111 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 112 | glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Disable resizing, for simplicity 113 | glfwWindowHint(GLFW_SAMPLES, 4); 114 | auto ret = glfwCreateWindow(window_width, window_height, window_title.data(), 115 | nullptr, nullptr); 116 | CHECK_SUCCESS(ret != nullptr); 117 | glfwSetInputMode(ret, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 118 | glfwMakeContextCurrent(ret); 119 | glewExperimental = GL_TRUE; 120 | CHECK_SUCCESS(glewInit() == GLEW_OK); 121 | glGetError(); // clear GLEW's error for it 122 | glfwSwapInterval(1); 123 | const GLubyte *renderer = glGetString(GL_RENDERER); // get renderer string 124 | const GLubyte *version = glGetString(GL_VERSION); // version as a string 125 | std::cout << "Renderer: " << renderer << "\n"; 126 | std::cout << "OpenGL version supported:" << version << "\n"; 127 | 128 | return ret; 129 | } 130 | 131 | int main(int argc, char *argv[]) { 132 | auto start = std::chrono::high_resolution_clock::now(); 133 | GLFWwindow *window = init_glefw(); 134 | GUI gui(window); 135 | 136 | glm::vec4 light_position = glm::vec4(0.0f, SUN_RADIUS, 0.0f, 1.0f); 137 | MatrixPointers mats; 138 | 139 | // Define MatrixPointers here for lambda to capture 140 | /* 141 | * In the following we are going to define several lambda functions to bind 142 | * Uniforms. 143 | * 144 | * Introduction about lambda functions: 145 | * http://en.cppreference.com/w/cpp/language/lambda 146 | * http://www.stroustrup.com/C++11FAQ.html#lambda 147 | */ 148 | /* 149 | * The following lambda functions are defined to bind uniforms 150 | */ 151 | auto matrix_binder = [](int loc, const void *data) { 152 | glUniformMatrix4fv(loc, 1, GL_FALSE, (const GLfloat *)data); 153 | }; 154 | auto vector_binder = [](int loc, const void *data) { 155 | glUniform4fv(loc, 1, (const GLfloat *)data); 156 | }; 157 | auto vector3_binder = [](int loc, const void *data) { 158 | glUniform3fv(loc, 1, (const GLfloat *)data); 159 | }; 160 | auto float_binder = [](int loc, const void *data) { 161 | glUniform1fv(loc, 1, (const GLfloat *)data); 162 | }; 163 | auto int_binder = [](int loc, const void *data) { 164 | glUniform1iv(loc, 1, (const GLint *)data); 165 | }; 166 | 167 | /* 168 | * The lambda functions below are used to retrieve data 169 | */ 170 | auto std_model_data = [&mats]() -> const void * { 171 | return mats.model; 172 | }; // This returns point to model matrix 173 | auto std_view_data = [&mats]() -> const void * { return mats.view; }; 174 | auto std_camera_data = [&gui]() -> const void * { 175 | return &gui.getCamera()[0]; 176 | }; 177 | auto std_center_data = [&gui]() -> const void * { 178 | return &gui.getCenter()[0]; 179 | }; 180 | auto std_proj_data = [&mats]() -> const void * { return mats.projection; }; 181 | auto inv_proj_data = [&mats]() -> const void * { return mats.inv_proj_view; }; 182 | auto std_light_data = [&light_position]() -> const void * { 183 | return &light_position[0]; 184 | }; 185 | float time_from_start; 186 | auto prev = start; 187 | auto std_time_data = [&start, &time_from_start, &prev, 188 | &gui]() -> const void * { 189 | auto now = std::chrono::high_resolution_clock::now(); 190 | time_from_start = std::chrono::duration(now - start).count(); 191 | float since_last = std::chrono::duration(now - prev).count(); 192 | gui.incrementTimeOfDay(since_last); 193 | prev = now; 194 | return &time_from_start; 195 | }; 196 | auto std_time_of_day_data = [&gui]() -> const void * { 197 | return &gui.getTimeOfDay(); 198 | }; 199 | auto prev_move_data = [&gui]() -> const void * { 200 | return &gui.getPreviousMoveVec(); 201 | }; 202 | glm::vec3 boat_pos_normal{0.0f}; 203 | auto boat_pos_normal_data = [&boat_pos_normal]() -> const void * { 204 | return &boat_pos_normal; 205 | }; 206 | auto is_raining_data = [&gui]() -> const void * { return &gui.isRaining(); }; 207 | 208 | ShaderUniform std_model = {"model", matrix_binder, std_model_data}; 209 | ShaderUniform std_view = {"view", matrix_binder, std_view_data}; 210 | ShaderUniform inv_proj_view = {"inverse_projection_view", matrix_binder, 211 | inv_proj_data}; 212 | ShaderUniform std_camera = {"camera_position", vector3_binder, 213 | std_camera_data}; 214 | ShaderUniform std_proj = {"projection", matrix_binder, std_proj_data}; 215 | ShaderUniform std_light = {"light_position", vector_binder, std_light_data}; 216 | ShaderUniform std_center = {"center_position", vector3_binder, 217 | std_center_data}; 218 | ShaderUniform std_time = {"time", float_binder, std_time_data}; 219 | ShaderUniform std_time_of_day = {"time_of_day", float_binder, 220 | std_time_of_day_data}; 221 | ShaderUniform std_prev_move = {"prev_move", vector3_binder, prev_move_data}; 222 | ShaderUniform std_boat_pos_normal = {"boat_pos_normal", vector3_binder, 223 | boat_pos_normal_data}; 224 | ShaderUniform std_is_raining = {"is_raining", int_binder, is_raining_data}; 225 | 226 | // 227 | // Boat render pass 228 | // 229 | auto boat_mesh = util::LoadObj("../assets/rowboat.obj"); 230 | auto boat_pass_input = RenderDataInput{}; 231 | boat_pass_input.assign(0, "vertex_position", boat_mesh.vertices.data(), 232 | boat_mesh.vertices.size(), 4, GL_FLOAT); 233 | boat_pass_input.assign(1, "normal", boat_mesh.normals.data(), 234 | boat_mesh.vertices.size(), 3, GL_FLOAT); 235 | boat_pass_input.assignIndex(boat_mesh.vertex_indices.data(), 236 | boat_mesh.vertex_indices.size(), 3); 237 | RenderPass boat_pass( 238 | -1, boat_pass_input, 239 | {boat_vertex_shader, boat_geometry_shader, boat_fragment_shader}, 240 | {std_model, std_light, std_center, std_view, std_proj, std_prev_move, 241 | std_boat_pos_normal, std_time_of_day}, 242 | {"fragment_color"}); 243 | 244 | // 245 | // Sun render pass 246 | // 247 | auto sun_pass_input = RenderDataInput{}; 248 | sun_pass_input.assign(0, "vertex_position", SUN_VERTICES.data(), 249 | SUN_VERTICES.size(), 4, GL_FLOAT); 250 | sun_pass_input.assignIndex(SUN_FACES.data(), SUN_FACES.size(), 3); 251 | RenderPass sun_pass(-1, sun_pass_input, 252 | {sun_vertex_shader, sun_geometry_shader, 253 | sun_fragment_shader, sun_tcs_shader, sun_tes_shader}, 254 | {std_view, std_proj, std_light, std_time_of_day}, 255 | {"fragment_color"}); 256 | 257 | // 258 | // Skybox render pass 259 | // 260 | RenderPass sky_pass( 261 | -1, RenderDataInput{}, {sky_vertex_shader, nullptr, sky_fragment_shader}, 262 | {inv_proj_view, std_time_of_day, std_is_raining}, {"fragment_color"}); 263 | 264 | // 265 | // Terrain render pass 266 | // 267 | TerrainRender terrainRender(height_map_rows, height_map_cols, 268 | {std_model, std_view, std_proj, std_light, 269 | std_camera, std_center, std_time, 270 | std_time_of_day, std_is_raining}); 271 | terrainRender.setStartTime(start); 272 | gui.terrainRender = &terrainRender; 273 | 274 | // 275 | // Rain render pass 276 | // 277 | RainRender rainRender( 278 | height_map_rows, height_map_cols, 279 | {std_view, std_proj, std_light, std_camera, std_center}); 280 | 281 | bool draw_terrain = true; 282 | double previousTime = glfwGetTime(); 283 | int frameCount = 0; 284 | while (!glfwWindowShouldClose(window)) { 285 | boat_pos_normal = terrainRender.getWaveNormal(gui.getCenter()); 286 | // FPS Counter 287 | double currentTime = glfwGetTime(); 288 | frameCount++; 289 | if (currentTime - previousTime >= 1.0) { 290 | // Display the frame count here any way you want. 291 | std::cout << "FPS: " << frameCount << std::endl; 292 | frameCount = 0; 293 | previousTime = currentTime; 294 | } 295 | 296 | // Calculating light_position 297 | float time_of_day = gui.getTimeOfDay(); 298 | float angle = (time_of_day / gui.kMaxTimeOfDay) * 2 * M_PI; 299 | glm::vec4 light_vec_from_center = 300 | SUN_RADIUS * glm::vec4{0.0f, -cos(angle), sin(angle), 0.0f}; 301 | light_position = light_vec_from_center + glm::vec4(gui.getCenter(), 1.0f); 302 | 303 | // Setup some basic window stuff. 304 | glfwGetFramebufferSize(window, &window_width, &window_height); 305 | glViewport(0, 0, window_width, window_height); 306 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 307 | glEnable(GL_DEPTH_TEST); 308 | glEnable(GL_MULTISAMPLE); 309 | glEnable(GL_BLEND); 310 | glEnable(GL_CULL_FACE); 311 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 312 | glDepthFunc(GL_LESS); 313 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 314 | glCullFace(GL_BACK); 315 | 316 | gui.updatePosition(); 317 | gui.updateMatrices(); 318 | mats = gui.getMatrixPointers(); 319 | 320 | // Draw sky 321 | sky_pass.setup(); 322 | glDepthMask(false); 323 | CHECK_GL_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); 324 | glDepthMask(true); 325 | 326 | // Draw terrain 327 | if (draw_terrain) { 328 | terrainRender.renderVisible(gui.getCamera()); 329 | } 330 | 331 | // Draw boat 332 | boat_pass.setup(); 333 | CHECK_GL_ERROR(glDrawElements( 334 | GL_TRIANGLES, boat_mesh.vertex_indices.size() * 3, GL_UNSIGNED_INT, 0)); 335 | 336 | // Draw sun 337 | sun_pass.setup(); 338 | CHECK_GL_ERROR( 339 | glDrawElements(GL_PATCHES, SUN_FACES.size() * 3, GL_UNSIGNED_INT, 0)); 340 | 341 | // Draw rain 342 | rainRender.update(gui.isRaining()); 343 | 344 | // Poll and swap. 345 | glfwPollEvents(); 346 | glfwSwapBuffers(window); 347 | } 348 | glfwDestroyWindow(window); 349 | glfwTerminate(); 350 | exit(EXIT_SUCCESS); 351 | } 352 | -------------------------------------------------------------------------------- /src/render_pass.cc: -------------------------------------------------------------------------------- 1 | #include "render_pass.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * For students: 9 | * 10 | * Although RenderPass simplifies the implementation of the reference code. 11 | * THE USE OF RENDERPASS CLASS IS TOTALLY OPTIONAL. 12 | * You can implement your system without even taking a look of this. 13 | */ 14 | 15 | RenderInputMeta::RenderInputMeta() {} 16 | 17 | bool RenderInputMeta::isInteger() const { 18 | return element_type == GL_INT || element_type == GL_UNSIGNED_INT; 19 | } 20 | 21 | RenderInputMeta::RenderInputMeta(int _position, const std::string &_name, 22 | const void *_data, size_t _nelements, 23 | size_t _element_length, int _element_type, 24 | bool _divisor) 25 | : position(_position), name(_name), data(_data), nelements(_nelements), 26 | element_length(_element_length), element_type(_element_type), 27 | divisor(_divisor) {} 28 | 29 | RenderDataInput::RenderDataInput() {} 30 | 31 | RenderPass::RenderPass( 32 | int vao, // -1: create new VAO, otherwise use given VAO 33 | const RenderDataInput &input, 34 | const std::vector shaders, // Order: VS, GS, FS, TCS, TES 35 | const std::vector uniforms, 36 | const std::vector output // Order: 0, 1, 2... 37 | ) 38 | : vao_(vao), input_(input), uniforms_(uniforms) { 39 | if (vao_ < 0) { 40 | CHECK_GL_ERROR(glGenVertexArrays(1, (GLuint *)&vao_)); 41 | } 42 | CHECK_GL_ERROR(glBindVertexArray(vao_)); 43 | 44 | // Program first 45 | vs_ = compileShader(shaders[0], GL_VERTEX_SHADER); 46 | gs_ = compileShader(shaders[1], GL_GEOMETRY_SHADER); 47 | fs_ = compileShader(shaders[2], GL_FRAGMENT_SHADER); 48 | if (shaders.size() == 5) { 49 | tcs_ = compileShader(shaders[3], GL_TESS_CONTROL_SHADER); 50 | tes_ = compileShader(shaders[4], GL_TESS_EVALUATION_SHADER); 51 | } 52 | CHECK_GL_ERROR(sp_ = glCreateProgram()); 53 | glAttachShader(sp_, vs_); 54 | glAttachShader(sp_, fs_); 55 | if (shaders[1]) 56 | glAttachShader(sp_, gs_); 57 | if (shaders.size() == 5) { 58 | glAttachShader(sp_, tcs_); 59 | glAttachShader(sp_, tes_); 60 | } 61 | 62 | // ... and then buffers 63 | size_t nbuffer = input.getNBuffers(); 64 | if (input.hasIndex()) 65 | nbuffer++; 66 | glbuffers_.resize(nbuffer); 67 | CHECK_GL_ERROR(glGenBuffers(nbuffer, glbuffers_.data())); 68 | for (int i = 0; i < input.getNBuffers(); i++) { 69 | auto meta = input.getBufferMeta(i); 70 | CHECK_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, glbuffers_[i])); 71 | CHECK_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, 72 | meta.getElementSize() * meta.nelements, 73 | meta.data, GL_STATIC_DRAW)); 74 | if (meta.isInteger()) { 75 | CHECK_GL_ERROR(glVertexAttribIPointer(meta.position, meta.element_length, 76 | meta.element_type, 0, 0)); 77 | } else { 78 | CHECK_GL_ERROR(glVertexAttribPointer(meta.position, meta.element_length, 79 | meta.element_type, GL_FALSE, 0, 0)); 80 | } 81 | CHECK_GL_ERROR(glEnableVertexAttribArray(meta.position)); 82 | // ... because we need program to bind location 83 | CHECK_GL_ERROR(glBindAttribLocation(sp_, meta.position, meta.name.c_str())); 84 | if (meta.divisor) { 85 | glVertexAttribDivisor(meta.position, 1); 86 | } 87 | } 88 | // .. bind output position 89 | for (size_t i = 0; i < output.size(); i++) { 90 | CHECK_GL_ERROR(glBindFragDataLocation(sp_, i, output[i])); 91 | } 92 | // ... then we can link 93 | glLinkProgram(sp_); 94 | CHECK_GL_PROGRAM_ERROR(sp_); 95 | 96 | if (input.hasIndex()) { 97 | auto meta = input.getIndexMeta(); 98 | CHECK_GL_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glbuffers_.back())); 99 | CHECK_GL_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, 100 | meta.getElementSize() * meta.nelements, 101 | meta.data, GL_STATIC_DRAW)); 102 | } 103 | // after linking uniform locations can be determined 104 | unilocs_.resize(uniforms.size()); 105 | for (size_t i = 0; i < uniforms.size(); i++) { 106 | CHECK_GL_ERROR(unilocs_[i] = 107 | glGetUniformLocation(sp_, uniforms[i].name.c_str())); 108 | } 109 | if (input_.hasMaterial()) { 110 | createMaterialTexture(); 111 | initMaterialUniform(); 112 | } 113 | } 114 | 115 | void RenderPass::initMaterialUniform() { 116 | auto float_binder = [](int loc, const void *data) { 117 | glUniform1fv(loc, 1, (const GLfloat *)data); 118 | }; 119 | auto vector_binder = [](int loc, const void *data) { 120 | glUniform4fv(loc, 1, (const GLfloat *)data); 121 | }; 122 | auto sampler0_binder = [](int loc, const void *data) { 123 | CHECK_GL_ERROR(glBindSampler(0, (GLuint)(long)data)); 124 | }; 125 | auto texture0_binder = [](int loc, const void *data) { 126 | CHECK_GL_ERROR(glUniform1i(loc, 0)); 127 | CHECK_GL_ERROR(glActiveTexture(GL_TEXTURE0 + 0)); 128 | CHECK_GL_ERROR(glBindTexture(GL_TEXTURE_2D, (long)data)); 129 | // std::cerr << " bind texture " << long(data) << std::endl; 130 | }; 131 | material_uniforms_.clear(); 132 | for (size_t i = 0; i < input_.getNMaterials(); i++) { 133 | auto &ma = input_.getMaterial(i); 134 | auto diffuse_data = [&ma]() -> const void * { return &ma.diffuse[0]; }; 135 | auto ambient_data = [&ma]() -> const void * { return &ma.ambient[0]; }; 136 | auto specular_data = [&ma]() -> const void * { return &ma.specular[0]; }; 137 | auto shininess_data = [&ma]() -> const void * { return &ma.shininess; }; 138 | int texid = matexids_[i]; 139 | auto texture_data = [texid]() -> const void * { 140 | return (const void *)(intptr_t)texid; 141 | }; 142 | int sam = sampler2d_; 143 | auto sampler_data = [sam]() -> const void * { 144 | return (const void *)(intptr_t)sam; 145 | }; 146 | ShaderUniform diffuse = {"diffuse", vector_binder, diffuse_data}; 147 | ShaderUniform ambient = {"ambient", vector_binder, ambient_data}; 148 | ShaderUniform specular = {"specular", vector_binder, specular_data}; 149 | ShaderUniform shininess = {"shininess", float_binder, shininess_data}; 150 | ShaderUniform texture = {"GL_TEXTURE_2D", texture0_binder, texture_data}; 151 | ShaderUniform sampler = {"textureSampler", sampler0_binder, sampler_data}; 152 | std::vector munis = {diffuse, ambient, specular, 153 | shininess, texture, sampler}; 154 | material_uniforms_.emplace_back(munis); 155 | } 156 | malocs_.clear(); 157 | CHECK_GL_ERROR(malocs_.emplace_back(glGetUniformLocation(sp_, "diffuse"))); 158 | CHECK_GL_ERROR(malocs_.emplace_back(glGetUniformLocation(sp_, "ambient"))); 159 | CHECK_GL_ERROR(malocs_.emplace_back(glGetUniformLocation(sp_, "specular"))); 160 | CHECK_GL_ERROR(malocs_.emplace_back(glGetUniformLocation(sp_, "shininess"))); 161 | CHECK_GL_ERROR( 162 | malocs_.emplace_back(glGetUniformLocation(sp_, "textureSampler"))); 163 | CHECK_GL_ERROR( 164 | malocs_.emplace_back(glGetUniformLocation(sp_, "textureSampler"))); 165 | std::cerr << "textureSampler location: " << malocs_.back() << std::endl; 166 | } 167 | 168 | /* 169 | * Create textures to gltextures_ 170 | * and assign material specified textures to matexids_ 171 | * 172 | * Different materials may share textures 173 | */ 174 | void RenderPass::createMaterialTexture() { 175 | CHECK_GL_ERROR(glActiveTexture(GL_TEXTURE0 + 0)); 176 | matexids_.clear(); 177 | std::map tex2id; 178 | for (size_t i = 0; i < input_.getNMaterials(); i++) { 179 | auto &ma = input_.getMaterial(i); 180 | #if 0 181 | std::cerr << __func__ << " Material " << i << " has texture pointer " << ma.texture.get() << std::endl; 182 | #endif 183 | if (!ma.texture) { 184 | matexids_.emplace_back(0); 185 | continue; 186 | } 187 | // Do not create multiple texture for the same data. 188 | auto iter = tex2id.find(ma.texture.get()); 189 | if (iter != tex2id.end()) { 190 | matexids_.emplace_back(iter->second); 191 | continue; 192 | } 193 | 194 | // Now create and upload texture data 195 | int w = ma.texture->width; 196 | int h = ma.texture->height; 197 | // TODO: enable stride 198 | // Translate RGB to RGBA for alignment 199 | std::vector dummy(w * h); 200 | const unsigned char *bytes = ma.texture->bytes.data(); 201 | for (int row = 0; row < h; row++) { 202 | for (int col = 0; col < w; col++) { 203 | unsigned r = bytes[row * w * 3 + col * 3]; 204 | unsigned g = bytes[row * w * 3 + col * 3 + 1]; 205 | unsigned b = bytes[row * w * 3 + col * 3 + 1]; 206 | dummy[row * w + col] = r | (g << 8) | (b << 16) | (0xFF << 24); 207 | } 208 | } 209 | GLuint tex = 0; 210 | CHECK_GL_ERROR(glGenTextures(1, &tex)); 211 | CHECK_GL_ERROR(glBindTexture(GL_TEXTURE_2D, tex)); 212 | CHECK_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w, h)); 213 | CHECK_GL_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, 214 | GL_UNSIGNED_BYTE, dummy.data())); 215 | // CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); 216 | std::cerr << __func__ << " load data into texture " << tex << " dim: " << w 217 | << " x " << h << std::endl; 218 | CHECK_GL_ERROR(glBindTexture(GL_TEXTURE_2D, 0)); 219 | matexids_.emplace_back(tex); 220 | tex2id[ma.texture.get()] = tex; 221 | } 222 | CHECK_GL_ERROR(glGenSamplers(1, &sampler2d_)); 223 | CHECK_GL_ERROR(glSamplerParameteri(sampler2d_, GL_TEXTURE_WRAP_S, GL_REPEAT)); 224 | CHECK_GL_ERROR(glSamplerParameteri(sampler2d_, GL_TEXTURE_WRAP_T, GL_REPEAT)); 225 | CHECK_GL_ERROR( 226 | glSamplerParameteri(sampler2d_, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); 227 | CHECK_GL_ERROR( 228 | glSamplerParameteri(sampler2d_, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 229 | } 230 | 231 | RenderPass::~RenderPass() { 232 | // TODO: Free resources 233 | } 234 | 235 | void RenderPass::updateVBO(int position, const void *data, size_t size) { 236 | int bufferid = -1; 237 | for (int i = 0; i < input_.getNBuffers(); i++) { 238 | auto meta = input_.getBufferMeta(i); 239 | if (meta.position == position) { 240 | bufferid = i; 241 | break; 242 | } 243 | } 244 | if (bufferid < 0) 245 | throw __func__ + std::string(": error, can't find buffer with position ") + 246 | std::to_string(position); 247 | auto meta = input_.getBufferMeta(bufferid); 248 | CHECK_GL_ERROR(glBindBuffer(GL_ARRAY_BUFFER, glbuffers_[bufferid])); 249 | CHECK_GL_ERROR(glBufferData(GL_ARRAY_BUFFER, size * meta.getElementSize(), 250 | data, GL_STATIC_DRAW)); 251 | } 252 | 253 | void RenderPass::setup() { 254 | // Switch to our object VAO. 255 | CHECK_GL_ERROR(glBindVertexArray(vao_)); 256 | // Use our program. 257 | CHECK_GL_ERROR(glUseProgram(sp_)); 258 | 259 | bindUniforms(uniforms_, unilocs_); 260 | } 261 | 262 | bool RenderPass::renderWithMaterial(int mid) { 263 | if (mid >= int(material_uniforms_.size()) || mid < 0) 264 | return false; 265 | const auto &mat = input_.getMaterial(mid); 266 | #if 0 267 | if (!mat.texture) 268 | return true; 269 | #endif 270 | auto &matuni = material_uniforms_[mid]; 271 | bindUniforms(matuni, malocs_); 272 | CHECK_GL_ERROR( 273 | glDrawElements(GL_TRIANGLES, mat.nfaces * 3, GL_UNSIGNED_INT, 274 | (const void *)(mat.offset * 3 * 4)) // Offset is in bytes 275 | ); 276 | return true; 277 | } 278 | 279 | void RenderPass::bindUniforms(std::vector &uniforms, 280 | const std::vector &unilocs) { 281 | for (size_t i = 0; i < uniforms.size(); i++) { 282 | const auto &uni = uniforms[i]; 283 | // std::cerr << "binding " << uni.name << " to " << unilocs[i] << std::endl; 284 | auto ptr = uni.data_source(); 285 | CHECK_GL_ERROR(uni.binder(unilocs[i], ptr)); 286 | } 287 | } 288 | 289 | unsigned RenderPass::compileShader(const char *source_ptr, int type) { 290 | if (!source_ptr) 291 | return 0; 292 | auto iter = shader_cache_.find(source_ptr); 293 | if (iter != shader_cache_.end()) { 294 | return iter->second; 295 | } 296 | GLuint ret = 0; 297 | CHECK_GL_ERROR(ret = glCreateShader(type)); 298 | #if 0 299 | std::cerr << __func__ << " shader id " << ret << " type " << type << "\tsource:\n" << source_ptr << std::endl; 300 | #endif 301 | CHECK_GL_ERROR(glShaderSource(ret, 1, &source_ptr, nullptr)); 302 | glCompileShader(ret); 303 | CHECK_GL_SHADER_ERROR(ret); 304 | shader_cache_[source_ptr] = ret; 305 | return ret; 306 | } 307 | 308 | void RenderDataInput::assign(int position, const std::string &name, 309 | const void *data, size_t nelements, 310 | size_t element_length, int element_type, 311 | bool divisor) { 312 | meta_.emplace_back(position, name, data, nelements, element_length, 313 | element_type, divisor); 314 | } 315 | 316 | void RenderDataInput::assignIndex(const void *data, size_t nelements, 317 | size_t element_length) { 318 | has_index_ = true; 319 | index_meta_ = {-1, "", data, nelements, element_length, GL_UNSIGNED_INT, 320 | false}; 321 | } 322 | 323 | void RenderDataInput::useMaterials(const std::vector &ms) { 324 | materials_ = ms; 325 | for (const auto &ma : ms) { 326 | std::cerr << "Use Material from " << ma.offset << " size: " << ma.nfaces 327 | << std::endl; 328 | } 329 | } 330 | 331 | size_t RenderInputMeta::getElementSize() const { 332 | size_t element_size = 4; 333 | if (element_type == GL_FLOAT) 334 | element_size = 4; 335 | else if (element_type == GL_UNSIGNED_INT) 336 | element_size = 4; 337 | else if (element_type == GL_INT) 338 | element_size = 4; 339 | return element_size * element_length; 340 | } 341 | 342 | std::map RenderPass::shader_cache_; 343 | -------------------------------------------------------------------------------- /src/terrain_render.cc: -------------------------------------------------------------------------------- 1 | #include "terrain_render.h" 2 | 3 | #include "perlin.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const char *terrain_vertex_shader = 12 | #include "shaders/terrain.vert" 13 | ; 14 | 15 | const char *terrain_geometry_shader = 16 | #include "shaders/terrain.geom" 17 | ; 18 | 19 | const char *terrain_fragment_shader = 20 | #include "shaders/terrain.frag" 21 | ; 22 | 23 | const char *ocean_vertex_shader = 24 | #include "shaders/ocean.vert" 25 | ; 26 | 27 | const char *ocean_geometry_shader = 28 | #include "shaders/ocean.geom" 29 | ; 30 | 31 | const char *ocean_fragment_shader = 32 | #include "shaders/ocean.frag" 33 | ; 34 | 35 | const char *ocean_tcs_shader = 36 | #include "shaders/ocean.tcs" 37 | ; 38 | 39 | const char *ocean_tes_shader = 40 | #include "shaders/ocean.tes" 41 | ; 42 | 43 | using std::array; 44 | using std::vector; 45 | 46 | constexpr float BLOCK_SIZE = 1.0f; 47 | constexpr int UPDATE_STEP = 5; 48 | constexpr double kPi = 3.141592653589793; 49 | constexpr double kG = 9.8000001; 50 | 51 | // Defines a basic unit cube in 3-space 52 | const array cube_vertices = { 53 | {{0.0f, 0.0f, 0.0f, 1.0f}, 54 | {0.0f, 0.0f, BLOCK_SIZE, 1.0f}, 55 | {BLOCK_SIZE, 0.0f, 0.0f, 1.0f}, 56 | {BLOCK_SIZE, 0.0f, BLOCK_SIZE, 1.0f}}}; 57 | 58 | const array cube_faces = {{{2, 0, 1}, {1, 3, 2}}}; 59 | 60 | // Wave simulation parameters 61 | constexpr int kNumWaves = 10; 62 | float gMedianWave = 30.0f; /* wavelengths sampled based on this average wave */ 63 | float gMedianAmp = 0.10f; /* amplitudes sampled based on this average amp */ 64 | float gSteepness = 0.3f; /* tunable *sharpness* of wave in [0, 1] */ 65 | glm::vec3 gMedianDir = {0.5f, 0.1f, 0.5f}; /* sample directions relative */ 66 | float kAngleRange = kPi / 3; /* sample directions within range */ 67 | array gAmp{}; 68 | array gFreq{}; 69 | array gPhi{}; 70 | array gDir{}; 71 | 72 | TerrainRender::TerrainRender(size_t rows, size_t cols, 73 | std::vector uniforms) 74 | : ticks_(0), rows_(rows), cols_(cols), cached_x_(0), cached_z_(0), 75 | instanceOffsets_(rows * cols), sortedOffsets_(rows * cols), 76 | heightVec_(rows * cols), norm0_(rows * cols), norm1_(rows * cols), 77 | norm2_(rows * cols), norm3_(rows * cols) { 78 | 79 | // WAVES 80 | // Binders 81 | auto param_binder = [](int loc, const void *data) { 82 | glUniform1fv(loc, kNumWaves, (const GLfloat *)data); 83 | }; 84 | auto dir_binder = [](int loc, const void *data) { 85 | glUniform3fv(loc, kNumWaves, (const GLfloat *)data); 86 | }; 87 | auto int_binder = [](int loc, const void *data) { 88 | glUniform1iv(loc, 1, (const GLint *)data); 89 | }; 90 | auto float_binder = [](int loc, const void *data) { 91 | glUniform1fv(loc, 1, (const GLfloat *)data); 92 | }; 93 | 94 | // Data 95 | auto amp_data = []() -> const void * { return gAmp.data(); }; 96 | auto freq_data = []() -> const void * { return gFreq.data(); }; 97 | auto phi_data = []() -> const void * { return gPhi.data(); }; 98 | auto dir_data = []() -> const void * { return gDir.data(); }; 99 | auto steepness_data = []() -> const void * { return &gSteepness; }; 100 | auto num_waves_data = []() -> const void * { return &kNumWaves; }; 101 | 102 | // Uniforms 103 | uniforms.push_back({"amp", param_binder, amp_data}); 104 | uniforms.push_back({"freq", param_binder, freq_data}); 105 | uniforms.push_back({"phi", param_binder, phi_data}); 106 | uniforms.push_back({"dir", dir_binder, dir_data}); 107 | uniforms.push_back({"steepness", float_binder, steepness_data}); 108 | uniforms.push_back({"num_waves", int_binder, num_waves_data}); 109 | 110 | auto terrain_pass_input = RenderDataInput{}; 111 | terrain_pass_input.assign(0, "vertex_position", cube_vertices.data(), 112 | cube_vertices.size(), 4, GL_FLOAT); 113 | 114 | // Set up offsets for each instanced cube 115 | updateInstanceOffsets(0, 0); 116 | terrain_pass_input.assign(1, "offset", instanceOffsets_.data(), 117 | instanceOffsets_.size(), 3, GL_FLOAT, true); 118 | terrain_pass_input.assign(2, "heightVec", heightVec_.data(), 119 | heightVec_.size(), 4, GL_FLOAT, true); 120 | terrain_pass_input.assign(3, "norm0", norm0_.data(), norm0_.size(), 3, 121 | GL_FLOAT, true); 122 | terrain_pass_input.assign(4, "norm1", norm1_.data(), norm1_.size(), 3, 123 | GL_FLOAT, true); 124 | terrain_pass_input.assign(5, "norm2", norm2_.data(), norm2_.size(), 3, 125 | GL_FLOAT, true); 126 | terrain_pass_input.assign(6, "norm3", norm3_.data(), norm3_.size(), 3, 127 | GL_FLOAT, true); 128 | terrain_pass_input.assignIndex(cube_faces.data(), cube_faces.size(), 3); 129 | 130 | // Shader-related construct arguments for RenderPass 131 | auto terrain_shaders = 132 | vector{{terrain_vertex_shader, terrain_geometry_shader, 133 | terrain_fragment_shader}}; 134 | auto output = vector{{"fragment_color"}}; 135 | this->terrain_pass_ = std::make_unique( 136 | -1, terrain_pass_input, terrain_shaders, uniforms, output); 137 | 138 | // WATER 139 | auto ocean_pass_input = RenderDataInput{}; 140 | ocean_pass_input.assign(0, "vertex_position", cube_vertices.data(), 141 | cube_vertices.size(), 4, GL_FLOAT); 142 | ocean_pass_input.assign(1, "offset", sortedOffsets_.data(), 143 | sortedOffsets_.size(), 3, GL_FLOAT, true); 144 | ocean_pass_input.assignIndex(cube_faces.data(), cube_faces.size(), 3); 145 | auto ocean_shaders = vector{ 146 | {ocean_vertex_shader, ocean_geometry_shader, ocean_fragment_shader, 147 | ocean_tcs_shader, ocean_tes_shader}}; 148 | this->ocean_pass_ = std::make_unique( 149 | -1, ocean_pass_input, ocean_shaders, uniforms, output); 150 | 151 | // Initialize wave parameters 152 | updateWaveParams(); 153 | } 154 | 155 | void TerrainRender::renderVisible(glm::vec3 eye) { 156 | int x_coord = std::floor(eye.x / BLOCK_SIZE); 157 | int z_coord = std::floor(eye.z / BLOCK_SIZE); 158 | 159 | // Update wave parameters every ~3sec 160 | ticks_++; 161 | if (ticks_ % 180 == 0) { 162 | // updateWaveParams(); 163 | } 164 | 165 | // Only update instanceOffsets_ if eye changes 166 | if (x_coord / UPDATE_STEP != cached_x_ / UPDATE_STEP || 167 | z_coord / UPDATE_STEP != cached_z_ / UPDATE_STEP) { 168 | updateInstanceOffsets(x_coord, z_coord); 169 | 170 | terrain_pass_->updateVBO(1, instanceOffsets_.data(), 171 | instanceOffsets_.size()); 172 | terrain_pass_->updateVBO(2, heightVec_.data(), heightVec_.size()); 173 | terrain_pass_->updateVBO(3, norm0_.data(), norm0_.size()); 174 | terrain_pass_->updateVBO(4, norm1_.data(), norm1_.size()); 175 | terrain_pass_->updateVBO(5, norm2_.data(), norm2_.size()); 176 | terrain_pass_->updateVBO(6, norm3_.data(), norm3_.size()); 177 | ocean_pass_->updateVBO(1, sortedOffsets_.data(), sortedOffsets_.size()); 178 | } 179 | 180 | // Draw each cube, instanced 181 | terrain_pass_->setup(); 182 | glDrawElementsInstanced(GL_TRIANGLES, cube_faces.size() * 3, GL_UNSIGNED_INT, 183 | 0, instanceOffsets_.size()); 184 | ocean_pass_->setup(); 185 | glDrawElementsInstanced(GL_PATCHES, cube_faces.size() * 3, GL_UNSIGNED_INT, 0, 186 | sortedOffsets_.size()); 187 | } 188 | 189 | /** 190 | * Vary the ocean parameters with time to achieve a dynamic wave simulation. 191 | * This also allows us to achieve "stormy" weather vs. "sunny" weather. 192 | */ 193 | void TerrainRender::updateWaveParams() { 194 | static std::mt19937 engine(std::random_device{}()); 195 | 196 | double now = getTime(); 197 | 198 | // TODO: Update median wave and amplitude 199 | 200 | // Resample wavelengths to generate new frequencies 201 | auto lengths = array{}; 202 | auto freq_dist = std::uniform_real_distribution(gMedianWave / 2.0, 203 | gMedianWave * 2.0); 204 | std::generate(lengths.begin(), lengths.end(), 205 | [&freq_dist]() { return freq_dist(engine); }); 206 | std::transform(lengths.begin(), lengths.end(), gFreq.begin(), 207 | [](const auto &wavelength) { 208 | return std::sqrt(kG * 2 * kPi / wavelength); 209 | }); 210 | 211 | // Derive amplitudes based on wavelengths (constant ratio) 212 | auto amp_dist = 213 | std::uniform_real_distribution(gMedianAmp / 2.0, gMedianAmp * 2.0); 214 | std::generate(gAmp.begin(), gAmp.end(), 215 | [&_dist]() { return amp_dist(engine); }); 216 | /* 217 | std::transform(lengths.begin(), lengths.end(), gAmp.begin(), 218 | [](const auto &wavelength) { 219 | return 220 | // return gMedianAmp / gMedianWave * wavelength; 221 | });*/ 222 | 223 | // Resample direction vectors 224 | auto dir_dist = 225 | std::uniform_real_distribution(-kAngleRange / 2, kAngleRange / 2); 226 | std::generate(gDir.begin(), gDir.end(), [&dir_dist]() { 227 | return glm::rotateY(gMedianDir, dir_dist(engine)); 228 | }); 229 | 230 | // Update phase values 231 | auto phase_dist = std::uniform_real_distribution(-kPi, kPi); 232 | std::generate(gPhi.begin(), gPhi.end(), 233 | [&phase_dist]() { return phase_dist(engine); }); 234 | } 235 | 236 | void TerrainRender::updateInstanceOffsets(int x, int z) { 237 | int index = 0; 238 | instanceOffsets_.resize(rows_ * cols_); 239 | for (size_t i = 0; i < rows_; i++) { 240 | for (size_t j = 0; j < cols_; j++) { 241 | float newX = ((float)i - (float)(rows_ / 2) + (float)x) * BLOCK_SIZE; 242 | float newZ = ((float)j - (float)(cols_ / 2) + (float)z) * BLOCK_SIZE; 243 | float perlin = perlin::getHeight(newX, newZ); 244 | instanceOffsets_[index++] = {newX, perlin, newZ}; 245 | } 246 | } 247 | index = 0; 248 | heightVec_.resize(rows_ * cols_); 249 | std::vector normals(rows_ * cols_); 250 | for (size_t i = 0; i < rows_; i++) { 251 | for (size_t j = 0; j < cols_; j++) { 252 | float botLeft = instanceOffsets_[index].y; 253 | glm::vec4 localHeights = glm::vec4{botLeft}; 254 | if (i < rows_ - 1) { // Up 255 | localHeights[1] = instanceOffsets_[index + cols_].y; 256 | } 257 | if (j < cols_ - 1) { // Right 258 | localHeights[2] = instanceOffsets_[index + 1].y; 259 | } 260 | if (i < rows_ - 1 && j < cols_ - 1) { // Diag 261 | localHeights[3] = instanceOffsets_[index + cols_ + 1].y; 262 | } 263 | normals[index] = -glm::normalize( 264 | glm::cross(glm::vec3{1.0f, localHeights[1] - botLeft, 0.0f}, 265 | glm::vec3{0.0f, localHeights[2] - botLeft, 1.0f})); 266 | heightVec_[index] = localHeights; 267 | index++; 268 | } 269 | } 270 | 271 | index = 0; 272 | for (size_t i = 0; i < rows_; i++) { 273 | for (size_t j = 0; j < cols_; j++) { 274 | norm0_[index] = normals[index]; 275 | norm1_[index] = (i < rows_ - 1) ? normals[index + cols_] : normals[index]; 276 | norm2_[index] = (j < cols_ - 1) ? normals[index + 1] : normals[index]; 277 | norm3_[index] = (i < rows_ - 1 && j < cols_ - 1) 278 | ? normals[index + cols_ + 1] 279 | : normals[index]; 280 | index++; 281 | } 282 | } 283 | 284 | sortedOffsets_ = instanceOffsets_; 285 | glm::vec2 center_pos = {x, z}; 286 | std::sort(sortedOffsets_.begin(), sortedOffsets_.end(), 287 | [¢er_pos](const glm::vec3 &a, const glm::vec3 &b) { 288 | return glm::distance(center_pos, glm::vec2{a.x, a.z}) < 289 | glm::distance(center_pos, glm::vec2{b.x, b.z}); 290 | }); 291 | 292 | cached_x_ = x; 293 | cached_z_ = z; 294 | } 295 | 296 | bool TerrainRender::isPositionLegal(const glm::vec3 &loc) { 297 | // Check that player (if treated as a line) lies above terrain 298 | int i = int(int(floor(loc.x)) - cached_x_ + rows_ / 2); 299 | int j = int(int(floor(loc.z)) - cached_z_ + cols_ / 2); 300 | float currentBlockHeight = instanceOffsets_[i * cols_ + j].y; 301 | if (currentBlockHeight >= 0.0f && loc.y < currentBlockHeight) { 302 | return false; 303 | } 304 | 305 | // Check for collisions with left, right, front, back 306 | float x_center = std::floor(loc.x); 307 | float z_center = std::floor(loc.z); 308 | auto loc_left = glm::vec3{x_center - 1.0f, 0, z_center}; 309 | auto loc_right = glm::vec3{x_center + 1.0f, 0, z_center}; 310 | auto loc_front = glm::vec3{x_center, 0, z_center + 1.0f}; 311 | auto loc_back = glm::vec3{x_center, 0, z_center - 1.0f}; 312 | auto loc_left_front = glm::vec3{x_center - 1.0f, 0, z_center + 1.0f}; 313 | auto loc_right_front = glm::vec3{x_center + 1.0f, 0, z_center + 1.0f}; 314 | auto loc_left_back = glm::vec3{x_center - 1.0f, 0, z_center - 1.0f}; 315 | auto loc_right_back = glm::vec3{x_center + 1.0f, 0, z_center - 1.0f}; 316 | auto neighbors = vector{ 317 | loc_left, loc_right, loc_front, loc_back, 318 | loc_left_front, loc_right_front, loc_left_back, loc_right_back}; 319 | 320 | for (const auto &neighbor : neighbors) { 321 | // Check circle-rectangle intersection (xz-plane) 322 | auto closest = glm::vec2{glm::clamp(loc.x, neighbor.x, neighbor.x + 1.0f), 323 | glm::clamp(loc.z, neighbor.z, neighbor.z + 1.0f)}; 324 | float distance = glm::distance(closest, {loc.x, loc.z}); 325 | if (distance < 0.25f) { 326 | // Check heights (y) 327 | int i = int(floor(neighbor.x)) - cached_x_ + rows_ / 2; 328 | int j = int(floor(neighbor.z)) - cached_z_ + cols_ / 2; 329 | auto block_height = instanceOffsets_[i * cols_ + j].y; 330 | if (block_height >= 0.0f && loc.y < block_height) { 331 | return false; 332 | } 333 | } 334 | } 335 | 336 | return true; 337 | } 338 | 339 | float TerrainRender::getWaveHeight(const glm::vec3 &loc) { 340 | // Calculate wave height at a given location 341 | float waveHeight = 0.0f; 342 | // Naive sum of sines 343 | /*for (size_t i = 0; i < gAmp.size(); i++) { 344 | if (gAmp.at(i) == 0.0f) { 345 | break; 346 | } 347 | waveHeight += 348 | gAmp.at(i) * glm::sin(glm::dot(glm::vec2(gDir.at(i).x, gDir.at(i).z), 349 | glm::vec2(loc.x, loc.z)) * 350 | gFreq.at(i) + 351 | getTime() * gPhi.at(i)); 352 | }*/ 353 | glm::vec2 loc_xz = {loc.x, loc.z}; 354 | for (size_t i = 0; i < kNumWaves; i++) { 355 | glm::vec2 dir_xz = {gDir.at(i).x, gDir.at(i).z}; 356 | waveHeight += gAmp.at(i) * glm::sin(glm::dot(gFreq.at(i) * dir_xz, loc_xz) + 357 | gPhi.at(i) * getTime()); 358 | } 359 | return waveHeight + 0.1875f; 360 | } 361 | 362 | glm::vec3 TerrainRender::getWaveNormal(const glm::vec3 &loc) { 363 | glm::vec3 pos = loc; 364 | pos.y = getWaveHeight(loc); 365 | glm::vec3 norm = {0.0f, 1.0f, 0.0f}; 366 | for (size_t i = 0; i < kNumWaves; i++) { 367 | float q = gSteepness / (gFreq.at(i) * gAmp.at(i) * kNumWaves); 368 | float WA = gFreq.at(i) * gAmp.at(i); 369 | float S = glm::sin(glm::dot(gFreq.at(i) * gDir.at(i), pos) + 370 | gPhi.at(i) * getTime()); 371 | float C = glm::cos(glm::dot(gFreq.at(i) * gDir.at(i), pos) + 372 | gPhi.at(i) * getTime()); 373 | glm::vec3 temp{0.0f}; 374 | temp.x = -(gDir.at(i).x * WA * C); 375 | temp.z = -(gDir.at(i).z * WA * C); 376 | temp.y = -(q * WA * S); 377 | norm += temp; 378 | } 379 | return glm::normalize(norm); 380 | } 381 | 382 | void TerrainRender::toggle_storm(bool is_raining) { 383 | if (is_raining) { 384 | kAngleRange = kPi / 2; 385 | gMedianWave = 150.0f; 386 | gMedianAmp = 0.5f; 387 | gSteepness = 0.2f; 388 | updateWaveParams(); 389 | } else { 390 | kAngleRange = kPi / 3; 391 | gMedianWave = 30.0f; 392 | gMedianAmp = 0.10f; 393 | gSteepness = 0.3f; 394 | updateWaveParams(); 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /assets/rowboat.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.77 (sub 0) OBJ File: 'rowboat.blend' 2 | # www.blender.org 3 | mtllib rowboat.mtl 4 | o Cube 5 | v -0.533806 0.279894 0.495160 6 | v -0.538033 0.242739 -0.900080 7 | v 0.020783 0.043398 -1.000000 8 | v -0.297033 0.275073 -0.950005 9 | v -0.267236 0.279894 0.825926 10 | v -0.543831 0.279894 -0.000000 11 | v -0.296919 0.043398 -1.000000 12 | v -0.296919 0.043398 0.787796 13 | v -0.506702 0.081992 -0.179060 14 | v 0.020783 0.161646 -1.000000 15 | v -0.582108 0.161646 0.512132 16 | v -0.593838 0.161646 -1.000000 17 | v -0.445746 0.262137 -0.950076 18 | v -0.406628 0.279894 0.700795 19 | v -0.543831 0.263237 -0.500269 20 | v -0.148472 0.280201 -0.949993 21 | v -0.128087 0.279894 0.907858 22 | v -0.543831 0.279894 0.413336 23 | v -0.445378 0.043398 -1.000000 24 | v -0.148459 0.043398 -1.000000 25 | v -0.296919 0.161646 -1.000000 26 | v -0.296919 -0.074850 -1.000000 27 | v -0.437613 0.049393 0.714192 28 | v -0.148460 0.043398 0.875211 29 | v -0.296919 -0.074850 0.705051 30 | v -0.296919 0.161646 0.836096 31 | v 0.020783 -0.074850 0.000000 32 | v -0.269227 0.040298 -0.863626 33 | v -0.242106 0.044686 0.752856 34 | v 0.020783 -0.074850 -0.500000 35 | v 0.020783 -0.074850 0.500000 36 | v -0.148460 0.161646 0.923511 37 | v -0.148460 -0.074850 0.794566 38 | v -0.445379 -0.074850 0.245341 39 | v -0.148459 -0.074850 -1.000000 40 | v -0.148459 0.161646 -1.000000 41 | v -0.445378 0.161646 -1.000000 42 | v -0.445378 -0.074850 -1.000000 43 | v -0.593838 0.161646 -0.395903 44 | v -0.445379 0.161646 0.733209 45 | v -0.148459 -0.178811 -1.000000 46 | v -0.296919 -0.146812 -1.000000 47 | v -0.561056 0.043398 -1.000000 48 | v -0.176996 -0.150541 0.650138 49 | v -0.561056 0.043398 0.512132 50 | v -0.296920 -0.136017 0.564801 51 | v -0.593838 0.161646 0.416388 52 | v -0.445378 0.259119 -1.000000 53 | v -0.593838 0.239272 -1.000000 54 | v -0.445379 0.279894 0.733209 55 | v -0.582108 0.279894 0.512132 56 | v -0.593838 0.259931 -0.500000 57 | v -0.148459 0.279894 -1.000000 58 | v -0.296919 0.273737 -1.000000 59 | v -0.148460 0.279894 0.953894 60 | v -0.296920 0.279894 0.866480 61 | v -0.593838 0.279894 0.416388 62 | v -0.593838 0.279894 -0.000000 63 | v 0.020783 0.279894 -1.000000 64 | v -0.077952 -0.016699 0.561789 65 | v -0.266736 0.187462 0.826403 66 | v -0.090534 -0.016699 0.001100 67 | v -0.090535 -0.016699 0.309898 68 | v -0.249978 -0.016699 0.433908 69 | v -0.296532 0.182641 -0.949528 70 | v -0.530496 0.150674 -0.179352 71 | v -0.273910 -0.016699 0.001100 72 | v -0.445245 0.169705 -0.949599 73 | v -0.530496 0.153373 -0.383143 74 | v -0.182223 -0.016699 0.309898 75 | v -0.147972 0.187769 -0.949515 76 | v -0.273910 -0.016699 0.309898 77 | v -0.127587 0.187462 0.908335 78 | v -0.334714 -0.016699 0.256374 79 | v -0.419426 0.147141 0.645210 80 | v -0.543331 0.187462 0.413813 81 | v -0.496261 0.147141 0.520976 82 | v -0.525273 0.157239 -0.882752 83 | v -0.404588 0.028523 -0.863690 84 | v -0.506702 0.084548 -0.361244 85 | v -0.134004 0.044966 -0.863615 86 | v -0.115450 0.044686 0.827432 87 | v -0.403261 0.085007 0.618092 88 | v -0.493867 0.044686 0.377310 89 | v -0.475127 0.085007 0.501893 90 | v -0.469284 0.033532 -0.788549 91 | v 0.003585 0.044686 -0.863614 92 | v -0.182222 -0.016699 -0.307698 93 | v -0.090534 -0.016699 -0.307698 94 | v -0.090542 -0.016509 -0.585611 95 | v -0.334714 -0.014657 -0.307864 96 | v -0.273910 -0.016699 -0.307698 97 | v -0.274137 -0.027665 -0.585662 98 | v -0.334714 -0.016699 0.001100 99 | v -0.182222 -0.016699 0.001100 100 | v -0.182293 -0.019676 -0.585619 101 | v -0.163890 -0.016699 0.511188 102 | v -0.328523 -0.016699 0.306909 103 | v -0.351345 -0.021745 -0.585737 104 | v -0.543331 0.190768 -0.499792 105 | v -0.493867 0.047695 -0.454268 106 | v -0.493867 0.044686 0.001084 107 | v -0.543331 0.187462 0.000477 108 | v -0.406127 0.187462 0.701272 109 | v -0.368982 0.044686 0.638960 110 | v -0.533306 0.187462 0.495638 111 | v -0.484742 0.044686 0.451788 112 | v -0.371149 -0.074850 0.475196 113 | v -0.441496 -0.012729 0.479767 114 | v -0.222689 -0.162811 -1.000000 115 | v -0.371149 -0.110831 -1.000000 116 | v -0.577447 0.102522 -1.000000 117 | v -0.074230 -0.185954 -1.000000 118 | v -0.503217 -0.015726 -1.000000 119 | v -0.162728 -0.164676 -0.174931 120 | v -0.296919 -0.141414 -0.217599 121 | v -0.371149 -0.105433 0.405071 122 | v -0.503218 -0.015726 0.378737 123 | v -0.236958 -0.143279 0.607470 124 | v -0.296919 -0.105433 0.634926 125 | v -0.561056 0.043398 -0.243934 126 | v -0.445379 -0.074850 -0.377329 127 | v -0.499335 0.046395 0.613162 128 | v -0.367266 -0.012729 0.709622 129 | v -0.088498 -0.171820 0.575069 130 | v -0.074230 -0.185954 -0.250000 131 | v -0.577447 0.102522 -0.291806 132 | v -0.577447 0.102522 0.464260 133 | v -0.222690 -0.157414 -0.217599 134 | v -0.503217 -0.015726 -0.377329 135 | v -0.371149 -0.110831 -0.377329 136 | v 0.000000 -0.193098 -1.000000 137 | v 0.000000 -0.148477 0.769592 138 | v 0.000000 0.279894 -0.949992 139 | v 0.000000 0.279894 0.932107 140 | v 0.533806 0.279894 0.495160 141 | v 0.538033 0.242739 -0.900080 142 | v 0.000000 -0.193098 0.000000 143 | v -0.020783 0.043398 -1.000000 144 | v 0.000000 0.043398 0.921317 145 | v 0.297033 0.275073 -0.950005 146 | v 0.267236 0.279894 0.825926 147 | v 0.543831 0.279894 -0.000000 148 | v 0.296919 0.043398 -1.000000 149 | v 0.296919 0.043398 0.787796 150 | v 0.506702 0.081992 -0.179060 151 | v -0.020783 0.161646 -1.000000 152 | v 0.000000 0.161646 0.969617 153 | v 0.582108 0.161646 0.512132 154 | v 0.593838 0.161646 -1.000000 155 | v 0.445746 0.262137 -0.950076 156 | v 0.406628 0.279894 0.700795 157 | v 0.543831 0.263237 -0.500269 158 | v 0.000000 -0.193098 -0.500000 159 | v 0.000000 -0.074850 -1.000000 160 | v 0.000000 -0.074850 0.840673 161 | v 0.148472 0.280201 -0.949993 162 | v 0.128087 0.279894 0.907858 163 | v 0.543831 0.279894 0.413336 164 | v 0.445378 0.043398 -1.000000 165 | v 0.148459 0.043398 -1.000000 166 | v 0.296919 0.161646 -1.000000 167 | v 0.296919 -0.074850 -1.000000 168 | v 0.437613 0.049393 0.714192 169 | v 0.148460 0.043398 0.875211 170 | v 0.296919 -0.074850 0.705051 171 | v 0.296919 0.161646 0.836096 172 | v -0.020783 -0.074850 0.000000 173 | v 0.269227 0.040298 -0.863626 174 | v 0.242106 0.044686 0.752856 175 | v -0.020783 -0.074850 -0.500000 176 | v -0.020783 -0.074850 0.500000 177 | v 0.148460 0.161646 0.923511 178 | v 0.148460 -0.074850 0.794566 179 | v 0.445379 -0.074850 0.245341 180 | v 0.148459 -0.074850 -1.000000 181 | v 0.148459 0.161646 -1.000000 182 | v 0.445378 0.161646 -1.000000 183 | v 0.445378 -0.074850 -1.000000 184 | v 0.593838 0.161646 -0.395903 185 | v 0.445379 0.161646 0.733209 186 | v 0.148459 -0.178811 -1.000000 187 | v 0.296919 -0.146812 -1.000000 188 | v 0.561056 0.043398 -1.000000 189 | v 0.176996 -0.150541 0.650138 190 | v 0.561056 0.043398 0.512132 191 | v 0.296920 -0.136017 0.564801 192 | v 0.593838 0.161646 0.416388 193 | v 0.000000 -0.193098 0.500000 194 | v 0.000000 0.279894 1.000001 195 | v 0.445378 0.259119 -1.000000 196 | v 0.593838 0.239272 -1.000000 197 | v 0.445379 0.279894 0.733209 198 | v 0.582108 0.279894 0.512132 199 | v 0.593838 0.259931 -0.500000 200 | v 0.148459 0.279894 -1.000000 201 | v 0.296919 0.273737 -1.000000 202 | v 0.148460 0.279894 0.953894 203 | v 0.296920 0.279894 0.866480 204 | v 0.593838 0.279894 0.416388 205 | v 0.593838 0.279894 -0.000000 206 | v -0.020783 0.279894 -1.000000 207 | v 0.077952 -0.016699 0.561789 208 | v 0.266736 0.187462 0.826403 209 | v 0.090534 -0.016699 0.001100 210 | v 0.000000 -0.016699 0.309898 211 | v 0.090535 -0.016699 0.309898 212 | v 0.249978 -0.016699 0.433908 213 | v 0.296532 0.182641 -0.949528 214 | v 0.530496 0.150674 -0.179352 215 | v 0.273910 -0.016699 0.001100 216 | v 0.445245 0.169705 -0.949599 217 | v 0.530496 0.153373 -0.383143 218 | v 0.182223 -0.016699 0.309898 219 | v 0.147972 0.187769 -0.949515 220 | v 0.273910 -0.016699 0.309898 221 | v 0.127587 0.187462 0.908335 222 | v 0.334714 -0.016699 0.256374 223 | v 0.419426 0.147141 0.645210 224 | v 0.543331 0.187462 0.413813 225 | v 0.496261 0.147141 0.520976 226 | v 0.000000 0.187462 0.932584 227 | v 0.525273 0.157239 -0.882752 228 | v 0.000000 0.187462 -0.949515 229 | v 0.404588 0.028523 -0.863690 230 | v 0.506702 0.084548 -0.361244 231 | v 0.134004 0.044966 -0.863615 232 | v 0.115450 0.044686 0.827432 233 | v 0.403261 0.085007 0.618092 234 | v 0.493867 0.044686 0.377310 235 | v 0.475127 0.085007 0.501893 236 | v 0.000000 0.044686 0.849504 237 | v 0.469284 0.033532 -0.788549 238 | v -0.003585 0.044686 -0.863614 239 | v 0.182222 -0.016699 -0.307698 240 | v 0.090534 -0.016699 -0.307698 241 | v 0.000000 -0.016699 -0.307698 242 | v 0.090542 -0.016509 -0.585611 243 | v 0.334714 -0.014657 -0.307864 244 | v 0.273910 -0.016699 -0.307698 245 | v 0.274137 -0.027665 -0.585662 246 | v 0.334714 -0.016699 0.001100 247 | v 0.182222 -0.016699 0.001100 248 | v 0.182293 -0.019676 -0.585619 249 | v 0.163890 -0.016699 0.511188 250 | v 0.000000 -0.016699 0.001100 251 | v 0.328523 -0.016699 0.306909 252 | v 0.000000 -0.016699 0.576765 253 | v 0.351345 -0.021745 -0.585737 254 | v 0.000000 -0.016699 -0.585611 255 | v 0.543331 0.190768 -0.499792 256 | v 0.493867 0.047695 -0.454268 257 | v 0.493867 0.044686 0.001084 258 | v 0.543331 0.187462 0.000477 259 | v 0.000000 0.153373 -0.383143 260 | v 0.000000 0.084548 -0.361244 261 | v 0.000000 0.081992 -0.179060 262 | v 0.000000 0.150674 -0.179352 263 | v 0.406127 0.187462 0.701272 264 | v 0.368982 0.044686 0.638960 265 | v 0.533306 0.187462 0.495638 266 | v 0.484742 0.044686 0.451788 267 | v 0.000000 0.147141 0.645210 268 | v 0.000000 0.085007 0.618092 269 | v 0.000000 0.147141 0.520976 270 | v 0.000000 0.085007 0.501893 271 | v 0.000000 -0.193098 -0.250000 272 | v 0.371149 -0.074850 0.475196 273 | v 0.441496 -0.012729 0.479767 274 | v 0.222689 -0.162811 -1.000000 275 | v 0.371149 -0.110831 -1.000000 276 | v 0.577447 0.102522 -1.000000 277 | v 0.074230 -0.185954 -1.000000 278 | v 0.503217 -0.015726 -1.000000 279 | v 0.162728 -0.164676 -0.174931 280 | v 0.296919 -0.141414 -0.217599 281 | v 0.371149 -0.105433 0.405071 282 | v 0.503218 -0.015726 0.378737 283 | v 0.236958 -0.143279 0.607470 284 | v 0.296919 -0.105433 0.634926 285 | v 0.561056 0.043398 -0.243934 286 | v 0.445379 -0.074850 -0.377329 287 | v 0.499335 0.046395 0.613162 288 | v 0.367266 -0.012729 0.709622 289 | v 0.088498 -0.171820 0.575069 290 | v 0.074230 -0.185954 -0.250000 291 | v 0.577447 0.102522 -0.291806 292 | v 0.577447 0.102522 0.464260 293 | v 0.222690 -0.157414 -0.217599 294 | v 0.503217 -0.015726 -0.377329 295 | v 0.371149 -0.110831 -0.377329 296 | v -0.593838 0.161646 -1.000000 297 | v -0.593838 0.239272 -1.000000 298 | v -0.593838 0.239272 -1.000000 299 | v -0.445378 0.259119 -1.000000 300 | v -0.445379 0.279894 0.733209 301 | v -0.582108 0.279894 0.512132 302 | v -0.593838 0.259931 -0.500000 303 | v 0.000000 -0.193098 -0.500000 304 | v 0.000000 -0.193098 -0.500000 305 | v 0.000000 -0.193098 0.000000 306 | v 0.000000 -0.074850 -1.000000 307 | v 0.000000 -0.074850 -1.000000 308 | v 0.000000 -0.074850 -1.000000 309 | v 0.000000 -0.074850 -1.000000 310 | v 0.020783 0.043398 -1.000000 311 | v -0.148459 0.279894 -1.000000 312 | v -0.296919 0.273737 -1.000000 313 | v -0.148460 0.279894 0.953894 314 | v -0.296920 0.279894 0.866480 315 | v -0.593838 0.279894 0.416388 316 | v -0.593838 0.279894 -0.000000 317 | v 0.000000 -0.193098 -1.000000 318 | v 0.000000 -0.193098 -1.000000 319 | v 0.000000 -0.193098 -1.000000 320 | v 0.000000 -0.148477 0.769592 321 | v 0.000000 -0.148477 0.769592 322 | v 0.000000 -0.148477 0.769592 323 | v 0.000000 -0.074850 0.840673 324 | v 0.000000 -0.074850 0.840673 325 | v 0.020783 0.279894 -1.000000 326 | v 0.000000 0.279894 1.000001 327 | v -0.222689 -0.162811 -1.000000 328 | v -0.296919 -0.146812 -1.000000 329 | v -0.371149 -0.110831 -1.000000 330 | v -0.445378 -0.074850 -1.000000 331 | v -0.577447 0.102522 -1.000000 332 | v -0.074230 -0.185954 -1.000000 333 | v -0.148459 -0.178811 -1.000000 334 | v -0.503217 -0.015726 -1.000000 335 | v -0.561056 0.043398 -1.000000 336 | v 0.000000 -0.193098 0.500000 337 | v 0.000000 -0.193098 0.500000 338 | v -0.445746 0.262137 -0.950076 339 | v -0.538033 0.242739 -0.900080 340 | v -0.406628 0.279894 0.700795 341 | v -0.533806 0.279894 0.495160 342 | v -0.543831 0.263237 -0.500269 343 | v -0.148472 0.280201 -0.949993 344 | v -0.297033 0.275073 -0.950005 345 | v -0.128087 0.279894 0.907858 346 | v -0.267236 0.279894 0.825926 347 | v -0.543831 0.279894 0.413336 348 | v -0.543831 0.279894 -0.000000 349 | v 0.000000 0.279894 -0.949992 350 | v 0.000000 0.279894 -0.949992 351 | v 0.000000 0.279894 0.932107 352 | v -0.530496 0.153373 -0.383143 353 | v -0.530496 0.153373 -0.383143 354 | v -0.506702 0.084548 -0.361244 355 | v -0.506702 0.084548 -0.361244 356 | v -0.506702 0.081992 -0.179060 357 | v -0.506702 0.081992 -0.179060 358 | v -0.530496 0.150674 -0.179352 359 | v -0.530496 0.150674 -0.179352 360 | v 0.000000 0.153373 -0.383143 361 | v 0.000000 0.153373 -0.383143 362 | v 0.000000 0.153373 -0.383143 363 | v 0.000000 0.153373 -0.383143 364 | v 0.000000 0.084548 -0.361244 365 | v 0.000000 0.084548 -0.361244 366 | v 0.000000 0.084548 -0.361244 367 | v 0.000000 0.084548 -0.361244 368 | v 0.000000 0.081992 -0.179060 369 | v 0.000000 0.081992 -0.179060 370 | v 0.000000 0.081992 -0.179060 371 | v 0.000000 0.081992 -0.179060 372 | v 0.000000 0.150674 -0.179352 373 | v 0.000000 0.150674 -0.179352 374 | v 0.000000 0.150674 -0.179352 375 | v 0.000000 0.150674 -0.179352 376 | v -0.419426 0.147141 0.645210 377 | v -0.419426 0.147141 0.645210 378 | v -0.403261 0.085007 0.618092 379 | v -0.403261 0.085007 0.618092 380 | v -0.496261 0.147141 0.520976 381 | v -0.475127 0.085007 0.501893 382 | v 0.000000 0.147141 0.645210 383 | v 0.000000 0.147141 0.645210 384 | v 0.000000 0.147141 0.645210 385 | v 0.000000 0.147141 0.645210 386 | v 0.000000 0.085007 0.618092 387 | v 0.000000 0.085007 0.618092 388 | v 0.000000 0.085007 0.618092 389 | v 0.000000 0.085007 0.618092 390 | v 0.000000 0.147141 0.520976 391 | v 0.000000 0.147141 0.520976 392 | v 0.000000 0.147141 0.520976 393 | v 0.000000 0.147141 0.520976 394 | v 0.000000 0.085007 0.501893 395 | v 0.000000 0.085007 0.501893 396 | v 0.000000 0.085007 0.501893 397 | v 0.000000 0.085007 0.501893 398 | v 0.593838 0.161646 -1.000000 399 | v 0.593838 0.239272 -1.000000 400 | v 0.593838 0.239272 -1.000000 401 | v 0.445378 0.259119 -1.000000 402 | v 0.445379 0.279894 0.733209 403 | v 0.582108 0.279894 0.512132 404 | v 0.593838 0.259931 -0.500000 405 | v -0.020783 0.043398 -1.000000 406 | v 0.148459 0.279894 -1.000000 407 | v 0.296919 0.273737 -1.000000 408 | v 0.148460 0.279894 0.953894 409 | v 0.296920 0.279894 0.866480 410 | v 0.593838 0.279894 0.416388 411 | v 0.593838 0.279894 -0.000000 412 | v -0.020783 0.279894 -1.000000 413 | v 0.222689 -0.162811 -1.000000 414 | v 0.296919 -0.146812 -1.000000 415 | v 0.371149 -0.110831 -1.000000 416 | v 0.445378 -0.074850 -1.000000 417 | v 0.577447 0.102522 -1.000000 418 | v 0.074230 -0.185954 -1.000000 419 | v 0.148459 -0.178811 -1.000000 420 | v 0.503217 -0.015726 -1.000000 421 | v 0.561056 0.043398 -1.000000 422 | v 0.445746 0.262137 -0.950076 423 | v 0.538033 0.242739 -0.900080 424 | v 0.406628 0.279894 0.700795 425 | v 0.533806 0.279894 0.495160 426 | v 0.543831 0.263237 -0.500269 427 | v 0.148472 0.280201 -0.949993 428 | v 0.297033 0.275073 -0.950005 429 | v 0.128087 0.279894 0.907858 430 | v 0.267236 0.279894 0.825926 431 | v 0.543831 0.279894 0.413336 432 | v 0.543831 0.279894 -0.000000 433 | v 0.530496 0.153373 -0.383143 434 | v 0.530496 0.153373 -0.383143 435 | v 0.506702 0.084548 -0.361244 436 | v 0.506702 0.084548 -0.361244 437 | v 0.506702 0.081992 -0.179060 438 | v 0.506702 0.081992 -0.179060 439 | v 0.530496 0.150674 -0.179352 440 | v 0.530496 0.150674 -0.179352 441 | v 0.419426 0.147141 0.645210 442 | v 0.419426 0.147141 0.645210 443 | v 0.403261 0.085007 0.618092 444 | v 0.403261 0.085007 0.618092 445 | v 0.496261 0.147141 0.520976 446 | v 0.475127 0.085007 0.501893 447 | vn -0.740600 -0.036000 0.670900 448 | vn -0.941500 -0.082400 0.326800 449 | vn -0.707100 -0.099600 0.700100 450 | vn -0.990800 -0.134800 0.000000 451 | vn -1.000000 0.000000 0.000000 452 | vn 0.000000 0.000000 -1.000000 453 | vn -0.991500 -0.121000 0.047900 454 | vn -0.332700 -0.663400 0.670200 455 | vn -0.466500 -0.411500 0.782900 456 | vn -0.494600 -0.710800 0.500100 457 | vn -0.386300 -0.289000 0.875900 458 | vn -0.570900 -0.204300 0.795200 459 | vn -0.540500 -0.247400 0.804100 460 | vn 0.984600 -0.173000 -0.021800 461 | vn 0.984900 -0.173100 0.000000 462 | vn 0.984700 -0.173100 -0.018800 463 | vn 0.984100 -0.172900 -0.040900 464 | vn 0.986400 -0.163500 0.017000 465 | vn 0.995400 -0.078700 0.055200 466 | vn 0.984800 -0.173100 0.013500 467 | vn -0.362700 -0.439400 0.821800 468 | vn 0.000000 -0.465300 0.885100 469 | vn 0.000000 -0.310400 0.950600 470 | vn -0.387400 -0.229400 0.892900 471 | vn 0.000000 -0.623500 0.781800 472 | vn -0.554100 -0.664400 0.501600 473 | vn -0.713200 -0.662000 0.230300 474 | vn -0.998100 0.000000 0.060900 475 | vn -0.654400 -0.455300 0.603700 476 | vn -0.216600 -0.849500 0.481100 477 | vn -0.161600 -0.982900 0.088100 478 | vn 0.000000 -0.998500 0.054600 479 | vn -0.973400 -0.221200 0.060000 480 | vn -0.779700 -0.560400 0.279100 481 | vn -0.155600 -0.987800 0.004000 482 | vn -0.170800 -0.985200 0.010700 483 | vn -0.210700 -0.977500 0.006700 484 | vn -0.866600 -0.499000 0.000000 485 | vn -0.714800 -0.699300 0.000000 486 | vn -0.323700 -0.946100 0.003600 487 | vn -0.414500 -0.910000 0.003100 488 | vn -0.436200 -0.899800 0.000000 489 | vn -0.255700 -0.946200 0.198300 490 | vn -0.592300 -0.738500 0.322100 491 | vn -0.481700 -0.857300 0.181500 492 | vn -0.551900 -0.824300 0.126000 493 | vn -0.679000 -0.593200 0.432400 494 | vn -0.565200 -0.823900 0.041200 495 | vn -0.610700 -0.780400 0.134400 496 | vn -0.241100 -0.932600 0.268400 497 | vn 0.843700 0.161100 0.512000 498 | vn 0.270400 0.300500 0.914600 499 | vn 0.223400 0.006200 0.974700 500 | vn -0.943400 0.000000 0.331500 501 | vn 0.943700 0.003400 -0.330600 502 | vn 0.982500 0.174700 -0.064000 503 | vn 0.998100 0.005100 -0.060700 504 | vn 0.985700 0.168700 0.000200 505 | vn 1.000000 0.005500 0.000000 506 | vn 0.000000 -0.004700 -1.000000 507 | vn 0.336800 0.244900 -0.909100 508 | vn 0.352600 -0.002900 -0.935800 509 | vn 0.570900 0.233200 -0.787200 510 | vn 0.591000 -0.000900 -0.806600 511 | vn 0.000000 0.005200 1.000000 512 | vn -0.004600 0.266700 0.963700 513 | vn 0.000000 0.272100 0.962200 514 | vn -0.000600 0.005200 1.000000 515 | vn -0.000100 0.005200 1.000000 516 | vn 0.984400 0.174300 0.024800 517 | vn 0.999600 0.017100 0.019800 518 | vn 0.825500 0.383300 0.414200 519 | vn 0.996400 -0.058700 0.060800 520 | vn 0.000000 -0.248800 0.968500 521 | vn 0.744500 0.218700 -0.630800 522 | vn 0.767400 0.000800 -0.641100 523 | vn 0.921700 0.196600 -0.334300 524 | vn -0.016800 0.263900 0.964400 525 | vn -0.177200 -0.984100 0.013800 526 | vn 0.000000 -1.000000 0.000000 527 | vn -0.095800 -0.995400 0.000000 528 | vn -0.963700 -0.267100 0.000000 529 | vn -0.303000 -0.952900 0.006600 530 | vn 0.011300 0.999800 -0.012500 531 | vn 0.184000 0.982900 0.001800 532 | vn 0.000000 1.000000 0.000000 533 | vn -0.053400 0.998400 -0.018900 534 | vn -0.136200 0.990200 -0.031300 535 | vn -0.085900 0.996200 0.013400 536 | vn -0.060600 0.997300 -0.042300 537 | vn -0.066100 0.996900 -0.042700 538 | vn 0.000000 1.000000 0.000300 539 | vn -0.019000 0.999800 -0.008300 540 | vn -0.067000 0.997400 -0.024700 541 | vn -0.065100 0.997400 -0.030300 542 | vn 0.000000 0.999800 -0.019900 543 | vn 0.080800 0.991100 -0.105600 544 | vn 0.048500 0.991800 -0.118500 545 | vn -0.172000 0.983200 -0.060700 546 | vn -0.002100 0.999800 -0.019700 547 | vn 0.170400 0.984200 -0.047500 548 | vn 0.116600 0.989000 -0.090800 549 | vn 0.000300 1.000000 -0.001000 550 | vn -0.019900 0.999800 -0.005500 551 | vn -0.001700 1.000000 -0.003100 552 | vn -0.053600 0.993200 0.103400 553 | vn -0.017400 0.996000 0.087400 554 | vn 0.204500 0.978800 -0.012200 555 | vn 0.000000 1.000000 0.000200 556 | vn -0.013600 0.994100 0.107700 557 | vn 0.000000 0.994000 0.108900 558 | vn 0.000000 0.992300 -0.123600 559 | vn 0.185000 0.982700 -0.007000 560 | vn 0.225300 0.778100 -0.586300 561 | vn 0.378500 0.770600 -0.512700 562 | vn -0.013800 0.809200 0.587300 563 | vn 0.001600 0.812900 0.582400 564 | vn -0.050800 0.803100 0.593600 565 | vn 0.000000 0.999900 0.013200 566 | vn 0.634600 0.753400 0.172200 567 | vn 0.510500 0.749200 -0.421900 568 | vn 0.200800 0.807500 0.554600 569 | vn 0.663800 0.712800 -0.226300 570 | vn 0.715400 0.697400 -0.042900 571 | vn 0.718600 0.695400 0.001900 572 | vn 0.000000 0.784100 -0.620600 573 | vn 0.738900 0.673800 0.001700 574 | vn 0.233600 0.971600 0.038200 575 | vn 0.943000 0.332700 0.002900 576 | vn 0.943400 0.331500 0.001600 577 | vn 0.946300 0.323300 0.001500 578 | vn 0.946700 0.321900 0.003100 579 | vn 0.000000 -0.303200 -0.952900 580 | vn 0.000000 -0.999900 -0.014000 581 | vn 0.000000 0.004200 1.000000 582 | vn 0.775300 0.411000 -0.479500 583 | vn 0.610800 0.414900 -0.674300 584 | vn 0.658300 0.416500 -0.627000 585 | vn 0.000000 -0.400000 0.916500 586 | vn 0.000000 0.293600 -0.955900 587 | vn -0.584200 -0.811600 0.000000 588 | vn -0.159900 -0.987100 0.006400 589 | vn -0.505300 -0.832000 0.228800 590 | vn 0.000000 0.250500 -0.968100 591 | vn 0.740600 -0.036000 0.670900 592 | vn 0.707100 -0.099600 0.700100 593 | vn 0.941500 -0.082400 0.326800 594 | vn 0.990800 -0.134800 0.000000 595 | vn 1.000000 0.000000 0.000000 596 | vn 0.991500 -0.121000 0.047900 597 | vn 0.332700 -0.663400 0.670200 598 | vn 0.494600 -0.710800 0.500100 599 | vn 0.466500 -0.411500 0.782900 600 | vn 0.386300 -0.289000 0.875900 601 | vn 0.540500 -0.247400 0.804100 602 | vn 0.570900 -0.204300 0.795200 603 | vn -0.984600 -0.173000 -0.021800 604 | vn -0.984700 -0.173100 -0.018800 605 | vn -0.984900 -0.173100 0.000000 606 | vn -0.984100 -0.172900 -0.040900 607 | vn -0.986400 -0.163500 0.017000 608 | vn -0.984800 -0.173100 0.013500 609 | vn -0.995400 -0.078700 0.055200 610 | vn 0.362700 -0.439400 0.821800 611 | vn 0.387400 -0.229400 0.892900 612 | vn 0.713200 -0.662000 0.230300 613 | vn 0.554100 -0.664400 0.501600 614 | vn 0.998100 0.000000 0.060900 615 | vn 0.654400 -0.455300 0.603700 616 | vn 0.216600 -0.849500 0.481100 617 | vn 0.161600 -0.982900 0.088100 618 | vn 0.779700 -0.560400 0.279100 619 | vn 0.973400 -0.221200 0.060000 620 | vn 0.155600 -0.987800 0.004000 621 | vn 0.210700 -0.977500 0.006700 622 | vn 0.170800 -0.985200 0.010700 623 | vn 0.866600 -0.499000 0.000000 624 | vn 0.714800 -0.699300 0.000000 625 | vn 0.323700 -0.946100 0.003600 626 | vn 0.436200 -0.899800 0.000000 627 | vn 0.414500 -0.910000 0.003100 628 | vn 0.255700 -0.946200 0.198300 629 | vn 0.481700 -0.857300 0.181500 630 | vn 0.592300 -0.738500 0.322100 631 | vn 0.679000 -0.593200 0.432400 632 | vn 0.551900 -0.824300 0.126000 633 | vn 0.565200 -0.823900 0.041200 634 | vn 0.610700 -0.780400 0.134400 635 | vn 0.241100 -0.932600 0.268400 636 | vn -0.843700 0.161100 0.512000 637 | vn -0.223400 0.006200 0.974700 638 | vn -0.270400 0.300500 0.914600 639 | vn 0.943400 0.000000 0.331500 640 | vn -0.943700 0.003400 -0.330600 641 | vn -0.998100 0.005100 -0.060700 642 | vn -0.982500 0.174700 -0.064000 643 | vn -1.000000 0.005500 0.000000 644 | vn -0.985700 0.168700 0.000200 645 | vn -0.352600 -0.002900 -0.935800 646 | vn -0.336800 0.244900 -0.909100 647 | vn -0.591000 -0.000900 -0.806600 648 | vn -0.570900 0.233200 -0.787200 649 | vn 0.004600 0.266700 0.963700 650 | vn 0.000600 0.005200 1.000000 651 | vn 0.000100 0.005200 1.000000 652 | vn -0.999600 0.017100 0.019800 653 | vn -0.984400 0.174300 0.024800 654 | vn -0.825500 0.383300 0.414200 655 | vn -0.996400 -0.058700 0.060800 656 | vn -0.767400 0.000800 -0.641100 657 | vn -0.744500 0.218700 -0.630800 658 | vn -0.921700 0.196600 -0.334300 659 | vn 0.016800 0.263900 0.964400 660 | vn 0.177200 -0.984100 0.013800 661 | vn 0.095800 -0.995400 0.000000 662 | vn 0.963700 -0.267100 0.000000 663 | vn 0.303000 -0.952900 0.006600 664 | vn -0.011300 0.999800 -0.012500 665 | vn -0.184000 0.982900 0.001800 666 | vn 0.053400 0.998400 -0.018900 667 | vn 0.085900 0.996200 0.013400 668 | vn 0.136200 0.990200 -0.031300 669 | vn 0.066100 0.996900 -0.042700 670 | vn 0.060600 0.997300 -0.042300 671 | vn 0.019000 0.999800 -0.008300 672 | vn 0.065100 0.997400 -0.030300 673 | vn 0.067000 0.997400 -0.024700 674 | vn -0.048500 0.991800 -0.118500 675 | vn -0.080800 0.991100 -0.105600 676 | vn 0.172000 0.983200 -0.060700 677 | vn 0.002100 0.999800 -0.019700 678 | vn -0.116600 0.989000 -0.090800 679 | vn -0.170400 0.984200 -0.047500 680 | vn -0.000300 1.000000 -0.001000 681 | vn 0.019900 0.999800 -0.005500 682 | vn 0.001700 1.000000 -0.003100 683 | vn 0.053600 0.993200 0.103400 684 | vn 0.017400 0.996000 0.087400 685 | vn -0.204500 0.978800 -0.012200 686 | vn 0.013600 0.994100 0.107700 687 | vn -0.185000 0.982700 -0.007000 688 | vn -0.378500 0.770600 -0.512700 689 | vn -0.225300 0.778100 -0.586300 690 | vn -0.001600 0.812900 0.582400 691 | vn 0.013800 0.809200 0.587300 692 | vn 0.050800 0.803100 0.593600 693 | vn -0.634600 0.753400 0.172200 694 | vn -0.510500 0.749200 -0.421900 695 | vn -0.200800 0.807500 0.554600 696 | vn -0.715400 0.697400 -0.042900 697 | vn -0.663800 0.712800 -0.226300 698 | vn -0.718600 0.695400 0.001900 699 | vn -0.738900 0.673800 0.001700 700 | vn -0.233600 0.971600 0.038200 701 | vn -0.943000 0.332700 0.002900 702 | vn -0.943400 0.331500 0.001600 703 | vn -0.946300 0.323300 0.001500 704 | vn -0.946700 0.321900 0.003100 705 | vn -0.775300 0.411000 -0.479500 706 | vn -0.610800 0.414900 -0.674300 707 | vn -0.658300 0.416500 -0.627000 708 | vn 0.584200 -0.811600 0.000000 709 | vn 0.159900 -0.987100 0.006400 710 | vn 0.505300 -0.832000 0.228800 711 | usemtl Material 712 | s 1 713 | f 296//1 11//2 40//3 714 | f 39//4 293//5 292//4 715 | f 37//6 327//6 12//6 716 | f 20//6 21//6 36//6 717 | f 35//6 324//6 22//6 718 | f 312//5 39//4 47//7 719 | f 33//8 8//9 25//10 720 | f 32//11 310//12 26//13 721 | f 30//14 301//15 299//16 722 | f 306//17 30//14 302//17 723 | f 31//18 318//19 333//20 724 | f 27//15 333//20 301//15 725 | f 24//21 26//13 8//9 726 | f 140//22 32//11 24//21 727 | f 148//23 309//24 32//11 728 | f 319//25 24//21 33//8 729 | f 25//10 124//26 108//27 730 | f 11//2 311//28 47//7 731 | f 35//6 7//6 20//6 732 | f 304//6 20//6 3//6 733 | f 304//6 329//6 35//6 734 | f 36//6 308//6 307//6 735 | f 10//6 307//6 321//6 736 | f 3//6 36//6 10//6 737 | f 37//6 294//6 295//6 738 | f 21//6 295//6 308//6 739 | f 21//6 19//6 37//6 740 | f 19//6 330//6 331//6 741 | f 22//6 19//6 7//6 742 | f 22//6 325//6 326//6 743 | f 8//9 40//3 23//29 744 | f 26//13 296//1 40//3 745 | f 317//30 125//31 189//32 746 | f 11//2 128//33 45//34 747 | f 41//35 129//36 110//37 748 | f 43//38 130//39 121//38 749 | f 42//40 131//41 111//42 750 | f 119//43 120//44 46//45 751 | f 45//34 118//46 123//47 752 | f 40//3 123//47 23//29 753 | f 34//48 117//49 108//27 754 | f 40//3 11//2 45//34 755 | f 317//30 33//8 44//50 756 | f 8//9 124//26 25//10 757 | f 335//51 68//52 334//53 758 | f 296//1 297//54 11//2 759 | f 39//4 298//5 293//5 760 | f 37//6 19//6 331//6 761 | f 20//6 7//6 21//6 762 | f 35//6 323//6 324//6 763 | f 47//7 311//28 312//5 764 | f 33//8 24//21 8//9 765 | f 32//11 309//24 310//12 766 | f 337//55 76//56 343//57 767 | f 343//57 103//58 344//59 768 | f 347//60 73//61 341//62 769 | f 341//62 61//63 342//64 770 | f 345//65 71//66 224//67 771 | f 340//68 71//66 339//69 772 | f 344//59 100//70 338//71 773 | f 338//71 78//72 335//51 774 | f 30//14 27//15 301//15 775 | f 302//17 30//14 299//16 776 | f 31//18 156//73 318//19 777 | f 27//15 31//18 333//20 778 | f 24//21 32//11 26//13 779 | f 140//22 148//23 32//11 780 | f 148//23 322//74 309//24 781 | f 319//25 140//22 24//21 782 | f 11//2 297//54 311//28 783 | f 35//6 22//6 7//6 784 | f 304//6 35//6 20//6 785 | f 304//6 328//6 329//6 786 | f 36//6 21//6 308//6 787 | f 10//6 36//6 307//6 788 | f 3//6 20//6 36//6 789 | f 37//6 12//6 294//6 790 | f 21//6 37//6 295//6 791 | f 21//6 7//6 19//6 792 | f 22//6 326//6 19//6 793 | f 312//5 298//5 39//4 794 | f 8//9 26//13 40//3 795 | f 26//13 310//12 296//1 796 | f 342//64 104//75 336//76 797 | f 336//76 106//77 337//55 798 | f 334//53 65//78 340//68 799 | f 44//50 115//79 125//31 800 | f 313//80 267//80 113//81 801 | f 47//7 39//4 127//82 802 | f 43//38 121//38 127//82 803 | f 41//35 115//79 129//36 804 | f 43//38 114//39 130//39 805 | f 42//40 116//83 131//41 806 | f 44//50 33//8 25//10 807 | f 317//30 319//25 33//8 808 | f 92//84 94//85 67//86 809 | f 2//87 48//88 49//89 810 | f 1//86 50//86 14//86 811 | f 2//87 52//90 15//91 812 | f 89//92 95//86 62//86 813 | f 16//93 54//94 4//95 814 | f 17//86 56//86 55//86 815 | f 18//86 58//96 57//86 816 | f 63//86 97//97 60//98 817 | f 4//95 48//88 13//99 818 | f 14//86 56//86 5//86 819 | f 6//100 52//90 58//96 820 | f 72//86 98//101 64//102 821 | f 346//103 53//104 16//93 822 | f 135//86 55//86 190//86 823 | f 1//86 57//86 51//86 824 | f 88//105 67//86 95//86 825 | f 96//106 92//84 88//105 826 | f 93//107 91//108 92//84 827 | f 237//109 62//86 246//86 828 | f 90//110 237//109 250//111 829 | f 90//110 88//105 89//92 830 | f 206//86 60//98 248//112 831 | f 246//86 63//86 206//86 832 | f 62//86 70//86 63//86 833 | f 70//86 64//102 97//97 834 | f 95//86 72//86 70//86 835 | f 67//86 74//113 72//86 836 | f 61//63 82//114 29//115 837 | f 224//67 81//116 87//117 838 | f 71//66 28//118 81//116 839 | f 349//119 371//119 358//119 840 | f 100//70 86//120 78//72 841 | f 104//75 29//115 105//121 842 | f 373//86 388//86 77//86 843 | f 65//78 79//122 28//118 844 | f 78//72 79//122 68//52 845 | f 76//56 107//123 84//124 846 | f 103//58 84//124 102//125 847 | f 73//61 232//126 82//114 848 | f 102//125 91//108 101//127 849 | f 101//127 99//128 86//120 850 | f 105//121 97//97 64//102 851 | f 107//123 64//102 98//101 852 | f 28//118 93//107 96//106 853 | f 79//122 99//128 93//107 854 | f 84//124 98//101 74//113 855 | f 102//125 74//113 94//85 856 | f 82//114 248//112 60//98 857 | f 29//115 60//98 97//97 858 | f 87//117 90//110 250//111 859 | f 81//116 96//106 90//110 860 | f 72//86 74//113 98//101 861 | f 63//86 70//86 97//97 862 | f 89//92 88//105 95//86 863 | f 92//84 91//108 94//85 864 | f 88//105 92//84 67//86 865 | f 96//106 93//107 92//84 866 | f 93//107 99//128 91//108 867 | f 237//109 89//92 62//86 868 | f 90//110 89//92 237//109 869 | f 90//110 96//106 88//105 870 | f 206//86 63//86 60//98 871 | f 246//86 62//86 63//86 872 | f 62//86 95//86 70//86 873 | f 70//86 72//86 64//102 874 | f 95//86 67//86 72//86 875 | f 67//86 94//85 74//113 876 | f 348//129 101//127 100//70 877 | f 353//130 103//58 102//125 878 | f 66//131 100//70 103//58 879 | f 353//130 101//127 350//132 880 | f 351//133 359//133 361//133 881 | f 80//134 365//134 9//134 882 | f 352//135 370//135 354//135 883 | f 372//136 105//121 374//136 884 | f 377//137 106//77 376//138 885 | f 372//136 106//77 104//75 886 | f 377//137 105//121 107//123 887 | f 83//139 380//139 75//139 888 | f 375//80 392//80 383//80 889 | f 377//137 389//140 391//140 890 | f 131//41 117//49 34//48 891 | f 131//41 116//83 117//49 892 | f 116//83 46//45 117//49 893 | f 130//39 122//141 34//48 894 | f 130//39 114//39 122//141 895 | f 114//39 38//141 122//141 896 | f 129//36 119//43 46//45 897 | f 129//36 115//79 119//43 898 | f 115//79 44//50 119//43 899 | f 127//82 128//33 47//7 900 | f 127//82 121//38 128//33 901 | f 121//38 45//34 128//33 902 | f 127//82 112//82 43//38 903 | f 127//82 39//4 112//82 904 | f 39//4 292//4 112//82 905 | f 113//81 126//142 41//35 906 | f 113//81 267//80 126//142 907 | f 267//80 189//32 126//142 908 | f 125//31 126//142 189//32 909 | f 125//31 115//79 126//142 910 | f 115//79 41//35 126//142 911 | f 108//27 120//44 25//10 912 | f 108//27 117//49 120//44 913 | f 117//49 46//45 120//44 914 | f 123//47 109//143 23//29 915 | f 123//47 118//46 109//143 916 | f 118//46 34//48 109//143 917 | f 44//50 120//44 119//43 918 | f 111//42 122//141 38//141 919 | f 111//42 131//41 122//141 920 | f 131//41 34//48 122//141 921 | f 121//38 118//46 45//34 922 | f 121//38 130//39 118//46 923 | f 130//39 34//48 118//46 924 | f 110//37 116//83 42//40 925 | f 110//37 129//36 116//83 926 | f 129//36 46//45 116//83 927 | f 108//27 109//143 34//48 928 | f 108//27 124//26 109//143 929 | f 124//26 23//29 109//143 930 | f 37//6 331//6 327//6 931 | f 19//6 326//6 330//6 932 | f 22//6 324//6 325//6 933 | f 317//30 44//50 125//31 934 | f 11//2 47//7 128//33 935 | f 40//3 45//34 123//47 936 | f 8//9 23//29 124//26 937 | f 335//51 78//72 68//52 938 | f 35//6 329//6 323//6 939 | f 337//55 106//77 76//56 940 | f 343//57 76//56 103//58 941 | f 347//60 222//144 73//61 942 | f 341//62 73//61 61//63 943 | f 345//65 339//69 71//66 944 | f 340//68 65//78 71//66 945 | f 344//59 103//58 100//70 946 | f 338//71 100//70 78//72 947 | f 304//6 132//6 328//6 948 | f 342//64 61//63 104//75 949 | f 336//76 104//75 106//77 950 | f 334//53 68//52 65//78 951 | f 2//87 13//99 48//88 952 | f 1//86 51//86 50//86 953 | f 2//87 49//89 52//90 954 | f 16//93 53//104 54//94 955 | f 17//86 5//86 56//86 956 | f 18//86 6//100 58//96 957 | f 4//95 54//94 48//88 958 | f 14//86 50//86 56//86 959 | f 6//100 15//91 52//90 960 | f 346//103 59//86 53//104 961 | f 135//86 17//86 55//86 962 | f 1//86 18//86 57//86 963 | f 61//63 73//61 82//114 964 | f 224//67 71//66 81//116 965 | f 71//66 65//78 28//118 966 | f 349//119 355//119 371//119 967 | f 100//70 101//127 86//120 968 | f 104//75 61//63 29//115 969 | f 373//86 263//86 388//86 970 | f 65//78 68//52 79//122 971 | f 78//72 86//120 79//122 972 | f 76//56 106//77 107//123 973 | f 103//58 76//56 84//124 974 | f 73//61 222//144 232//126 975 | f 102//125 94//85 91//108 976 | f 101//127 91//108 99//128 977 | f 105//121 29//115 97//97 978 | f 107//123 105//121 64//102 979 | f 28//118 79//122 93//107 980 | f 79//122 86//120 99//128 981 | f 84//124 107//123 98//101 982 | f 102//125 84//124 74//113 983 | f 82//114 232//126 248//112 984 | f 29//115 82//114 60//98 985 | f 87//117 81//116 90//110 986 | f 81//116 28//118 96//106 987 | f 348//129 350//132 101//127 988 | f 353//130 66//131 103//58 989 | f 66//131 348//129 100//70 990 | f 353//130 102//125 101//127 991 | f 351//133 69//133 359//133 992 | f 80//134 363//134 365//134 993 | f 352//135 367//135 370//135 994 | f 372//136 104//75 105//121 995 | f 377//137 107//123 106//77 996 | f 372//136 376//138 106//77 997 | f 377//137 374//136 105//121 998 | f 83//139 385//139 380//139 999 | f 375//80 85//80 392//80 1000 | f 377//137 376//138 389//140 1001 | f 44//50 25//10 120//44 1002 | f 398//145 181//146 149//147 1003 | f 180//148 394//148 395//149 1004 | f 178//6 150//6 413//6 1005 | f 161//6 177//6 162//6 1006 | f 176//6 163//6 410//6 1007 | f 407//149 188//150 180//148 1008 | f 174//151 166//152 145//153 1009 | f 173//154 167//155 405//156 1010 | f 171//157 154//158 138//159 1011 | f 303//5 314//5 300//5 1012 | f 401//160 305//160 171//157 1013 | f 172//161 332//162 316//163 1014 | f 168//159 138//159 332//162 1015 | f 165//164 145//153 167//155 1016 | f 140//22 165//164 173//154 1017 | f 148//23 173//154 404//165 1018 | f 319//25 174//151 165//164 1019 | f 166//152 268//166 284//167 1020 | f 149//147 188//150 406//168 1021 | f 176//6 161//6 144//6 1022 | f 155//6 139//6 161//6 1023 | f 155//6 176//6 415//6 1024 | f 177//6 402//6 403//6 1025 | f 147//6 408//6 402//6 1026 | f 139//6 147//6 177//6 1027 | f 178//6 397//6 396//6 1028 | f 162//6 403//6 397//6 1029 | f 162//6 178//6 160//6 1030 | f 160//6 417//6 416//6 1031 | f 163//6 144//6 160//6 1032 | f 163//6 412//6 411//6 1033 | f 145//153 164//169 181//146 1034 | f 167//155 181//146 398//145 1035 | f 133//170 189//32 285//171 1036 | f 149//147 186//172 288//173 1037 | f 182//174 270//175 289//176 1038 | f 184//177 281//177 290//178 1039 | f 183//179 271//180 291//181 1040 | f 279//182 187//183 280//184 1041 | f 186//172 283//185 278//186 1042 | f 181//146 164//169 283//185 1043 | f 175//187 268//166 277//188 1044 | f 181//146 186//172 149//147 1045 | f 133//170 185//189 174//151 1046 | f 145//153 166//152 284//167 1047 | f 419//190 418//191 212//192 1048 | f 398//145 149//147 399//193 1049 | f 180//148 395//149 400//149 1050 | f 178//6 417//6 160//6 1051 | f 161//6 162//6 144//6 1052 | f 176//6 410//6 409//6 1053 | f 188//150 407//149 406//168 1054 | f 174//151 145//153 165//164 1055 | f 173//154 405//156 404//165 1056 | f 421//194 427//195 220//196 1057 | f 427//195 428//197 254//198 1058 | f 347//60 425//199 217//200 1059 | f 425//199 426//201 204//202 1060 | f 345//65 224//67 215//203 1061 | f 424//204 423//205 215//203 1062 | f 428//197 422//206 251//207 1063 | f 422//206 419//190 223//208 1064 | f 171//157 138//159 168//159 1065 | f 305//160 154//158 171//157 1066 | f 172//161 316//163 320//209 1067 | f 168//159 332//162 172//161 1068 | f 165//164 167//155 173//154 1069 | f 140//22 173//154 148//23 1070 | f 148//23 404//165 322//74 1071 | f 319//25 165//164 140//22 1072 | f 149//147 406//168 399//193 1073 | f 176//6 144//6 163//6 1074 | f 155//6 161//6 176//6 1075 | f 155//6 415//6 414//6 1076 | f 177//6 403//6 162//6 1077 | f 147//6 402//6 177//6 1078 | f 139//6 177//6 161//6 1079 | f 178//6 396//6 150//6 1080 | f 162//6 397//6 178//6 1081 | f 162//6 160//6 144//6 1082 | f 163//6 160//6 412//6 1083 | f 407//149 180//148 400//149 1084 | f 145//153 181//146 167//155 1085 | f 167//155 398//145 405//156 1086 | f 426//201 420//210 259//211 1087 | f 420//210 421//194 261//212 1088 | f 418//191 424//204 209//213 1089 | f 185//189 285//171 275//214 1090 | f 313//80 273//215 267//80 1091 | f 188//150 287//216 180//148 1092 | f 184//177 287//216 281//177 1093 | f 182//174 289//176 275//214 1094 | f 184//177 290//178 274//178 1095 | f 183//179 291//181 276//217 1096 | f 185//189 166//152 174//151 1097 | f 133//170 174//151 319//25 1098 | f 240//218 211//86 242//219 1099 | f 137//220 192//221 191//222 1100 | f 136//86 152//86 193//86 1101 | f 137//220 153//223 195//224 1102 | f 236//92 205//86 243//86 1103 | f 157//225 141//226 197//227 1104 | f 158//86 198//86 199//86 1105 | f 159//86 200//86 201//96 1106 | f 207//86 203//228 245//229 1107 | f 141//226 151//230 191//222 1108 | f 152//86 142//86 199//86 1109 | f 143//231 201//96 195//224 1110 | f 216//86 208//232 247//233 1111 | f 134//234 157//225 196//235 1112 | f 135//86 190//86 198//86 1113 | f 136//86 194//86 200//86 1114 | f 235//236 243//86 211//86 1115 | f 244//237 235//236 240//218 1116 | f 241//238 240//218 239//239 1117 | f 237//109 246//86 205//86 1118 | f 238//240 250//111 237//109 1119 | f 238//240 236//92 235//236 1120 | f 206//86 248//112 203//228 1121 | f 246//86 206//86 207//86 1122 | f 205//86 207//86 214//86 1123 | f 214//86 245//229 208//232 1124 | f 243//86 214//86 216//86 1125 | f 211//86 216//86 218//241 1126 | f 204//202 170//242 228//243 1127 | f 224//67 234//244 227//245 1128 | f 215//203 227//245 169//246 1129 | f 430//119 357//119 369//119 1130 | f 251//207 223//208 233//247 1131 | f 259//211 260//248 170//242 1132 | f 438//86 221//86 387//86 1133 | f 209//213 169//246 225//249 1134 | f 223//208 212//192 225//249 1135 | f 220//196 230//250 262//251 1136 | f 254//198 253//252 230//250 1137 | f 217//200 228//243 232//126 1138 | f 253//252 252//253 239//239 1139 | f 252//253 233//247 249//254 1140 | f 260//248 208//232 245//229 1141 | f 262//251 247//233 208//232 1142 | f 169//246 244//237 241//238 1143 | f 225//249 241//238 249//254 1144 | f 230//250 218//241 247//233 1145 | f 253//252 242//219 218//241 1146 | f 228//243 203//228 248//112 1147 | f 170//242 245//229 203//228 1148 | f 234//244 250//111 238//240 1149 | f 227//245 238//240 244//237 1150 | f 216//86 247//233 218//241 1151 | f 207//86 245//229 214//86 1152 | f 236//92 243//86 235//236 1153 | f 240//218 242//219 239//239 1154 | f 235//236 211//86 240//218 1155 | f 244//237 240//218 241//238 1156 | f 241//238 239//239 249//254 1157 | f 237//109 205//86 236//92 1158 | f 238//240 237//109 236//92 1159 | f 238//240 235//236 244//237 1160 | f 206//86 203//228 207//86 1161 | f 246//86 207//86 205//86 1162 | f 205//86 214//86 243//86 1163 | f 214//86 208//232 216//86 1164 | f 243//86 216//86 211//86 1165 | f 211//86 218//241 242//219 1166 | f 429//255 251//207 252//253 1167 | f 434//256 253//252 254//198 1168 | f 210//257 254//198 251//207 1169 | f 434//256 431//258 252//253 1170 | f 368//5 356//5 360//5 1171 | f 432//133 362//133 255//133 1172 | f 226//134 146//134 366//134 1173 | f 433//135 435//135 258//135 1174 | f 437//259 439//259 260//248 1175 | f 442//260 441//261 261//212 1176 | f 437//259 259//211 261//212 1177 | f 442//260 262//251 260//248 1178 | f 386//5 390//5 382//5 1179 | f 229//139 219//139 381//139 1180 | f 440//80 384//80 266//80 1181 | f 442//260 393//140 265//140 1182 | f 291//181 175//187 277//188 1183 | f 291//181 277//188 276//217 1184 | f 276//217 277//188 187//183 1185 | f 290//178 175//187 282//262 1186 | f 290//178 282//262 274//178 1187 | f 274//178 282//262 179//262 1188 | f 289//176 187//183 279//182 1189 | f 289//176 279//182 275//214 1190 | f 275//214 279//182 185//189 1191 | f 287//216 188//150 288//173 1192 | f 287//216 288//173 281//177 1193 | f 281//177 288//173 186//172 1194 | f 287//216 184//177 272//216 1195 | f 287//216 272//216 180//148 1196 | f 180//148 272//216 394//148 1197 | f 273//215 182//174 286//263 1198 | f 273//215 286//263 267//80 1199 | f 267//80 286//263 189//32 1200 | f 285//171 189//32 286//263 1201 | f 285//171 286//263 275//214 1202 | f 275//214 286//263 182//174 1203 | f 268//166 166//152 280//184 1204 | f 268//166 280//184 277//188 1205 | f 277//188 280//184 187//183 1206 | f 283//185 164//169 269//264 1207 | f 283//185 269//264 278//186 1208 | f 278//186 269//264 175//187 1209 | f 185//189 279//182 280//184 1210 | f 271//180 179//262 282//262 1211 | f 271//180 282//262 291//181 1212 | f 291//181 282//262 175//187 1213 | f 281//177 186//172 278//186 1214 | f 281//177 278//186 290//178 1215 | f 290//178 278//186 175//187 1216 | f 270//175 183//179 276//217 1217 | f 270//175 276//217 289//176 1218 | f 289//176 276//217 187//183 1219 | f 268//166 175//187 269//264 1220 | f 268//166 269//264 284//167 1221 | f 284//167 269//264 164//169 1222 | f 178//6 413//6 417//6 1223 | f 160//6 416//6 412//6 1224 | f 163//6 411//6 410//6 1225 | f 133//170 285//171 185//189 1226 | f 149//147 288//173 188//150 1227 | f 181//146 283//185 186//172 1228 | f 145//153 284//167 164//169 1229 | f 419//190 212//192 223//208 1230 | f 176//6 409//6 415//6 1231 | f 421//194 220//196 261//212 1232 | f 427//195 254//198 220//196 1233 | f 347//60 217//200 222//144 1234 | f 425//199 204//202 217//200 1235 | f 345//65 215//203 423//205 1236 | f 424//204 215//203 209//213 1237 | f 428//197 251//207 254//198 1238 | f 422//206 223//208 251//207 1239 | f 155//6 414//6 315//6 1240 | f 426//201 259//211 204//202 1241 | f 420//210 261//212 259//211 1242 | f 418//191 209//213 212//192 1243 | f 137//220 191//222 151//230 1244 | f 136//86 193//86 194//86 1245 | f 137//220 195//224 192//221 1246 | f 157//225 197//227 196//235 1247 | f 158//86 199//86 142//86 1248 | f 159//86 201//96 143//231 1249 | f 141//226 191//222 197//227 1250 | f 152//86 199//86 193//86 1251 | f 143//231 195//224 153//223 1252 | f 134//234 196//235 202//86 1253 | f 135//86 198//86 158//86 1254 | f 136//86 200//86 159//86 1255 | f 204//202 228//243 217//200 1256 | f 224//67 227//245 215//203 1257 | f 215//203 169//246 209//213 1258 | f 430//119 369//119 436//119 1259 | f 251//207 233//247 252//253 1260 | f 259//211 170//242 204//202 1261 | f 438//86 387//86 379//86 1262 | f 209//213 225//249 212//192 1263 | f 223//208 225//249 233//247 1264 | f 220//196 262//251 261//212 1265 | f 254//198 230//250 220//196 1266 | f 217//200 232//126 222//144 1267 | f 253//252 239//239 242//219 1268 | f 252//253 249//254 239//239 1269 | f 260//248 245//229 170//242 1270 | f 262//251 208//232 260//248 1271 | f 169//246 241//238 225//249 1272 | f 225//249 249//254 233//247 1273 | f 230//250 247//233 262//251 1274 | f 253//252 218//241 230//250 1275 | f 228//243 248//112 232//126 1276 | f 170//242 203//228 228//243 1277 | f 234//244 238//240 227//245 1278 | f 227//245 244//237 169//246 1279 | f 429//255 252//253 431//258 1280 | f 434//256 254//198 210//257 1281 | f 210//257 251//207 429//255 1282 | f 434//256 252//253 253//252 1283 | f 368//5 360//5 364//5 1284 | f 432//133 255//133 213//133 1285 | f 226//134 366//134 256//134 1286 | f 433//135 258//135 257//135 1287 | f 437//259 260//248 259//211 1288 | f 442//260 261//212 262//251 1289 | f 437//259 261//212 441//261 1290 | f 442//260 260//248 439//259 1291 | f 386//5 382//5 378//5 1292 | f 229//139 381//139 264//139 1293 | f 440//80 266//80 231//80 1294 | f 442//260 265//140 441//261 1295 | f 185//189 280//184 166//152 1296 | --------------------------------------------------------------------------------