From c222b87f259af6e20f58016b74219f3efcd1c9f1 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Mon, 17 Oct 2022 21:58:50 +0800 Subject: [PATCH] Improve vector class. Add simplex noise, fBm, and hash functions. Start to revise terrain system --- src/config.hpp.in | 2 +- src/game/ant/morphogenesis.cpp | 42 +- src/game/component/orbit.hpp | 2 +- src/game/fonts.cpp | 2 +- src/game/graphics.cpp | 26 +- src/game/load.cpp | 121 +- src/game/menu.cpp | 40 +- src/game/state/boot.cpp | 22 +- src/game/state/credits.cpp | 4 +- src/game/state/graphics-menu.cpp | 6 +- src/game/state/main-menu.cpp | 16 +- src/game/state/nest-selection.cpp | 15 +- src/game/state/nuptial-flight.cpp | 12 +- src/game/system/astronomy.cpp | 10 +- src/game/system/atmosphere.cpp | 18 +- src/game/system/constraint.cpp | 18 +- src/game/system/painting.cpp | 66 +- src/game/system/samara.cpp | 8 +- src/game/system/subterrain.cpp | 16 +- src/game/system/terrain.cpp | 773 ++++++----- src/game/system/terrain.hpp | 107 +- src/game/world.cpp | 44 +- src/geom/aabb.hpp | 26 +- src/geom/cartesian.hpp | 8 +- src/geom/convex-hull.hpp | 18 +- src/geom/hyperoctree.hpp | 4 +- src/geom/intersection.cpp | 6 +- src/geom/mesh-accelerator.cpp | 12 +- src/geom/mesh-functions.cpp | 68 +- src/geom/mesh.cpp | 12 +- src/geom/mesh.hpp | 49 +- src/geom/meshes/grid.cpp | 10 +- src/geom/rect-pack.hpp | 12 +- src/geom/rect.hpp | 2 +- src/geom/sphere.hpp | 6 +- src/geom/view-frustum.hpp | 1 + src/math/constants.hpp | 34 +- src/math/math.hpp | 4 +- src/math/matrix-functions.hpp | 3 +- src/math/matrix-type.hpp | 2 +- src/math/noise/fbm.hpp | 70 + src/math/noise/hash.hpp | 142 ++ src/math/noise/noise.hpp | 34 + src/math/noise/simplex.hpp | 189 +++ src/math/operators.hpp | 1 - src/math/quaternion-functions.hpp | 9 +- src/math/se3.hpp | 9 +- src/math/stream-operators.hpp | 49 +- src/math/transform-functions.hpp | 3 +- src/math/transform-type.hpp | 2 +- src/math/vector-functions.hpp | 645 --------- src/math/vector-operators.hpp | 202 --- src/math/vector-type.hpp | 154 --- src/math/vector.hpp | 1492 +++++++++++++++++++++ src/physics/orbit/frame.hpp | 32 +- src/physics/orbit/trajectory.hpp | 7 +- src/render/passes/ground-pass.cpp | 2 +- src/render/passes/material-pass.cpp | 2 +- src/render/passes/outline-pass.cpp | 2 +- src/render/passes/shadow-map-pass.cpp | 24 +- src/render/passes/sky-pass.cpp | 22 +- src/resources/entity-archetype-loader.cpp | 18 +- src/resources/image-loader.cpp | 4 +- src/resources/image.hpp | 74 +- src/resources/model-loader.cpp | 10 +- src/resources/texture-loader.cpp | 4 +- src/scene/camera.cpp | 9 +- src/scene/object.hpp | 2 +- src/scene/spot-light.cpp | 2 +- src/scene/text.cpp | 55 +- src/type/bitmap-font.cpp | 6 +- src/type/freetype/typeface.cpp | 10 +- src/utility/fundamental-types.hpp | 3 +- 73 files changed, 3048 insertions(+), 1888 deletions(-) create mode 100644 src/math/noise/fbm.hpp create mode 100644 src/math/noise/hash.hpp create mode 100644 src/math/noise/noise.hpp create mode 100644 src/math/noise/simplex.hpp delete mode 100644 src/math/vector-functions.hpp delete mode 100644 src/math/vector-operators.hpp delete mode 100644 src/math/vector-type.hpp create mode 100644 src/math/vector.hpp diff --git a/src/config.hpp.in b/src/config.hpp.in index a02bd2a..bf78091 100644 --- a/src/config.hpp.in +++ b/src/config.hpp.in @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_CONFIG_HPP #define ANTKEEPER_CONFIG_HPP -#include "math/vector-type.hpp" +#include "math/vector.hpp" /// Global configuration constants. namespace config { diff --git a/src/game/ant/morphogenesis.cpp b/src/game/ant/morphogenesis.cpp index 25d9a18..94a72be 100644 --- a/src/game/ant/morphogenesis.cpp +++ b/src/game/ant/morphogenesis.cpp @@ -992,15 +992,15 @@ void reskin_vertices float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; // Update vertex data - *x = position.x; - *y = position.y; - *z = position.z; - *nx = normal.x; - *ny = normal.y; - *nz = normal.z; - *tx = tangent.x; - *ty = tangent.y; - *tz = tangent.z; + *x = position.x(); + *y = position.y(); + *z = position.z(); + *nx = normal.x(); + *ny = normal.y(); + *nz = normal.z(); + *tx = tangent.x(); + *ty = tangent.y(); + *tz = tangent.z(); //*bts = ... *bone_index = static_cast(new_bone_index); } @@ -1011,12 +1011,12 @@ geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_ std::uint8_t* position_data = vertex_data + position_attribute.offset; geom::aabb bounds; - bounds.min_point.x = std::numeric_limits::infinity(); - bounds.min_point.y = std::numeric_limits::infinity(); - bounds.min_point.z = std::numeric_limits::infinity(); - bounds.max_point.x = -std::numeric_limits::infinity(); - bounds.max_point.y = -std::numeric_limits::infinity(); - bounds.max_point.z = -std::numeric_limits::infinity(); + bounds.min_point.x() = std::numeric_limits::infinity(); + bounds.min_point.y() = std::numeric_limits::infinity(); + bounds.min_point.z() = std::numeric_limits::infinity(); + bounds.max_point.x() = -std::numeric_limits::infinity(); + bounds.max_point.y() = -std::numeric_limits::infinity(); + bounds.max_point.z() = -std::numeric_limits::infinity(); for (std::size_t i = 0; i < index_count; ++i) { @@ -1025,12 +1025,12 @@ geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_ float* y = x + 1; float* z = y + 1; - bounds.min_point.x = std::min(*x, bounds.min_point.x); - bounds.min_point.y = std::min(*y, bounds.min_point.y); - bounds.min_point.z = std::min(*z, bounds.min_point.z); - bounds.max_point.x = std::max(*x, bounds.max_point.x); - bounds.max_point.y = std::max(*y, bounds.max_point.y); - bounds.max_point.z = std::max(*z, bounds.max_point.z); + bounds.min_point.x() = std::min(*x, bounds.min_point.x()); + bounds.min_point.y() = std::min(*y, bounds.min_point.y()); + bounds.min_point.z() = std::min(*z, bounds.min_point.z()); + bounds.max_point.x() = std::max(*x, bounds.max_point.x()); + bounds.max_point.y() = std::max(*y, bounds.max_point.y()); + bounds.max_point.z() = std::max(*z, bounds.max_point.z()); } return bounds; diff --git a/src/game/component/orbit.hpp b/src/game/component/orbit.hpp index 4d61c8d..1fd9fe5 100644 --- a/src/game/component/orbit.hpp +++ b/src/game/component/orbit.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_GAME_COMPONENT_ORBIT_HPP #include "entity/id.hpp" -#include "math/vector-type.hpp" +#include "math/vector.hpp" namespace game { namespace component { diff --git a/src/game/fonts.cpp b/src/game/fonts.cpp index a25a490..3c979d2 100644 --- a/src/game/fonts.cpp +++ b/src/game/fonts.cpp @@ -56,7 +56,7 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const font.pack(); // Create font texture from bitmap - gl::texture_2d* font_texture = new gl::texture_2d(font_bitmap.get_width(), font_bitmap.get_height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.get_pixels()); + gl::texture_2d* font_texture = new gl::texture_2d(font_bitmap.get_width(), font_bitmap.get_height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.data()); font_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); font_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index 43606ae..3a8457f 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -45,18 +45,18 @@ void create_framebuffers(game::context& ctx) // Calculate render resolution const int2& viewport_dimensions = ctx.app->get_viewport_dimensions(); - ctx.render_resolution = {static_cast(viewport_dimensions.x * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y * ctx.render_resolution_scale + 0.5f)}; + ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_resolution_scale + 0.5f)}; // Create HDR framebuffer (32F color, 32F depth) - ctx.hdr_color_texture = new gl::texture_2d(ctx.render_resolution.x, ctx.render_resolution.y, gl::pixel_type::float_32, gl::pixel_format::rgb); + ctx.hdr_color_texture = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::rgb); ctx.hdr_color_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.hdr_color_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.hdr_color_texture->set_max_anisotropy(0.0f); - ctx.hdr_depth_texture = new gl::texture_2d(ctx.render_resolution.x, ctx.render_resolution.y, gl::pixel_type::float_32, gl::pixel_format::ds); + ctx.hdr_depth_texture = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::ds); ctx.hdr_depth_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.hdr_depth_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.hdr_depth_texture->set_max_anisotropy(0.0f); - ctx.hdr_framebuffer = new gl::framebuffer(ctx.render_resolution.x, ctx.render_resolution.y); + ctx.hdr_framebuffer = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::color, ctx.hdr_color_texture); ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.hdr_depth_texture); ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::stencil, ctx.hdr_depth_texture); @@ -65,11 +65,11 @@ void create_framebuffers(game::context& ctx) int2 bloom_resolution = ctx.render_resolution / 2; // Create bloom framebuffer (16F color, no depth) - ctx.bloom_color_texture = new gl::texture_2d(bloom_resolution.x, bloom_resolution.y, gl::pixel_type::float_16, gl::pixel_format::rgb); + ctx.bloom_color_texture = new gl::texture_2d(bloom_resolution.x(), bloom_resolution.y(), gl::pixel_type::float_16, gl::pixel_format::rgb); ctx.bloom_color_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.bloom_color_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.bloom_color_texture->set_max_anisotropy(0.0f); - ctx.bloom_framebuffer = new gl::framebuffer(bloom_resolution.x, bloom_resolution.y); + ctx.bloom_framebuffer = new gl::framebuffer(bloom_resolution.x(), bloom_resolution.y()); ctx.bloom_framebuffer->attach(gl::framebuffer_attachment_type::color, ctx.bloom_color_texture); // Load shadow map resolution from config @@ -124,10 +124,10 @@ void change_render_resolution(game::context& ctx, float scale) // Recalculate render resolution const int2& viewport_dimensions = ctx.app->get_viewport_dimensions(); - ctx.render_resolution = {static_cast(viewport_dimensions.x * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y * ctx.render_resolution_scale + 0.5f)}; + ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_resolution_scale + 0.5f)}; // Resize HDR framebuffer and attachments - ctx.hdr_framebuffer->resize({ctx.render_resolution.x, ctx.render_resolution.y}); + ctx.hdr_framebuffer->resize({ctx.render_resolution.x(), ctx.render_resolution.y()}); resize_framebuffer_attachment(*ctx.hdr_color_texture, ctx.render_resolution); resize_framebuffer_attachment(*ctx.hdr_depth_texture, ctx.render_resolution); @@ -135,7 +135,7 @@ void change_render_resolution(game::context& ctx, float scale) int2 bloom_resolution = ctx.render_resolution / 2; // Resize bloom framebuffer and attachments - ctx.bloom_framebuffer->resize({bloom_resolution.x, bloom_resolution.y}); + ctx.bloom_framebuffer->resize({bloom_resolution.x(), bloom_resolution.y()}); resize_framebuffer_attachment(*ctx.bloom_color_texture, bloom_resolution); ctx.logger->pop_task(EXIT_SUCCESS); @@ -143,7 +143,7 @@ void change_render_resolution(game::context& ctx, float scale) void resize_framebuffer_attachment(gl::texture_2d& texture, const int2& resolution) { - texture.resize(resolution.x, resolution.y, texture.get_pixel_type(), texture.get_pixel_format(), texture.get_color_space(), nullptr); + texture.resize(resolution.x(), resolution.y(), texture.get_pixel_type(), texture.get_pixel_format(), texture.get_color_space(), nullptr); } void save_screenshot(game::context& ctx) @@ -159,11 +159,11 @@ void save_screenshot(game::context& ctx) // Allocate image std::shared_ptr frame = std::make_shared(); frame->format(1, 3); - frame->resize(viewport_dimensions.x, viewport_dimensions.y); + frame->resize(viewport_dimensions.x(), viewport_dimensions.y()); // Read pixel data from backbuffer into image glReadBuffer(GL_BACK); - glReadPixels(0, 0, viewport_dimensions.x, viewport_dimensions.y, GL_RGB, GL_UNSIGNED_BYTE, frame->get_pixels()); + glReadPixels(0, 0, viewport_dimensions.x(), viewport_dimensions.y(), GL_RGB, GL_UNSIGNED_BYTE, frame->data()); // Write screenshot file in separate thread std::thread @@ -171,7 +171,7 @@ void save_screenshot(game::context& ctx) [frame, path] { stbi_flip_vertically_on_write(1); - stbi_write_png(path.string().c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->get_pixels(), frame->get_width() * frame->get_channel_count()); + stbi_write_png(path.string().c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->data(), frame->get_width() * frame->get_channel_count()); } ).detach(); diff --git a/src/game/load.cpp b/src/game/load.cpp index 925b01c..adf17d6 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -28,7 +28,17 @@ #include "render/passes/sky-pass.hpp" #include "render/passes/ground-pass.hpp" #include "game/system/astronomy.hpp" +#include "game/system/terrain.hpp" +#include "math/random.hpp" +#include "math/noise/noise.hpp" #include +#include +#include +#include "resources/image.hpp" + +#include +#include + namespace game { namespace load { @@ -36,6 +46,68 @@ namespace load { void biome(game::context& ctx, const std::filesystem::path& path) { ctx.logger->push_task("Loading biome from \"" + path.string() + "\""); + + image img; + img.format(1, 1); + img.resize(2048, 2048); + + float frequency = 10.0f; + std::size_t octaves = 4; + float lacunarity = 2.0f; + float gain = 0.5f; + auto hash = static_cast&)>(math::noise::hash::pcg3d_1); + auto noise = static_cast&, decltype(hash))>(math::noise::simplex); + + auto fbm = [&](const float2& x) + { + return math::noise::fbm + ( + x, + octaves, + lacunarity, + gain, + noise, + hash + ); + }; + + auto width = img.get_width(); + auto height = img.get_height(); + unsigned char* pixels = (unsigned char*)img.data(); + + float scale_x = 1.0f / static_cast(width - 1) * frequency; + float scale_y = 1.0f / static_cast(height - 1) * frequency; + + + + std::for_each + ( + std::execution::par_unseq, + img.begin(), + img.end(), + [pixels, width, height, scale_x, scale_y, &fbm](auto& pixel) + { + std::size_t i = &pixel - pixels; + std::size_t y = i / width; + std::size_t x = i % width; + + float2 position = + { + static_cast(x) * scale_x, + static_cast(y) * scale_y + }; + + //float n = math::noise::simplex(position, &math::noise::hash::pcg3d_1); + float n = fbm(position); + + pixel = static_cast((n * 0.5f + 0.5f) * 255.0f); + } + ); + + stbi_flip_vertically_on_write(1); + stbi_write_png((ctx.config_path / "gallery" / "simplex-noise.png").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data(), img.get_width() * img.get_channel_count()); + + try { json* data = ctx.resource_manager->load(path); @@ -77,6 +149,16 @@ void biome(game::context& ctx, const std::filesystem::path& path) if (auto terrain = data->find("terrain"); terrain != data->end()) { if (auto material = terrain->find("material"); material != terrain->end()) + { + render::material* terrain_material = ctx.resource_manager->load(material->get()); + ctx.terrain_system->set_patch_material(terrain_material); + } + else + { + ctx.logger->warning("Biome terrain material undefined"); + } + + if (auto material = terrain->find("horizon_material"); material != terrain->end()) { render::model* terrestrial_hemisphere_model = ctx.resource_manager->load("terrestrial-hemisphere.mdl"); (*terrestrial_hemisphere_model->get_groups())[0]->set_material(ctx.resource_manager->load(material->get())); @@ -84,9 +166,46 @@ void biome(game::context& ctx, const std::filesystem::path& path) } else { - ctx.logger->warning("Biome terrain material undefined"); + ctx.logger->warning("Biome terrain horizon material undefined"); } + // Terrain elevation function + ctx.terrain_system->set_elevation_function + ( + [](float x, float z) -> float + { + float angle = math::radians(30.0f); + float c = std::cos(angle); + float s = std::sin(angle); + + x = x * c - z * s; + z = x * s + z * c; + + + + float frequency = 0.01f; + std::size_t octaves = 4; + float lacunarity = 3.0f; + float gain = 0.5f; + auto noise = static_cast&, decltype(hash))>(math::noise::simplex); + auto hash = static_cast&)>(math::noise::hash::pcg3d_1); + + float2 position = float2{x, z} * frequency; + + float n = math::noise::fbm + ( + position, + octaves, + lacunarity, + gain, + noise, + hash + ); + + return 2.0f * n; + } + ); + // Setup lighting double3 terrain_albedo = {0, 0, 0}; if (terrain->contains("albedo")) diff --git a/src/game/menu.cpp b/src/game/menu.cpp index b870eaf..1352939 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -93,13 +93,13 @@ void align_text(game::context& ctx, bool center, bool has_back, float anchor_y) // Add name width to width const auto& name_bounds = static_cast&>(name->get_local_bounds()); - row_width += name_bounds.max_point.x - name_bounds.min_point.x; + row_width += name_bounds.max_point.x() - name_bounds.min_point.x(); if (value) { // Add value width to width //const auto& value_bounds = static_cast&>(value->get_local_bounds()); - //row_width += value_bounds.max_point.x - value_bounds.min_point.x; + //row_width += value_bounds.max_point.x() - value_bounds.min_point.x(); // Add spacing to row width row_width += menu_spacing * 8.0f; @@ -130,7 +130,7 @@ void align_text(game::context& ctx, bool center, bool has_back, float anchor_y) if (center || i == ctx.menu_item_texts.size() - 1) { const auto& name_bounds = static_cast&>(name->get_local_bounds()); - const float name_width = name_bounds.max_point.x - name_bounds.min_point.x; + const float name_width = name_bounds.max_point.x() - name_bounds.min_point.x(); x = -name_width * 0.5f; } @@ -139,7 +139,7 @@ void align_text(game::context& ctx, bool center, bool has_back, float anchor_y) if (value) { const auto& value_bounds = static_cast&>(value->get_local_bounds()); - const float value_width = value_bounds.max_point.x - value_bounds.min_point.x; + const float value_width = value_bounds.max_point.x() - value_bounds.min_point.x(); if (center || i == ctx.menu_item_texts.size() - 1) x = -value_width * 0.5f; @@ -365,17 +365,17 @@ void setup_controls(game::context& ctx) auto [name, value] = ctx.menu_item_texts[i]; const auto& name_bounds = static_cast&>(name->get_world_bounds()); - float min_x = name_bounds.min_point.x; - float min_y = name_bounds.min_point.y; - float max_x = name_bounds.max_point.x; - float max_y = name_bounds.max_point.y; + float min_x = name_bounds.min_point.x(); + float min_y = name_bounds.min_point.y(); + float max_x = name_bounds.max_point.x(); + float max_y = name_bounds.max_point.y(); if (value) { const auto& value_bounds = static_cast&>(value->get_world_bounds()); - min_x = std::min(min_x, value_bounds.min_point.x); - min_y = std::min(min_y, value_bounds.min_point.y); - max_x = std::max(max_x, value_bounds.max_point.x); - max_y = std::max(max_y, value_bounds.max_point.y); + min_x = std::min(min_x, value_bounds.min_point.x()); + min_y = std::min(min_y, value_bounds.min_point.y()); + max_x = std::max(max_x, value_bounds.max_point.x()); + max_y = std::max(max_y, value_bounds.max_point.y()); } min_x -= padding; @@ -411,17 +411,17 @@ void setup_controls(game::context& ctx) auto [name, value] = ctx.menu_item_texts[i]; const auto& name_bounds = static_cast&>(name->get_world_bounds()); - float min_x = name_bounds.min_point.x; - float min_y = name_bounds.min_point.y; - float max_x = name_bounds.max_point.x; - float max_y = name_bounds.max_point.y; + float min_x = name_bounds.min_point.x(); + float min_y = name_bounds.min_point.y(); + float max_x = name_bounds.max_point.x(); + float max_y = name_bounds.max_point.y(); if (value) { const auto& value_bounds = static_cast&>(value->get_world_bounds()); - min_x = std::min(min_x, value_bounds.min_point.x); - min_y = std::min(min_y, value_bounds.min_point.y); - max_x = std::max(max_x, value_bounds.max_point.x); - max_y = std::max(max_y, value_bounds.max_point.y); + min_x = std::min(min_x, value_bounds.min_point.x()); + min_y = std::min(min_y, value_bounds.min_point.y()); + max_x = std::max(max_x, value_bounds.max_point.x()); + max_y = std::max(max_y, value_bounds.max_point.y()); } min_x -= padding; diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 7c670ee..4810b7f 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -384,19 +384,19 @@ void boot::setup_window() { if (config->contains("fullscreen_resolution")) { - resolution.x = (*config)["fullscreen_resolution"][0].get(); - resolution.y = (*config)["fullscreen_resolution"][1].get(); + resolution.x() = (*config)["fullscreen_resolution"][0].get(); + resolution.y() = (*config)["fullscreen_resolution"][1].get(); } } else { if (config->contains("windowed_resolution")) { - resolution.x = (*config)["windowed_resolution"][0].get(); - resolution.y = (*config)["windowed_resolution"][1].get(); + resolution.x() = (*config)["windowed_resolution"][0].get(); + resolution.y() = (*config)["windowed_resolution"][1].get(); } } - app->resize_window(resolution.x, resolution.y); + app->resize_window(resolution.x(), resolution.y()); // Set v-sync bool v_sync = true; @@ -654,7 +654,7 @@ void boot::setup_scenes() // Setup surface camera ctx.surface_camera = new scene::camera(); - ctx.surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f); + ctx.surface_camera->set_perspective(math::radians(45.0f), viewport_aspect_ratio, 0.1f, 500.0f); ctx.surface_camera->set_compositor(ctx.surface_compositor); ctx.surface_camera->set_composite_index(0); ctx.surface_camera->set_active(false); @@ -836,9 +836,9 @@ void boot::setup_systems() // Setup terrain system ctx.terrain_system = new game::system::terrain(*ctx.entity_registry); + ctx.terrain_system->set_patch_side_length(31.0f); ctx.terrain_system->set_patch_subdivisions(30); - ctx.terrain_system->set_patch_scene_collection(ctx.surface_scene); - ctx.terrain_system->set_max_error(200.0); + ctx.terrain_system->set_scene_collection(ctx.surface_scene); // Setup vegetation system //ctx.vegetation_system = new game::system::vegetation(*ctx.entity_registry); @@ -1003,10 +1003,10 @@ void boot::setup_controls() if (!fullscreen) { int2 resolution; - resolution.x = (*ctx.config)["windowed_resolution"][0].get(); - resolution.y = (*ctx.config)["windowed_resolution"][1].get(); + resolution.x() = (*ctx.config)["windowed_resolution"][0].get(); + resolution.y() = (*ctx.config)["windowed_resolution"][1].get(); - ctx.app->resize_window(resolution.x, resolution.y); + ctx.app->resize_window(resolution.x(), resolution.y()); } // Save display mode config diff --git a/src/game/state/credits.cpp b/src/game/state/credits.cpp index 9a3779b..a98508d 100644 --- a/src/game/state/credits.cpp +++ b/src/game/state/credits.cpp @@ -43,8 +43,8 @@ credits::credits(game::context& ctx): // Align credits text const auto& credits_aabb = static_cast&>(credits_text.get_local_bounds()); - float credits_w = credits_aabb.max_point.x - credits_aabb.min_point.x; - float credits_h = credits_aabb.max_point.y - credits_aabb.min_point.y; + float credits_w = credits_aabb.max_point.x() - credits_aabb.min_point.x(); + float credits_h = credits_aabb.max_point.y() - credits_aabb.min_point.y(); credits_text.set_translation({std::round(-credits_w * 0.5f), std::round(-credits_h * 0.5f), 0.0f}); credits_text.update_tweens(); diff --git a/src/game/state/graphics-menu.cpp b/src/game/state/graphics-menu.cpp index 47cac30..66b43f5 100644 --- a/src/game/state/graphics-menu.cpp +++ b/src/game/state/graphics-menu.cpp @@ -87,10 +87,10 @@ graphics_menu::graphics_menu(game::context& ctx): if (!fullscreen) { int2 resolution; - resolution.x = (*ctx.config)["windowed_resolution"][0].get(); - resolution.y = (*ctx.config)["windowed_resolution"][1].get(); + resolution.x() = (*ctx.config)["windowed_resolution"][0].get(); + resolution.y() = (*ctx.config)["windowed_resolution"][1].get(); - ctx.app->resize_window(resolution.x, resolution.y); + ctx.app->resize_window(resolution.x(), resolution.y()); } this->update_value_text_content(); diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index c5e8af7..045b9bc 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -60,9 +60,9 @@ main_menu::main_menu(game::context& ctx, bool fade_in): // Align title text const auto& title_aabb = static_cast&>(title_text.get_local_bounds()); - float title_w = title_aabb.max_point.x - title_aabb.min_point.x; - float title_h = title_aabb.max_point.y - title_aabb.min_point.y; - title_text.set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (ctx.app->get_viewport_dimensions().y / 3.0f) / 2.0f), 0.0f}); + float title_w = title_aabb.max_point.x() - title_aabb.min_point.x(); + float title_h = title_aabb.max_point.y() - title_aabb.min_point.y(); + title_text.set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (ctx.app->get_viewport_dimensions().y() / 3.0f) / 2.0f), 0.0f}); title_text.update_tweens(); // Add title text to UI @@ -105,7 +105,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): game::menu::update_text_color(ctx); game::menu::update_text_font(ctx); - game::menu::align_text(ctx, true, false, (-ctx.app->get_viewport_dimensions().y / 3.0f) / 2.0f); + game::menu::align_text(ctx, true, false, (-ctx.app->get_viewport_dimensions().y() / 3.0f) / 2.0f); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); game::menu::setup_animations(ctx); @@ -262,8 +262,12 @@ main_menu::main_menu(game::context& ctx, bool fade_in): const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); const float aspect_ratio = static_cast(viewport_dimensions[0]) / static_cast(viewport_dimensions[1]); - ctx.surface_camera->look_at({0, 3.0f, 0}, {0, 0, 0}, {0, 0, 1}); - ctx.surface_camera->set_perspective(math::vertical_fov(math::radians(100.0f), aspect_ratio), ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far()); + float fov = math::vertical_fov(math::radians(100.0f), aspect_ratio); + if (ctx.config->contains("near_fov")) + fov = math::vertical_fov(math::radians((*ctx.config)["near_fov"].get()), aspect_ratio); + + ctx.surface_camera->look_at({0, 2.0f, 0}, {0, 0, 0}, {0, 0, 1}); + ctx.surface_camera->set_perspective(fov, ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far()); ctx.surface_camera->update_tweens(); // Setup and enable sky and ground passes diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp index 6192c0d..147b9e3 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/state/nest-selection.cpp @@ -132,6 +132,9 @@ nest_selection::nest_selection(game::context& ctx): // Satisfy first person camera rig constraints satisfy_first_person_camera_rig_constraints(); + auto ruler_archetype = ctx.resource_manager->load("ruler-10cm.ent"); + ruler_archetype->create(*ctx.entity_registry); + // Queue control setup ctx.function_queue.push(std::bind(&nest_selection::enable_controls, this)); @@ -670,7 +673,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.x() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -681,7 +684,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.x() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -693,7 +696,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.y() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -704,7 +707,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.y() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -716,7 +719,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.z() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -727,7 +730,7 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.z() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index efb63ac..2336c1a 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -773,7 +773,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.x() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -784,7 +784,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.x() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -796,7 +796,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.y() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -807,7 +807,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.y() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -819,7 +819,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z -= wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.z() -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -830,7 +830,7 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z += wavelength_speed * ctx.loop.get_update_period(); + ctx.rgb_wavelengths.z() += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; diff --git a/src/game/system/astronomy.cpp b/src/game/system/astronomy.cpp index 9de1c4d..dbcdbd0 100644 --- a/src/game/system/astronomy.cpp +++ b/src/game/system/astronomy.cpp @@ -222,7 +222,7 @@ void astronomy::update(double t, double dt) { // Calculate sky illuminance double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(enu_to_eus.inverse() * blackbody_position_eus); - const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y)); + const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y())); // Add sky illuminance to sky light illuminance sky_light_illuminance += {sky_illuminance, sky_illuminance, sky_illuminance}; @@ -592,7 +592,7 @@ double3 astronomy::integrate_transmittance(const game::component::observer& obse double3 transmittance = {1, 1, 1}; // Make ray height relative to center of reference body - ray.origin.y += body.radius + observer.elevation; + ray.origin.y() += body.radius + observer.elevation; // Construct sphere representing upper limit of the atmosphere geom::sphere atmosphere_sphere; @@ -616,9 +616,9 @@ double3 astronomy::integrate_transmittance(const game::component::observer& obse const double extinction_m = atmosphere.mie_extinction * optical_depth_m; const double3 extinction_o = atmosphere.ozone_absorption * optical_depth_o; transmittance = extinction_r + double3{extinction_m, extinction_m, extinction_m} + extinction_o; - transmittance.x = std::exp(-transmittance.x); - transmittance.y = std::exp(-transmittance.y); - transmittance.z = std::exp(-transmittance.z); + transmittance.x() = std::exp(-transmittance.x()); + transmittance.y() = std::exp(-transmittance.y()); + transmittance.z() = std::exp(-transmittance.z()); } return transmittance; diff --git a/src/game/system/atmosphere.cpp b/src/game/system/atmosphere.cpp index 13da655..b03761d 100644 --- a/src/game/system/atmosphere.cpp +++ b/src/game/system/atmosphere.cpp @@ -54,9 +54,9 @@ void atmosphere::set_rgb_wavelengths(const double3& wavelengths) // Update ozone cross sections rgb_ozone_cross_sections = { - physics::gas::ozone::cross_section_293k(wavelengths.x * 1e9), - physics::gas::ozone::cross_section_293k(wavelengths.y * 1e9), - physics::gas::ozone::cross_section_293k(wavelengths.z * 1e9) + physics::gas::ozone::cross_section_293k(wavelengths.x() * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.y() * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.z() * 1e9) }; // Update atmosphere components @@ -98,9 +98,9 @@ void atmosphere::update_atmosphere(entity::id entity_id) const double rayleigh_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, rayleigh_density); component->rayleigh_scattering = { - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x), - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y), - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.z) + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x()), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y()), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.z()) }; // Calculate Mie scattering and extinction coefficients @@ -113,9 +113,9 @@ void atmosphere::update_atmosphere(entity::id entity_id) const double ozone_density = physics::number_density(component->ozone_concentration); component->ozone_absorption = { - physics::gas::ozone::absorption(rgb_ozone_cross_sections.x, ozone_density), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.y, ozone_density), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.z, ozone_density) + physics::gas::ozone::absorption(rgb_ozone_cross_sections.x(), ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.y(), ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.z(), ozone_density) }; // Update sky pass parameters diff --git a/src/game/system/constraint.cpp b/src/game/system/constraint.cpp index cbeb3a2..312ac3a 100644 --- a/src/game/system/constraint.cpp +++ b/src/game/system/constraint.cpp @@ -181,11 +181,11 @@ void constraint::handle_copy_scale_constraint(component::transform& transform, c const auto& target_scale = target_transform->world.scale; if (constraint.copy_x) - transform.world.scale.x = target_scale.x; + transform.world.scale.x() = target_scale.x(); if (constraint.copy_y) - transform.world.scale.y = target_scale.y; + transform.world.scale.y() = target_scale.y(); if (constraint.copy_z) - transform.world.scale.z = target_scale.z; + transform.world.scale.z() = target_scale.z(); } } } @@ -214,20 +214,20 @@ void constraint::handle_copy_translation_constraint(component::transform& transf if (constraint.offset) { if (constraint.copy_x) - transform.world.translation.x += (constraint.invert_x) ? -target_translation.x : target_translation.x; + transform.world.translation.x() += (constraint.invert_x) ? -target_translation.x() : target_translation.x(); if (constraint.copy_y) - transform.world.translation.y += (constraint.invert_y) ? -target_translation.y : target_translation.y; + transform.world.translation.y() += (constraint.invert_y) ? -target_translation.y() : target_translation.y(); if (constraint.copy_z) - transform.world.translation.z += (constraint.invert_z) ? -target_translation.z : target_translation.z; + transform.world.translation.z() += (constraint.invert_z) ? -target_translation.z() : target_translation.z(); } else { if (constraint.copy_x) - transform.world.translation.x = (constraint.invert_x) ? -target_translation.x : target_translation.x; + transform.world.translation.x() = (constraint.invert_x) ? -target_translation.x() : target_translation.x(); if (constraint.copy_y) - transform.world.translation.y = (constraint.invert_y) ? -target_translation.y : target_translation.y; + transform.world.translation.y() = (constraint.invert_y) ? -target_translation.y() : target_translation.y(); if (constraint.copy_z) - transform.world.translation.z = (constraint.invert_z) ? -target_translation.z : target_translation.z; + transform.world.translation.z() = (constraint.invert_z) ? -target_translation.z() : target_translation.z(); } } } diff --git a/src/game/system/painting.cpp b/src/game/system/painting.cpp index 03c9558..b14d823 100644 --- a/src/game/system/painting.cpp +++ b/src/game/system/painting.cpp @@ -78,12 +78,12 @@ painting::painting(entity::registry& registry, ::event_dispatcher* event_dispatc stroke_model_instance->set_model(stroke_model); stroke_model_instance->update_tweens(); - stroke_bounds_min.x = std::numeric_limits::infinity(); - stroke_bounds_min.y = std::numeric_limits::infinity(); - stroke_bounds_min.z = std::numeric_limits::infinity(); - stroke_bounds_max.x = -std::numeric_limits::infinity(); - stroke_bounds_max.y = -std::numeric_limits::infinity(); - stroke_bounds_max.z = -std::numeric_limits::infinity(); + stroke_bounds_min.x() = std::numeric_limits::infinity(); + stroke_bounds_min.y() = std::numeric_limits::infinity(); + stroke_bounds_min.z() = std::numeric_limits::infinity(); + stroke_bounds_max.x() = -std::numeric_limits::infinity(); + stroke_bounds_max.y() = -std::numeric_limits::infinity(); + stroke_bounds_max.z() = -std::numeric_limits::infinity(); midstroke = false; */ @@ -132,8 +132,8 @@ void painting::update(double t, double dt) // Find miter float3 tangent = math::normalize(math::normalize(p2 - p1) + math::normalize(p1 - p0)); - float2 miter = float2{-tangent.z, tangent.x}; - float2 normal = float2{segment_right.x, segment_right.z}; + float2 miter = float2{-tangent.z(), tangent.x()}; + float2 normal = float2{segment_right.x(), segment_right.z()}; float miter_length = stroke_width / math::dot(miter, normal); float3 a = p0a; @@ -151,8 +151,8 @@ void painting::update(double t, double dt) if (angle < max_miter_angle) { mitered = true; - c = p1 - float3{miter.x, 0.0f, miter.y} * miter_length * 0.5f + segment_up * decal_offset; - d = p1 + float3{miter.x, 0.0f, miter.y} * miter_length * 0.5f + segment_up * decal_offset; + c = p1 - float3{miter.x(), 0.0f, miter.y()} * miter_length * 0.5f + segment_up * decal_offset; + d = p1 + float3{miter.x(), 0.0f, miter.y()} * miter_length * 0.5f + segment_up * decal_offset; } } @@ -198,9 +198,9 @@ void painting::update(double t, double dt) float2 uvba = uvb - uva; float2 uvca = uvc - uva; - float f = 1.0f / (uvba.x * uvca.y - uvca.x * uvba.y); - float3 tangent = math::normalize((ba * uvca.y - ca * uvba.y) * f); - float3 bitangent = math::normalize((ba * -uvca.x + ca * uvba.x) * f); + float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); + float3 tangent = math::normalize((ba * uvca.y() - ca * uvba.y()) * f); + float3 bitangent = math::normalize((ba * -uvca.x() + ca * uvba.x()) * f); // Rotate tangent and bitangent according to segment rotation tangent = math::normalize(tangent_rotation * tangent); @@ -209,30 +209,30 @@ void painting::update(double t, double dt) // Calculate sign of bitangent float bitangent_sign = (math::dot(math::cross(surface_normal, tangent), bitangent) < 0.0f) ? -1.0f : 1.0f; - tangents[i * 3] = {tangent.x, tangent.y, tangent.z, bitangent_sign}; - tangents[i * 3 + 1] = {tangent.x, tangent.y, tangent.z, bitangent_sign}; - tangents[i * 3 + 2] = {tangent.x, tangent.y, tangent.z, bitangent_sign}; + tangents[i * 3] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; + tangents[i * 3 + 1] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; + tangents[i * 3 + 2] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; } float vertex_data[13 * 12]; float* v = &vertex_data[0]; for (int i = 0; i < 12; ++i) { - *(v++) = positions[i].x; - *(v++) = positions[i].y; - *(v++) = positions[i].z; + *(v++) = positions[i].x(); + *(v++) = positions[i].y(); + *(v++) = positions[i].z(); *(v++) = w; - *(v++) = surface_normal.x; - *(v++) = surface_normal.y; - *(v++) = surface_normal.z; + *(v++) = surface_normal.x(); + *(v++) = surface_normal.y(); + *(v++) = surface_normal.z(); - *(v++) = texcoords[i].x; - *(v++) = texcoords[i].y; + *(v++) = texcoords[i].x(); + *(v++) = texcoords[i].y(); - *(v++) = tangents[i].x; - *(v++) = tangents[i].y; - *(v++) = tangents[i].z; + *(v++) = tangents[i].x(); + *(v++) = tangents[i].y(); + *(v++) = tangents[i].z(); *(v++) = tangents[i].w; } @@ -251,12 +251,12 @@ void painting::update(double t, double dt) stroke_model_group->set_index_count(current_stroke_segment * 6); // Update stroke bounds - stroke_bounds_min.x = std::min(stroke_bounds_min.x, std::min(c.x, std::min(d.x, std::min(e.x, f.x)))); - stroke_bounds_min.y = std::min(stroke_bounds_min.y, std::min(c.y, std::min(d.y, std::min(e.y, f.y)))); - stroke_bounds_min.z = std::min(stroke_bounds_min.z, std::min(c.z, std::min(d.z, std::min(e.z, f.z)))); - stroke_bounds_max.x = std::max(stroke_bounds_max.x, std::max(c.x, std::max(d.x, std::max(e.x, f.x)))); - stroke_bounds_max.y = std::max(stroke_bounds_max.y, std::max(c.y, std::max(d.y, std::max(e.y, f.y)))); - stroke_bounds_max.z = std::max(stroke_bounds_max.z, std::max(c.z, std::max(d.z, std::max(e.z, f.z)))); + stroke_bounds_min.x() = std::min(stroke_bounds_min.x(), std::min(c.x(), std::min(d.x(), std::min(e.x(), f.x())))); + stroke_bounds_min.y() = std::min(stroke_bounds_min.y(), std::min(c.y(), std::min(d.y(), std::min(e.y(), f.y())))); + stroke_bounds_min.z() = std::min(stroke_bounds_min.z(), std::min(c.z(), std::min(d.z(), std::min(e.z(), f.z())))); + stroke_bounds_max.x() = std::max(stroke_bounds_max.x(), std::max(c.x(), std::max(d.x(), std::max(e.x(), f.x())))); + stroke_bounds_max.y() = std::max(stroke_bounds_max.y(), std::max(c.y(), std::max(d.y(), std::max(e.y(), f.y())))); + stroke_bounds_max.z() = std::max(stroke_bounds_max.z(), std::max(c.z(), std::max(d.z(), std::max(e.z(), f.z())))); stroke_model->set_bounds(geom::aabb{stroke_bounds_min, stroke_bounds_max}); stroke_model_instance->update_bounds(); diff --git a/src/game/system/samara.cpp b/src/game/system/samara.cpp index 04855b1..f66fbbb 100644 --- a/src/game/system/samara.cpp +++ b/src/game/system/samara.cpp @@ -44,12 +44,12 @@ void samara::update(double t, double dt) math::angle_axis(math::radians(20.0f), float3{1, 0, 0}) * ((samara.chirality < 0.0f) ? math::angle_axis(math::radians(180.0f), float3{0, 0, -1}) : math::quaternion{1, 0, 0, 0}); - if (transform.local.translation.y < 0.0f) + if (transform.local.translation.y() < 0.0f) { const float zone = 200.0f; - transform.local.translation.x = math::random(-zone, zone); - transform.local.translation.y = math::random(100.0f, 150.0f); - transform.local.translation.z = math::random(-zone, zone); + transform.local.translation.x() = math::random(-zone, zone); + transform.local.translation.y() = math::random(100.0f, 150.0f); + transform.local.translation.z() = math::random(-zone, zone); transform.warp = true; samara.chirality = (math::random(0.0f, 1.0f) < 0.5f) ? -1.0f : 1.0f; diff --git a/src/game/system/subterrain.cpp b/src/game/system/subterrain.cpp index da4f3f5..d4567b7 100644 --- a/src/game/system/subterrain.cpp +++ b/src/game/system/subterrain.cpp @@ -81,14 +81,14 @@ cube_tree::cube_tree(const geom::aabb& bounds, int max_depth, int depth): max_depth(max_depth), depth(depth) { - corners[0] = {bounds.min_point.x, bounds.min_point.y, bounds.min_point.z}; - corners[1] = {bounds.max_point.x, bounds.min_point.y, bounds.min_point.z}; - corners[2] = {bounds.max_point.x, bounds.max_point.y, bounds.min_point.z}; - corners[3] = {bounds.min_point.x, bounds.max_point.y, bounds.min_point.z}; - corners[4] = {bounds.min_point.x, bounds.min_point.y, bounds.max_point.z}; - corners[5] = {bounds.max_point.x, bounds.min_point.y, bounds.max_point.z}; - corners[6] = {bounds.max_point.x, bounds.max_point.y, bounds.max_point.z}; - corners[7] = {bounds.min_point.x, bounds.max_point.y, bounds.max_point.z}; + corners[0] = {bounds.min_point.x(), bounds.min_point.y(), bounds.min_point.z()}; + corners[1] = {bounds.max_point.x(), bounds.min_point.y(), bounds.min_point.z()}; + corners[2] = {bounds.max_point.x(), bounds.max_point.y(), bounds.min_point.z()}; + corners[3] = {bounds.min_point.x(), bounds.max_point.y(), bounds.min_point.z()}; + corners[4] = {bounds.min_point.x(), bounds.min_point.y(), bounds.max_point.z()}; + corners[5] = {bounds.max_point.x(), bounds.min_point.y(), bounds.max_point.z()}; + corners[6] = {bounds.max_point.x(), bounds.max_point.y(), bounds.max_point.z()}; + corners[7] = {bounds.min_point.x(), bounds.max_point.y(), bounds.max_point.z()}; for (int i = 0; i < 8; ++i) { diff --git a/src/game/system/terrain.cpp b/src/game/system/terrain.cpp index bace8ec..cfdc20b 100644 --- a/src/game/system/terrain.cpp +++ b/src/game/system/terrain.cpp @@ -18,16 +18,13 @@ */ #include "game/system/terrain.hpp" -#include "game/component/celestial-body.hpp" -#include "game/component/observer.hpp" #include "game/component/terrain.hpp" +#include "game/component/camera.hpp" #include "geom/meshes/grid.hpp" #include "geom/mesh-functions.hpp" #include "geom/morton.hpp" #include "geom/quadtree.hpp" -#include "geom/spherical.hpp" #include "gl/vertex-attribute.hpp" -#include "math/constants.hpp" #include "math/quaternion-operators.hpp" #include "render/vertex-attribute.hpp" #include "utility/fundamental-types.hpp" @@ -39,441 +36,525 @@ namespace system { terrain::terrain(entity::registry& registry): updatable(registry), + patch_side_length(0.0f), patch_subdivisions(0), + patch_material(nullptr), + elevation_function(nullptr), + scene_collection(nullptr), patch_base_mesh(nullptr), patch_vertex_size(0), - patch_vertex_count(0), - patch_vertex_data(nullptr), - patch_scene_collection(nullptr), - max_error(0.0) + patch_vertex_stride(0), + patch_vertex_data(nullptr) { - // Build set of quaternions to rotate quadtree cube coordinates into BCBF space according to face index - face_rotations[0] = math::quaternion::identity; // +x - face_rotations[1] = math::quaternion::rotate_z(math::pi); // -x - face_rotations[2] = math::quaternion::rotate_z( math::half_pi); // +y - face_rotations[3] = math::quaternion::rotate_z(-math::half_pi); // -y - face_rotations[4] = math::quaternion::rotate_y(-math::half_pi); // +z - face_rotations[5] = math::quaternion::rotate_y( math::half_pi); // -z - // Specify vertex size and stride // (position + uv + normal + tangent + barycentric + target) patch_vertex_size = 3 + 2 + 3 + 4 + 3 + 3; patch_vertex_stride = patch_vertex_size * sizeof(float); - // Init patch subdivisions to zero - set_patch_subdivisions(0); + // Init quadtee node sizes at each depth + for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) + quadtree_node_size[i] = 0.0f; registry.on_construct().connect<&terrain::on_terrain_construct>(this); + registry.on_update().connect<&terrain::on_terrain_update>(this); registry.on_destroy().connect<&terrain::on_terrain_destroy>(this); } terrain::~terrain() { registry.on_construct().disconnect<&terrain::on_terrain_construct>(this); + registry.on_update().disconnect<&terrain::on_terrain_update>(this); registry.on_destroy().disconnect<&terrain::on_terrain_destroy>(this); } void terrain::update(double t, double dt) { - /* - // Refine the level of detail of each terrain quadsphere - registry.view().each( - [&](entity::id terrain_eid, const auto& terrain_component, const auto& terrain_body) - { - // Retrieve terrain quadsphere - terrain_quadsphere* quadsphere = terrain_quadspheres[terrain_eid]; - - // For each observer - this->registry.view().each( - [&](entity::id observer_eid, const auto& observer) + // Clear quadtree + quadtree.clear(); + + // For each camera + this->registry.view().each + ( + [&](entity::id camera_eid, const auto& camera) { - // Skip observers with invalid reference body - if (!this->registry.has(observer.reference_body_eid) || - !this->registry.has(observer.reference_body_eid)) + if (!camera.object) return; - // Get celestial body and terrain component of observer reference body - const auto& reference_celestial_body = this->registry.get(observer.reference_body_eid); - const auto& reference_terrain = this->registry.get(observer.reference_body_eid); + const scene::camera& cam = *camera.object; - // Calculate reference BCBF-space position of observer. - //double3 observer_spherical = {reference_celestial_body.radius + observer.elevation, observer.latitude, observer.longitude}; - double3 observer_spherical = {observer.elevation, observer.latitude, observer.longitude}; - double3 observer_cartesian = geom::spherical::to_cartesian(observer_spherical); + - observer_cartesian = math::type_cast(observer.camera->get_translation()); + - /// @TODO Transform observer position into BCBF space of terrain body (use orbit component?) + // for (int i = 0; i < 8; ++i) + // std::cout << "corner " << i << ": " << cam.get_view_frustum().get_corners()[i] << std::endl; - // For each terrain quadsphere face - for (int i = 0; i < 6; ++i) - { - terrain_quadsphere_face& quadsphere_face = quadsphere->faces[i]; - - // Get the quadsphere faces's quadtree - quadtree_type& quadtree = quadsphere_face.quadtree; - - // Clear quadsphere face quadtree - quadtree.clear(); - - // For each node in the face quadtree - for (auto node_it = quadtree.begin(); node_it != quadtree.end(); ++node_it) - { - quadtree_node_type node = *node_it; - - // Skip non leaf nodes - if (!quadtree.is_leaf(node)) - continue; - - // Extract node depth - quadtree_type::node_type node_depth = quadtree_type::depth(node); - - // Skip nodes at max depth level - if (node_depth >= terrain_component.max_lod) - continue; - - // Extract node location from Morton location code - quadtree_type::node_type node_location = quadtree_type::location(node); - quadtree_type::node_type node_location_x; - quadtree_type::node_type node_location_y; - geom::morton::decode(node_location, node_location_x, node_location_y); - - - - const double nodes_per_axis = std::exp2(node_depth); - const double node_width = 2.0 / nodes_per_axis; - - // Determine node center on front face of unit BCBF cube. - double3 center; - center.y = -(nodes_per_axis * 0.5 * node_width) + node_width * 0.5; - center.z = center.y; - center.y += static_cast(node_location_x) * node_width; - center.z += static_cast(node_location_y) * node_width; - center.x = 1.0; - - // Rotate node center according to cube face - /// @TODO Rather than rotating every center, "unrotate" observer position 6 times - center = face_rotations[i] * center; - - // Project node center onto unit sphere - double xx = center.x * center.x; - double yy = center.y * center.y; - double zz = center.z * center.z; - center.x *= std::sqrt(std::max(0.0, 1.0 - yy * 0.5 - zz * 0.5 + yy * zz / 3.0)); - center.y *= std::sqrt(std::max(0.0, 1.0 - xx * 0.5 - zz * 0.5 + xx * zz / 3.0)); - center.z *= std::sqrt(std::max(0.0, 1.0 - xx * 0.5 - yy * 0.5 + xx * yy / 3.0)); - - // Scale node center by body radius - center *= terrain_body.radius; - center.y -= terrain_body.radius; - //center *= 50.0; - - const double horizontal_fov = observer.camera->get_fov(); - const double horizontal_resolution = 1920.0; - const double distance = math::length(center - observer_cartesian); - const double geometric_error = static_cast(524288.0 / std::exp2(node_depth)); - const double screen_error = screen_space_error(horizontal_fov, horizontal_resolution, distance, geometric_error); - - if (screen_error > max_error) - { - //std::cout << screen_error << std::endl; - quadtree.insert(quadtree_type::child(node, 0)); - } - } - } - }); - }); - - // Handle meshes and models for each terrain patch - registry.view().each( - [&](entity::id terrain_eid, const auto& terrain_component, const auto& terrain_body) - { - // Retrieve terrain quadsphere - terrain_quadsphere* quadsphere = terrain_quadspheres[terrain_eid]; - - // For each terrain quadsphere face - for (int i = 0; i < 6; ++i) - { - terrain_quadsphere_face& quadsphere_face = quadsphere->faces[i]; - const quadtree_type& quadtree = quadsphere_face.quadtree; - - // For each quadtree node - for (auto node_it = quadtree.unordered_begin(); node_it != quadtree.unordered_end(); ++node_it) - { - quadtree_node_type node = *node_it; - - // Look up cached patch for this node - auto patch_it = quadsphere_face.patches.find(node); - - // If there is no cached patch instance for this node - if (patch_it == quadsphere_face.patches.end()) - { - // Construct a terrain patch - terrain_patch* patch = new terrain_patch(); - - // Generate a patch mesh - patch->mesh = generate_patch_mesh(i, *node_it, terrain_body.radius, terrain_component.elevation); - //patch->mesh = generate_patch_mesh(i, *node_it, 50.0, terrain_component.elevation); - - // Generate a patch model - patch->model = generate_patch_model(*patch->mesh, terrain_component.patch_material); - - // Construct patch model instance - patch->model_instance = new scene::model_instance(patch->model); - - - // Cache the terrain patch - quadsphere_face.patches[node] = patch; - - // Add patch model instance to the patch scene collection - if (patch_scene_collection) - patch_scene_collection->add_object(patch->model_instance); - } - } + geom::ray rays[8]; + rays[0] = cam.pick({-1, -1}); + rays[1] = cam.pick({-1, 1}); + rays[2] = cam.pick({ 1, 1}); + rays[3] = cam.pick({ 1, -1}); - // For each terrain pach - for (auto patch_it = quadsphere_face.patches.begin(); patch_it != quadsphere_face.patches.end(); ++patch_it) - { - quadtree_node_type node = patch_it->first; - - // Set patch model instance active if its node is a leaf node, otherwise deactivate it - bool active = (quadtree.contains(node) && quadtree.is_leaf(node)); - patch_it->second->model_instance->set_active(active); - } + float3 ntl = rays[0].origin; + float3 nbl = rays[1].origin; + float3 nbr = rays[2].origin; + float3 ntr = rays[3].origin; + + float3 ftl = rays[0].origin + rays[0].direction * (cam.get_clip_far() - cam.get_clip_near()); + float3 fbl = rays[1].origin + rays[1].direction * (cam.get_clip_far() - cam.get_clip_near()); + float3 fbr = rays[2].origin + rays[2].direction * (cam.get_clip_far() - cam.get_clip_near()); + float3 ftr = rays[3].origin + rays[3].direction * (cam.get_clip_far() - cam.get_clip_near()); + + // for (int i = 0; i < 8; ++i) + // std::cout << "ray or " << i << ": " << rays[i].origin << std::endl; + + geom::convex_hull hull(6); + hull.planes[0] = geom::plane(ftl, fbl, nbl); + hull.planes[1] = geom::plane(ntr, nbr, fbr); + hull.planes[2] = geom::plane(fbl, fbr, nbr); + hull.planes[3] = geom::plane(ftl, ntl, ntr); + hull.planes[4] = geom::plane(ntl, nbl, nbr); + hull.planes[5] = geom::plane(ftr, fbr, fbl); + + geom::sphere sphere; + sphere.center = cam.get_translation(); + sphere.radius = cam.get_clip_far() * 0.25; + + //visit_quadtree(cam.get_view_frustum().get_bounds(), quadtree_type::root); + visit_quadtree(sphere, quadtree_type::root); } - }); - */ -} + ); + + //std::cout << "qsize: " << quadtree.size() << std::endl; + std::size_t qvis = 0; -void terrain::set_patch_subdivisions(std::uint8_t n) -{ - patch_subdivisions = n; - // Rebuid patch base mesh + /// Toggle visibility of terrain scene objects + for (auto it = patches.begin(); it != patches.end(); ++it) { - delete patch_base_mesh; - patch_base_mesh = geom::meshes::grid_xy(2.0f, patch_subdivisions, patch_subdivisions); + bool active = (quadtree.contains(it->first) && quadtree.is_leaf(it->first)); + it->second->model_instance->set_active(active); - // Convert quads to triangle fans - for (std::size_t i = 0; i < patch_base_mesh->get_faces().size(); ++i) - { - geom::mesh::face* face = patch_base_mesh->get_faces()[i]; - - std::size_t edge_count = 1; - for (geom::mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) - ++edge_count; - - if (edge_count > 3) - { - geom::poke_face(*patch_base_mesh, face->index); - --i; - } - } + if (active) + ++qvis; } - // Transform patch base mesh coordinates to match the front face of a BCBF cube - const math::quaternion xy_to_zy = math::quaternion::rotate_y(-math::half_pi); - for (geom::mesh::vertex* vertex: patch_base_mesh->get_vertices()) + //std::cout << "qvis: " << qvis << std::endl; +} + +void terrain::set_patch_side_length(float length) +{ + patch_side_length = length; + + // Recalculate node sizes at each quadtree depth + for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) { - vertex->position = xy_to_zy * vertex->position; - vertex->position.x = 1.0f; + quadtree_node_size[i] = std::exp2(quadtree_type::max_depth - i) * patch_side_length; + //std::cout << quadtree_node_size[i] << std::endl; } +} + +void terrain::set_patch_subdivisions(std::size_t n) +{ + patch_subdivisions = n; - // Recalculate number of vertices per patch - patch_vertex_count = patch_base_mesh->get_faces().size() * 3; + // Recalculate patch properties + patch_cell_count = (patch_subdivisions + 1) * (patch_subdivisions + 1); + patch_triangle_count = patch_cell_count * 4; // Resize patch vertex data buffer delete[] patch_vertex_data; - patch_vertex_data = new float[patch_vertex_count * patch_vertex_size]; + patch_vertex_data = new float[patch_triangle_count * 3 * patch_vertex_size]; + + // Resize patch buffers + + std::size_t vertex_buffer_row_size = patch_subdivisions + 4; + std::size_t vertex_buffer_column_size = vertex_buffer_row_size * 2; + + patch_vertex_buffer.resize(vertex_buffer_row_size); + for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) + patch_vertex_buffer[i].resize(vertex_buffer_column_size); + + rebuild_patch_base_mesh(); +} + +void terrain::set_patch_material(::render::material* material) +{ + patch_material = material; } -void terrain::set_patch_scene_collection(scene::collection* collection) +void terrain::set_elevation_function(const std::function& f) { - patch_scene_collection = collection; + elevation_function = f; } -void terrain::set_max_error(double error) +void terrain::set_scene_collection(scene::collection* collection) { - max_error = error; + scene_collection = collection; } void terrain::on_terrain_construct(entity::registry& registry, entity::id entity_id) { - terrain_quadsphere* quadsphere = new terrain_quadsphere(); - terrain_quadspheres[entity_id] = quadsphere; +} + +void terrain::on_terrain_update(entity::registry& registry, entity::id entity_id) +{ } void terrain::on_terrain_destroy(entity::registry& registry, entity::id entity_id) { - // Find terrain quadsphere for the given entity ID - auto quadsphere_it = terrain_quadspheres.find(entity_id); +} + +float terrain::get_patch_size(quadtree_node_type node) const +{ + return quadtree_node_size[quadtree_type::depth(node)]; +} + +float3 terrain::get_patch_center(quadtree_node_type node) const +{ + const float node_size = get_patch_size(node); + const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; - if (quadsphere_it != terrain_quadspheres.end()) + // Extract node location from Morton location code + quadtree_type::node_type node_location = quadtree_type::location(node); + quadtree_type::node_type node_location_x; + quadtree_type::node_type node_location_y; + geom::morton::decode(node_location, node_location_x, node_location_y); + + return float3 { - terrain_quadsphere* quadsphere = quadsphere_it->second; + node_offset + static_cast(node_location_x) * node_size, + 0.0f, + node_offset + static_cast(node_location_y) * node_size + }; +} + +void terrain::rebuild_patch_base_mesh() +{ + // Rebuild grid + delete patch_base_mesh; + patch_base_mesh = geom::meshes::grid_xy(1.0f, patch_subdivisions, patch_subdivisions); + + // Convert quads to triangle fans + for (std::size_t i = 0; i < patch_base_mesh->get_faces().size(); ++i) + { + geom::mesh::face* face = patch_base_mesh->get_faces()[i]; + + std::size_t edge_count = 1; + for (geom::mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) + ++edge_count; - // For each terrain quadsphere face - for (int i = 0; i < 6; ++i) + if (edge_count > 3) { - terrain_quadsphere_face& quadsphere_face = quadsphere->faces[i]; + geom::poke_face(*patch_base_mesh, face->index); + --i; + } + } + + // Transform patch base mesh coordinates from XY plane to XZ plane + const math::quaternion xy_to_xz = math::quaternion::rotate_x(math::half_pi); + for (geom::mesh::vertex* vertex: patch_base_mesh->get_vertices()) + { + vertex->position = xy_to_xz * vertex->position; + } +} + +void terrain::visit_quadtree(const geom::bounding_volume& volume, quadtree_node_type node) +{ + const float root_offset = quadtree_node_size[0] * -0.5f; + + // Extract node depth + quadtree_type::node_type node_depth = quadtree_type::depth(node); + + const float node_size = get_patch_size(node); + const float3 node_center = get_patch_center(node); + + // Build node bounds AABB + geom::aabb node_bounds; + node_bounds.min_point = + { + node_center.x() - node_size * 0.5f, + quadtree_node_size[quadtree_type::max_depth] * -0.5f, + node_center.z() - node_size * 0.5f + }; + node_bounds.max_point = + { + node_bounds.min_point[0] + node_size, + node_bounds.min_point[1] + quadtree_node_size[quadtree_type::max_depth], + node_bounds.min_point[2] + node_size + }; + + // If volume intersects node + if (volume.intersects(node_bounds)) + { + // Subdivide leaf nodes + if (quadtree.is_leaf(node)) + { + quadtree.insert(quadtree_type::child(node, 0)); - for (auto patch_it = quadsphere_face.patches.begin(); patch_it != quadsphere_face.patches.end(); ++patch_it) + for (quadtree_node_type i = 0; i < quadtree_type::children_per_node; ++i) { - terrain_patch* patch = patch_it->second; + quadtree_node_type child = quadtree_type::child(node, i); - if (patch_scene_collection) - patch_scene_collection->remove_object(patch->model_instance); - - delete patch->model_instance; - delete patch->model; - delete patch->mesh; - - delete patch; + if (patches.find(child) == patches.end()) + { + patch* child_patch = generate_patch(child); + patches[child] = child_patch; + scene_collection->add_object(child_patch->model_instance); + } } } - // Free terrain quadsphere - delete quadsphere; + // Visit children + if (node_depth < quadtree_type::max_depth - 1) + { + for (quadtree_node_type i = 0; i < quadtree_type::children_per_node; ++i) + visit_quadtree(volume, quadtree_type::child(node, i)); + } } - - // Remove terrain quadsphere from the map - terrain_quadspheres.erase(quadsphere_it); } -geom::mesh* terrain::generate_patch_mesh(std::uint8_t face_index, quadtree_node_type node, double body_radius, const std::function& elevation) const +geom::mesh* terrain::generate_patch_mesh(quadtree_node_type node) const { // Extract node depth - const quadtree_type::node_type depth = quadtree_type::depth(node); - - // Extract node Morton location code and decode location - const quadtree_type::node_type location = quadtree_type::location(node); - quadtree_type::node_type location_x; - quadtree_type::node_type location_y; - geom::morton::decode(location, location_x, location_y); - - const double nodes_per_axis = std::exp2(depth); - - const double scale_yz = 1.0 / nodes_per_axis; + const quadtree_type::node_type node_depth = quadtree_type::depth(node); - const double node_width = 2.0 / nodes_per_axis; + // Get size of node at depth + const float node_size = quadtree_node_size[node_depth]; - // Determine vertex offset according to node location - double offset_y = -(nodes_per_axis * 0.5 * node_width) + node_width * 0.5; - double offset_z = offset_y; - offset_y += static_cast(location_x) * node_width; - offset_z += static_cast(location_y) * node_width; + // Extract node Morton location code and decode location + const quadtree_type::node_type node_location = quadtree_type::location(node); + quadtree_type::node_type node_location_x; + quadtree_type::node_type node_location_y; + geom::morton::decode(node_location, node_location_x, node_location_y); + + // Determine center of node + const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; + const float3 node_center = + { + node_offset + static_cast(node_location_x) * node_size, + 0.0f, + node_offset + static_cast(node_location_y) * node_size + }; - // Copy base mesh + // Copy patch base mesh geom::mesh* patch_mesh = new geom::mesh(*patch_base_mesh); // Modify patch mesh vertex positions for (geom::mesh::vertex* v: patch_mesh->get_vertices()) { - double3 position = math::type_cast(v->position); - - // Offset and scale vertex position - position.y *= scale_yz; - position.z *= scale_yz; - position.y += offset_y; - position.z += offset_z; - - // Rotate according to cube face - position = face_rotations[face_index] * position; - - // Project onto unit sphere - //position = math::normalize(position); - - // Cartesian Spherical Cube projection (KSC) - /// @see https://catlikecoding.com/unity/tutorials/cube-sphere/ - /// @see https://core.ac.uk/download/pdf/228552506.pdf - double xx = position.x * position.x; - double yy = position.y * position.y; - double zz = position.z * position.z; - position.x *= std::sqrt(std::max(0.0, 1.0 - yy * 0.5 - zz * 0.5 + yy * zz / 3.0)); - position.y *= std::sqrt(std::max(0.0, 1.0 - xx * 0.5 - zz * 0.5 + xx * zz / 3.0)); - position.z *= std::sqrt(std::max(0.0, 1.0 - xx * 0.5 - yy * 0.5 + xx * yy / 3.0)); - - // Calculate latitude and longitude of vertex position - const double latitude = std::atan2(position.z, std::sqrt(position.x * position.x + position.y * position.y)); - const double longitude = std::atan2(position.y, position.x); - - // Look up elevation at latitude and longitude and use to calculate radial distance - const double radial_distance = body_radius + elevation(latitude, longitude); - - // Scale vertex position by radial distance - position *= radial_distance; - position.y -= body_radius; - - v->position = math::type_cast(position); + v->position.x() = node_center.x() + v->position.x() * node_size; + v->position.z() = node_center.z() + v->position.z() * node_size; + v->position.y() = elevation_function(v->position.x(), v->position.z()); } return patch_mesh; } -::render::model* terrain::generate_patch_model(const geom::mesh& patch_mesh, ::render::material* patch_material) const +::render::model* terrain::generate_patch_model(quadtree_node_type node) const { - // Barycentric coordinates - static const float3 barycentric[3] = + // Get size and position of patch + const float patch_size = get_patch_size(node); + const float3 patch_center = get_patch_center(node); + + // Calculate size of a patch cell + const float cell_size = patch_size / static_cast(patch_subdivisions + 1); + const float half_cell_size = cell_size * 0.5f; + + // Init patch bounds + geom::aabb patch_bounds; + patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f; + patch_bounds.min_point.y() = std::numeric_limits::infinity(); + patch_bounds.min_point.z() = patch_center.z() - patch_size * 0.5f; + patch_bounds.max_point.x() = patch_center.x() + patch_size * 0.5f; + patch_bounds.max_point.y() = -std::numeric_limits::infinity(); + patch_bounds.max_point.z() = patch_center.z() + patch_size * 0.5f; + + // Calculate positions of patch vertices and immediately neighboring vertices + float3 first_vertex_position = { - {1, 0, 0}, - {0, 1, 0}, - {0, 0, 1} + patch_bounds.min_point.x() - cell_size, + patch_center.y(), + patch_bounds.min_point.z() - cell_size }; - - // Fill vertex data buffer - float* v = patch_vertex_data; - for (const geom::mesh::face* face: patch_mesh.get_faces()) + float3 vertex_position = first_vertex_position; + for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) { - const geom::mesh::vertex* a = face->edge->vertex; - const geom::mesh::vertex* b = face->edge->next->vertex; - const geom::mesh::vertex* c = face->edge->previous->vertex; - const geom::mesh::vertex* face_vertices[] = {a, b, c}; + // For each column + for (std::size_t j = 0; j < patch_vertex_buffer[i].size(); j += 2) + { + // Get elevation of vertex + vertex_position.y() = elevation_function(vertex_position.x(), vertex_position.z()); + + // Update patch bounds + patch_bounds.min_point.y() = std::min(patch_bounds.min_point.y(), vertex_position.y()); + patch_bounds.max_point.y() = std::max(patch_bounds.max_point.y(), vertex_position.y()); + + patch_vertex_buffer[i][j].position = vertex_position; + + vertex_position.x() += cell_size; + } - // Calculate facted normal - float3 normal = math::normalize(math::cross(b->position - a->position, c->position - a->position)); + vertex_position.z() += cell_size; + vertex_position.x() = first_vertex_position.x(); + } + + first_vertex_position.x() += cell_size * 0.5f; + first_vertex_position.z() += cell_size * 0.5f; + vertex_position = first_vertex_position; + for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) + { + // For each column + for (std::size_t j = 1; j < patch_vertex_buffer[i].size(); j += 2) + { + // Get elevation of vertex + vertex_position.y() = elevation_function(vertex_position.x(), vertex_position.z()); + + // Update patch bounds + patch_bounds.min_point.y() = std::min(patch_bounds.min_point.y(), vertex_position.y()); + patch_bounds.max_point.y() = std::max(patch_bounds.max_point.y(), vertex_position.y()); + + patch_vertex_buffer[i][j].position = vertex_position; + + vertex_position.x() += cell_size; + } - for (std::size_t i = 0; i < 3; ++i) + vertex_position.z() += cell_size; + vertex_position.x() = first_vertex_position.x(); + } + + // Calculate tangents, bitangents, and normals of patch vertices + for (std::size_t i = 1; i < patch_vertex_buffer.size() - 1; ++i) + { + // For each column + for (std::size_t j = 2; j < patch_vertex_buffer[i].size() - 3; ++j) + { + const float3& c = patch_vertex_buffer[i ][j ].position; + const float3& n = patch_vertex_buffer[i+1][j ].position; + const float3& s = patch_vertex_buffer[i-1][j ].position; + const float3& e = patch_vertex_buffer[i ][j+2].position; + const float3& w = patch_vertex_buffer[i ][j-2].position; + + const float3 tangent = math::normalize(float3{2.0f, (e.y() - w.y()) / cell_size, 0.0f}); + const float3 bitangent = math::normalize(float3{0.0f, (n.y() - s.y()) / cell_size, 2.0f}); + const float3 normal = math::cross(bitangent, tangent); + const float bitangent_sign = std::copysign(1.0f, math::dot(math::cross(normal, tangent), bitangent)); + + patch_vertex_buffer[i][j].uv.x() = (c.x() - patch_bounds.min_point.x()) / patch_size; + patch_vertex_buffer[i][j].uv.y() = (c.z() - patch_bounds.min_point.z()) / patch_size; + patch_vertex_buffer[i][j].normal = normal; + patch_vertex_buffer[i][j].tangent = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; + } + } + + /* + + 0 subdivisions: + +---+---+---+ + | | + + +---+ + + | | | | + + +---+ + + | | + +---+---+---+ + + 1 subdivision: + +---+---+---+---+ + | | + + +---+---+ + + | | | | | + + +---+---+ + + | | | | | + + +---+---+ + + | | + +---+---+---+---+ + + 2 subdivisions: + +---+---+---+---+---+ + | x x x x x | x + + +---+---+---+ + + | x | x | x | x | x | x + + +---+---+---+ + + | x | x | x | x | x | x + + +---+---+---+ + + | x | x | x | x | x | x + + +---+---+---+ + + | x x x x x | x + +---+---+---+---+---+ + x x x x x x + */ + + // For each row + float* v = patch_vertex_data; + for (std::size_t i = 1; i < patch_vertex_buffer.size() - 2; ++i) + { + // For each column + for (std::size_t j = 3; j < patch_vertex_buffer[i].size() - 4; j += 2) { - const geom::mesh::vertex* vertex = face_vertices[i]; + // a---c + // | x | + // b---d - // Vertex position - const float3& position = vertex->position; - *(v++) = position.x; - *(v++) = position.y; - *(v++) = position.z; + const patch_vertex& x = patch_vertex_buffer[i ][j ]; + const patch_vertex& a = patch_vertex_buffer[i ][j-1]; + const patch_vertex& b = patch_vertex_buffer[i+1][j-1]; + const patch_vertex& c = patch_vertex_buffer[i ][j+1]; + const patch_vertex& d = patch_vertex_buffer[i+1][j+1]; - // Vertex UV coordinates (latitude, longitude) - const float latitude = std::atan2(position.z, std::sqrt(position.x * position.x + position.y * position.y)); - const float longitude = std::atan2(position.y, position.x); - *(v++) = latitude; - *(v++) = longitude; + const float4& td = patch_vertex_buffer[i+1][j+1].tangent; + + auto add_triangle = [&v](const patch_vertex& a, const patch_vertex& b, const patch_vertex& c) + { + auto add_vertex = [&v](const patch_vertex& vertex, const float3& barycentric) + { + // Position + *(v++) = vertex.position[0]; + *(v++) = vertex.position[1]; + *(v++) = vertex.position[2]; + + // UV + *(v++) = vertex.uv[0]; + *(v++) = vertex.uv[1]; + + // Normal + *(v++) = vertex.normal[0]; + *(v++) = vertex.normal[1]; + *(v++) = vertex.normal[2]; + + /// Tangent + *(v++) = vertex.tangent[0]; + *(v++) = vertex.tangent[1]; + *(v++) = vertex.tangent[2]; + *(v++) = vertex.tangent[3]; + + // Barycentric + *(v++) = barycentric[0]; + *(v++) = barycentric[1]; + *(v++) = barycentric[2]; + + // Morph target (LOD transition) + *(v++) = 0.0f; + *(v++) = 0.0f; + *(v++) = 0.0f; + }; + + add_vertex(a, float3{1, 0, 0}); + add_vertex(b, float3{0, 1, 0}); + add_vertex(c, float3{0, 0, 1}); + }; - // Vertex normal - *(v++) = normal.x; - *(v++) = normal.y; - *(v++) = normal.z; - /// @TODO Vertex tangent - *(v++) = 0.0f; - *(v++) = 0.0f; - *(v++) = 0.0f; - *(v++) = 0.0f; + add_triangle(x, a, b); + add_triangle(x, b, d); + add_triangle(x, d, c); + add_triangle(x, c, a); - // Vertex barycentric coordinates - *(v++) = barycentric[i].x; - *(v++) = barycentric[i].y; - *(v++) = barycentric[i].z; - // Vertex morph target (LOD transition) - *(v++) = 0.0f; - *(v++) = 0.0f; - *(v++) = 0.0f; + // add_triangle(a, b, c); + // add_triangle(c, b, d); } } - // Get triangle count of patch mesh - std::size_t patch_triangle_count = patch_mesh.get_faces().size(); - // Allocate patch model ::render::model* patch_model = new ::render::model(); @@ -555,19 +636,19 @@ geom::mesh* terrain::generate_patch_mesh(std::uint8_t face_index, quadtree_node_ patch_model_group->set_start_index(0); patch_model_group->set_index_count(patch_triangle_count * 3); - // Calculate model bounds - geom::aabb bounds = geom::calculate_bounds(patch_mesh); - patch_model->set_bounds(bounds); + // Set patch model bounds + patch_model->set_bounds(patch_bounds); return patch_model; } -double terrain::screen_space_error(double horizontal_fov, double horizontal_resolution, double distance, double geometric_error) +terrain::patch* terrain::generate_patch(quadtree_node_type node) { - // Calculate view frustum width at given distance - const double frustum_width = 2.0 * distance * std::tan(horizontal_fov * 0.5); - - return (geometric_error * horizontal_resolution) / frustum_width; + patch* node_patch = new patch(); + node_patch->mesh = nullptr;//generate_patch_mesh(node); + node_patch->model = generate_patch_model(node); + node_patch->model_instance = new scene::model_instance(node_patch->model); + return node_patch; } } // namespace system diff --git a/src/game/system/terrain.hpp b/src/game/system/terrain.hpp index 04c1498..f420c6a 100644 --- a/src/game/system/terrain.hpp +++ b/src/game/system/terrain.hpp @@ -31,13 +31,14 @@ #include "render/material.hpp" #include "scene/model-instance.hpp" #include "scene/collection.hpp" +#include "geom/view-frustum.hpp" #include namespace game { namespace system { /** - * Generates and manages terrain with LOD based on distance to observers. + * Generates terrain patches and performs terrain patch LOD selection. */ class terrain: public updatable { @@ -48,87 +49,107 @@ public: virtual void update(double t, double dt); /** - * Sets the number of subdivisions for a patch. Zero subdivisions results in a single quad, one subdivison results in four quads, etc. + * Sets the size of a patch. * - * @param n Number of subdivisions. + * @param length Side length of a patch. */ - void set_patch_subdivisions(std::uint8_t n); + void set_patch_side_length(float length); /** - * Sets the scene collection into which terrain patch model instances will be inserted. + * Sets the number of subdivisions of a patch. Zero subdivisions results in a single quad, one subdivison results in four quads, etc. + * + * @param n Number of subdivisions. */ - void set_patch_scene_collection(scene::collection* collection); + void set_patch_subdivisions(std::size_t n); /** - * Sets the maximum tolerable screen-space error. + * Sets the material of each patch. * - * If the screen-space error of a terrain patch exceeds the maximum tolerable value, it will be subdivided. + * @param material Patch material. + */ + void set_patch_material(::render::material* material); + + /** + * Sets the terrain elevation function. * - * @param error Maximum tolerable screen-space error. + * @param f Function which returns the terrain height (Y-coordinate) given X- and Z-coordinates. + */ + void set_elevation_function(const std::function& f); + + /** + * Sets the scene collection into which terrain patch model instances will be inserted. */ - void set_max_error(double error); + void set_scene_collection(scene::collection* collection); private: - typedef geom::quadtree64 quadtree_type; + typedef geom::quadtree32 quadtree_type; typedef quadtree_type::node_type quadtree_node_type; - struct terrain_patch + struct patch { geom::mesh* mesh; ::render::model* model; scene::model_instance* model_instance; - float error; - float morph; }; - /// Single face of a terrain quadsphere - struct terrain_quadsphere_face - { - /// Quadtree describing level of detail - quadtree_type quadtree; - - /// Map linking quadtree nodes to terrain patches - std::unordered_map patches; - }; + void on_terrain_construct(entity::registry& registry, entity::id entity_id); + void on_terrain_update(entity::registry& registry, entity::id entity_id); + void on_terrain_destroy(entity::registry& registry, entity::id entity_id); - /// A terrain quadsphere with six faces. - struct terrain_quadsphere - { - /// Array of six terrain quadsphere faces, in the order of +x, -x, +y, -y, +z, -z. - terrain_quadsphere_face faces[6]; - }; + float get_patch_size(quadtree_node_type node) const; + float3 get_patch_center(quadtree_node_type node) const; + void rebuild_patch_base_mesh(); - static double screen_space_error(double horizontal_fov, double horizontal_resolution, double distance, double geometric_error); + - void on_terrain_construct(entity::registry& registry, entity::id entity_id); - void on_terrain_destroy(entity::registry& registry, entity::id entity_id); + void visit_quadtree(const geom::bounding_volume& volume, quadtree_node_type node); + + /** * Generates a mesh for a terrain patch given the patch's quadtree node */ - geom::mesh* generate_patch_mesh(std::uint8_t face_index, quadtree_node_type node, double body_radius, const std::function& elevation) const; + geom::mesh* generate_patch_mesh(quadtree_node_type node) const; /** * Generates a model for a terrain patch given the patch's mesh. */ - ::render::model* generate_patch_model(const geom::mesh& patch_mesh, ::render::material* patch_material) const; - - + ::render::model* generate_patch_model(quadtree_node_type node) const; - /// @TODO horizon culling + patch* generate_patch(quadtree_node_type node); - std::uint8_t patch_subdivisions; + float patch_side_length; + std::size_t patch_subdivisions; + std::size_t patch_cell_count; + std::size_t patch_triangle_count; std::size_t patch_vertex_size; std::size_t patch_vertex_stride; - std::size_t patch_vertex_count; float* patch_vertex_data; - math::quaternion face_rotations[6]; + + struct patch_vertex + { + float3 position; + float2 uv; + float3 normal; + float4 tangent; + }; + + mutable std::vector> patch_vertex_buffer; + + + ::render::material* patch_material; + std::function elevation_function; + scene::collection* scene_collection; + geom::mesh* patch_base_mesh; - scene::collection* patch_scene_collection; - double max_error; - std::unordered_map terrain_quadspheres; + /// Quadtree describing level of detail + quadtree_type quadtree; + float quadtree_node_size[quadtree_type::max_depth + 1]; + + /// Map linking quadtree nodes to terrain patches + std::unordered_map patches; }; } // namespace system diff --git a/src/game/world.cpp b/src/game/world.cpp index e1cd8e2..f7850fa 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -139,21 +139,9 @@ void set_location(game::context& ctx, double elevation, double latitude, double { entity::id observer_eid = it->second; - if (observer_eid != entt::null) + if (ctx.entity_registry->valid(observer_eid) && ctx.entity_registry->all_of(observer_eid)) { - // Get pointer to observer component - const auto observer = ctx.entity_registry->try_get(observer_eid); - - // Set observer location - if (observer) - { - observer->elevation = elevation; - observer->latitude = latitude; - observer->longitude = longitude; - ctx.entity_registry->replace(observer_eid, *observer); - } - - /* + // Update observer location ctx.entity_registry->patch ( observer_eid, @@ -164,7 +152,6 @@ void set_location(game::context& ctx, double elevation, double latitude, double component.longitude = longitude; } ); - */ } } } @@ -193,7 +180,7 @@ void set_time(game::context& ctx, int year, int month, int day, int hour, int mi if (auto it = ctx.entities.find("observer"); it != ctx.entities.end()) { entity::id observer_eid = it->second; - if (observer_eid != entt::null) + if (ctx.entity_registry->valid(observer_eid)) { const auto observer = ctx.entity_registry->try_get(observer_eid); if (observer) @@ -330,12 +317,12 @@ void create_stars(game::context& ctx) double brightness = physics::light::vmag::to_brightness(vmag); // Build vertex - *(star_vertex++) = static_cast(position.x); - *(star_vertex++) = static_cast(position.y); - *(star_vertex++) = static_cast(position.z); - *(star_vertex++) = static_cast(color_acescg.x); - *(star_vertex++) = static_cast(color_acescg.y); - *(star_vertex++) = static_cast(color_acescg.z); + *(star_vertex++) = static_cast(position.x()); + *(star_vertex++) = static_cast(position.y()); + *(star_vertex++) = static_cast(position.z()); + *(star_vertex++) = static_cast(color_acescg.x()); + *(star_vertex++) = static_cast(color_acescg.y()); + *(star_vertex++) = static_cast(color_acescg.z()); *(star_vertex++) = static_cast(brightness); // Calculate spectral illuminance @@ -434,7 +421,7 @@ void create_sun(game::context& ctx) // Add sun light scene objects to surface scene ctx.surface_scene->add_object(sun_light); ctx.surface_scene->add_object(sky_light); - ctx.surface_scene->add_object(bounce_light); + //ctx.surface_scene->add_object(bounce_light); // Pass direct sun light scene object to shadow map pass and astronomy system ctx.surface_shadow_map_pass->set_light(sun_light); @@ -490,17 +477,6 @@ void create_earth(game::context& ctx) // Assign orbital parent ctx.entity_registry->get(earth_eid).parent = ctx.entities["em_bary"]; - - // Assign earth terrain component - game::component::terrain terrain; - terrain.elevation = [](double, double) -> double - { - //return math::random(0.0, 1.0); - return 0.0; - }; - terrain.max_lod = 0; - terrain.patch_material = nullptr; - //ctx.entity_registry->emplace(earth_eid, terrain); } catch (const std::exception&) { diff --git a/src/geom/aabb.hpp b/src/geom/aabb.hpp index 408b66b..732d337 100644 --- a/src/geom/aabb.hpp +++ b/src/geom/aabb.hpp @@ -106,7 +106,7 @@ aabb aabb::transform(const aabb& a, const matrix_type& m) for (std::size_t i = 0; i < 8; ++i) { vector_type corner = a.corner(i); - math::vector transformed_corner = math::mul(m, math::vector{corner.x, corner.y, corner.z, T(1)}); + math::vector transformed_corner = math::mul(m, math::vector{corner.x(), corner.y(), corner.z(), T(1)}); for (std::size_t j = 0; j < 3; ++j) { @@ -144,11 +144,11 @@ bool aabb::intersects(const sphere& sphere) const template bool aabb::intersects(const aabb& aabb) const { - if (max_point.x < aabb.min_point.x || min_point.x > aabb.max_point.x) + if (max_point.x() < aabb.min_point.x() || min_point.x() > aabb.max_point.x()) return false; - if (max_point.y < aabb.min_point.y || min_point.y > aabb.max_point.y) + if (max_point.y() < aabb.min_point.y() || min_point.y() > aabb.max_point.y()) return false; - if (max_point.z < aabb.min_point.z || min_point.z > aabb.max_point.z) + if (max_point.z() < aabb.min_point.z() || min_point.z() > aabb.max_point.z()) return false; return true; } @@ -156,11 +156,11 @@ bool aabb::intersects(const aabb& aabb) const template bool aabb::contains(const sphere& sphere) const { - if (sphere.center.x - sphere.radius < min_point.x || sphere.center.x + sphere.radius > max_point.x) + if (sphere.center.x() - sphere.radius < min_point.x() || sphere.center.x() + sphere.radius > max_point.x()) return false; - if (sphere.center.y - sphere.radius < min_point.y || sphere.center.y + sphere.radius > max_point.y) + if (sphere.center.y() - sphere.radius < min_point.y() || sphere.center.y() + sphere.radius > max_point.y()) return false; - if (sphere.center.z - sphere.radius < min_point.z || sphere.center.z + sphere.radius > max_point.z) + if (sphere.center.z() - sphere.radius < min_point.z() || sphere.center.z() + sphere.radius > max_point.z()) return false; return true; } @@ -168,11 +168,11 @@ bool aabb::contains(const sphere& sphere) const template bool aabb::contains(const aabb& aabb) const { - if (aabb.min_point.x < min_point.x || aabb.max_point.x > max_point.x) + if (aabb.min_point.x() < min_point.x() || aabb.max_point.x() > max_point.x()) return false; - if (aabb.min_point.y < min_point.y || aabb.max_point.y > max_point.y) + if (aabb.min_point.y() < min_point.y() || aabb.max_point.y() > max_point.y()) return false; - if (aabb.min_point.z < min_point.z || aabb.max_point.z > max_point.z) + if (aabb.min_point.z() < min_point.z() || aabb.max_point.z() > max_point.z()) return false; return true; } @@ -180,11 +180,11 @@ bool aabb::contains(const aabb& aabb) const template bool aabb::contains(const vector_type& point) const { - if (point.x < min_point.x || point.x > max_point.x) + if (point.x() < min_point.x() || point.x() > max_point.x()) return false; - if (point.y < min_point.y || point.y > max_point.y) + if (point.y() < min_point.y() || point.y() > max_point.y()) return false; - if (point.z < min_point.z || point.z > max_point.z) + if (point.z() < min_point.z() || point.z() > max_point.z()) return false; return true; } diff --git a/src/geom/cartesian.hpp b/src/geom/cartesian.hpp index 360990e..b7632a0 100644 --- a/src/geom/cartesian.hpp +++ b/src/geom/cartesian.hpp @@ -42,13 +42,13 @@ math::vector3 to_spherical(const math::vector3& v); template math::vector3 to_spherical(const math::vector3& v) { - const T xx_yy = v.x * v.x + v.y * v.y; + const T xx_yy = v.x() * v.x() + v.y() * v.y(); return math::vector3 { - std::sqrt(xx_yy + v.z * v.z), - std::atan2(v.z, std::sqrt(xx_yy)), - std::atan2(v.y, v.x) + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) }; } diff --git a/src/geom/convex-hull.hpp b/src/geom/convex-hull.hpp index a847c40..d0779cf 100644 --- a/src/geom/convex-hull.hpp +++ b/src/geom/convex-hull.hpp @@ -86,9 +86,9 @@ bool convex_hull::intersects(const aabb& aabb) const math::vector pv; for (const plane& plane: planes) { - pv.x = (plane.normal.x > T(0)) ? aabb.max_point.x : aabb.min_point.x; - pv.y = (plane.normal.y > T(0)) ? aabb.max_point.y : aabb.min_point.y; - pv.z = (plane.normal.z > T(0)) ? aabb.max_point.z : aabb.min_point.z; + pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); if (plane.signed_distance(pv) < T(0)) return false; } @@ -113,12 +113,12 @@ bool convex_hull::contains(const aabb& aabb) const for (const plane& plane: planes) { - pv.x = (plane.normal.x > T(0)) ? aabb.max_point.x : aabb.min_point.x; - pv.y = (plane.normal.y > T(0)) ? aabb.max_point.y : aabb.min_point.y; - pv.z = (plane.normal.z > T(0)) ? aabb.max_point.z : aabb.min_point.z; - nv.x = (plane.normal.x < T(0)) ? aabb.max_point.x : aabb.min_point.x; - nv.y = (plane.normal.y < T(0)) ? aabb.max_point.y : aabb.min_point.y; - nv.z = (plane.normal.z < T(0)) ? aabb.max_point.z : aabb.min_point.z; + pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); + nv.x() = (plane.normal.x() < T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + nv.y() = (plane.normal.y() < T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + nv.z() = (plane.normal.z() < T(0)) ? aabb.max_point.z() : aabb.min_point.z(); if (plane.signed_distance(pv) < T(0) || plane.signed_distance(nv) < T(0)) return false; diff --git a/src/geom/hyperoctree.hpp b/src/geom/hyperoctree.hpp index 6ffb4f9..29b93e2 100644 --- a/src/geom/hyperoctree.hpp +++ b/src/geom/hyperoctree.hpp @@ -191,7 +191,7 @@ public: * Returns the nth child of a node. * * @param node Parent node. - * @param n Offset to the nth sibling of the first child node. (Automatically wraps to 0..7) + * @param n Offset to the nth sibling of the first child node. (Automatically wraps to 0..children_per_node-1) * @return nth child node. */ static node_type child(node_type node, T n); @@ -405,7 +405,7 @@ void hyperoctree::clear() template inline bool hyperoctree::contains(node_type node) const { - return (nodes.find(node) != nodes.end()); + return nodes.count(node); } template diff --git a/src/geom/intersection.cpp b/src/geom/intersection.cpp index 55a1b43..067b5b7 100644 --- a/src/geom/intersection.cpp +++ b/src/geom/intersection.cpp @@ -160,11 +160,11 @@ std::tuple ray_mesh_intersection(c bool aabb_aabb_intersection(const aabb& a, const aabb& b) { - if (a.max_point.x < b.min_point.x || a.min_point.x > b.max_point.x) + if (a.max_point.x() < b.min_point.x() || a.min_point.x() > b.max_point.x()) return false; - if (a.max_point.y < b.min_point.y || a.min_point.y > b.max_point.y) + if (a.max_point.y() < b.min_point.y() || a.min_point.y() > b.max_point.y()) return false; - if (a.max_point.z < b.min_point.z || a.min_point.z > b.max_point.z) + if (a.max_point.z() < b.min_point.z() || a.min_point.z() > b.max_point.z()) return false; return true; } diff --git a/src/geom/mesh-accelerator.cpp b/src/geom/mesh-accelerator.cpp index 197000d..588672e 100644 --- a/src/geom/mesh-accelerator.cpp +++ b/src/geom/mesh-accelerator.cpp @@ -160,18 +160,18 @@ octree32::node_type mesh_accelerator::find_node(const float3& point) const // Account for floating-point tolerance const float epsilon = 0.00001f; - transformed_point.x = std::max(0.0f, std::min(node_dimensions[0].x - epsilon, transformed_point.x)); - transformed_point.y = std::max(0.0f, std::min(node_dimensions[0].y - epsilon, transformed_point.y)); - transformed_point.z = std::max(0.0f, std::min(node_dimensions[0].z - epsilon, transformed_point.z)); + transformed_point.x() = std::max(0.0f, std::min(node_dimensions[0].x() - epsilon, transformed_point.x())); + transformed_point.y() = std::max(0.0f, std::min(node_dimensions[0].y() - epsilon, transformed_point.y())); + transformed_point.z() = std::max(0.0f, std::min(node_dimensions[0].z() - epsilon, transformed_point.z())); // Transform point to max-depth node space transformed_point = transformed_point / node_dimensions[octree32::max_depth]; // Encode transformed point as a Morton location code std::uint32_t location = morton::encode( - static_cast(transformed_point.x), - static_cast(transformed_point.y), - static_cast(transformed_point.z)); + static_cast(transformed_point.x()), + static_cast(transformed_point.y()), + static_cast(transformed_point.z())); // Return max depth node at the determined location return octree32::node(octree32::max_depth, location); diff --git a/src/geom/mesh-functions.cpp b/src/geom/mesh-functions.cpp index cebb6f4..52f3b04 100644 --- a/src/geom/mesh-functions.cpp +++ b/src/geom/mesh-functions.cpp @@ -128,9 +128,9 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float2 uvba = uvb - uva; float2 uvca = uvc - uva; - float f = 1.0f / (uvba.x * uvca.y - uvca.x * uvba.y); - float3 tangent = (ba * uvca.y - ca * uvba.y) * f; - float3 bitangent = (ba * -uvca.x + ca * uvba.x) * f; + float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); + float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f; + float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f; tangent_buffer[ia] += tangent; tangent_buffer[ib] += tangent; @@ -153,7 +153,7 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const // Calculate bitangent sign float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f; - tangents[i] = {tangent.x, tangent.y, tangent.z, bitangent_sign}; + tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; } // Free faceted tangents and bitangents @@ -189,49 +189,47 @@ mesh::vertex* poke_face(mesh& mesh, std::size_t index) mesh::face* face = mesh.get_faces()[index]; // Collect face edges and sum edge vertex positions - std::vector edges = {face->edge}; + mesh::loop loop = {face->edge}; float3 sum_positions = face->edge->vertex->position; for (mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) { - edges.push_back(edge); + loop.push_back(edge); sum_positions += edge->vertex->position; } - if (edges.size() > 2) + if (loop.size() <= 2) + return nullptr; + + // Remove face + mesh.remove_face(face); + + // Add vertex in center + mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast(loop.size())); + + // Create first triangle + geom::mesh::edge* ab = loop[0]; + geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center); + geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex); + mesh.add_face({ab, bc, ca}); + + // Save first triangle CA edge + geom::mesh::edge* first_triangle_ca = ca; + + // Create remaining triangles + for (std::size_t i = 1; i < loop.size(); ++i) { - // Remove face - mesh.remove_face(face); + ab = loop[i]; + ca = bc->symmetric; - // Add vertex in center - mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast(edges.size())); + if (i == loop.size() - 1) + bc = first_triangle_ca->symmetric; + else + bc = mesh.add_edge(ab->next->vertex, center); - // Create first triangle - geom::mesh::edge* ab = edges[0]; - geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center); - geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex); mesh.add_face({ab, bc, ca}); - - // Save first triangle CA edge - geom::mesh::edge* first_triangle_ca = ca; - - // Create remaining triangles - for (std::size_t i = 1; i < edges.size(); ++i) - { - ab = edges[i]; - ca = bc->symmetric; - - if (i == edges.size() - 1) - bc = first_triangle_ca->symmetric; - else - bc = mesh.add_edge(ab->next->vertex, center); - - mesh.add_face({ab, bc, ca}); - } - - return center; } - return nullptr; + return center; } } // namespace geom diff --git a/src/geom/mesh.cpp b/src/geom/mesh.cpp index a5f2150..a4ac37e 100644 --- a/src/geom/mesh.cpp +++ b/src/geom/mesh.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include "mesh.hpp" +#include "geom/mesh.hpp" #include namespace geom { @@ -157,21 +157,21 @@ mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) { mesh::edge* ab = new mesh::edge(); mesh::edge* ba = new mesh::edge(); - + ab->vertex = a; ab->face = nullptr; ab->previous = ba; ab->next = ba; ab->symmetric = ba; ab->index = edges.size(); - + ba->vertex = b; ba->face = nullptr; ba->previous = ab; ba->next = ab; ba->symmetric = ab; ba->index = edges.size(); - + if (!a->edge) { a->edge = ab; @@ -199,10 +199,10 @@ mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) ab->next = b_out; b_out->previous = ab; } - + // Add edge edges.push_back(ab); - + return ab; } diff --git a/src/geom/mesh.hpp b/src/geom/mesh.hpp index e54b43a..4953759 100644 --- a/src/geom/mesh.hpp +++ b/src/geom/mesh.hpp @@ -20,8 +20,8 @@ #ifndef ANTKEEPER_GEOM_MESH_HPP #define ANTKEEPER_GEOM_MESH_HPP -#include #include "utility/fundamental-types.hpp" +#include namespace geom { @@ -36,8 +36,10 @@ public: struct vertex; struct edge; struct face; + + /// List of edges which form a face. typedef std::vector loop; - + /** * Constructs a mesh. */ @@ -118,61 +120,61 @@ public: const std::vector& get_faces() const; /** - * Half-edge vertex which contains a pointer to its parent edge, a position vector, and an index. + * Half-edge vertex which contains its index, a pointer to its parent edge, and its position vector. */ struct vertex { - /// Pointer to the edge to which this vertex belongs + /// Index of this vertex. + std::size_t index; + + /// Pointer to the edge to which this vertex belongs. mesh::edge* edge; - /// Vertex position + /// Vertex position. float3 position; - - /// Index of this vertex - std::size_t index; }; /** - * Half-edge edge which contains pointers to its starting vertex, parent face, and related edges. + * Half-edge edge which contains its index and pointers to its starting vertex, parent face, and related edges. */ struct edge { - /// Pointer to the vertex at which the edge starts + /// Index of this edge. + std::size_t index; + + /// Pointer to the vertex at which the edge starts. mesh::vertex* vertex; - /// Pointer to the face on the left of this edge + /// Pointer to the face on the left of this edge. mesh::face* face; - /// Pointer to the previous edge in the parent face + /// Pointer to the previous edge in the parent face. mesh::edge* previous; - /// Pointer to the next edge in the parent face + /// Pointer to the next edge in the parent face. mesh::edge* next; - /// Pointer to the symmetric edge + /// Pointer to the symmetric edge. mesh::edge* symmetric; - - /// Index of this edge - std::size_t index; }; /** - * Half-edge face which contains a pointer to its first edge and its normal vector. + * Half-edge face which contains its index and a pointer to its first edge. */ struct face { - /// Pointer to the first edge in this face - mesh::edge* edge; - - /// Index of this face + /// Index of this face. std::size_t index; + + /// Pointer to the first edge in this face. + mesh::edge* edge; }; private: mesh::edge* find_free_incident(mesh::vertex* vertex) const; mesh::edge* find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const; bool make_adjacent(mesh::edge* in_edge, mesh::edge* out_edge); - + std::vector vertices; std::vector edges; std::vector faces; @@ -196,4 +198,3 @@ inline const std::vector& mesh::get_faces() const } // namespace geom #endif // ANTKEEPER_GEOM_MESH_HPP - diff --git a/src/geom/meshes/grid.cpp b/src/geom/meshes/grid.cpp index c9630fd..4e18426 100644 --- a/src/geom/meshes/grid.cpp +++ b/src/geom/meshes/grid.cpp @@ -38,19 +38,19 @@ geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdiv // Generate mesh vertices float3 position; - position.z = 0.0f; - position.y = -length * 0.5f; + position.z() = 0.0f; + position.y() = -length * 0.5f; for (std::size_t i = 0; i <= rows; ++i) { - position.x = -length * 0.5f; + position.x() = -length * 0.5f; for (std::size_t j = 0; j <= columns; ++j) { mesh->add_vertex(position); - position.x += column_length; + position.x() += column_length; } - position.y += row_length; + position.y() += row_length; } // Function to eliminate duplicate edges diff --git a/src/geom/rect-pack.hpp b/src/geom/rect-pack.hpp index a83e375..112092b 100644 --- a/src/geom/rect-pack.hpp +++ b/src/geom/rect-pack.hpp @@ -194,8 +194,8 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t return nullptr; // Determine node dimensions - scalar_type node_w = node.bounds.max.x - node.bounds.min.x; - scalar_type node_h = node.bounds.max.y - node.bounds.min.y; + scalar_type node_w = node.bounds.max.x() - node.bounds.min.x(); + scalar_type node_h = node.bounds.max.y() - node.bounds.min.y(); // Check if rect is larger than node if (w > node_w || h > node_h) @@ -218,15 +218,15 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t if (dw > dh) { node.children[0]->bounds.min = node.bounds.min; - node.children[0]->bounds.max = {node.bounds.min.x + w, node.bounds.max.y}; - node.children[1]->bounds.min = {node.bounds.min.x + w, node.bounds.min.y}; + node.children[0]->bounds.max = {node.bounds.min.x() + w, node.bounds.max.y()}; + node.children[1]->bounds.min = {node.bounds.min.x() + w, node.bounds.min.y()}; node.children[1]->bounds.max = {node.bounds.max}; } else { node.children[0]->bounds.min = node.bounds.min; - node.children[0]->bounds.max = {node.bounds.max.x, node.bounds.min.y + h}; - node.children[1]->bounds.min = {node.bounds.min.x, node.bounds.min.y + h}; + node.children[0]->bounds.max = {node.bounds.max.x(), node.bounds.min.y() + h}; + node.children[1]->bounds.min = {node.bounds.min.x(), node.bounds.min.y() + h}; node.children[1]->bounds.max = {node.bounds.max}; } diff --git a/src/geom/rect.hpp b/src/geom/rect.hpp index 391940b..69f3a2a 100644 --- a/src/geom/rect.hpp +++ b/src/geom/rect.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GEOM_RECT_HPP #define ANTKEEPER_GEOM_RECT_HPP -#include "math/vector-type.hpp" +#include "math/vector.hpp" namespace geom { diff --git a/src/geom/sphere.hpp b/src/geom/sphere.hpp index bbff112..02c3c69 100644 --- a/src/geom/sphere.hpp +++ b/src/geom/sphere.hpp @@ -98,9 +98,9 @@ bool sphere::contains(const aabb& aabb) const vector_type a = center - aabb.min_point; vector_type b = center - aabb.max_point; - distance += std::max(a.x * a.x, b.x * b.x); - distance += std::max(a.y * a.y, b.y * b.y); - distance += std::max(a.z * a.z, b.z * b.z); + distance += std::max(a.x() * a.x(), b.x() * b.x()); + distance += std::max(a.y() * a.y(), b.y() * b.y()); + distance += std::max(a.z() * a.z(), b.z() * b.z()); return (distance <= radius * radius); } diff --git a/src/geom/view-frustum.hpp b/src/geom/view-frustum.hpp index 15fb5db..df38f24 100644 --- a/src/geom/view-frustum.hpp +++ b/src/geom/view-frustum.hpp @@ -172,6 +172,7 @@ void view_frustum::recalculate_planes(const matrix_type& view_projection) template void view_frustum::recalculate_corners() { + /// @TODO THESE CORNERS MAY BE INCORRECT!!!!!!!!!!! corners[0] = plane_type::intersection(get_near(), get_top(), get_left()); corners[1] = plane_type::intersection(get_near(), get_top(), get_right()); corners[2] = plane_type::intersection(get_near(), get_bottom(), get_left()); diff --git a/src/math/constants.hpp b/src/math/constants.hpp index 21899f0..83ce01b 100644 --- a/src/math/constants.hpp +++ b/src/math/constants.hpp @@ -20,31 +20,49 @@ #ifndef ANTKEEPER_MATH_CONSTANTS_HPP #define ANTKEEPER_MATH_CONSTANTS_HPP -#include "math/matrix-type.hpp" -#include "math/quaternion-type.hpp" -#include "math/transform-type.hpp" +#include namespace math { +/// Positive infinity. +template +constexpr T inf{std::numeric_limits::infinity()}; + /// Pi. template -constexpr T pi = T(3.1415926535897932384626433832795); +constexpr T pi = T{3.1415926535897932384626433832795}; /// Pi * 2. template -constexpr T two_pi = pi * T(2); +constexpr T two_pi = pi * T{2}; /// Pi * 4. template -constexpr T four_pi = pi * T(4); +constexpr T four_pi = pi * T{4}; /// Pi / 2. template -constexpr T half_pi = pi * T(0.5); +constexpr T half_pi = pi / T{2}; /// 1 / Pi. template -constexpr T inverse_pi = T(1) / pi; +constexpr T inverse_pi = T{1} / pi; + +/// sqrt(0.5) +template +constexpr T sqrt_half = T{0.70710678118654752440084436210485}; + +/// sqrt(2) +template +constexpr T sqrt_2 = T{1.4142135623730950488016887242097}; + +/// sqrt(3) +template +constexpr T sqrt_3 = T{1.7320508075688772935274463415059}; + +/// sqrt(5) +template +constexpr T sqrt_5 = T{2.2360679774997896964091736687313}; } // namespace math diff --git a/src/math/math.hpp b/src/math/math.hpp index bd94e42..c5e034f 100644 --- a/src/math/math.hpp +++ b/src/math/math.hpp @@ -23,9 +23,7 @@ /// Mathematical functions and data types. namespace math {} -#include "math/vector-type.hpp" -#include "math/vector-functions.hpp" -#include "math/vector-operators.hpp" +#include "math/vector.hpp" #include "math/matrix-type.hpp" #include "math/matrix-functions.hpp" diff --git a/src/math/matrix-functions.hpp b/src/math/matrix-functions.hpp index c335899..7859c8d 100644 --- a/src/math/matrix-functions.hpp +++ b/src/math/matrix-functions.hpp @@ -21,8 +21,7 @@ #define ANTKEEPER_MATH_MATRIX_FUNCTIONS_HPP #include "math/matrix-type.hpp" -#include "math/vector-type.hpp" -#include "math/vector-functions.hpp" +#include "math/vector.hpp" #include namespace math { diff --git a/src/math/matrix-type.hpp b/src/math/matrix-type.hpp index 0ecdd04..a45b463 100644 --- a/src/math/matrix-type.hpp +++ b/src/math/matrix-type.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_MATH_MATRIX_TYPE_HPP #define ANTKEEPER_MATH_MATRIX_TYPE_HPP -#include "math/vector-type.hpp" +#include "math/vector.hpp" #include #include diff --git a/src/math/noise/fbm.hpp b/src/math/noise/fbm.hpp new file mode 100644 index 0000000..c3abbe9 --- /dev/null +++ b/src/math/noise/fbm.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_FBM_HPP +#define ANTKEEPER_MATH_NOISE_FBM_HPP + +#include "math/vector.hpp" +#include + +namespace math { +namespace noise { + +/** + * Fractional Brownian motion (fBm). + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param x n-dimensional input value. + * @param octaves Number of octaves. + * @param lacunarity Frequency multiplier. + * @param gain Amplitude multiplier. + * @param noise Noise function. + * @param hash Hash function. + * + */ +template +T fbm +( + vector x, + std::size_t octaves, + T lacunarity, + T gain, + T (*noise)(const vector&, std::uint32_t (*)(const vector&)), + std::uint32_t (*hash)(const vector&) +) +{ + T amplitude{1}; + T value{0}; + + while (octaves--) + { + value += noise(x, hash) * amplitude; + x *= lacunarity; + amplitude *= gain; + } + + return value; +} + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_FBM_HPP diff --git a/src/math/noise/hash.hpp b/src/math/noise/hash.hpp new file mode 100644 index 0000000..bee41e0 --- /dev/null +++ b/src/math/noise/hash.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_HASH_HPP +#define ANTKEEPER_MATH_NOISE_HASH_HPP + +#include "math/vector.hpp" +#include + +namespace math { +namespace noise { + +/// Hash functions. +namespace hash { + +/** + * PCG3D hash. + * + * @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020. + */ +/// @{ +template +math::vector pcg3d_3(const math::vector& x) +{ + math::vector u = math::vector(x) * 1664525U + 1013904223U; + + u[0] += u[1] * u[2]; + u[1] += u[2] * u[0]; + u[2] += u[0] * u[1]; + + u[0] ^= u[0] >> 16U; + u[1] ^= u[1] >> 16U; + u[2] ^= u[2] >> 16U; + + u[0] += u[1] * u[2]; + u[1] += u[2] * u[0]; + u[2] += u[0] * u[1]; + + return u; +} + +template +math::vector pcg3d_3(const math::vector& x) +{ + return pcg3d_3({x[0], x[1], T{1}}); +} + +template +math::vector pcg3d_3(const math::vector& x) +{ + return pcg3d_3({x[0], T{1}, T{1}}); +} + +template +math::vector pcg3d_3(T x) +{ + return pcg3d_3({x, T{1}, T{1}}); +} + +template +std::uint32_t pcg3d_1(const math::vector& x) +{ + return pcg3d_3(x)[0]; +} + +template +std::uint32_t pcg3d_1(const math::vector& x) +{ + return pcg3d_3({x[0], x[1], T{1}})[0]; +} + +template +std::uint32_t pcg3d_1(const math::vector& x) +{ + return pcg3d_3({x[0], T{1}, T{1}})[0]; +} + +template +std::uint32_t pcg3d_1(T x) +{ + return pcg3d_3({x, T{1}, T{1}})[0]; +} +/// @} + +/** + * PCG4D hash. + * + * @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020. + */ +/// @{ +template +math::vector pcg4d_4(const math::vector& x) +{ + math::vector u = math::vector(x) * 1664525U + 1013904223U; + + u[0] += u[1] * u[3]; + u[1] += u[2] * u[0]; + u[2] += u[0] * u[1]; + u[3] += u[1] * u[2]; + + u[0] ^= u[0] >> 16U; + u[1] ^= u[1] >> 16U; + u[2] ^= u[2] >> 16U; + u[3] ^= u[3] >> 16U; + + u[0] += u[1] * u[3]; + u[1] += u[2] * u[0]; + u[2] += u[0] * u[1]; + u[3] += u[1] * u[2]; + + return u; +} + +template +std::uint32_t pcg4d_1(const math::vector& x) +{ + return pcg4d_4(x)[0]; +} +/// @} + +} // namespace hash + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_HASH_HPP diff --git a/src/math/noise/noise.hpp b/src/math/noise/noise.hpp new file mode 100644 index 0000000..0c13180 --- /dev/null +++ b/src/math/noise/noise.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_HPP +#define ANTKEEPER_MATH_NOISE_HPP + +namespace math { + +/// Noise functions. +namespace noise {} + +} // namespace math + +#include "math/noise/fbm.hpp" +#include "math/noise/hash.hpp" +#include "math/noise/simplex.hpp" + +#endif // ANTKEEPER_MATH_NOISE_HPP diff --git a/src/math/noise/simplex.hpp b/src/math/noise/simplex.hpp new file mode 100644 index 0000000..36d5f75 --- /dev/null +++ b/src/math/noise/simplex.hpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP +#define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP + +#include "math/vector.hpp" +#include +#include + +namespace math { +namespace noise { + +/// @private +/// @{ + +/// Number of corners in an *n*-dimensional simplex lattice cell. +template +constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max(0, N - 1); + +/// Number of edges in an *n*-dimensional simplex lattice cell. +template +constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count : 2; + +/// Returns the simplex lattice cell corner vector for a given dimension and index. +template +constexpr vector make_simplex_corner(std::size_t i, std::index_sequence) +{ + return {((i >> I) % 2) * T{2} - T{1}...}; +} + +/// Builds an array of simplex lattice cell corner vectors for a given dimension. +template +constexpr std::array, simplex_corner_count> make_simplex_corners(std::index_sequence) +{ + return {make_simplex_corner(I, std::make_index_sequence{})...}; +} + +/// Array of simplex lattice cell corner vectors for a given dimension. +template +constexpr auto simplex_corners = make_simplex_corners(std::make_index_sequence>{}); + +/// Returns the simplex lattice cell edge vector for a given dimension and index. +template +constexpr vector make_simplex_edge(std::size_t i, std::index_sequence) +{ + std::size_t j = i / (simplex_edge_count / N); + + return + { + I < j ? + simplex_corners[i % simplex_corner_count][I] + : + I > j ? + simplex_corners[i % simplex_corner_count][I - 1] + : + T{0} + ... + }; +} + +/// Builds an array of simplex lattice cell edge vectors for a given dimension. +template +constexpr std::array, simplex_edge_count> make_simplex_edges(std::index_sequence) +{ + if constexpr (N == 1) + return std::array, simplex_edge_count>{vector{T{1}}, vector{T{-1}}}; + else + return {make_simplex_edge(I, std::make_index_sequence{})...}; +} + +/// Array of simplex lattice cell edge vectors for a given dimension. +template +constexpr auto simplex_edges = make_simplex_edges(std::make_index_sequence>{}); + +/// @} + +/** + * *n*-dimensional simplex noise. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param x Input vector. + * @param hash Hash function. + * + * @return Noise value, on `[-1, 1]`. + * + * @see https://en.wikipedia.org/wiki/Simplex_noise + * @see https://catlikecoding.com/unity/tutorials/pseudorandom-noise/simplex-noise/ + * @see https://briansharpe.wordpress.com/2012/01/13/simplex-noise/ + * @see https://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/ + * @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 + */ +template +T simplex(const vector& x, std::uint32_t (*hash)(const vector&)) +{ + // Skewing (F) and unskewing (G) factors + static const T f = (std::sqrt(static_cast(N + 1)) - T{1}) / static_cast(N); + static const T g = f / (T{1} + f * static_cast(N)); + + // Kernel radius set to the height of the equilateral triangle, `sqrt(0.5)` + constexpr T sqr_kernel_radius = T{0.5}; + + /** + * C2-continuous kernel falloff function. + * + * @param sqr_distance Squared distance from the kernel center. + * + * @return Kernel strength at the given distance. + */ + auto falloff = [sqr_kernel_radius](T sqr_distance) constexpr + { + sqr_distance = sqr_kernel_radius - sqr_distance; + return sqr_distance * sqr_distance * sqr_distance; + }; + + // Normalization factor when using corner gradient vectors + // @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 + static const T corner_normalization = T{1} / ((static_cast(N) / std::sqrt(static_cast(N + 1))) * falloff(static_cast(N) / (T{4} * static_cast(N + 1)))); + + // Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors + static const T edge_normalization = corner_normalization * (std::sqrt(static_cast(N)) / math::length(simplex_edges[0])); + + // Skew input position to get the origin vertex of the unit hypercube cell to which they belong + const vector origin_vertex = math::floor(x + math::sum(x) * f); + + // Displacement vector from origin vertex position to input position + const vector dx = x - origin_vertex + math::sum(origin_vertex) * g; + + // Find axis traversal order + vector axis_order; + for (std::size_t i = 0; i < N; ++i) + axis_order[i] = i; + std::sort + ( + axis_order.begin(), + axis_order.end(), + [&dx](std::size_t lhs, std::size_t rhs) + { + return dx[lhs] > dx[rhs]; + } + ); + + T n = T{0}; + vector current_vertex = origin_vertex; + for (std::size_t i = 0; i <= N; ++i) + { + if (i) + ++current_vertex[axis_order[i - 1]]; + + // Calculate displacement vector from current vertex to input position + const vector d = dx - (current_vertex - origin_vertex) + g * static_cast(i); + + // Calculate falloff + T t = falloff(math::length_squared(d)); + if (t > T{0}) + { + const auto gradient_index = hash(current_vertex) % simplex_edges.size(); + const vector& gradient = simplex_edges[gradient_index]; + + n += math::dot(d, gradient) * t; + } + } + + // Normalize value + return n * edge_normalization; +} + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP diff --git a/src/math/operators.hpp b/src/math/operators.hpp index 0c35750..64ed239 100644 --- a/src/math/operators.hpp +++ b/src/math/operators.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_MATH_OPERATORS_HPP #define ANTKEEPER_MATH_OPERATORS_HPP -#include "math/vector-operators.hpp" #include "math/matrix-operators.hpp" #include "math/quaternion-operators.hpp" #include "math/transform-operators.hpp" diff --git a/src/math/quaternion-functions.hpp b/src/math/quaternion-functions.hpp index 514f773..dff58a0 100644 --- a/src/math/quaternion-functions.hpp +++ b/src/math/quaternion-functions.hpp @@ -23,8 +23,7 @@ #include "math/constants.hpp" #include "math/matrix-type.hpp" #include "math/quaternion-type.hpp" -#include "math/vector-type.hpp" -#include "math/vector-functions.hpp" +#include "math/vector.hpp" #include namespace math { @@ -377,7 +376,7 @@ template quaternion angle_axis(T angle, const vector& axis) { T s = std::sin(angle * T(0.5)); - return {static_cast(std::cos(angle * T(0.5))), axis.x * s, axis.y * s, axis.z * s}; + return {static_cast(std::cos(angle * T(0.5))), axis.x() * s, axis.y() * s, axis.z() * s}; } template @@ -421,8 +420,8 @@ void swing_twist(const quaternion& q, const vector& a, quaternion& q { if (q.x * q.x + q.y * q.y + q.z * q.z > epsilon) { - const vector pa = mul(a, (a.x * q.x + a.y * q.y + a.z * q.z)); - qt = normalize(quaternion{q.w, pa.x, pa.y, pa.z}); + const vector pa = mul(a, (a.x() * q.x + a.y() * q.y + a.z() * q.z)); + qt = normalize(quaternion{q.w, pa.x(), pa.y(), pa.z()}); qs = mul(q, conjugate(qt)); } else diff --git a/src/math/se3.hpp b/src/math/se3.hpp index 4141719..91693a5 100644 --- a/src/math/se3.hpp +++ b/src/math/se3.hpp @@ -20,8 +20,7 @@ #ifndef ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP #define ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP -#include "math/vector-type.hpp" -#include "math/vector-operators.hpp" +#include "math/vector.hpp" #include "math/quaternion-type.hpp" #include "math/quaternion-operators.hpp" #include "math/quaternion-functions.hpp" @@ -98,9 +97,9 @@ typename se3::matrix_type se3::matrix() const { matrix_type m = math::resize<4, 4>(math::matrix_cast(r)); - m[3].x = t.x; - m[3].y = t.y; - m[3].z = t.z; + m[3].x() = t.x(); + m[3].y() = t.y(); + m[3].z() = t.z(); return m; } diff --git a/src/math/stream-operators.hpp b/src/math/stream-operators.hpp index 386a3d8..5e9872d 100644 --- a/src/math/stream-operators.hpp +++ b/src/math/stream-operators.hpp @@ -20,21 +20,11 @@ #ifndef ANTKEEPER_MATH_STREAM_OPERATORS_HPP #define ANTKEEPER_MATH_STREAM_OPERATORS_HPP -#include "math/vector-type.hpp" #include "math/matrix-type.hpp" #include "math/quaternion-type.hpp" +#include #include -/** - * Writes the elements of a vector to an output stream, with each element delimeted by a space. - * - * @param os Output stream. - * @param v Vector. - * @return Output stream. - */ -template -std::ostream& operator<<(std::ostream& os, const math::vector& v); - /** * Writes the elements of a matrix to an output stream, with each element delimeted by a space. * @@ -55,16 +45,6 @@ std::ostream& operator<<(std::ostream& os, const math::matrix& m); template std::ostream& operator<<(std::ostream& os, const math::quaternion& q); -/** - * Reads the elements of a vector from an input stream, with each element delimeted by a space. - * - * @param is Input stream. - * @param v Vector. - * @return Input stream. - */ -template -std::istream& operator>>(std::istream& is, math::vector& v); - /** * Reads the elements of a matrix from an input stream, with each element delimeted by a space. * @@ -85,22 +65,6 @@ std::istream& operator>>(std::istream& is, math::matrix& m); template std::istream& operator>>(std::istream& is, const math::quaternion& q); -template -std::ostream& operator<<(std::ostream& os, const math::vector& v) -{ - for (std::size_t i = 0; i < N; ++i) - { - os << v[i]; - - if (i < N - 1) - { - os << ' '; - } - } - - return os; -} - template std::ostream& operator<<(std::ostream& os, const math::matrix& m) { @@ -123,19 +87,10 @@ std::ostream& operator<<(std::ostream& os, const math::matrix& m) template std::ostream& operator<<(std::ostream& os, const math::quaternion& q) { - os << q.w << ' ' << q.x << ' ' << q.y << ' ' << q.z; + os << q.w << ' ' << q.x() << ' ' << q.y() << ' ' << q.z(); return os; } -template -std::istream& operator>>(std::istream& is, math::vector& v) -{ - for (std::size_t i = 0; i < N; ++i) - is >> v[i]; - - return is; -} - template std::istream& operator>>(std::istream& is, math::matrix& m) { diff --git a/src/math/transform-functions.hpp b/src/math/transform-functions.hpp index e744c3e..9230dfc 100644 --- a/src/math/transform-functions.hpp +++ b/src/math/transform-functions.hpp @@ -21,7 +21,6 @@ #define ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP #include "math/transform-type.hpp" -#include "math/vector-functions.hpp" #include "math/matrix-functions.hpp" #include "math/quaternion-functions.hpp" @@ -68,7 +67,7 @@ template transform inverse(const transform& t) { transform inverse_t; - inverse_t.scale = {T(1) / t.scale.x, T(1) / t.scale.y, T(1) / t.scale.z}; + inverse_t.scale = {T(1) / t.scale.x(), T(1) / t.scale.y(), T(1) / t.scale.z()}; inverse_t.rotation = conjugate(t.rotation); inverse_t.translation = negate(mul(inverse_t.rotation, t.translation)); return inverse_t; diff --git a/src/math/transform-type.hpp b/src/math/transform-type.hpp index dd213dc..351f2a3 100644 --- a/src/math/transform-type.hpp +++ b/src/math/transform-type.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_MATH_TRANSFORM_TYPE_HPP #define ANTKEEPER_MATH_TRANSFORM_TYPE_HPP -#include "math/vector-type.hpp" +#include "math/vector.hpp" #include "math/quaternion-type.hpp" namespace math { diff --git a/src/math/vector-functions.hpp b/src/math/vector-functions.hpp deleted file mode 100644 index 8b52c6e..0000000 --- a/src/math/vector-functions.hpp +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP -#define ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP - -#include "math/vector-type.hpp" -#include -#include -#include -#include - -namespace math { - -/** - * Adds two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Sum of the two vectors. - */ -template -constexpr vector add(const vector& x, const vector& y); - -/** - * Checks if all elements of a boolean vector are `true`. - * - * @param x Vector to be tested for truth. - * @return `true` if all elements are `true`, `false` otherwise. - */ -template -constexpr bool all(const vector& x); - -/** - * Checks if any elements of a boolean vector are `true`. - * - * @param x Vector to be tested for truth. - * @return `true` if any elements are `true`, `false` otherwise. - */ -template -constexpr bool any(const vector& x); - -/** - * Reinterprets data as an `N`-dimensional vector of type `T`. - * - * @tparam N Number of vector dimensions. - * @tparam T Scalar type. - * @param data Data to reinterpret. - */ -template -constexpr vector& as_vector(T& data); - -/** - * Clamps the values of a vector's elements. - * - * @param x Vector to clamp. - * @param min_value Minimum element value. - * @param max_value Maximum element value. - * @return Clamped vector. - */ -template -constexpr vector clamp(const vector& x, T min_value, T max_value); - -/** - * Clamps the length of a vector. - * - * @param x Vector to clamp. - * @param max_length Maximum length. - * @return Length-clamped vector. - */ -template -vector clamp_length(const vector& x, T max_length); - -/** - * Calculate the cross product of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Cross product of the two vectors. - */ -template -constexpr vector cross(const vector& x, const vector& y); - -/** - * Calculates the distance between two points. - * - * @param p0 First of two points. - * @param p1 Second of two points. - * @return Distance between the two points. - */ -template -T distance(const vector& p0, const vector& p1); - -/** - * Calculates the squared distance between two points. The squared distance can be calculated faster than the distance because a call to `std::sqrt` is saved. - * - * @param p0 First of two points. - * @param p1 Second of two points. - * @return Squared distance between the two points. - */ -template -constexpr T distance_squared(const vector& p0, const vector& p1); - -/** - * Divides a vector by another vector. - * - * @param x First vector. - * @param y Second vector. - * @return Result of the division. - */ -template -constexpr vector div(const vector& x, const vector& y); - -/** - * Divides a vector by a scalar. - * - * @param v Vector. - * @param s Scalar. - * @return Result of the division. - */ -template -constexpr vector div(const vector& v, T s); - -/** - * Calculates the dot product of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Dot product of the two vectors. - */ -template -constexpr T dot(const vector& x, const vector& y); - -/** - * Compares two vectors for equality - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector equal(const vector& x, const vector& y); - -/** - * Performs a component-wise greater-than comparison of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector greater_than(const vector& x, const vector& y); - -/** - * Performs a component-wise greater-than or equal-to comparison of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector greater_than_equal(const vector& x, const vector& y); - -/** - * Calculates the length of a vector. - * - * @param x Vector of which to calculate the length. - * @return Length of the vector. - */ -template -T length(const vector& x); - -/** - * Calculates the squared length of a vector. The squared length can be calculated faster than the length because a call to `std::sqrt` is saved. - * - * @param x Vector of which to calculate the squared length. - * @return Squared length of the vector. - */ -template -constexpr T length_squared(const vector& x); - -/** - * Performs a component-wise less-than comparison of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector less_than(const vector& x, const vector& y); - -/** - * Performs a component-wise less-than or equal-to comparison of two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector less_than_equal(const vector& x, const vector& y); - -/** - * Multiplies two vectors. - * - * @param x First vector. - * @param y Second vector. - * @return Product of the two vectors. - */ -template -constexpr vector mul(const vector& x, const vector& y); - -/** - * Multiplies a vector by a scalar. - * - * @param v Vector. - * @param s Scalar. - * @return Product of the vector and scalar. - */ -template -constexpr vector mul(const vector& v, T s); - -/** - * Negates a vector. - * - * @param x Vector to negate. - * @return Negated vector. - */ -template -constexpr vector negate(const vector& x); - -/** - * Calculates the unit vector in the same direction as the original vector. - * - * @param x Vector to normalize. - * @return Normalized vector. - */ -template -vector normalize(const vector& x); - -/** - * Logically inverts a boolean vector. - * - * @param x Vector to be inverted. - * @return Logically inverted vector. - */ -template -constexpr vector not(const vector& x); - -/** - * Compares two vectors for inequality - * - * @param x First vector. - * @param y Second vector. - * @return Boolean vector containing the result of the element comparisons. - */ -template -constexpr vector not_equal(const vector& x, const vector& y); - -/** - * Resizes a vector. Any new elements will be set to `0`. - * - * @param v Vector to resize. - * @return Resized vector. - */ -template -constexpr vector resize(const vector& v); - -/** - * Subtracts a vector from another vector. - * - * @param x First vector. - * @param y Second vector. - * @return Difference between the two vectors. - */ -template -constexpr vector sub(const vector& x, const vector& y); - -/** - * Makes an m-dimensional vector by rearranging and/or duplicating elements of an n-dimensional vector. - * - * @tparam Indices List of indices of elements in the vector `v`. - * @tparam T Vector component type. - * @tparam N Number of dimensions in vector `v`. - * @return Vector containing elements from vector `v` in the order specified by `Indices`. The size of the returned vector is equivalent to the number of indices in `Indices`. - */ -template -constexpr vector swizzle(const vector& v); - -/** - * Types casts each vector component and returns a vector of the casted type. - * - * @tparam T2 Target vector component type. - * @tparam T1 Source vector component type. - * @tparam N Number of dimensions. - * @param v Vector to type cast. - * @return Type-casted vector. - */ -template -constexpr vector type_cast(const vector& v); - -/// @private -template -constexpr inline vector add(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] + y[I])...}; -} - -template -constexpr inline vector add(const vector& x, const vector& y) -{ - return add(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline bool all(const vector& x, std::index_sequence) -{ - return (x[I] && ...); -} - -template -constexpr inline bool all(const vector& x) -{ - return all(x, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline bool any(const vector& x, std::index_sequence) -{ - return (x[I] || ...); -} - -template -constexpr inline bool any(const vector& x) -{ - return any(x, std::make_index_sequence{}); -} - -template -constexpr inline vector& as_vector(T& data) -{ - static_assert(std::is_pod>::value); - return reinterpret_cast&>(data); -} - -/// @private -template -constexpr inline vector clamp(const vector& x, T min_value, T max_value, std::index_sequence) -{ - return {std::min(max_value, std::max(min_value, x[I]))...}; -} - -template -constexpr inline vector clamp(const vector& x, T min_value, T max_value) -{ - return clamp(x, min_value, max_value, std::make_index_sequence{}); -} - -template -vector clamp_length(const vector& x, T max_length) -{ - T length2 = length_squared(x); - return (length2 > max_length * max_length) ? (x * max_length / std::sqrt(length2)) : x; -} - -template -constexpr inline vector cross(const vector& x, const vector& y) -{ - return - { - x[1] * y[2] - y[1] * x[2], - x[2] * y[0] - y[2] * x[0], - x[0] * y[1] - y[0] * x[1] - }; -} - -template -inline T distance(const vector& p0, const vector& p1) -{ - static_assert(std::is_floating_point::value); - return length(sub(p0, p1)); -} - -template -constexpr inline T distance_squared(const vector& p0, const vector& p1) -{ - static_assert(std::is_floating_point::value); - return length_squared(sub(p0, p1)); -} - -/// @private -template -constexpr inline vector div(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] / y[I])...}; -} - -template -constexpr inline vector div(const vector& x, const vector& y) -{ - return div(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector div(const vector& v, T s, std::index_sequence) -{ - return {(v[I] / s)...}; -} - -template -constexpr inline vector div(const vector& v, T s) -{ - return div(v, s, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline T dot(const vector& x, const vector& y, std::index_sequence) -{ - return ((x[I] * y[I]) + ...); -} - -template -constexpr inline T dot(const vector& x, const vector& y) -{ - return dot(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector equal(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] == y[I])...}; -} - -template -constexpr inline vector equal(const vector& x, const vector& y) -{ - return equal(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector greater_than(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] > y[I])...}; -} - -template -constexpr inline vector greater_than(const vector& x, const vector& y) -{ - return greater_than(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector greater_than_equal(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] >= y[I])...}; -} - -template -constexpr inline vector greater_than_equal(const vector& x, const vector& y) -{ - return greater_than_equal(x, y, std::make_index_sequence{}); -} - -template -inline T length(const vector& x) -{ - static_assert(std::is_floating_point::value); - return std::sqrt(dot(x, x)); -} - -template -constexpr inline T length_squared(const vector& x) -{ - return dot(x, x); -} - -/// @private -template -constexpr inline vector less_than(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] < y[I])...}; -} - -template -constexpr inline vector less_than(const vector& x, const vector& y) -{ - return less_than(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector less_than_equal(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] <= y[I])...}; -} - -template -constexpr inline vector less_than_equal(const vector& x, const vector& y) -{ - return less_than_equal(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector mul(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] * y[I])...}; -} - -template -constexpr inline vector mul(const vector& x, const vector& y) -{ - return mul(x, y, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector mul(const vector& v, T s, std::index_sequence) -{ - return {(v[I] * s)...}; -} - -template -constexpr inline vector mul(const vector& v, T s) -{ - return mul(v, s, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector negate(const vector& x, std::index_sequence) -{ - return {(-x[I])...}; -} - -template -constexpr inline vector negate(const vector& x) -{ - return negate(x, std::make_index_sequence{}); -} - -template -inline vector normalize(const vector& x) -{ - static_assert(std::is_floating_point::value); - return mul(x, T(1) / length(x)); -} - -/// @private -template -constexpr inline vector not(const vector& x, std::index_sequence) -{ - return {!x[I]...}; -} - -template -constexpr inline vector not(const vector& x) -{ - return not(x, std::make_index_sequence{}); -} - -/// @private -template -constexpr inline vector not_equal(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] != y[I])...}; -} - -template -constexpr inline vector not_equal(const vector& x, const vector& y) -{ - return not_equal(x, y, std::make_index_sequence{}); -} - -template -constexpr vector resize(const vector& v) -{ - vector resized; - - for (std::size_t i = 0; i < N1; ++i) - { - resized[i] = (i < N0) ? v[i] : T(0); - } - - return resized; -} - - -/// @private -template -constexpr inline vector sub(const vector& x, const vector& y, std::index_sequence) -{ - return {(x[I] - y[I])...}; -} - -template -constexpr inline vector sub(const vector& x, const vector& y) -{ - return sub(x, y, std::make_index_sequence{}); -} - -template -constexpr inline vector swizzle(const vector& v) -{ - return { v[Indices]... }; -} - -/// @private -template -constexpr inline vector type_cast(const vector& v, std::index_sequence) -{ - return {static_cast(v[I])...}; -} - -template -constexpr inline vector type_cast(const vector& v) -{ - return type_cast(v, std::make_index_sequence{}); -} - -} // namespace math - -#endif // ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP diff --git a/src/math/vector-operators.hpp b/src/math/vector-operators.hpp deleted file mode 100644 index b4d4b9e..0000000 --- a/src/math/vector-operators.hpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_VECTOR_OPERATORS_HPP -#define ANTKEEPER_MATH_VECTOR_OPERATORS_HPP - -#include "math/vector-type.hpp" -#include "math/vector-functions.hpp" - -/// @copydoc math::add(const math::vector&, const math::vector&) -template -constexpr math::vector operator+(const math::vector& x, const math::vector& y); - -/// @copydoc math::div(const math::vector&, const math::vector&) -template -constexpr math::vector operator/(const math::vector& x, const math::vector& y); - -/// @copydoc math::div(const math::vector&, T) -template -constexpr math::vector operator/(const math::vector& v, T s); - -/// @copydoc math::mul(const math::vector&, const math::vector&) -template -constexpr math::vector operator*(const math::vector& x, const math::vector& y); - -/// @copydoc math::mul(const math::vector&, T) -template -constexpr math::vector operator*(const math::vector& v, T s); - -/// @copydoc math::mul(const math::vector&, T) -template -constexpr math::vector operator*(T s, const math::vector& v); - -/// @copydoc math::negate(const math::vector&) -template -constexpr math::vector operator-(const math::vector& x); - -/// @copydoc math::sub(const math::vector&, const math::vector&) -template -constexpr math::vector operator-(const math::vector& x, const math::vector& y); - -/** - * Adds two vectors and stores the result in the first vector. - * - * @param x First vector. - * @param y Second vector. - * @return Reference to the first vector. - */ -template -constexpr math::vector& operator+=(math::vector& x, const math::vector& y); - -/** - * Subtracts two vectors and stores the result in the first vector. - * - * @param x First vector. - * @param y Second vector. - * @return Reference to the first vector. - */ -template -constexpr math::vector& operator-=(math::vector& x, const math::vector& y); - -/** - * Multiplies two vectors and stores the result in the first vector. - * - * @param x First vector. - * @param y Second vector. - * @return Reference to the first vector. - */ -template -constexpr math::vector& operator*=(math::vector& x, const math::vector& y); - -/** - * Multiplies a vector and a scalar and stores the result in the vector. - * - * @param v Vector. - * @param s Scalar. - * @return Reference to the vector. - */ -template -constexpr math::vector& operator*=(math::vector& v, T s); - -/** - * Divides the first vector by the second vector the result in the first vector. - * - * @param x First vector. - * @param y Second vector. - * @return Reference to the first vector. - */ -template -constexpr math::vector& operator/=(math::vector& x, const math::vector& y); - -/** - * Divides a vector by a scalar and stores the result in the vector. - * - * @param v Vector. - * @param s Scalar. - * @return Reference to the vector. - */ -template -constexpr math::vector& operator/=(math::vector& v, T s); - -template -constexpr inline math::vector operator+(const math::vector& x, const math::vector& y) -{ - return add(x, y); -} - -template -constexpr inline math::vector operator-(const math::vector& x) -{ - return negate(x); -} - -template -constexpr inline math::vector operator-(const math::vector& x, const math::vector& y) -{ - return sub(x, y); -} - -template -constexpr inline math::vector operator*(const math::vector& x, const math::vector& y) -{ - return mul(x, y); -} - -template -constexpr inline math::vector operator*(const math::vector& v, T s) -{ - return mul(v, s); -} - -template -constexpr inline math::vector operator*(T s, const math::vector& v) -{ - return mul(v, s); -} - -template -constexpr inline math::vector operator/(const math::vector& x, const math::vector& y) -{ - return div(x, y); -} - -template -constexpr inline math::vector operator/(const math::vector& v, T s) -{ - return div(v, s); -} - -template -constexpr inline math::vector& operator+=(math::vector& x, const math::vector& y) -{ - return (x = x + y); -} - -template -constexpr inline math::vector& operator-=(math::vector& x, const math::vector& y) -{ - return (x = x - y); -} - -template -constexpr inline math::vector& operator*=(math::vector& x, const math::vector& y) -{ - return (x = x * y); -} - -template -constexpr inline math::vector& operator*=(math::vector& v, T s) -{ - return (v = v * s); -} - -template -constexpr inline math::vector& operator/=(math::vector& x, const math::vector& y) -{ - return (x = x * y); -} - -template -constexpr inline math::vector& operator/=(math::vector& v, T s) -{ - return (v = v / s); -} - -#endif // ANTKEEPER_MATH_VECTOR_OPERATORS_HPP diff --git a/src/math/vector-type.hpp b/src/math/vector-type.hpp deleted file mode 100644 index 2d7c4a4..0000000 --- a/src/math/vector-type.hpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_VECTOR_TYPE_HPP -#define ANTKEEPER_MATH_VECTOR_TYPE_HPP - -#include - -namespace math { - -/** - * An `N`-dimensional Euclidean vector. - * - * @tparam T Scalar type. - * @tparam N Number of dimensions. - */ -template -struct vector -{ - typedef T element_type; - element_type elements[N]; - - inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } - inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } - inline constexpr const element_type* data() const noexcept { return elements; }; - inline constexpr element_type* data() noexcept { return elements; }; - inline constexpr std::size_t size() const noexcept { return N; }; -}; - -template -struct vector -{ - typedef T element_type; - - union - { - element_type elements[1]; - - struct - { - element_type x; - }; - }; - - inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } - inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } - inline constexpr const element_type* data() const noexcept { return elements; }; - inline constexpr element_type* data() noexcept { return elements; }; - inline constexpr std::size_t size() const noexcept { return 1; }; -}; - -template -struct vector -{ - typedef T element_type; - - union - { - element_type elements[2]; - - struct - { - element_type x; - element_type y; - }; - }; - - inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } - inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } - inline constexpr const element_type* data() const noexcept { return elements; }; - inline constexpr element_type* data() noexcept { return elements; }; - inline constexpr std::size_t size() const noexcept { return 2; }; -}; - -template -struct vector -{ - typedef T element_type; - - union - { - element_type elements[3]; - - struct - { - element_type x; - element_type y; - element_type z; - }; - }; - - inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } - inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } - inline constexpr const element_type* data() const noexcept { return elements; }; - inline constexpr element_type* data() noexcept { return elements; }; - inline constexpr std::size_t size() const noexcept { return 3; }; -}; - -template -struct vector -{ - typedef T element_type; - - union - { - element_type elements[4]; - - struct - { - element_type x; - element_type y; - element_type z; - element_type w; - }; - }; - - inline constexpr element_type& operator[](std::size_t i) noexcept { return elements[i]; } - inline constexpr const element_type& operator[](std::size_t i) const noexcept { return elements[i]; } - inline constexpr const element_type* data() const noexcept { return elements; }; - inline constexpr element_type* data() noexcept { return elements; }; - inline constexpr std::size_t size() const noexcept { return 4; }; -}; - -/// 2D vector. -template -using vector2 = vector; - -/// 3D vector. -template -using vector3 = vector; - -/// 4D vector. -template -using vector4 = vector; - -} // namespace math - -#endif // ANTKEEPER_MATH_VECTOR_TYPE_HPP diff --git a/src/math/vector.hpp b/src/math/vector.hpp new file mode 100644 index 0000000..4cd38e3 --- /dev/null +++ b/src/math/vector.hpp @@ -0,0 +1,1492 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_VECTOR_HPP +#define ANTKEEPER_MATH_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace math { + +/** + * *n*-dimensional Euclidean vector. + * + * @tparam T Element type. + * @tparam N Number of elements. + */ +template +struct vector +{ + /// Vector element data type. + typedef T element_type; + + /// Number of elements. + static constexpr std::size_t element_count = N; + + /// Array of vector elements. + element_type elements[N]; + + /** + * Returns a reference to the element at a given index. + * + * @param i Index of an element. + * + * @return Reference to the element at index @p i. + */ + /// @{ + constexpr inline element_type& operator[](std::size_t i) noexcept + { + return elements[i]; + } + constexpr inline const element_type& operator[](std::size_t i) const noexcept + { + return elements[i]; + } + /// @} + + /// Returns a reference to the first element. + /// @{ + constexpr inline element_type& front() noexcept + { + return elements[0]; + } + constexpr inline const element_type& front() const noexcept + { + return elements[0]; + } + /// @} + + /// Returns a reference to the last element. + /// @{ + constexpr inline element_type& back() noexcept + { + return elements[N - 1]; + } + constexpr inline const element_type& back() const noexcept + { + return elements[N - 1]; + } + /// @} + + /// Returns a reference to the first element. + /// @{ + constexpr inline element_type& x() noexcept + { + static_assert(N > 0, "Vector does not contain an x element."); + return elements[0]; + } + constexpr inline const element_type& x() const noexcept + { + static_assert(N > 0, "Vector does not contain an x element."); + return elements[0]; + } + /// @} + + /// Returns a reference to the second element. + /// @{ + constexpr inline element_type& y() noexcept + { + static_assert(N > 1, "Vector does not contain a y element."); + return elements[1]; + } + constexpr inline const element_type& y() const noexcept + { + static_assert(N > 1, "Vector does not contain a y element."); + return elements[1]; + } + /// @} + + /// Returns a reference to the third element. + /// @{ + constexpr inline element_type& z() noexcept + { + static_assert(N > 2, "Vector does not contain a z element."); + return elements[2]; + } + constexpr inline const element_type& z() const noexcept + { + static_assert(N > 2, "Vector does not contain a z element."); + return elements[2]; + } + /// @} + + /// Returns a pointer to the element array. + /// @{ + constexpr inline element_type* data() noexcept + { + return elements; + }; + constexpr inline const element_type* data() const noexcept + { + return elements; + }; + /// @} + + /// Returns an iterator to the first element. + /// @{ + constexpr inline element_type* begin() noexcept + { + return elements; + } + constexpr inline const element_type* begin() const noexcept + { + return elements; + } + constexpr inline const element_type* cbegin() const noexcept + { + return elements; + } + /// @} + + /// Returns an iterator to the element following the last element. + /// @{ + constexpr inline element_type* end() noexcept + { + return elements + N; + } + constexpr inline const element_type* end() const noexcept + { + return elements + N; + } + constexpr inline const element_type* cend() const noexcept + { + return elements + N; + } + /// @} + + /// Returns a reverse iterator to the first element of the reversed vector. + /// @{ + constexpr inline std::reverse_iterator rbegin() noexcept + { + return std::reverse_iterator(elements + N); + } + constexpr inline std::reverse_iterator rbegin() const noexcept + { + return std::reverse_iterator(elements + N); + } + constexpr inline std::reverse_iterator crbegin() const noexcept + { + return std::reverse_iterator(elements + N); + } + /// @} + + /// Returns a reverse iterator to the element following the last element of the reversed vector. + /// @{ + constexpr inline std::reverse_iterator rend() noexcept + { + return std::reverse_iterator(elements); + } + constexpr inline std::reverse_iterator rend() const noexcept + { + return std::reverse_iterator(elements); + } + constexpr inline std::reverse_iterator crend() const noexcept + { + return std::reverse_iterator(elements); + } + /// @} + + /// Returns the number of elements in the vector. + constexpr inline std::size_t size() const noexcept + { + return N; + }; + + /// @private + template + constexpr inline vector type_cast(std::index_sequence) const noexcept + { + return {static_cast(elements[I])...}; + } + + /** + * Type-casts the elements of this vector. + * + * @tparam U Target element type. + * + * @return Vector containing the type-casted elements. + */ + template + constexpr inline explicit operator vector() const noexcept + { + return type_cast(std::make_index_sequence{}); + } + + /// @private + template + constexpr inline vector size_cast(std::index_sequence) const noexcept + { + return {(I < N) ? elements[I] : T{0} ...}; + } + + /** + * Size-casts this vector to a vector with a different number of elements. Casting to a greater number of elements causes new elements to be set to `0`. + * + * @tparam M Target number of elements. + * + * @return Vector containing the type-casted elements. + */ + template + constexpr inline explicit operator vector() const noexcept + { + return size_cast(std::make_index_sequence{}); + } +}; + +// Ensure vector is a POD type +static_assert(std::is_standard_layout>::value, "Vector is not a standard-layout type."); +static_assert(std::is_trivial>::value, "Vector is not a trivial type."); +static_assert(sizeof(vector) == sizeof(float) * 3, "Vector size is greater than the size of its elements."); + +/// Vector with two elements. +template +using vector2 = vector; + +/// Vector with three elements. +template +using vector3 = vector; + +/// Vector with four elements. +template +using vector4 = vector; + +/** + * Returns the absolute values of each element. + * + * @param x Input vector + * @return Absolute values of input vector elements. + */ +template +constexpr vector abs(const vector& x); + +/** + * Adds two values. + * + * @param x First value. + * @param y Second value. + * @return Sum of the two values. + */ +/// @{ +template +constexpr vector add(const vector& x, const vector& y); +template +constexpr vector add(const vector& x, T y); +/// @} + +/** + * Checks if all elements of a boolean vector are `true`. + * + * @param x Vector to be tested for truth. + * @return `true` if all elements are `true`, `false` otherwise. + */ +template +constexpr bool all(const vector& x); + +/** + * Checks if any elements of a boolean vector are `true`. + * + * @param x Vector to be tested for truth. + * @return `true` if any elements are `true`, `false` otherwise. + */ +template +constexpr bool any(const vector& x); + +/** + * Reinterprets data as an *n*-dimensional vector of type `T`. + * + * @param data Data to reinterpret. + */ +template +constexpr vector& as_vector(T& data); + +/** + * Performs a element-wise ceil operation. + * + * @param x Input vector + * @return Component-wise ceil of input vector. + */ +template +constexpr vector ceil(const vector& x); + +/** + * Clamps the values of a vector's elements. + * + * @param x Vector to clamp. + * @param min_val Minimum value. + * @param max_val Maximum value. + * @return Clamped vector. + */ +/// @{ +template +constexpr vector clamp(const vector& x, const vector& min_val, const vector& max_val); +template +constexpr vector clamp(const vector& x, T min_val, T max_val); +/// @} + +/** + * Clamps the length of a vector. + * + * @param x Vector to clamp. + * @param max_length Maximum length. + * @return Length-clamped vector. + */ +template +vector clamp_length(const vector& x, T max_length); + +/** + * Calculate the cross product of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Cross product of the two vectors. + */ +template +constexpr vector cross(const vector& x, const vector& y); + +/** + * Calculates the distance between two points. + * + * @param p0 First of two points. + * @param p1 Second of two points. + * @return Distance between the two points. + */ +template +T distance(const vector& p0, const vector& p1); + +/** + * Calculates the squared distance between two points. The squared distance can be calculated faster than the distance because a call to `std::sqrt` is saved. + * + * @param p0 First of two points. + * @param p1 Second of two points. + * @return Squared distance between the two points. + */ +template +constexpr T distance_squared(const vector& p0, const vector& p1); + +/** + * Divides a vector by a value. + * + * @param x First value. + * @param y Second value. + * @return Result of the division. + */ +/// @{ +template +constexpr vector div(const vector& x, const vector& y); +template +constexpr vector div(const vector& x, T y); +/// @} + +/** + * Calculates the dot product of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Dot product of the two vectors. + */ +template +constexpr T dot(const vector& x, const vector& y); + +/** + * Compares two vectors for equality + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector equal(const vector& x, const vector& y); + +/** + * Performs a element-wise floor operation. + * + * @param x Input vector + * @return Component-wise floor of input vector. + */ +template +constexpr vector floor(const vector& x); + +/** + * Performs a multiply-add operation. + * + * @param x Input vector + * @param y Value to multiply. + * @param z Value to add. + * + * @return Vector containing the multiply-add results. + */ +/// @{ +template +constexpr vector fma(const vector& x, const vector& y, const vector& z); +template +constexpr vector fma(const vector& x, T y, T z); +/// @} + +/** + * Returns a vector containing the fractional part of each element. + * + * @param x Input vector + * @return Fractional parts of input vector. + */ +template +constexpr vector fract(const vector& x); + +/** + * Performs a element-wise greater-than comparison of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector greater_than(const vector& x, const vector& y); + +/** + * Performs a element-wise greater-than or equal-to comparison of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector greater_than_equal(const vector& x, const vector& y); + +/** + * Calculates the length of a vector. + * + * @param x Vector of which to calculate the length. + * @return Length of the vector. + */ +template +T length(const vector& x); + +/** + * Calculates the squared length of a vector. The squared length can be calculated faster than the length because a call to `std::sqrt` is saved. + * + * @param x Vector of which to calculate the squared length. + * @return Squared length of the vector. + */ +template +constexpr T length_squared(const vector& x); + +/** + * Performs a element-wise less-than comparison of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector less_than(const vector& x, const vector& y); + +/** + * Performs a element-wise less-than or equal-to comparison of two vectors. + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector less_than_equal(const vector& x, const vector& y); + +/** + * Returns the value of the greatest element in a vector. + * + * @param x Input vector. + * + * @return Value of the greatest element in the input vector. + */ +template +constexpr T max(const vector& x); + +/** + * Returns the value of the smallest element in a vector. + * + * @param x Input vector. + * + * @return Value of the smallest element in the input vector. + */ +template +constexpr T min(const vector& x); + +/** + * Calculates the element-wise remainder of the division operation `x / y`. + * + * @param x First vector. + * @param y Second vector. + * @return Remainders of `x / y`. + */ +/// @{ +template +constexpr vector mod(const vector& x, const vector& y); +template +constexpr vector mod(const vector& x, T y); +/// @} + +/** + * Multiplies two values. + * + * @param x First value. + * @param y Second value. + * @return Product of the two values. + */ +/// @{ +template +constexpr vector mul(const vector& x, const vector& y); +template +constexpr vector mul(const vector& x, T y); +/// @} + +/** + * Negates a vector. + * + * @param x Vector to negate. + * @return Negated vector. + */ +template +constexpr vector negate(const vector& x); + +/** + * Calculates the unit vector in the same direction as the original vector. + * + * @param x Vector to normalize. + * @return Normalized vector. + */ +template +vector normalize(const vector& x); + +/** + * Logically inverts a boolean vector. + * + * @param x Vector to be inverted. + * @return Logically inverted vector. + */ +template +constexpr vector not(const vector& x); + +/** + * Compares two vectors for inequality + * + * @param x First vector. + * @param y Second vector. + * @return Boolean vector containing the result of the element comparisons. + */ +template +constexpr vector not_equal(const vector& x, const vector& y); + +/** + * Resizes a vector. Any new elements will be set to `0`. + * + * @tparam M Target number of elements. + * + * @param x Vector to resize. + * + * @return Resized vector. + */ +template +constexpr vector resize(const vector& x); + +/** + * Performs a element-wise round operation. + * + * @param x Input vector + * @return Component-wise round of input vector. + */ +template +constexpr vector round(const vector& x); + +/** + * Returns a vector containing the signs of each element. + * + * @param x Input vector + * @return Signs of input vector elements. + */ +template +constexpr vector sign(const vector& x); + +/** + * Subtracts a value by another value. + * + * @param x First value. + * @param y Second value. + * @return Difference between the two values. + */ +/// @{ +template +constexpr vector sub(const vector& x, const vector& y); +template +constexpr vector sub(const vector& x, T y); +/// @} + +/** + * Calculates the sum of all elements in a vector. + * + * @param x Vector to sum. + * @return Sum of the vector's elements. + */ +template +constexpr T sum(const vector& x); + +/** + * Makes an *m*-dimensional vector by rearranging and/or duplicating elements of an *n*-dimensional vector. + * + * @tparam Indices List of indices of elements in @p x. + * @tparam T Vector element type. + * @tparam N Number of dimensions in @p x. + * + * @param x Vector to swizzle. + * + * @return Vector containing elements from @p x in the order specified by @p Indices. The size of the returned vector is equivalent to the number of indices in @p Indices. + */ +template +constexpr vector swizzle(const vector& x); + +/** + * Performs a element-wise trunc operation. + * + * @param x Input vector + * @return Component-wise trunc of input vector. + */ +template +constexpr vector trunc(const vector& x); + +/** + * Types casts each vector element and returns a vector of the casted type. + * + * @tparam U Target vector element type. + * @tparam T Source vector element type. + * @tparam N Number of dimensions. + * @param x Vector to type cast. + * + * @return Type-casted vector. + */ +template +constexpr vector type_cast(const vector& x); + +/// @private +template +constexpr inline vector abs(const vector& x, std::index_sequence) +{ + return {std::abs(x[I])...}; +} + +template +constexpr inline vector abs(const vector& x) +{ + return abs(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector add(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] + y[I])...}; +} + +template +constexpr inline vector add(const vector& x, const vector& y) +{ + return add(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector add(const vector& x, T y, std::index_sequence) +{ + return {(x[I] + y)...}; +} + +template +constexpr inline vector add(const vector& x, T y) +{ + return add(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline bool all(const vector& x, std::index_sequence) +{ + return (x[I] && ...); +} + +template +constexpr inline bool all(const vector& x) +{ + return all(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline bool any(const vector& x, std::index_sequence) +{ + return (x[I] || ...); +} + +template +constexpr inline bool any(const vector& x) +{ + return any(x, std::make_index_sequence{}); +} + +template +constexpr inline vector& as_vector(T& data) +{ + static_assert(std::is_pod>::value); + return reinterpret_cast&>(data); +} + +/// @private +template +constexpr inline vector ceil(const vector& x, std::index_sequence) +{ + return {std::ceil(x[I])...}; +} + +template +constexpr inline vector ceil(const vector& x) +{ + static_assert(std::is_floating_point::value); + return ceil(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector clamp(const vector& x, const vector& min_val, const vector& max_val, std::index_sequence) +{ + return {std::min(max_val[I], std::max(min_val[I], x[I]))...}; +} + +template +constexpr inline vector clamp(const vector& x, const vector& min_val, const vector& max_val) +{ + return clamp(x, min_val, max_val, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector clamp(const vector& x, T min_val, T max_val, std::index_sequence) +{ + return {std::min(max_val, std::max(min_val, x[I]))...}; +} + +template +constexpr inline vector clamp(const vector& x, T min_val, T max_val) +{ + return clamp(x, min_val, max_val, std::make_index_sequence{}); +} + +template +vector clamp_length(const vector& x, T max_length) +{ + static_assert(std::is_floating_point::value); + T length2 = length_squared(x); + return (length2 > max_length * max_length) ? (x * max_length / std::sqrt(length2)) : x; +} + +template +constexpr inline vector cross(const vector& x, const vector& y) +{ + return + { + x[1] * y[2] - y[1] * x[2], + x[2] * y[0] - y[2] * x[0], + x[0] * y[1] - y[0] * x[1] + }; +} + +template +inline T distance(const vector& p0, const vector& p1) +{ + static_assert(std::is_floating_point::value); + return length(sub(p0, p1)); +} + +template +constexpr inline T distance_squared(const vector& p0, const vector& p1) +{ + static_assert(std::is_floating_point::value); + return length_squared(sub(p0, p1)); +} + +/// @private +template +constexpr inline vector div(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] / y[I])...}; +} + +template +constexpr inline vector div(const vector& x, const vector& y) +{ + return div(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector div(const vector& x, T y, std::index_sequence) +{ + return {(x[I] / y)...}; +} + +template +constexpr inline vector div(const vector& x, T y) +{ + return div(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline T dot(const vector& x, const vector& y, std::index_sequence) +{ + return ((x[I] * y[I]) + ...); +} + +template +constexpr inline T dot(const vector& x, const vector& y) +{ + return dot(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector equal(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] == y[I])...}; +} + +template +constexpr inline vector equal(const vector& x, const vector& y) +{ + return equal(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector floor(const vector& x, std::index_sequence) +{ + return {std::floor(x[I])...}; +} + +template +constexpr inline vector floor(const vector& x) +{ + static_assert(std::is_floating_point::value); + return floor(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector fma(const vector& x, const vector& y, const vector& z, std::index_sequence) +{ + return {std::fma(x[I], y[I], z[I])...}; +} + +template +constexpr inline vector fma(const vector& x, const vector& y, const vector& z) +{ + static_assert(std::is_floating_point::value); + return fma(x, y, z, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector fma(const vector& x, T y, T z, std::index_sequence) +{ + return {std::fma(x[I], y, z)...}; +} + +template +constexpr inline vector fma(const vector& x, T y, T z) +{ + static_assert(std::is_floating_point::value); + return fma(x, y, z, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector fract(const vector& x, std::index_sequence) +{ + return {x[I] - std::floor(x[I])...}; +} + +template +constexpr inline vector fract(const vector& x) +{ + static_assert(std::is_floating_point::value); + return fract(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector greater_than(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] > y[I])...}; +} + +template +constexpr inline vector greater_than(const vector& x, const vector& y) +{ + return greater_than(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector greater_than_equal(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] >= y[I])...}; +} + +template +constexpr inline vector greater_than_equal(const vector& x, const vector& y) +{ + return greater_than_equal(x, y, std::make_index_sequence{}); +} + +template +inline T length(const vector& x) +{ + static_assert(std::is_floating_point::value); + return std::sqrt(dot(x, x)); +} + +template +constexpr inline T length_squared(const vector& x) +{ + return dot(x, x); +} + +/// @private +template +constexpr inline vector less_than(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] < y[I])...}; +} + +template +constexpr inline vector less_than(const vector& x, const vector& y) +{ + return less_than(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector less_than_equal(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] <= y[I])...}; +} + +template +constexpr inline vector less_than_equal(const vector& x, const vector& y) +{ + return less_than_equal(x, y, std::make_index_sequence{}); +} + +template +constexpr inline T max(const vector& x) +{ + return *std::max_element(x.elements, x.elements + N); +} + +template +constexpr inline T min(const vector& x) +{ + return *std::min_element(x.elements, x.elements + N); +} + +/// @private +template +constexpr inline vector mod(const vector& x, const vector& y, std::index_sequence) +{ + return {std::fmod(x[I], y[I])...}; +} + +template +constexpr inline vector mod(const vector& x, const vector& y) +{ + static_assert(std::is_floating_point::value); + return mod(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector mod(const vector& x, T y, std::index_sequence) +{ + return {std::fmod(x[I], y)...}; +} + +template +constexpr inline vector mod(const vector& x, T y) +{ + static_assert(std::is_floating_point::value); + return mod(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector mul(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] * y[I])...}; +} + +template +constexpr inline vector mul(const vector& x, const vector& y) +{ + return mul(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector mul(const vector& x, T y, std::index_sequence) +{ + return {(x[I] * y)...}; +} + +template +constexpr inline vector mul(const vector& x, T y) +{ + return mul(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector negate(const vector& x, std::index_sequence) +{ + return {(-x[I])...}; +} + +template +constexpr inline vector negate(const vector& x) +{ + return negate(x, std::make_index_sequence{}); +} + +template +inline vector normalize(const vector& x) +{ + static_assert(std::is_floating_point::value); + return mul(x, T(1) / length(x)); +} + +/// @private +template +constexpr inline vector not(const vector& x, std::index_sequence) +{ + return {!x[I]...}; +} + +template +constexpr inline vector not(const vector& x) +{ + return not(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector not_equal(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] != y[I])...}; +} + +template +constexpr inline vector not_equal(const vector& x, const vector& y) +{ + return not_equal(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector resize(const vector& x, std::index_sequence) +{ + return {(I < N) ? x[I] : T{0} ...}; +} + +template +constexpr inline vector resize(const vector& x) +{ + return resize(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector round(const vector& x, std::index_sequence) +{ + return {std::round(x[I])...}; +} + +template +constexpr inline vector round(const vector& x) +{ + static_assert(std::is_floating_point::value); + return round(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector sign(const vector& x, std::index_sequence) +{ + return {std::copysign(T{1}, x[I])...}; +} + +template +constexpr inline vector sign(const vector& x) +{ + static_assert(std::is_floating_point::value); + return sign(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector sub(const vector& x, const vector& y, std::index_sequence) +{ + return {(x[I] - y[I])...}; +} + +template +constexpr inline vector sub(const vector& x, const vector& y) +{ + return sub(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector sub(const vector& x, T y, std::index_sequence) +{ + return {(x[I] - y)...}; +} + +template +constexpr inline vector sub(const vector& x, T y) +{ + return sub(x, y, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline T sum(const vector& x, std::index_sequence) +{ + return (x[I] + ...); +} + +template +constexpr inline T sum(const vector& x) +{ + return sum(x, std::make_index_sequence{}); +} + +template +constexpr inline vector swizzle(const vector& x) +{ + return {x[Indices]...}; +} + +/// @private +template +constexpr inline vector trunc(const vector& x, std::index_sequence) +{ + return {std::trunc(x[I])...}; +} + +template +constexpr inline vector trunc(const vector& x) +{ + static_assert(std::is_floating_point::value); + return trunc(x, std::make_index_sequence{}); +} + +/// @private +template +constexpr inline vector type_cast(const vector& x, std::index_sequence) +{ + return {static_cast(x[I])...}; +} + +template +constexpr inline vector type_cast(const vector& x) +{ + return type_cast(x, std::make_index_sequence{}); +} + +} // namespace math + +/// @copydoc math::add(const math::vector&, const math::vector&) +template +constexpr math::vector operator+(const math::vector& x, const math::vector& y); + +/// @copydoc math::add(const math::vector&, T) +/// @{ +template +constexpr math::vector operator+(const math::vector& x, T y); +template +constexpr math::vector operator+(T x, const math::vector& y); +/// @} + +/// @copydoc math::div(const math::vector&, const math::vector&) +template +constexpr math::vector operator/(const math::vector& x, const math::vector& y); + +/// @copydoc math::div(const math::vector&, T y) +template +constexpr math::vector operator/(const math::vector& x, T y); + +/// @copydoc math::mul(const math::vector&, const math::vector&) +template +constexpr math::vector operator*(const math::vector& x, const math::vector& y); + +/// @copydoc math::mul(const math::vector&, T) +/// @{ +template +constexpr math::vector operator*(const math::vector& x, T y); +template +constexpr math::vector operator*(T x, const math::vector& y); +/// @} + +/// @copydoc math::negate(const math::vector&) +template +constexpr math::vector operator-(const math::vector& x); + +/// @copydoc math::sub(const math::vector&, const math::vector&) +template +constexpr math::vector operator-(const math::vector& x, const math::vector& y); + +/// @copydoc math::sub(const math::vector&, T) +template +constexpr math::vector operator-(const math::vector& x, T y); + +/** + * Adds two values and stores the result in the first value. + * + * @param x First value. + * @param y Second value. + * @return Reference to the first value. + */ +/// @{ +template +constexpr math::vector& operator+=(math::vector& x, const math::vector& y); +template +constexpr math::vector& operator+=(math::vector& x, T y); +/// @} + +/** + * Subtracts the first value by the second value and stores the result in the first value. + * + * @param x First value. + * @param y Second value. + * @return Reference to the first value. + */ +/// @{ +template +constexpr math::vector& operator-=(math::vector& x, const math::vector& y); +template +constexpr math::vector& operator-=(math::vector& x, T y); +/// @} + +/** + * Multiplies two values and stores the result in the first value. + * + * @param x First value. + * @param y Second value. + * @return Reference to the first value. + */ +/// @{ +template +constexpr math::vector& operator*=(math::vector& x, const math::vector& y); +template +constexpr math::vector& operator*=(math::vector& x, T y); +/// @} + +/** + * Divides the first value by the second value and stores the result in the first value. + * + * @param x First value. + * @param y Second value. + * @return Reference to the first value. + */ +/// @{ +template +constexpr math::vector& operator/=(math::vector& x, const math::vector& y); +template +constexpr math::vector& operator/=(math::vector& x, T y); +/// @} + +/** + * Writes the elements of a vector to an output stream, with each element delimeted by a space. + * + * @param os Output stream. + * @param x Vector. + * + * @return Output stream. + */ +template +std::ostream& operator<<(std::ostream& os, const math::vector& x); + +/** + * Reads the elements of a vector from an input stream, with each element delimeted by a space. + * + * @param is Input stream. + * @param x Vector. + * + * @return Input stream. + */ +template +std::istream& operator>>(std::istream& is, math::vector& v); + +template +constexpr inline math::vector operator+(const math::vector& x, const math::vector& y) +{ + return add(x, y); +} + +template +constexpr inline math::vector operator+(const math::vector& x, T y) +{ + return add(x, y); +} + +template +constexpr inline math::vector operator+(T x, const math::vector& y) +{ + return add(y, x); +} + +template +constexpr inline math::vector operator-(const math::vector& x) +{ + return negate(x); +} + +template +constexpr inline math::vector operator-(const math::vector& x, const math::vector& y) +{ + return sub(x, y); +} + +template +constexpr inline math::vector operator-(const math::vector& x, T y) +{ + return sub(x, y); +} + +template +constexpr inline math::vector operator*(const math::vector& x, const math::vector& y) +{ + return mul(x, y); +} + +template +constexpr inline math::vector operator*(const math::vector& x, T y) +{ + return mul(x, y); +} + +template +constexpr inline math::vector operator*(T x, const math::vector& y) +{ + return mul(y, x); +} + +template +constexpr inline math::vector operator/(const math::vector& x, const math::vector& y) +{ + return div(x, y); +} + +template +constexpr inline math::vector operator/(const math::vector& x, T y) +{ + return div(x, y); +} + +template +constexpr inline math::vector& operator+=(math::vector& x, const math::vector& y) +{ + return (x = x + y); +} + +template +constexpr inline math::vector& operator+=(math::vector& x, T y) +{ + return (x = x + y); +} + +template +constexpr inline math::vector& operator-=(math::vector& x, const math::vector& y) +{ + return (x = x - y); +} + +template +constexpr inline math::vector& operator-=(math::vector& x, T y) +{ + return (x = x - y); +} + +template +constexpr inline math::vector& operator*=(math::vector& x, const math::vector& y) +{ + return (x = x * y); +} + +template +constexpr inline math::vector& operator*=(math::vector& x, T y) +{ + return (x = x * y); +} + +template +constexpr inline math::vector& operator/=(math::vector& x, const math::vector& y) +{ + return (x = x / y); +} + +template +constexpr inline math::vector& operator/=(math::vector& x, T y) +{ + return (x = x / y); +} + +template +std::ostream& operator<<(std::ostream& os, const math::vector& x) +{ + for (std::size_t i = 0; i < N; ++i) + { + if (i) + os << ' '; + os << x[i]; + } + + return os; +} + +template +std::istream& operator>>(std::istream& is, math::vector& x) +{ + for (std::size_t i = 0; i < N; ++i) + is >> x[i]; + + return is; +} + +#endif // ANTKEEPER_MATH_VECTOR_HPP diff --git a/src/physics/orbit/frame.hpp b/src/physics/orbit/frame.hpp index c6c0210..0a35ae2 100644 --- a/src/physics/orbit/frame.hpp +++ b/src/physics/orbit/frame.hpp @@ -41,13 +41,13 @@ namespace pqw { template math::vector3 spherical(const math::vector3& v) { - const T xx_yy = v.x * v.x + v.y * v.y; + const T xx_yy = v.x() * v.x() + v.y() * v.y(); return math::vector3 { - std::sqrt(xx_yy + v.z * v.z), - std::atan2(v.z, std::sqrt(xx_yy)), - std::atan2(v.y, v.x) + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) }; } @@ -188,13 +188,13 @@ namespace bci { template math::vector3 spherical(const math::vector3& v) { - const T xx_yy = v.x * v.x + v.y * v.y; + const T xx_yy = v.x() * v.x() + v.y() * v.y(); return math::vector3 { - std::sqrt(xx_yy + v.z * v.z), - std::atan2(v.z, std::sqrt(xx_yy)), - std::atan2(v.y, v.x) + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) }; } @@ -276,13 +276,13 @@ namespace bcbf { template math::vector3 spherical(const math::vector3& v) { - const T xx_yy = v.x * v.x + v.y * v.y; + const T xx_yy = v.x() * v.x() + v.y() * v.y(); return math::vector3 { - std::sqrt(xx_yy + v.z * v.z), - std::atan2(v.z, std::sqrt(xx_yy)), - std::atan2(v.y, v.x) + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) }; } @@ -366,13 +366,13 @@ namespace enu { template math::vector3 spherical(const math::vector3& v) { - const T xx_yy = v.x * v.x + v.y * v.y; + const T xx_yy = v.x() * v.x() + v.y() * v.y(); return math::vector3 { - std::sqrt(xx_yy + v.z * v.z), - std::atan2(v.z, std::sqrt(xx_yy)), - math::half_pi - std::atan2(v.y, v.x) + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + math::half_pi - std::atan2(v.y(), v.x()) }; } diff --git a/src/physics/orbit/trajectory.hpp b/src/physics/orbit/trajectory.hpp index d4fcb6a..8de6b44 100644 --- a/src/physics/orbit/trajectory.hpp +++ b/src/physics/orbit/trajectory.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP #include "math/polynomial.hpp" +#include "math/vector.hpp" #include namespace physics { @@ -71,9 +72,9 @@ math::vector trajectory::position(T t) const t = (t / dt - i) * T(2) - T(1); math::vector3 r; - r.x = math::polynomial::chebyshev::evaluate(ax, ay, t); - r.y = math::polynomial::chebyshev::evaluate(ay, az, t); - r.z = math::polynomial::chebyshev::evaluate(az, az + n, t); + r.x() = math::polynomial::chebyshev::evaluate(ax, ay, t); + r.y() = math::polynomial::chebyshev::evaluate(ay, az, t); + r.z() = math::polynomial::chebyshev::evaluate(az, az + n, t); return r; } diff --git a/src/render/passes/ground-pass.cpp b/src/render/passes/ground-pass.cpp index 05c6f57..67dd525 100644 --- a/src/render/passes/ground-pass.cpp +++ b/src/render/passes/ground-pass.cpp @@ -124,7 +124,7 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const // Pre-expose light float3 light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - if (light_color.x < directional_light_color.x) + if (light_color.x() < directional_light_color.x()) break; directional_light_color = light_color; diff --git a/src/render/passes/material-pass.cpp b/src/render/passes/material-pass.cpp index 1b5f18b..dbd1fbb 100644 --- a/src/render/passes/material-pass.cpp +++ b/src/render/passes/material-pass.cpp @@ -209,7 +209,7 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); float2 scale = directional_light->get_light_texture_scale_tween().interpolate(ctx.alpha); - float4x4 light_projection = math::ortho(-scale.x, scale.x, -scale.y, scale.y, -1.0f, 1.0f); + float4x4 light_projection = math::ortho(-scale.x(), scale.x(), -scale.y(), scale.y(), -1.0f, 1.0f); directional_light_texture_matrices[directional_light_count] = light_projection * light_view; } diff --git a/src/render/passes/outline-pass.cpp b/src/render/passes/outline-pass.cpp index 7ab727e..cd28702 100644 --- a/src/render/passes/outline-pass.cpp +++ b/src/render/passes/outline-pass.cpp @@ -104,7 +104,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (outline_color.w < 1.0f) + if (outline_color[3] < 1.0f) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/render/passes/shadow-map-pass.cpp b/src/render/passes/shadow-map-pass.cpp index 81c6ac0..f413da8 100644 --- a/src/render/passes/shadow-map-pass.cpp +++ b/src/render/passes/shadow-map-pass.cpp @@ -187,27 +187,27 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c // Calculate scale float3 scale; - scale.x = 2.0f / (cropping_bounds.max_point.x - cropping_bounds.min_point.x); - scale.y = 2.0f / (cropping_bounds.max_point.y - cropping_bounds.min_point.y); - scale.z = 1.0f / (cropping_bounds.max_point.z - cropping_bounds.min_point.z); - //scale.z = 2.0f / (cropping_bounds.max_point.z - cropping_bounds.min_point.z); + scale.x() = 2.0f / (cropping_bounds.max_point.x() - cropping_bounds.min_point.x()); + scale.y() = 2.0f / (cropping_bounds.max_point.y() - cropping_bounds.min_point.y()); + scale.z() = 1.0f / (cropping_bounds.max_point.z() - cropping_bounds.min_point.z()); + //scale.z() = 2.0f / (cropping_bounds.max_point.z() - cropping_bounds.min_point.z()); // Quantize scale float scale_quantizer = 64.0f; - scale.x = 1.0f / std::ceil(1.0f / scale.x * scale_quantizer) * scale_quantizer; - scale.y = 1.0f / std::ceil(1.0f / scale.y * scale_quantizer) * scale_quantizer; + scale.x() = 1.0f / std::ceil(1.0f / scale.x() * scale_quantizer) * scale_quantizer; + scale.y() = 1.0f / std::ceil(1.0f / scale.y() * scale_quantizer) * scale_quantizer; // Calculate offset float3 offset; - offset.x = (cropping_bounds.max_point.x + cropping_bounds.min_point.x) * scale.x * -0.5f; - offset.y = (cropping_bounds.max_point.y + cropping_bounds.min_point.y) * scale.y * -0.5f; - offset.z = -cropping_bounds.min_point.z * scale.z; - //offset.z = (cropping_bounds.max_point.z + cropping_bounds.min_point.z) * scale.z * -0.5f; + offset.x() = (cropping_bounds.max_point.x() + cropping_bounds.min_point.x()) * scale.x() * -0.5f; + offset.y() = (cropping_bounds.max_point.y() + cropping_bounds.min_point.y()) * scale.y() * -0.5f; + offset.z() = -cropping_bounds.min_point.z() * scale.z(); + //offset.z() = (cropping_bounds.max_point.z() + cropping_bounds.min_point.z()) * scale.z() * -0.5f; // Quantize offset float half_shadow_map_resolution = static_cast(shadow_map_resolution) * 0.5f; - offset.x = std::ceil(offset.x * half_shadow_map_resolution) / half_shadow_map_resolution; - offset.y = std::ceil(offset.y * half_shadow_map_resolution) / half_shadow_map_resolution; + offset.x() = std::ceil(offset.x() * half_shadow_map_resolution) / half_shadow_map_resolution; + offset.y() = std::ceil(offset.y() * half_shadow_map_resolution) / half_shadow_map_resolution; // Crop the light view-projection matrix crop_matrix = math::translate(math::matrix4::identity, offset) * math::scale(math::matrix4::identity, scale); diff --git a/src/render/passes/sky-pass.cpp b/src/render/passes/sky-pass.cpp index adfca21..739ef3c 100644 --- a/src/render/passes/sky-pass.cpp +++ b/src/render/passes/sky-pass.cpp @@ -321,7 +321,7 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const } // Draw moon model - //if (moon_position.y >= -moon_angular_radius) + //if (moon_position.y() >= -moon_angular_radius) { float moon_distance = (clip_near + clip_far) * 0.5f; float moon_radius = moon_angular_radius * moon_distance; @@ -528,10 +528,10 @@ void sky_pass::set_sun_angular_radius(float radius) void sky_pass::set_planet_radius(float radius) { - atmosphere_radii.x = radius; - atmosphere_radii.y = atmosphere_radii.x + atmosphere_upper_limit; - atmosphere_radii.z = atmosphere_radii.y * atmosphere_radii.y; - observer_position_tween[1] = {0.0f, atmosphere_radii.x + observer_elevation, 0.0f}; + atmosphere_radii.x() = radius; + atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; + atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); + observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; // Trigger transmittance LUT render render_transmittance_lut = true; @@ -540,8 +540,8 @@ void sky_pass::set_planet_radius(float radius) void sky_pass::set_atmosphere_upper_limit(float limit) { atmosphere_upper_limit = limit; - atmosphere_radii.y = atmosphere_radii.x + atmosphere_upper_limit; - atmosphere_radii.z = atmosphere_radii.y * atmosphere_radii.y; + atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; + atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); // Trigger transmittance LUT render render_transmittance_lut = true; @@ -550,7 +550,7 @@ void sky_pass::set_atmosphere_upper_limit(float limit) void sky_pass::set_observer_elevation(float elevation) { observer_elevation = elevation; - observer_position_tween[1] = {0.0f, atmosphere_radii.x + observer_elevation, 0.0f}; + observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; } void sky_pass::set_rayleigh_parameters(float scale_height, const float3& scattering) @@ -558,9 +558,9 @@ void sky_pass::set_rayleigh_parameters(float scale_height, const float3& scatter rayleigh_parameters = { -1.0f / scale_height, - scattering.x, - scattering.y, - scattering.z + scattering.x(), + scattering.y(), + scattering.z() }; // Trigger transmittance LUT render diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index b5b591f..fd86de2 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -71,9 +71,9 @@ static bool load_component_atmosphere(entity::archetype& archetype, const json& if (element.contains("airglow_illuminance")) { const auto& airglow_illuminance = element["airglow_illuminance"]; - component.airglow_illuminance.x = airglow_illuminance[0].get(); - component.airglow_illuminance.y = airglow_illuminance[1].get(); - component.airglow_illuminance.z = airglow_illuminance[2].get(); + component.airglow_illuminance.x() = airglow_illuminance[0].get(); + component.airglow_illuminance.y() = airglow_illuminance[1].get(); + component.airglow_illuminance.z() = airglow_illuminance[2].get(); } archetype.stamps.push_back @@ -267,9 +267,9 @@ static bool load_component_transform(entity::archetype& archetype, const json& e if (element.contains("translation")) { auto translation = element["translation"]; - component.local.translation.x = translation[0].get(); - component.local.translation.y = translation[1].get(); - component.local.translation.z = translation[2].get(); + component.local.translation.x() = translation[0].get(); + component.local.translation.y() = translation[1].get(); + component.local.translation.z() = translation[2].get(); } if (element.contains("rotation")) @@ -284,9 +284,9 @@ static bool load_component_transform(entity::archetype& archetype, const json& e if (element.contains("scale")) { auto translation = element["scale"]; - component.local.scale.x = translation[0].get(); - component.local.scale.y = translation[1].get(); - component.local.scale.z = translation[2].get(); + component.local.scale.x() = translation[0].get(); + component.local.scale.y() = translation[1].get(); + component.local.scale.z() = translation[2].get(); } component.world = component.local; diff --git a/src/resources/image-loader.cpp b/src/resources/image-loader.cpp index aeadc50..8c04bbc 100644 --- a/src/resources/image-loader.cpp +++ b/src/resources/image-loader.cpp @@ -110,7 +110,7 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F image->resize(static_cast(exr_image.width), static_cast(exr_image.height)); // Fill image pixels - float* component = static_cast(image->get_pixels()); + float* component = static_cast(image->data()); for (int y = exr_image.height - 1; y >= 0; --y) { int row_offset = y * exr_image.width; @@ -168,7 +168,7 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F image = new ::image(); image->format(component_size, channels); image->resize(static_cast(width), static_cast(height)); - std::memcpy(image->get_pixels(), pixels, image->get_size()); + std::memcpy(image->data(), pixels, image->get_size()); // Free loaded image data stbi_image_free(pixels); diff --git a/src/resources/image.hpp b/src/resources/image.hpp index 33be9d2..4dce95c 100644 --- a/src/resources/image.hpp +++ b/src/resources/image.hpp @@ -20,7 +20,9 @@ #ifndef ANTKEEPER_IMAGE_HPP #define ANTKEEPER_IMAGE_HPP +#include "math/vector.hpp" #include +#include /** * Stores basic image data. @@ -48,6 +50,64 @@ public: */ image& operator=(const image& source); + /** + * Returns an iterator to the first pixel. + * + * @tparam T Pixel data type. + */ + /// @{ + template + constexpr inline T* begin() noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + template + constexpr inline const T* begin() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + template + constexpr inline const T* cbegin() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + /// @} + + /** + * Returns an iterator to the pixel following the last pixel. + * + * @tparam T Pixel data type. + */ + /// @{ + template + constexpr inline T* end() noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(static_cast(pixels) + size); + } + template + constexpr inline const T* end() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(static_cast(pixels) + size); + } + template + constexpr inline const T* cend() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(static_cast(pixels) + size); + } + /// @} + /** * Checks whether another image has the same number of channels and pixel size as this image. * @@ -100,12 +160,12 @@ public: /// Returns the number of color channels in the image. std::size_t get_channel_count() const; - - /// Returns a pointer to the pixel data. - const void* get_pixels() const; - /// @copydoc image::get_pixels() const - void* get_pixels(); + /// Returns a pointer to the pixel data. + /// @{ + void* data() noexcept; + const void* data() const noexcept; + /// @} /// Returns the size of a single pixel, in bytes. std::size_t get_pixel_size() const; @@ -146,12 +206,12 @@ inline std::size_t image::get_channel_count() const return channel_count; } -inline const void* image::get_pixels() const +inline void* image::data() noexcept { return pixels; } -inline void* image::get_pixels() +inline const void* image::data() const noexcept { return pixels; } diff --git a/src/resources/model-loader.cpp b/src/resources/model-loader.cpp index 6e45fa8..13d1fb1 100644 --- a/src/resources/model-loader.cpp +++ b/src/resources/model-loader.cpp @@ -99,14 +99,14 @@ render::model* resource_loader::load(resource_manager* resource_m { if (auto min_node = bounds_node.value().find("min"); min_node != bounds_node.value().end()) { - float* v = &bounds.min_point.x; + float* v = &bounds.min_point.x(); for (const auto& element: min_node.value()) *(v++) = element.get(); } if (auto max_node = bounds_node.value().find("max"); max_node != bounds_node.value().end()) { - float* v = &bounds.max_point.x; + float* v = &bounds.max_point.x(); for (const auto& element: max_node.value()) *(v++) = element.get(); } @@ -248,9 +248,9 @@ render::model* resource_loader::load(resource_manager* resource_m { if (translation_node->size() == 3) { - bone_transform.translation.x = (*translation_node)[0].get(); - bone_transform.translation.y = (*translation_node)[1].get(); - bone_transform.translation.z = (*translation_node)[2].get(); + bone_transform.translation.x() = (*translation_node)[0].get(); + bone_transform.translation.y() = (*translation_node)[1].get(); + bone_transform.translation.z() = (*translation_node)[2].get(); } } diff --git a/src/resources/texture-loader.cpp b/src/resources/texture-loader.cpp index 749bfd0..746964d 100644 --- a/src/resources/texture-loader.cpp +++ b/src/resources/texture-loader.cpp @@ -131,7 +131,7 @@ gl::texture_1d* resource_loader::load(resource_manager* resource } // Create texture - gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->get_pixels()); + gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->data()); // Set wrapping and filtering texture->set_wrapping(wrapping); @@ -243,7 +243,7 @@ gl::texture_2d* resource_loader::load(resource_manager* resource } // Create texture - gl::texture_2d* texture = new gl::texture_2d(image->get_width(), image->get_height(), type, format, color_space, image->get_pixels()); + gl::texture_2d* texture = new gl::texture_2d(image->get_width(), image->get_height(), type, format, color_space, image->data()); // Set wrapping and filtering texture->set_wrapping(wrapping, wrapping); diff --git a/src/scene/camera.cpp b/src/scene/camera.cpp index 05400aa..5595c73 100644 --- a/src/scene/camera.cpp +++ b/src/scene/camera.cpp @@ -133,7 +133,8 @@ void camera::set_perspective(float fov, float aspect_ratio, float clip_near, flo view_projection[1] = projection[1] * view[1]; // Recalculate view frustum - view_frustum.set_matrix(view_projection[1]); + /// @TODO: this is a hack to fix the half z projection matrix view frustum + view_frustum.set_matrix(math::perspective(this->fov[1], this->aspect_ratio[1], this->clip_near[1], this->clip_far[1]) * view[1]); } void camera::set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far) @@ -197,7 +198,11 @@ void camera::transformed() view_projection[1] = projection[1] * view[1]; // Recalculate view frustum - view_frustum.set_matrix(view_projection[1]); + /// @TODO: this is a hack to fix the half z projection matrix view frustum + if (orthographic) + view_frustum.set_matrix(view_projection[1]); + else + view_frustum.set_matrix(math::perspective(fov[1], aspect_ratio[1], clip_near[1], clip_far[1]) * view[1]); } } // namespace scene diff --git a/src/scene/object.hpp b/src/scene/object.hpp index a487ecd..e38b38a 100644 --- a/src/scene/object.hpp +++ b/src/scene/object.hpp @@ -22,7 +22,7 @@ #include "animation/tween.hpp" #include "geom/bounding-volume.hpp" -#include "math/vector-type.hpp" +#include "math/vector.hpp" #include "math/quaternion-type.hpp" #include "math/transform-type.hpp" #include "render/context.hpp" diff --git a/src/scene/spot-light.cpp b/src/scene/spot-light.cpp index b620aa4..c4ba065 100644 --- a/src/scene/spot-light.cpp +++ b/src/scene/spot-light.cpp @@ -46,7 +46,7 @@ void spot_light::set_attenuation(const float3& attenuation) void spot_light::set_cutoff(const float2& cutoff) { this->cutoff[1] = cutoff; - this->cosine_cutoff[1] = {std::cos(cutoff.x), std::cos(cutoff.y)}; + this->cosine_cutoff[1] = {std::cos(cutoff.x()), std::cos(cutoff.y())}; } void spot_light::update_tweens() diff --git a/src/scene/text.cpp b/src/scene/text.cpp index 9af8dd9..2f5b8ee 100644 --- a/src/scene/text.cpp +++ b/src/scene/text.cpp @@ -19,7 +19,6 @@ #include "scene/text.hpp" #include "render/vertex-attribute.hpp" -#include "math/vector-operators.hpp" #include "type/unicode/convert.hpp" #include @@ -218,7 +217,7 @@ void text::update_content() // Apply kerning if (previous_code) { - pen_position.x += font->get_kerning(previous_code, code).x; + pen_position.x() += font->get_kerning(previous_code, code).x(); } if (font->contains(code)) @@ -229,48 +228,48 @@ void text::update_content() // Calculate vertex positions float2 positions[6]; positions[0] = pen_position + glyph.metrics.horizontal_bearing; - positions[1] = {positions[0].x, positions[0].y - glyph.metrics.height}; - positions[2] = {positions[0].x + glyph.metrics.width, positions[1].y}; - positions[3] = {positions[2].x, positions[0].y}; + positions[1] = {positions[0].x(), positions[0].y() - glyph.metrics.height}; + positions[2] = {positions[0].x() + glyph.metrics.width, positions[1].y()}; + positions[3] = {positions[2].x(), positions[0].y()}; positions[4] = positions[0]; positions[5] = positions[2]; // Calculate vertex UVs float2 uvs[6]; - uvs[0] = {static_cast(glyph.position.x), static_cast(glyph.position.y)}; - uvs[1] = {uvs[0].x, uvs[0].y + glyph.metrics.height}; - uvs[2] = {uvs[0].x + glyph.metrics.width, uvs[1].y}; - uvs[3] = {uvs[2].x, uvs[0].y}; + uvs[0] = {static_cast(glyph.position.x()), static_cast(glyph.position.y())}; + uvs[1] = {uvs[0].x(), uvs[0].y() + glyph.metrics.height}; + uvs[2] = {uvs[0].x() + glyph.metrics.width, uvs[1].y()}; + uvs[3] = {uvs[2].x(), uvs[0].y()}; uvs[4] = uvs[0]; uvs[5] = uvs[2]; for (int i = 0; i < 6; ++i) { // Round positions - positions[i].x = std::round(positions[i].x); - positions[i].y = std::round(positions[i].y); + positions[i].x() = std::round(positions[i].x()); + positions[i].y() = std::round(positions[i].y()); // Normalize UVs - uvs[i].x = uvs[i].x / static_cast(font_bitmap.get_width()); - uvs[i].y = uvs[i].y / static_cast(font_bitmap.get_height()); + uvs[i].x() = uvs[i].x() / static_cast(font_bitmap.get_width()); + uvs[i].y() = uvs[i].y() / static_cast(font_bitmap.get_height()); } // Add vertex to vertex data buffer for (int i = 0; i < 6; ++i) { - *(v++) = positions[i].x; - *(v++) = positions[i].y; + *(v++) = positions[i].x(); + *(v++) = positions[i].y(); *(v++) = 0.0f; - *(v++) = uvs[i].x; - *(v++) = uvs[i].y; - *(v++) = color.x; - *(v++) = color.y; - *(v++) = color.z; - *(v++) = color.w; + *(v++) = uvs[i].x(); + *(v++) = uvs[i].y(); + *(v++) = color[0]; + *(v++) = color[1]; + *(v++) = color[2]; + *(v++) = color[3]; } // Advance pen position - pen_position.x += glyph.metrics.horizontal_advance; + pen_position.x() += glyph.metrics.horizontal_advance; // Update local-space bounds for (int i = 0; i < 4; ++i) @@ -293,8 +292,8 @@ void text::update_content() // Handle newlines if (code == static_cast('\n')) { - pen_position.x = 0.0f; - pen_position.y -= font_metrics.linegap; + pen_position.x() = 0.0f; + pen_position.y() -= font_metrics.linegap; } // Update previous UTF-32 character code @@ -329,10 +328,10 @@ void text::update_color() v += (3 + 2); // Update vertex color - *(v++) = color.x; - *(v++) = color.y; - *(v++) = color.z; - *(v++) = color.w; + *(v++) = color[0]; + *(v++) = color[1]; + *(v++) = color[2]; + *(v++) = color[3]; } // Update VBO diff --git a/src/type/bitmap-font.cpp b/src/type/bitmap-font.cpp index 0d533a3..2c81382 100644 --- a/src/type/bitmap-font.cpp +++ b/src/type/bitmap-font.cpp @@ -152,10 +152,10 @@ bool bitmap_font::pack(bool resize) // Copy glyph bitmap data into font bitmap image& glyph_bitmap = it->second.bitmap; - bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x, node->bounds.min.y); + bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x(), node->bounds.min.y()); // Record coordinates of glyph bitmap within font bitmap - it->second.position = {node->bounds.min.x, node->bounds.min.y}; + it->second.position = {node->bounds.min.x(), node->bounds.min.y()}; // Clear glyph bitmap data glyph_bitmap.resize(0, 0); @@ -185,7 +185,7 @@ void bitmap_font::unpack(bool resize) glyph.bitmap.resize(glyph_width, glyph_height); // Copy pixel data from font bitmap to glyph bitmap - glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x, glyph.position.y); + glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x(), glyph.position.y()); } // Free font bitmap pixel data diff --git a/src/type/freetype/typeface.cpp b/src/type/freetype/typeface.cpp index 48d7d2d..046cc12 100644 --- a/src/type/freetype/typeface.cpp +++ b/src/type/freetype/typeface.cpp @@ -85,10 +85,10 @@ bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) // Get glyph metrics metrics.width = face->glyph->metrics.width / 64.0f; metrics.height = face->glyph->metrics.height / 64.0f; - metrics.horizontal_bearing.x = face->glyph->metrics.horiBearingX / 64.0f; - metrics.horizontal_bearing.y = face->glyph->metrics.horiBearingY / 64.0f; - metrics.vertical_bearing.x = face->glyph->metrics.vertBearingX / 64.0f; - metrics.vertical_bearing.y = face->glyph->metrics.vertBearingY / 64.0f; + metrics.horizontal_bearing.x() = face->glyph->metrics.horiBearingX / 64.0f; + metrics.horizontal_bearing.y() = face->glyph->metrics.horiBearingY / 64.0f; + metrics.vertical_bearing.x() = face->glyph->metrics.vertBearingX / 64.0f; + metrics.vertical_bearing.y() = face->glyph->metrics.vertBearingY / 64.0f; metrics.horizontal_advance = face->glyph->metrics.horiAdvance / 64.0f; metrics.vertical_advance = face->glyph->metrics.vertAdvance / 64.0f; @@ -116,7 +116,7 @@ bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const bitmap.resize(face->glyph->bitmap.width, face->glyph->bitmap.rows); // Copy glyph bitmap data in bitmap - std::memcpy(bitmap.get_pixels(), face->glyph->bitmap.buffer, bitmap.get_size()); + std::memcpy(bitmap.data(), face->glyph->bitmap.buffer, bitmap.get_size()); return true; } diff --git a/src/utility/fundamental-types.hpp b/src/utility/fundamental-types.hpp index d8a57b0..01a773f 100644 --- a/src/utility/fundamental-types.hpp +++ b/src/utility/fundamental-types.hpp @@ -20,8 +20,7 @@ #ifndef ANTKEEPER_FUNDAMENTAL_TYPES_HPP #define ANTKEEPER_FUNDAMENTAL_TYPES_HPP -#include "math/vector-type.hpp" -#include "math/vector-operators.hpp" +#include "math/vector.hpp" #include "math/matrix-type.hpp" #include "math/matrix-operators.hpp"