diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7642978..8bf7f0b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(histogram) add_subdirectory(texture2d) +add_subdirectory(point-cloud) \ No newline at end of file diff --git a/examples/common/argument_parsing.hpp b/examples/common/argument_parsing.hpp index 525c524..059d790 100644 --- a/examples/common/argument_parsing.hpp +++ b/examples/common/argument_parsing.hpp @@ -1,5 +1,3 @@ -#include - #include #include #include diff --git a/examples/point-cloud/CMakeLists.txt b/examples/point-cloud/CMakeLists.txt new file mode 100644 index 0000000..15e043f --- /dev/null +++ b/examples/point-cloud/CMakeLists.txt @@ -0,0 +1,16 @@ +project(noiz-point-cloud) + +add_executable(${PROJECT_NAME}) + +target_link_libraries(${PROJECT_NAME} PRIVATE + noiz::noiz-lib + noiz::noiz-compile-options +) + +target_sources(${PROJECT_NAME} PRIVATE + pointcloud.cpp +) + +target_include_directories(${PROJECT_NAME} PRIVATE + ../common +) diff --git a/examples/point-cloud/pointcloud.cpp b/examples/point-cloud/pointcloud.cpp new file mode 100644 index 0000000..f6754cd --- /dev/null +++ b/examples/point-cloud/pointcloud.cpp @@ -0,0 +1,116 @@ +#include +#include + +#include + + +#include +#include +#include +#include +#include +#include + +constexpr int32_t constant_base_resolution = 128; +constexpr int32_t constant_max_resolution_factor = 32; + +struct Config { + noiz::Seed seed{noiz::detail::Generator::make_random_seed()}; + noiz::GridExtent3 grid_extent{constant_base_resolution, constant_base_resolution, constant_base_resolution}; // NOLINT + float step{0.1f}; // NOLINT + int image_size_factor{1}; + + // syntax: [count] [step] + auto parse_args(Args args) -> bool { + if (!args.next_as(step, "step")) { return false; } + if (!args.next_as(image_size_factor, "image_size_factor")) { return false; } + if (image_size_factor < 0 || image_size_factor > constant_max_resolution_factor) { + std::cerr << std::format("invalid image size factor: '{}'\n", image_size_factor); + return false; + } + if (!args.args.empty()) { + return false; + } + grid_extent.x *= image_size_factor; + grid_extent.y *= image_size_factor; + grid_extent.z *= image_size_factor; + return true; + } +}; + +// pulls noise from noiz::lib, writes a pixel to ppm file +class Point_Cloud { + public: + explicit Point_Cloud(int image_size_factor) : image_size{static_cast(constant_base_resolution * image_size_factor)} {} + + void build_and_write_object_with_noise(noiz::Noise3f& noise, float const& step) const { + + std::ofstream out_file{"hybrid_multifractal_noise.obj", std::ios::binary}; + + float vertex_z; + float vertex_y; + + noiz::Processor3f noise_processor{noise}; + + for(int z = 0; z < image_size; z++){ + vertex_z = (static_cast(z) / static_cast(image_size)) - 0.5f; + + for (int y = 0; y < image_size; y++) { + vertex_y = (static_cast(y) / static_cast(image_size)) - 0.5f; + for(int x = 0; x < image_size; x++) { + // add noise at point + //raw noise +#if 0 //raw noise + const float noise_value = noise.at(noiz::Vec3f{.x = static_cast(x) * step, .y = static_cast(y) * step, .z = static_cast(z) * step}); + if(noise_value > 0.0f){ //this should render a half of the points with raw noise + + //no point in assigning x here? just write it directly to + out_file << "v " << ((static_cast(x) / static_cast(image_size)) - 0.5f) << " " << vertex_y << " " << vertex_z << '\n'; + } +#else //hybrid multi fractal noise + const float noise_value = noise_processor.hybrid_multi_fractal_processing( + noiz::Vec3f{.x = static_cast(x) * step, .y = static_cast(y) * step, .z = static_cast(z) * step} + ); + if(noise_value > 2.0f){ //this should render a half of the points with hmf noise + + //no point in assigning x here? just write it directly to + out_file << std::format("v {} {} {} '\n", (static_cast(x) / static_cast(image_size)) - 0.5f, vertex_y, vertex_z); + //out_file << "v " << ((static_cast(x) / static_cast(image_size)) - 0.5f) << " " << vertex_y << " " << vertex_z << '\n'; + } +#endif + } + } + } + out_file.close(); + } + + private: + uint16_t image_size; +}; + +auto main(int argc, char** argv) -> int { + auto config = Config{}; + + // skip exe name (argv[0]) + auto const args = Args{std::span{argv, static_cast(argc)}.subspan(1)}; + + // handle --help + if (!args.args.empty() && args.args.front() == std::string_view{"--help"}) { + std::cout << std::format("Usage: {} [step(=0.1)] [image_size_factor(=1)]\n", std::filesystem::path{*argv}.stem().string()); + std::cout << "\t output image resolution is 128x128, with each dimension multiplied by image_size_factor, maximum scaling factor is 32[image size of 4096]" << std::endl; + return EXIT_SUCCESS; + } + + // parse args, if any + if (!config.parse_args(args)) { + std::cout << args.args.front() << std::endl; + return EXIT_FAILURE; + } + + + auto noise = noiz::Noise3f{noiz::Seed{config.seed}, config.grid_extent}; + + // build and write noise to image + auto point_cloud = Point_Cloud{config.image_size_factor}; + point_cloud.build_and_write_object_with_noise(noise, config.step); +} diff --git a/noiz/CMakeLists.txt b/noiz/CMakeLists.txt index b067507..b1ee6e7 100644 --- a/noiz/CMakeLists.txt +++ b/noiz/CMakeLists.txt @@ -13,7 +13,25 @@ target_sources(${PROJECT_NAME} PUBLIC FILE_SET HEADERS BASE_DIRS include FILES include/noiz/noise2.hpp include/noiz/seed.hpp include/noiz/vec2.hpp -) + + + include/noiz/detail/data3.hpp + include/noiz/detail/grid3.hpp + + include/noiz/cell3.hpp + include/noiz/index3.hpp + include/noiz/noise3.hpp + include/noiz/vec3.hpp + + + include/noiz/detail/data4.hpp + include/noiz/detail/grid4.hpp + + include/noiz/cell4.hpp + include/noiz/index4.hpp + include/noiz/noise4.hpp + include/noiz/vec4.hpp + ) target_include_directories(${PROJECT_NAME} INTERFACE include diff --git a/noiz/include/noiz/cell3.hpp b/noiz/include/noiz/cell3.hpp new file mode 100644 index 0000000..7ed8263 --- /dev/null +++ b/noiz/include/noiz/cell3.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include "index3.hpp" +#include "vec3.hpp" + +namespace noiz { +template +struct Corner3 { + Vec3 location{}; + Vec3 gradient{}; +}; + +template +struct TCell3 { + Type left_top_front{}; + Type right_top_front{}; + Type left_bottom_front{}; + Type right_bottom_front{}; + + Type left_top_back{}; + Type right_top_back{}; + Type left_bottom_back{}; + Type right_bottom_back{}; +}; + +template +using Cell3 = TCell3>; + +template +using CornerCell3 = TCell3>; +} // namespace noiz diff --git a/noiz/include/noiz/cell4.hpp b/noiz/include/noiz/cell4.hpp new file mode 100644 index 0000000..da7b0ca --- /dev/null +++ b/noiz/include/noiz/cell4.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include "index4.hpp" +#include "vec4.hpp" + +namespace noiz { +template +struct Corner4 { + Vec4 location{}; + Vec4 gradient{}; +}; + +template +struct TCell4 { + //16 is 2^4 + std::array corners{}; +}; + +template +using Cell4 = TCell4>; + +template +using CornerCell4 = TCell4>; +} // namespace noiz diff --git a/noiz/include/noiz/detail/data3.hpp b/noiz/include/noiz/detail/data3.hpp new file mode 100644 index 0000000..39cb68f --- /dev/null +++ b/noiz/include/noiz/detail/data3.hpp @@ -0,0 +1,58 @@ +#pragma once +#include "generator.hpp" +#include "grid3.hpp" + +namespace noiz::detail { +template +auto make_populated_grid(Index3 const grid_extent, Seed seed = Generator::make_random_seed()) -> Grid3 { + auto ret = make_grid3(grid_extent); + auto generator = Generator{seed}; + for (auto& corner : ret.corners) { generator.next(corner.gradient); } + return ret; +} + +template +constexpr auto compute_offsets(CornerCell3 const& corner, Vec3 const point) -> Cell3 { + return Cell3{ + .left_top_front = point - corner.left_top_front.location, + .right_top_front = point - corner.right_top_front.location, + .left_bottom_front = point - corner.left_bottom_front.location, + .right_bottom_front = point - corner.right_bottom_front.location, + .left_top_back = point - corner.left_top_back.location, + .right_top_back = point - corner.right_top_back.location, + .left_bottom_back = point - corner.left_bottom_back.location, + .right_bottom_back = point - corner.right_bottom_back.location, + }; +} + +template +constexpr auto compute_dot_products(CornerCell3 const& corner, Cell3 const& offset) -> TCell3 { + return TCell3{ + .left_top_front = dot(corner.left_top_front.gradient, offset.left_top_front), + .right_top_front = dot(corner.right_top_front.gradient, offset.right_top_front), + .left_bottom_front = dot(corner.left_bottom_front.gradient, offset.left_bottom_front), + .right_bottom_front = dot(corner.right_bottom_front.gradient, offset.right_bottom_front), + + .left_top_back = dot(corner.left_top_back.gradient, offset.left_top_back), + .right_top_back = dot(corner.right_top_back.gradient, offset.right_top_back), + .left_bottom_back = dot(corner.left_bottom_back.gradient, offset.left_bottom_back), + .right_bottom_back = dot(corner.right_bottom_back.gradient, offset.right_bottom_back), + }; +} + +template +constexpr auto interpolate(Vec3 const point, TCell3 const& dot_products) -> Type { + auto const uvw = point.fract().fade(); + + auto const below_a = std::lerp(dot_products.left_top_front, dot_products.right_top_front, uvw.x); + auto const below_b = std::lerp(dot_products.left_bottom_front, dot_products.right_bottom_front, uvw.x); + auto const below = std::lerp(below_a, below_b, uvw.y); + + auto const above_a = std::lerp(dot_products.left_top_back, dot_products.right_top_back, uvw.x); + auto const above_b = std::lerp(dot_products.left_bottom_back, dot_products.right_bottom_back, uvw.x); + auto const above = std::lerp(above_a, above_b, uvw.y); + + //i might need to swap the position of below and above, not really sure + return std::lerp(below, above, uvw.z); +} +} // namespace noiz::detail diff --git a/noiz/include/noiz/detail/data4.hpp b/noiz/include/noiz/detail/data4.hpp new file mode 100644 index 0000000..16be83c --- /dev/null +++ b/noiz/include/noiz/detail/data4.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "generator.hpp" +#include "grid4.hpp" + +namespace noiz::detail { +template +auto make_populated_grid(Index4 const grid_extent, Seed seed = Generator::make_random_seed()) -> Grid4 { + auto ret = make_grid4(grid_extent); + auto generator = Generator{seed}; + for (auto& corner : ret.corners) { generator.next(corner.gradient); } + return ret; +} + +template +constexpr auto compute_offsets(CornerCell4 const& corner, Vec4 const point) -> Cell4 { + + Cell4 ret; + for(uint8_t i = 0; i < 16; i++){ + ret.corners[i] = point - corner.corners[i]; + } + return ret; +} + +template +constexpr auto compute_dot_products(CornerCell4 const& corner, Cell4 const& offset) -> TCell4 { + + TCell4 ret; + for(int i = 0; i < 16; i++){ + ret.corners[i] = dot(corner.corners[i].gradient, offset.corners[i]); + } + return ret; +} + +template +constexpr auto interpolate(Vec4 const point, TCell4 const& dot_products) -> Type { + auto const cell_interpolated_position = point.fract().fade(); + + //1st dimension + auto const below_a = std::lerp(dot_products.corners[0], dot_products.corners[1], cell_interpolated_position.x); + //2nd dimension + auto const below_b = std::lerp(dot_products.corners[2], dot_products.corners[3], cell_interpolated_position.x); + auto const below = std::lerp(below_a, below_b, cell_interpolated_position.y); + + //3rd dimension + auto const above_a = std::lerp(dot_products.corners[4], dot_products.corners[5], cell_interpolated_position.x); + auto const above_b = std::lerp(dot_products.corners[6], dot_products.corners[7], cell_interpolated_position.x); + auto const above = std::lerp(above_a, above_b, cell_interpolated_position.y); + + auto const third_lerp_a = std::lerp(below, above, cell_interpolated_position.z); + + //4th dimension + auto const fourth_below_a = std::lerp(dot_products.corners[8], dot_products.corners[9], cell_interpolated_position.x); + auto const fourth_below_b = std::lerp(dot_products.corners[10], dot_products.corners[11], cell_interpolated_position.x); + auto const fourth_below = std::lerp(fourth_below_a, fourth_below_b, cell_interpolated_position.y); + + auto const fourth_above_a = std::lerp(dot_products.corners[12], dot_products.corners[13], cell_interpolated_position.x); + auto const fourth_above_b = std::lerp(dot_products.corners[14], dot_products.corners[15], cell_interpolated_position.x); + auto const fourth_above = std::lerp(fourth_above_a, fourth_above_b, cell_interpolated_position.y); + + auto const third_lerp_b = std::lerp(fourth_below, fourth_above, cell_interpolated_position.z); + + + + //i might need to swap the position of below and above, not really sure + return std::lerp(third_lerp_a, third_lerp_b, cell_interpolated_position.w); +} +} // namespace noiz::detail diff --git a/noiz/include/noiz/detail/generator.hpp b/noiz/include/noiz/detail/generator.hpp index 3eaeed4..973832e 100644 --- a/noiz/include/noiz/detail/generator.hpp +++ b/noiz/include/noiz/detail/generator.hpp @@ -2,6 +2,8 @@ #include #include "../seed.hpp" #include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" namespace noiz::detail { /// @@ -36,16 +38,40 @@ class Generator { out = Vec2{.x = distribution(m_engine), .y = distribution(m_engine)}.normalized(); } + template + void next(Vec3& out) { + auto distribution = std::uniform_real_distribution{Type{-1}, Type{1}}; + out = Vec3{.x = distribution(m_engine), .y = distribution(m_engine), .z = distribution(m_engine)}.normalized(); + } + + template + void next(Vec4& out) { + auto distribution = std::uniform_real_distribution{Type{-1}, Type{1}}; + out = Vec4{.x = distribution(m_engine), .y = distribution(m_engine), .z = distribution(m_engine), .w = distribution(m_engine)}.normalized(); + } + /// /// \brief Obtain the next random unit Vec. /// \returns The next random unit Vec. /// template - auto next() -> Vec2 { + auto next2() -> Vec2 { auto ret = Vec2{}; next(ret); return ret; } + template + auto next3() -> Vec3 { + auto ret = Vec3{}; + next(ret); + return ret; + } + template + auto next4() -> Vec4 { + auto ret = Vec4{}; + next(ret); + return ret; + } private: std::default_random_engine m_engine{}; diff --git a/noiz/include/noiz/detail/grid2.hpp b/noiz/include/noiz/detail/grid2.hpp index 6a5c957..60a263a 100644 --- a/noiz/include/noiz/detail/grid2.hpp +++ b/noiz/include/noiz/detail/grid2.hpp @@ -40,7 +40,7 @@ template for (int col = 0; col <= grid_extent.x; ++col) { auto const index2 = Index2{.x = col, .y = row}; auto const index = static_cast(index2.flatten(grid_extent)); - ret.corners.at(index).location = to_vec2(Index2{.x = col, .y = row}); + ret.corners.at(index).location = to_vec2(index2); } } diff --git a/noiz/include/noiz/detail/grid3.hpp b/noiz/include/noiz/detail/grid3.hpp new file mode 100644 index 0000000..4142fcc --- /dev/null +++ b/noiz/include/noiz/detail/grid3.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include "../cell3.hpp" + +namespace noiz::detail { +template +[[nodiscard]] constexpr auto to_index3(Vec3 const point) -> Index3 { + return Index3{.x = static_cast(point.x), .y = static_cast(point.y), .z = static_cast(point.z)}; +} + +template +[[nodiscard]] constexpr auto to_vec3(Index3 const index) -> Vec3 { + return Vec3{.x = static_cast(index.x), .y = static_cast(index.y), .z = static_cast(index.z)}; +} + +template +struct Grid3 { + std::vector> corners{}; + Index3 grid_extent{}; + + [[nodiscard]] auto at(CellIndex3 const index) const -> CornerCell3 { + return CornerCell3{ + corners.at(index.ltf), corners.at(index.rtf), corners.at(index.lbf), corners.at(index.rbf), + corners.at(index.ltb), corners.at(index.rtb), corners.at(index.lbb), corners.at(index.rbb) + }; + } + + [[nodiscard]] auto at(Index3 index) const -> CornerCell3 { return at(CellIndex3::make(index, grid_extent)); } +}; + +template +[[nodiscard]] auto make_grid3(Index3 grid_extent) -> Grid3 { + auto const corner_count = (grid_extent.x + 1) * (grid_extent.y + 1) * (grid_extent.z + 1); + if (corner_count <= 0) { return {}; } + + auto ret = Grid3{ + .corners = std::vector>(static_cast(corner_count)), + .grid_extent = grid_extent, + }; + + for(int depth = 0; depth <= grid_extent.z; ++depth){ + for (int row = 0; row <= grid_extent.y; ++row) { + for (int col = 0; col <= grid_extent.x; ++col) { + auto const index3 = Index3{.x = col, .y = row, .z = depth}; + auto const index = static_cast(index3.flatten(grid_extent)); + ret.corners.at(index).location = to_vec3(index3); + } + } + } + + return ret; +} +} // namespace noiz::detail diff --git a/noiz/include/noiz/detail/grid4.hpp b/noiz/include/noiz/detail/grid4.hpp new file mode 100644 index 0000000..06b727b --- /dev/null +++ b/noiz/include/noiz/detail/grid4.hpp @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include "../cell4.hpp" + +namespace noiz::detail { +template +[[nodiscard]] constexpr auto to_index4(Vec4 const point) -> Index4 { + return Index4{.x = static_cast(point.x), .y = static_cast(point.y), .z = static_cast(point.z), .w = static_cast(point.w)}; +} + +template +[[nodiscard]] constexpr auto to_vec4(Index4 const index) -> Vec4 { + return Vec4{.x = static_cast(index.x), .y = static_cast(index.y), .z = static_cast(index.z), .w = static_cast(index.w)}; +} + +template +struct Grid4 { + std::vector> corners{}; + Index4 grid_extent{}; + + [[nodiscard]] auto at(CellIndex4 const index) const -> CornerCell4 { + return CornerCell4{ + corners.at(index.positions.at(0)), corners.at(index.positions.at(1)), corners.at(index.positions.at(2)), corners.at(index.positions.at(3)), + corners.at(index.positions.at(4)), corners.at(index.positions.at(5)), corners.at(index.positions.at(6)), corners.at(index.positions.at(7)), + corners.at(index.positions.at(8)), corners.at(index.positions.at(9)), corners.at(index.positions.at(10)), corners.at(index.positions.at(11)), + corners.at(index.positions.at(12)), corners.at(index.positions.at(13)), corners.at(index.positions.at(14)), corners.at(index.positions.at(15)) + }; + } + + [[nodiscard]] auto at(Index4 index) const -> CornerCell4 { return at(CellIndex4::make(index, grid_extent)); } +}; + +template +[[nodiscard]] auto make_grid4(Index4 grid_extent) -> Grid4 { + auto const corner_count = (grid_extent.x + 1) * (grid_extent.y + 1) * (grid_extent.z + 1) * (grid_extent.w + 1); + if (corner_count <= 0) { return {}; } + + auto ret = Grid4{ + .corners = std::vector>(static_cast(corner_count)), + .grid_extent = grid_extent, + }; + + for(int fourth = 0; fourth <= grid_extent.w; ++fourth){ //fourth dimension??? + for(int depth = 0; depth <= grid_extent.z; ++depth) { + for (int row = 0; row <= grid_extent.y; ++row) { + for (int col = 0; col <= grid_extent.x; ++col) { + auto const index4 = Index4{.x = col, .y = row, .z = depth, .w = fourth}; + auto const index = static_cast(index4.flatten(grid_extent)); + ret.corners.at(index).location = to_vec4(Index4{.x = col, .y = row, .z = depth, .w = fourth}); + } + } + } + } + return ret; +} +} // namespace noiz::detail diff --git a/noiz/include/noiz/index3.hpp b/noiz/include/noiz/index3.hpp new file mode 100644 index 0000000..ff1890b --- /dev/null +++ b/noiz/include/noiz/index3.hpp @@ -0,0 +1,50 @@ +#pragma once +#include +#include + +namespace noiz { +struct Index3 { + int x{}; + int y{}; + int z{}; + + [[nodiscard]] constexpr auto modulo(Index3 const extent) const -> Index3 { + assert(extent.x > 0 && extent.y > 0 && extent.z > 0); + return Index3{.x = x % extent.x, .y = y % extent.y, .z = z % extent.z}; + } + + [[nodiscard]] constexpr auto flatten(Index3 const extent) const -> int64_t { + return z * ((extent.x + 1) * (extent.y + 1)) + y * (extent.x + 1) + x; + } +}; + +struct CellIndex3 { + std::size_t ltf{}; + std::size_t rtf{}; + std::size_t lbf{}; + std::size_t rbf{}; + + std::size_t ltb{}; + std::size_t rtb{}; + std::size_t lbb{}; + std::size_t rbb{}; + + static constexpr auto make(Index3 index, Index3 const grid_extent) -> CellIndex3 { + index = index.modulo(grid_extent); + auto const cell_index = index.flatten(grid_extent); + assert(cell_index >= 0); + auto ret = CellIndex3{}; + ret.ltf = static_cast(cell_index); + ret.rtf = ret.ltf + 1; + ret.lbf = ret.ltf + static_cast(grid_extent.x + 1); + ret.rbf = ret.rtf + static_cast(grid_extent.x + 1); + + const auto dimension_offset3 = static_cast((grid_extent.x + 1) * (grid_extent.y + 1)); + ret.ltb = ret.ltf + dimension_offset3; + ret.rtb = ret.rtf + dimension_offset3; + ret.lbb = ret.lbf + dimension_offset3; + ret.rbb = ret.rbf + dimension_offset3; + return ret; + } +}; +} // namespace noiz diff --git a/noiz/include/noiz/index4.hpp b/noiz/include/noiz/index4.hpp new file mode 100644 index 0000000..a2f457c --- /dev/null +++ b/noiz/include/noiz/index4.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include + +namespace noiz { +struct Index4 { + int x{}; + int y{}; + int z{}; + int w{}; + + [[nodiscard]] constexpr auto modulo(Index4 const extent) const -> Index4 { + assert(extent.x > 0 && extent.y > 0 && extent.z > 0 && extent.w > 0); + return Index4{.x = x % extent.x, .y = y % extent.y, .z = z % extent.z, .w = w % extent.w}; + } + + [[nodiscard]] constexpr auto flatten(Index4 const extent) const -> int64_t { + return w * ((extent.z + 1) * (extent.y + 1) * (extent.x + 1)) + + z * ((extent.x + 1) * (extent.y + 1)) + + y * (extent.x + 1) + + x; + } +}; + +struct CellIndex4 { + + //16 is 2^4 + std::array positions{}; + + static constexpr auto make(Index4 index, Index4 const grid_extent) -> CellIndex4 { + index = index.modulo(grid_extent); + auto const cell_index = index.flatten(grid_extent); + assert(cell_index >= 0); + auto ret = CellIndex4{}; + + + //1st dimension + ret.positions[0] = static_cast(cell_index); + ret.positions[1] = ret.positions[0] + 1; + + //2nd dimension + for(uint8_t i = 2; i < 4; i++){ + ret.positions.at(i) = ret.positions.at(i - 2) + static_cast(grid_extent.x + 1); + } + + //3rd dimension + const auto dimension_offset3 = static_cast((grid_extent.x + 1) * (grid_extent.y + 1)); + for(uint8_t i = 4; i < 8; i++){ + ret.positions.at(i) = ret.positions.at(i - 4) + dimension_offset3; + } + + //4th dimension + const auto dimension_offset4 = dimension_offset3 * static_cast(grid_extent.z + 1); + for(uint8_t i = 8; i < 16; i++){ + ret.positions.at(i) = ret.positions.at(i - 8) + dimension_offset4; + } + + return ret; + } +}; +} // namespace noiz diff --git a/noiz/include/noiz/noise2.hpp b/noiz/include/noiz/noise2.hpp index b23ec79..153389e 100644 --- a/noiz/include/noiz/noise2.hpp +++ b/noiz/include/noiz/noise2.hpp @@ -7,6 +7,9 @@ using GridExtent2 = Index2; template class Noise2 { public: + using vec_type = Vec2; + using value_type = Type; + static constexpr GridExtent2 grid_extent_v{256, 256}; explicit Noise2(GridExtent2 grid_extent = grid_extent_v) : Noise2(detail::Generator::make_random_seed(), grid_extent) {} diff --git a/noiz/include/noiz/noise3.hpp b/noiz/include/noiz/noise3.hpp new file mode 100644 index 0000000..6616247 --- /dev/null +++ b/noiz/include/noiz/noise3.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "detail/data3.hpp" + +namespace noiz { +using GridExtent3 = Index3; + +template +class Noise3 { + public: + using vec_type = Vec3; + using value_type = Type; + + static constexpr GridExtent3 grid_extent_v{256, 256, 256}; + + explicit Noise3(GridExtent3 grid_extent = grid_extent_v) : Noise3(detail::Generator::make_random_seed(), grid_extent) {} + + explicit Noise3(Seed generator_seed, GridExtent3 grid_extent) : m_grid(detail::make_populated_grid(grid_extent, generator_seed)) {} + + [[nodiscard]] auto grid_extent() const -> GridExtent3 { return m_grid.grid_extent; } + + [[nodiscard]] auto at(Vec3 point) const -> Type { + point = point.modulo(detail::to_vec3(m_grid.grid_extent)); + auto const corners = m_grid.at(detail::to_index3(point)); + auto const offsets = detail::compute_offsets(corners, point); + auto const dots = detail::compute_dot_products(corners, offsets); + + return detail::interpolate(point, dots); + } + + private: + detail::Grid3 m_grid{}; +}; + +using Noise3f = Noise3; +using Noise3d = Noise3; +} // namespace noiz diff --git a/noiz/include/noiz/noise4.hpp b/noiz/include/noiz/noise4.hpp new file mode 100644 index 0000000..7bdc41a --- /dev/null +++ b/noiz/include/noiz/noise4.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "detail/data4.hpp" + +namespace noiz { +using GridExtent4 = Index4; + +template +class Noise4 { + public: + using vec_type = Vec4; + using value_type = Type; + + static constexpr GridExtent4 grid_extent_v{64, 64, 64, 64}; + + explicit Noise4(GridExtent4 grid_extent = grid_extent_v) : Noise4(detail::Generator::make_random_seed(), grid_extent) {} + + explicit Noise4(Seed generator_seed, GridExtent4 grid_extent) : m_grid(detail::make_populated_grid(grid_extent, generator_seed)) {} + + [[nodiscard]] auto grid_extent() const -> GridExtent4 { return m_grid.grid_extent; } + + [[nodiscard]] auto at(Vec4 point) const -> Type { + point = point.modulo(detail::to_vec4(m_grid.grid_extent)); + auto const corners = m_grid.at(detail::to_index4(point)); + auto const offsets = detail::compute_offsets(corners, point); + auto const dots = detail::compute_dot_products(corners, offsets); + + return detail::interpolate(point, dots); + } + + private: + detail::Grid4 m_grid{}; +}; + +using Noise4f = Noise4; +using Noise4d = Noise4; +} // namespace noiz diff --git a/noiz/include/noiz/processing.hpp b/noiz/include/noiz/processing.hpp new file mode 100644 index 0000000..c93a431 --- /dev/null +++ b/noiz/include/noiz/processing.hpp @@ -0,0 +1,129 @@ +#include "noise2.hpp" +#include "noise3.hpp" +#include "noise4.hpp" + +#pragma once + +namespace noiz{ + /* +template > +class Processor { +public: +using noise_type = NoiseT; +using vec_type = typename NoiseT::vec_type; +*/ +template +class Processor { + public: + using noise_type = NoiseT; + using vec_type = typename NoiseT::vec_type; + using value_type = typename vec_type::value_type; + explicit Processor(noise_type& noise) : noise{noise}{} + + std::reference_wrapper noise; + + //default values + uint8_t octave{ 6 }; + value_type step = (value_type)0.1; + value_type persistence = (value_type)1.5; + value_type lacunarity = (value_type)2.0; + value_type frequency = (value_type)1.0; + value_type amplitude = (value_type)1.0; + + //hybrid multifractal variables + value_type hmf_H = (value_type)0.25; + value_type hmf_offset = (value_type)0.7; +protected: + ////hybrid multifractal variable that wont be accessed from outside + std::vector hmf_exponent_array; +public: + + auto raw_noise(vec_type const& point) const -> value_type{ + return noise.get().at(point * step); + } + + auto basic_processing(vec_type const& point) const -> value_type { + + value_type total = value_type(0); + value_type frequency = this->frequency; + value_type amplitude = this->amplitude; + value_type normalizer = (value_type)0; + for(int i = 0; i < octave; i++){ + total += noise.get().at(point * step * frequency) * amplitude; + normalizer += amplitude; + amplitude *= persistence; + frequency *= lacunarity; + } + return total/normalizer; + } + + auto turbulence_processing(vec_type const& point) const -> value_type { + value_type amplitude = this->amplitude; + value_type frequency = this->frequency; + + value_type sum = 0.f; + value_type normalizer = 0.f; + for (uint8_t i = 1; i < octave; i++) { + sum += noise.get().at(point * (step * frequency)) * amplitude; + normalizer += amplitude; + amplitude *= persistence; + frequency *= lacunarity; + } + return sum; + } + + auto billowy_processing(vec_type const& point) const -> value_type { + return std::abs(noise.get().at(point * step)); + } + + auto rigid_processing(vec_type const& point) const -> value_type { + return 1.f - std::abs(noise.get().at(point * step)); + } + + auto hybrid_multi_fractal_processing(vec_type const& point) -> const value_type { + auto temp_point = point; + + value_type frequency, result, signal, weight; + if (hmf_exponent_array.size() != octave) { + // precompute and store spectral weights + //recalculates if the octave count has changed + + hmf_exponent_array.resize(octave); + frequency = 1.0; + for (uint16_t i = 0; i < octave; i++) { + // compute weight for each frequency + hmf_exponent_array[i] = std::pow(frequency, -hmf_H); + frequency *= lacunarity; + } + } + // get first octave of function + result = (noise.get().at(temp_point * step) + hmf_offset) * hmf_exponent_array[0]; + weight = result; + // increase frequency + temp_point = temp_point * lacunarity; + // spectral construction inner loop, where the fractal is built + for (uint8_t i = 1; i < octave; i++) { + // prevent divergence + if (weight > 1.0) weight = 1.0; + // get next frequency + signal = (noise.get().at(temp_point * step) + hmf_offset) * hmf_exponent_array[i]; + result += weight * signal; + // this is why hmf_H must specify a high fractal dimension + weight *= signal; + temp_point = temp_point * lacunarity; + } + + return result; + } + +}; +using Processor2f = Processor; +using Processor2d = Processor; + +using Processor3f = Processor; +using Processor3d = Processor; + +using Processor4f = Processor; +using Processor4d = Processor; + +} //namespace noiz \ No newline at end of file diff --git a/noiz/include/noiz/vec2.hpp b/noiz/include/noiz/vec2.hpp index 7e1a7af..947400e 100644 --- a/noiz/include/noiz/vec2.hpp +++ b/noiz/include/noiz/vec2.hpp @@ -8,8 +8,10 @@ namespace noiz { template struct Vec2 { - static constexpr auto epsilon_v = Type(0.001); + using value_type = Type; + static constexpr auto epsilon_v = Type(0.001); + Type x{}; Type y{}; @@ -20,8 +22,8 @@ struct Vec2 { void normalize() { auto const mag = magnitude(); assert(mag > Type(0)); - x /= mag; - y /= mag; + + *this = *this / mag; } [[nodiscard]] auto normalized() const -> Vec2 { @@ -75,6 +77,11 @@ struct Vec2 { friend constexpr auto operator*(Vec2 lhs, Vec2 const rhs) -> Vec2 { return lhs *= rhs; } friend constexpr auto operator/(Vec2 lhs, Vec2 const rhs) -> Vec2 { return lhs /= rhs; } + //i think this forces the factor/divisor to be the same type as the vec, aka float==float, or double==double, + //wont allow float*double? not really sure + friend constexpr auto operator*(Vec2 lhs, Type const factor) -> Vec2 {return Vec2{.x = lhs.x * factor, .y = lhs.y * factor};} + friend constexpr auto operator/(Vec2 lhs, Type const divisor) -> Vec2 {return Vec2{.x = lhs.x / divisor, .y = lhs.y / divisor};} + auto operator==(Vec2 const&) const -> bool = default; }; diff --git a/noiz/include/noiz/vec3.hpp b/noiz/include/noiz/vec3.hpp new file mode 100644 index 0000000..be308cf --- /dev/null +++ b/noiz/include/noiz/vec3.hpp @@ -0,0 +1,112 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace noiz { +template +struct Vec3 { + using value_type = Type; + + static constexpr auto epsilon_v = Type(0.001); + + Type x{}; + Type y{}; + Type z{}; + + [[nodiscard]] constexpr auto sqr_magnitude() const -> Type { return x * x + y * y + z * z; } + + [[nodiscard]] auto magnitude() const -> Type { return std::sqrt(sqr_magnitude()); } + + void normalize() { + auto const mag = magnitude(); + assert(mag > Type(0)); + + *this = *this / mag; + } + + [[nodiscard]] auto normalized() const -> Vec3 { + auto ret = *this; + ret.normalize(); + return ret; + } + + [[nodiscard]] auto is_normalized() const -> bool { return std::abs(sqr_magnitude() - Type(1)) < epsilon_v; } + + [[nodiscard]] auto modulo(Vec3 const extent) const -> Vec3 { + assert(extent.x > Type(0) && extent.y > Type(0)); + return Vec3{.x = std::fmod(x, extent.x), .y = std::fmod(y, extent.y), .z = std::fmod(z, extent.z)}; + } + + [[nodiscard]] constexpr auto fract() const -> Vec3 { return Vec3{.x = x - std::floor(x), .y = y - std::floor(y), .z = z - std::floor(z)}; } + + [[nodiscard]] constexpr auto fade() const -> Vec3 { + return Vec3{ + .x = x * x * x * (x * (x * 6 - 15) + 10), + .y = y * y * y * (y * (y * 6 - 15) + 10), + .z = z * z * z * (z * (z * 6 - 15) + 10) + }; + } + + constexpr auto operator+=(Vec3 const rhs) -> Vec3& { + x += rhs.x; + y += rhs.y; + z += rhs.z; + return *this; + } + + constexpr auto operator-=(Vec3 const rhs) -> Vec3& { + x -= rhs.x; + y -= rhs.y; + z -= rhs.z; + return *this; + } + + constexpr auto operator*=(Vec3 const rhs) -> Vec3& { + x *= rhs.x; + y *= rhs.y; + z *= rhs.z; + return *this; + } + + constexpr auto operator/=(Vec3 const rhs) -> Vec3& { + x /= rhs.x; + y /= rhs.y; + z /= rhs.z; + return *this; + } + + friend constexpr auto operator+(Vec3 lhs, Vec3 const rhs) -> Vec3 { return lhs += rhs; } + friend constexpr auto operator-(Vec3 lhs, Vec3 const rhs) -> Vec3 { return lhs -= rhs; } + friend constexpr auto operator*(Vec3 lhs, Vec3 const rhs) -> Vec3 { return lhs *= rhs; } + friend constexpr auto operator/(Vec3 lhs, Vec3 const rhs) -> Vec3 { return lhs /= rhs; } + friend constexpr auto operator*(Vec3 lhs, Type const factor) -> Vec3 {return Vec3{.x = lhs.x * factor, .y = lhs.y * factor, .z = lhs.z * factor};} + friend constexpr auto operator/(Vec3 lhs, Type const divisor) -> Vec3 {return Vec3{.x = lhs.x / divisor, .y = lhs.y / divisor, .z = lhs.z / divisor};} + + auto operator==(Vec3 const&) const -> bool = default; +}; + +template +constexpr auto dot(Vec3 const lhs, Vec3 const rhs) -> Type { + return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; +} + +template +constexpr auto compare(Vec3 const lhs, Vec3 const rhs, Type const epsilon = Type(0.001)) { + return (std::abs(lhs.x - rhs.x) < epsilon) && (std::abs(lhs.y - rhs.y) < epsilon) && (std::abs(lhs.z - rhs.z) < epsilon); +} + +template +constexpr auto lerp(Vec3 const lhs, Vec3 const rhs, Type alpha) -> Vec3 { + return Vec3{ + .x = std::lerp(lhs.x, rhs.x, alpha), + .y = std::lerp(lhs.y, rhs.y, alpha), + .z = std::lerp(lhs.z, rhs.z, alpha) + }; +} + +using Vec3f = Vec3; +using Vec3d = Vec3; +} // namespace noiz diff --git a/noiz/include/noiz/vec4.hpp b/noiz/include/noiz/vec4.hpp new file mode 100644 index 0000000..7dd6d48 --- /dev/null +++ b/noiz/include/noiz/vec4.hpp @@ -0,0 +1,119 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace noiz { +template +struct Vec4 { + using value_type = Type; + + static constexpr auto epsilon_v = Type(0.001); + + Type x{}; + Type y{}; + Type z{}; + Type w{}; + + [[nodiscard]] constexpr auto sqr_magnitude() const -> Type { return x * x + y * y + z * z + w * w; } + + [[nodiscard]] auto magnitude() const -> Type { return std::sqrt(sqr_magnitude()); } + + void normalize() { + auto const mag = magnitude(); + assert(mag > Type(0)); + + *this = *this / mag; + } + + [[nodiscard]] auto normalized() const -> Vec4 { + auto ret = *this; + ret.normalize(); + return ret; + } + + [[nodiscard]] auto is_normalized() const -> bool { return std::abs(sqr_magnitude() - Type(1)) < epsilon_v; } + + [[nodiscard]] auto modulo(Vec4 const extent) const -> Vec4 { + assert(extent.x > Type(0) && extent.y > Type(0)); + return Vec4{.x = std::fmod(x, extent.x), .y = std::fmod(y, extent.y), .z = std::fmod(z, extent.z), .w = std::fmod(w, extent.w)}; + } + + [[nodiscard]] constexpr auto fract() const -> Vec4 { return Vec4{.x = x - std::floor(x), .y = y - std::floor(y), .z = z - std::floor(z), .w = w - std::floor(w)}; } + + [[nodiscard]] constexpr auto fade() const -> Vec4 { + return Vec4{ + .x = x * x * x * (x * (x * 6 - 15) + 10), + .y = y * y * y * (y * (y * 6 - 15) + 10), + .z = z * z * z * (z * (z * 6 - 15) + 10), + .w = w * w * w * (w * (w * 6 - 15) + 10) + }; + } + + constexpr auto operator+=(Vec4 const rhs) -> Vec4& { + x += rhs.x; + y += rhs.y; + z += rhs.z; + w += rhs.w; + return *this; + } + + constexpr auto operator-=(Vec4 const rhs) -> Vec4& { + x -= rhs.x; + y -= rhs.y; + z -= rhs.z; + w -= rhs.w; + return *this; + } + + constexpr auto operator*=(Vec4 const rhs) -> Vec4& { + x *= rhs.x; + y *= rhs.y; + z *= rhs.z; + w *= rhs.w; + return *this; + } + + constexpr auto operator/=(Vec4 const rhs) -> Vec4& { + x /= rhs.x; + y /= rhs.y; + z /= rhs.z; + w /= rhs.w; + return *this; + } + + friend constexpr auto operator+(Vec4 lhs, Vec4 const rhs) -> Vec4 { return lhs += rhs; } + friend constexpr auto operator-(Vec4 lhs, Vec4 const rhs) -> Vec4 { return lhs -= rhs; } + friend constexpr auto operator*(Vec4 lhs, Vec4 const rhs) -> Vec4 { return lhs *= rhs; } + friend constexpr auto operator/(Vec4 lhs, Vec4 const rhs) -> Vec4 { return lhs /= rhs; } + friend constexpr auto operator*(Vec4 lhs, Type const factor) -> Vec4 {return Vec4{.x = lhs.x * factor, .y = lhs.y * factor, .z = lhs.z * factor, .w = lhs.w * factor};} + friend constexpr auto operator/(Vec4 lhs, Type const divisor) -> Vec4 {return Vec4{.x = lhs.x / divisor, .y = lhs.y / divisor, .z = lhs.z / divisor .w = lhs.w / divisor};} + + auto operator==(Vec4 const&) const -> bool = default; +}; + +template +constexpr auto dot(Vec4 const lhs, Vec4 const rhs) -> Type { + return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z, + lhs.w * lhs.w; +} + +template +constexpr auto compare(Vec4 const lhs, Vec4 const rhs, Type const epsilon = Type(0.001)) { + return (std::abs(lhs.x - rhs.x) < epsilon) && (std::abs(lhs.y - rhs.y) < epsilon) && (std::abs(lhs.z - rhs.z) < epsilon) && (std::abs(lhs.w - rhs.w) < epsilon); +} + +template +constexpr auto lerp(Vec4 const lhs, Vec4 const rhs, Type alpha) -> Vec4 { + return Vec4{ + .x = std::lerp(lhs.x, rhs.x, alpha), + .y = std::lerp(lhs.y, rhs.y, alpha), + .z = std::lerp(lhs.z, rhs.z, alpha), + .w = std::lerp(lhs.w, rhs.w, alpha) + }; +} + +using Vec4f = Vec4; +using Vec4d = Vec4; +} // namespace noiz