#pragma once #include "gp_config.hpp" #include "gp/algorithms/min_of.hpp" #include "gp/utils/allocators/buddy.hpp" #include "gp/functional/function.hpp" #include "gp/containers/indexed_array.hpp" #include "gp/math.hpp" #include "gp/math/rendering_math.hpp" // TODO: Namespace this correctly namespace gp { namespace math { namespace rendering { using vec2 = gp::math::vec2_g<>; using vec3 = gp::math::vec3_g<>; using vec4 = gp::math::vec4_g<>; struct camera{ vec3 position; vec3 normal; }; using index_t = size_t; using distance_t = gp_config::rendering::default_type; using color_t = GP_CONFIG__RENDERING__COLOR_T; struct render_point{ distance_t distance; index_t material; bool operator<(const render_point& rhs) { return distance < rhs.distance; } }; using sdf_t = gp::function; using material_t = gp::function; /** * @brief A pure ray-marching renderer. Prints pixels on order. */ class renderer { using g_t = gp_config::rendering::default_type; constexpr static auto epsilon = gp_config::rendering::epsilon; public: gp::indexed_array scene_elements; gp::indexed_array materials; gp::buddy<> allocator; material_t sky_box; vec2 _resolution{128,64}; camera _camera{{0, 0, -5}, {0, 0, 0}}; vec2 _fov{90, 45}; distance_t projection_start = 1; distance_t projection_end = 50; size_t passes = 12; renderer(gp::buffer allocation_buffer) : allocator(allocation_buffer.begin().data, allocation_buffer.size()) , sky_box{gp::reference_wrapper>{allocator}} {} render_point sdf(vec3& render_target) { return gp::min_of( scene_elements.begin(), scene_elements.end(), [&](auto& p){ return p(render_target); } ); } auto& get_allocator() { return allocator; } color_t render(vec2 pixel) { g_t depth = projection_start; vec3 target = _camera.normal; auto half_res = _resolution/vec2{2.0, 2.0}; pixel = pixel - half_res; pixel = pixel / half_res; pixel = pixel * _fov; pixel = pixel / vec2{180, 180} * vec2{gp::math::pi,gp::math::pi}; // Y-rot (adjusts x) target = vec3{ target.x*gp::math::cos(pixel.x) + target.z*gp::math::sin(pixel.x), target.y, -target.x*gp::math::sin(pixel.x)+target.z*gp::math::cos(pixel.x) }; // X-rot (adjusts y) target = vec3{ target.x, target.y*gp::math::cos(pixel.y) - target.z*gp::math::sin(pixel.y), target.y*gp::math::sin(pixel.y) + target.z*gp::math::cos(pixel.y) }; vec3 render_target{_camera.position}; for(int i = 0; i < passes; ++i) { render_point distance = sdf(render_target); if(distance.distance < epsilon) { return materials[distance.material](render_target); } depth += distance.distance; if(depth >= projection_end) { break; } render_target = _camera.position+depth*target; } return sky_box(render_target); } }; } } }