|
|
- #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
- // BUG: The rendering behaves improperly, I don't know why
-
- 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<render_point(vec3&)>;
- using material_t = gp::function<color_t(vec3&)>;
-
-
- /**
- * @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<sdf_t, 4096> scene_elements;
- gp::indexed_array<material_t, 4096> 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<char> allocation_buffer)
- : allocator(allocation_buffer.begin().data, allocation_buffer.size())
- , sky_box{gp::reference_wrapper<gp::buddy<>>{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<g_t>,gp::math::pi<g_t>};
-
- // 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);
- }
- };
- }
- }
- }
|