Browse Source

Improve vector class. Add simplex noise, fBm, and hash functions. Start to revise terrain system

master
C. J. Howard 2 years ago
parent
commit
c222b87f25
73 changed files with 3048 additions and 1888 deletions
  1. +1
    -1
      src/config.hpp.in
  2. +21
    -21
      src/game/ant/morphogenesis.cpp
  3. +1
    -1
      src/game/component/orbit.hpp
  4. +1
    -1
      src/game/fonts.cpp
  5. +13
    -13
      src/game/graphics.cpp
  6. +120
    -1
      src/game/load.cpp
  7. +20
    -20
      src/game/menu.cpp
  8. +11
    -11
      src/game/state/boot.cpp
  9. +2
    -2
      src/game/state/credits.cpp
  10. +3
    -3
      src/game/state/graphics-menu.cpp
  11. +10
    -6
      src/game/state/main-menu.cpp
  12. +9
    -6
      src/game/state/nest-selection.cpp
  13. +6
    -6
      src/game/state/nuptial-flight.cpp
  14. +5
    -5
      src/game/system/astronomy.cpp
  15. +9
    -9
      src/game/system/atmosphere.cpp
  16. +9
    -9
      src/game/system/constraint.cpp
  17. +33
    -33
      src/game/system/painting.cpp
  18. +4
    -4
      src/game/system/samara.cpp
  19. +8
    -8
      src/game/system/subterrain.cpp
  20. +427
    -346
      src/game/system/terrain.cpp
  21. +64
    -43
      src/game/system/terrain.hpp
  22. +10
    -34
      src/game/world.cpp
  23. +13
    -13
      src/geom/aabb.hpp
  24. +4
    -4
      src/geom/cartesian.hpp
  25. +9
    -9
      src/geom/convex-hull.hpp
  26. +2
    -2
      src/geom/hyperoctree.hpp
  27. +3
    -3
      src/geom/intersection.cpp
  28. +6
    -6
      src/geom/mesh-accelerator.cpp
  29. +33
    -35
      src/geom/mesh-functions.cpp
  30. +6
    -6
      src/geom/mesh.cpp
  31. +25
    -24
      src/geom/mesh.hpp
  32. +5
    -5
      src/geom/meshes/grid.cpp
  33. +6
    -6
      src/geom/rect-pack.hpp
  34. +1
    -1
      src/geom/rect.hpp
  35. +3
    -3
      src/geom/sphere.hpp
  36. +1
    -0
      src/geom/view-frustum.hpp
  37. +26
    -8
      src/math/constants.hpp
  38. +1
    -3
      src/math/math.hpp
  39. +1
    -2
      src/math/matrix-functions.hpp
  40. +1
    -1
      src/math/matrix-type.hpp
  41. +70
    -0
      src/math/noise/fbm.hpp
  42. +142
    -0
      src/math/noise/hash.hpp
  43. +34
    -0
      src/math/noise/noise.hpp
  44. +189
    -0
      src/math/noise/simplex.hpp
  45. +0
    -1
      src/math/operators.hpp
  46. +4
    -5
      src/math/quaternion-functions.hpp
  47. +4
    -5
      src/math/se3.hpp
  48. +2
    -47
      src/math/stream-operators.hpp
  49. +1
    -2
      src/math/transform-functions.hpp
  50. +1
    -1
      src/math/transform-type.hpp
  51. +0
    -645
      src/math/vector-functions.hpp
  52. +0
    -202
      src/math/vector-operators.hpp
  53. +0
    -154
      src/math/vector-type.hpp
  54. +1492
    -0
      src/math/vector.hpp
  55. +16
    -16
      src/physics/orbit/frame.hpp
  56. +4
    -3
      src/physics/orbit/trajectory.hpp
  57. +1
    -1
      src/render/passes/ground-pass.cpp
  58. +1
    -1
      src/render/passes/material-pass.cpp
  59. +1
    -1
      src/render/passes/outline-pass.cpp
  60. +12
    -12
      src/render/passes/shadow-map-pass.cpp
  61. +11
    -11
      src/render/passes/sky-pass.cpp
  62. +9
    -9
      src/resources/entity-archetype-loader.cpp
  63. +2
    -2
      src/resources/image-loader.cpp
  64. +67
    -7
      src/resources/image.hpp
  65. +5
    -5
      src/resources/model-loader.cpp
  66. +2
    -2
      src/resources/texture-loader.cpp
  67. +7
    -2
      src/scene/camera.cpp
  68. +1
    -1
      src/scene/object.hpp
  69. +1
    -1
      src/scene/spot-light.cpp
  70. +27
    -28
      src/scene/text.cpp
  71. +3
    -3
      src/type/bitmap-font.cpp
  72. +5
    -5
      src/type/freetype/typeface.cpp
  73. +1
    -2
      src/utility/fundamental-types.hpp

+ 1
- 1
src/config.hpp.in View File

@ -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 {

+ 21
- 21
src/game/ant/morphogenesis.cpp View File

@ -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<float>(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<float> bounds;
bounds.min_point.x = std::numeric_limits<float>::infinity();
bounds.min_point.y = std::numeric_limits<float>::infinity();
bounds.min_point.z = std::numeric_limits<float>::infinity();
bounds.max_point.x = -std::numeric_limits<float>::infinity();
bounds.max_point.y = -std::numeric_limits<float>::infinity();
bounds.max_point.z = -std::numeric_limits<float>::infinity();
bounds.min_point.x() = std::numeric_limits<float>::infinity();
bounds.min_point.y() = std::numeric_limits<float>::infinity();
bounds.min_point.z() = std::numeric_limits<float>::infinity();
bounds.max_point.x() = -std::numeric_limits<float>::infinity();
bounds.max_point.y() = -std::numeric_limits<float>::infinity();
bounds.max_point.z() = -std::numeric_limits<float>::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<float>(*x, bounds.min_point.x);
bounds.min_point.y = std::min<float>(*y, bounds.min_point.y);
bounds.min_point.z = std::min<float>(*z, bounds.min_point.z);
bounds.max_point.x = std::max<float>(*x, bounds.max_point.x);
bounds.max_point.y = std::max<float>(*y, bounds.max_point.y);
bounds.max_point.z = std::max<float>(*z, bounds.max_point.z);
bounds.min_point.x() = std::min<float>(*x, bounds.min_point.x());
bounds.min_point.y() = std::min<float>(*y, bounds.min_point.y());
bounds.min_point.z() = std::min<float>(*z, bounds.min_point.z());
bounds.max_point.x() = std::max<float>(*x, bounds.max_point.x());
bounds.max_point.y() = std::max<float>(*y, bounds.max_point.y());
bounds.max_point.z() = std::max<float>(*z, bounds.max_point.z());
}
return bounds;

+ 1
- 1
src/game/component/orbit.hpp View File

@ -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 {

+ 1
- 1
src/game/fonts.cpp View File

@ -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);

+ 13
- 13
src/game/graphics.cpp View File

@ -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<int>(viewport_dimensions.x * ctx.render_resolution_scale + 0.5f), static_cast<int>(viewport_dimensions.y * ctx.render_resolution_scale + 0.5f)};
ctx.render_resolution = {static_cast<int>(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast<int>(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<int>(viewport_dimensions.x * ctx.render_resolution_scale + 0.5f), static_cast<int>(viewport_dimensions.y * ctx.render_resolution_scale + 0.5f)};
ctx.render_resolution = {static_cast<int>(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast<int>(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<image> frame = std::make_shared<image>();
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();

+ 120
- 1
src/game/load.cpp View File

@ -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 <fstream>
#include <iostream>
#include <stb/stb_image_write.h>
#include "resources/image.hpp"
#include <algorithm>
#include <execution>
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<std::uint32_t(*)(const math::vector<float, 2>&)>(math::noise::hash::pcg3d_1);
auto noise = static_cast<float(*)(const math::vector<float, 2>&, 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<float>(width - 1) * frequency;
float scale_y = 1.0f / static_cast<float>(height - 1) * frequency;
std::for_each
(
std::execution::par_unseq,
img.begin<unsigned char>(),
img.end<unsigned char>(),
[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<float>(x) * scale_x,
static_cast<float>(y) * scale_y
};
//float n = math::noise::simplex<float, 2>(position, &math::noise::hash::pcg3d_1);
float n = fbm(position);
pixel = static_cast<unsigned char>((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<json>(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<render::material>(material->get<std::string>());
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<render::model>("terrestrial-hemisphere.mdl");
(*terrestrial_hemisphere_model->get_groups())[0]->set_material(ctx.resource_manager->load<render::material>(material->get<std::string>()));
@ -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<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex);
auto hash = static_cast<std::uint32_t(*)(const math::vector<float, 2>&)>(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"))

+ 20
- 20
src/game/menu.cpp View File

@ -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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(value->get_world_bounds());
min_x = std::min<float>(min_x, value_bounds.min_point.x);
min_y = std::min<float>(min_y, value_bounds.min_point.y);
max_x = std::max<float>(max_x, value_bounds.max_point.x);
max_y = std::max<float>(max_y, value_bounds.max_point.y);
min_x = std::min<float>(min_x, value_bounds.min_point.x());
min_y = std::min<float>(min_y, value_bounds.min_point.y());
max_x = std::max<float>(max_x, value_bounds.max_point.x());
max_y = std::max<float>(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<const geom::aabb<float>&>(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<const geom::aabb<float>&>(value->get_world_bounds());
min_x = std::min<float>(min_x, value_bounds.min_point.x);
min_y = std::min<float>(min_y, value_bounds.min_point.y);
max_x = std::max<float>(max_x, value_bounds.max_point.x);
max_y = std::max<float>(max_y, value_bounds.max_point.y);
min_x = std::min<float>(min_x, value_bounds.min_point.x());
min_y = std::min<float>(min_y, value_bounds.min_point.y());
max_x = std::max<float>(max_x, value_bounds.max_point.x());
max_y = std::max<float>(max_y, value_bounds.max_point.y());
}
min_x -= padding;

+ 11
- 11
src/game/state/boot.cpp View File

@ -384,19 +384,19 @@ void boot::setup_window()
{
if (config->contains("fullscreen_resolution"))
{
resolution.x = (*config)["fullscreen_resolution"][0].get<int>();
resolution.y = (*config)["fullscreen_resolution"][1].get<int>();
resolution.x() = (*config)["fullscreen_resolution"][0].get<int>();
resolution.y() = (*config)["fullscreen_resolution"][1].get<int>();
}
}
else
{
if (config->contains("windowed_resolution"))
{
resolution.x = (*config)["windowed_resolution"][0].get<int>();
resolution.y = (*config)["windowed_resolution"][1].get<int>();
resolution.x() = (*config)["windowed_resolution"][0].get<int>();
resolution.y() = (*config)["windowed_resolution"][1].get<int>();
}
}
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<float>(45.0f), viewport_aspect_ratio, 0.1f, 1000.0f);
ctx.surface_camera->set_perspective(math::radians<float>(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<int>();
resolution.y = (*ctx.config)["windowed_resolution"][1].get<int>();
resolution.x() = (*ctx.config)["windowed_resolution"][0].get<int>();
resolution.y() = (*ctx.config)["windowed_resolution"][1].get<int>();
ctx.app->resize_window(resolution.x, resolution.y);
ctx.app->resize_window(resolution.x(), resolution.y());
}
// Save display mode config

+ 2
- 2
src/game/state/credits.cpp View File

@ -43,8 +43,8 @@ credits::credits(game::context& ctx):
// Align credits text
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(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();

+ 3
- 3
src/game/state/graphics-menu.cpp View File

@ -87,10 +87,10 @@ graphics_menu::graphics_menu(game::context& ctx):
if (!fullscreen)
{
int2 resolution;
resolution.x = (*ctx.config)["windowed_resolution"][0].get<int>();
resolution.y = (*ctx.config)["windowed_resolution"][1].get<int>();
resolution.x() = (*ctx.config)["windowed_resolution"][0].get<int>();
resolution.y() = (*ctx.config)["windowed_resolution"][1].get<int>();
ctx.app->resize_window(resolution.x, resolution.y);
ctx.app->resize_window(resolution.x(), resolution.y());
}
this->update_value_text_content();

+ 10
- 6
src/game/state/main-menu.cpp View File

@ -60,9 +60,9 @@ main_menu::main_menu(game::context& ctx, bool fade_in):
// Align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(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<float>(viewport_dimensions[0]) / static_cast<float>(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<float>()), 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

+ 9
- 6
src/game/state/nest-selection.cpp View File

@ -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<entity::archetype>("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;

+ 6
- 6
src/game/state/nuptial-flight.cpp View File

@ -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;

+ 5
- 5
src/game/system/astronomy.cpp View File

@ -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<double>(0.0, std::sin(blackbody_position_enu_spherical.y));
const double sky_illuminance = 25000.0 * std::max<double>(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<double> 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;

+ 9
- 9
src/game/system/atmosphere.cpp View File

@ -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<double>(wavelengths.x * 1e9),
physics::gas::ozone::cross_section_293k<double>(wavelengths.y * 1e9),
physics::gas::ozone::cross_section_293k<double>(wavelengths.z * 1e9)
physics::gas::ozone::cross_section_293k<double>(wavelengths.x() * 1e9),
physics::gas::ozone::cross_section_293k<double>(wavelengths.y() * 1e9),
physics::gas::ozone::cross_section_293k<double>(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

+ 9
- 9
src/game/system/constraint.cpp View File

@ -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();
}
}
}

+ 33
- 33
src/game/system/painting.cpp View File

@ -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<float>::infinity();
stroke_bounds_min.y = std::numeric_limits<float>::infinity();
stroke_bounds_min.z = std::numeric_limits<float>::infinity();
stroke_bounds_max.x = -std::numeric_limits<float>::infinity();
stroke_bounds_max.y = -std::numeric_limits<float>::infinity();
stroke_bounds_max.z = -std::numeric_limits<float>::infinity();
stroke_bounds_min.x() = std::numeric_limits<float>::infinity();
stroke_bounds_min.y() = std::numeric_limits<float>::infinity();
stroke_bounds_min.z() = std::numeric_limits<float>::infinity();
stroke_bounds_max.x() = -std::numeric_limits<float>::infinity();
stroke_bounds_max.y() = -std::numeric_limits<float>::infinity();
stroke_bounds_max.z() = -std::numeric_limits<float>::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<float>(stroke_bounds_min.x, std::min<float>(c.x, std::min<float>(d.x, std::min<float>(e.x, f.x))));
stroke_bounds_min.y = std::min<float>(stroke_bounds_min.y, std::min<float>(c.y, std::min<float>(d.y, std::min<float>(e.y, f.y))));
stroke_bounds_min.z = std::min<float>(stroke_bounds_min.z, std::min<float>(c.z, std::min<float>(d.z, std::min<float>(e.z, f.z))));
stroke_bounds_max.x = std::max<float>(stroke_bounds_max.x, std::max<float>(c.x, std::max<float>(d.x, std::max<float>(e.x, f.x))));
stroke_bounds_max.y = std::max<float>(stroke_bounds_max.y, std::max<float>(c.y, std::max<float>(d.y, std::max<float>(e.y, f.y))));
stroke_bounds_max.z = std::max<float>(stroke_bounds_max.z, std::max<float>(c.z, std::max<float>(d.z, std::max<float>(e.z, f.z))));
stroke_bounds_min.x() = std::min<float>(stroke_bounds_min.x(), std::min<float>(c.x(), std::min<float>(d.x(), std::min<float>(e.x(), f.x()))));
stroke_bounds_min.y() = std::min<float>(stroke_bounds_min.y(), std::min<float>(c.y(), std::min<float>(d.y(), std::min<float>(e.y(), f.y()))));
stroke_bounds_min.z() = std::min<float>(stroke_bounds_min.z(), std::min<float>(c.z(), std::min<float>(d.z(), std::min<float>(e.z(), f.z()))));
stroke_bounds_max.x() = std::max<float>(stroke_bounds_max.x(), std::max<float>(c.x(), std::max<float>(d.x(), std::max<float>(e.x(), f.x()))));
stroke_bounds_max.y() = std::max<float>(stroke_bounds_max.y(), std::max<float>(c.y(), std::max<float>(d.y(), std::max<float>(e.y(), f.y()))));
stroke_bounds_max.z() = std::max<float>(stroke_bounds_max.z(), std::max<float>(c.z(), std::max<float>(d.z(), std::max<float>(e.z(), f.z()))));
stroke_model->set_bounds(geom::aabb<float>{stroke_bounds_min, stroke_bounds_max});
stroke_model_instance->update_bounds();

+ 4
- 4
src/game/system/samara.cpp View File

@ -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<float>{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;

+ 8
- 8
src/game/system/subterrain.cpp View File

@ -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)
{

+ 427
- 346
src/game/system/terrain.cpp View File

@ -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<double>::identity; // +x
face_rotations[1] = math::quaternion<double>::rotate_z(math::pi<double>); // -x
face_rotations[2] = math::quaternion<double>::rotate_z( math::half_pi<double>); // +y
face_rotations[3] = math::quaternion<double>::rotate_z(-math::half_pi<double>); // -y
face_rotations[4] = math::quaternion<double>::rotate_y(-math::half_pi<double>); // +z
face_rotations[5] = math::quaternion<double>::rotate_y( math::half_pi<double>); // -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<component::terrain>().connect<&terrain::on_terrain_construct>(this);
registry.on_update<component::terrain>().connect<&terrain::on_terrain_update>(this);
registry.on_destroy<component::terrain>().connect<&terrain::on_terrain_destroy>(this);
}
terrain::~terrain()
{
registry.on_construct<component::terrain>().disconnect<&terrain::on_terrain_construct>(this);
registry.on_update<component::terrain>().disconnect<&terrain::on_terrain_update>(this);
registry.on_destroy<component::terrain>().disconnect<&terrain::on_terrain_destroy>(this);
}
void terrain::update(double t, double dt)
{
/*
// Refine the level of detail of each terrain quadsphere
registry.view<component::terrain, component::celestial_body>().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<component::observer>().each(
[&](entity::id observer_eid, const auto& observer)
// Clear quadtree
quadtree.clear();
// For each camera
this->registry.view<component::camera>().each
(
[&](entity::id camera_eid, const auto& camera)
{
// Skip observers with invalid reference body
if (!this->registry.has<component::celestial_body>(observer.reference_body_eid) ||
!this->registry.has<component::terrain>(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<component::celestial_body>(observer.reference_body_eid);
const auto& reference_terrain = this->registry.get<component::terrain>(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<double>(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<double>(node_location_x) * node_width;
center.z += static_cast<double>(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<double>(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<component::terrain, component::celestial_body>().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<float> 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<float> hull(6);
hull.planes[0] = geom::plane<float>(ftl, fbl, nbl);
hull.planes[1] = geom::plane<float>(ntr, nbr, fbr);
hull.planes[2] = geom::plane<float>(fbl, fbr, nbr);
hull.planes[3] = geom::plane<float>(ftl, ntl, ntr);
hull.planes[4] = geom::plane<float>(ntl, nbl, nbr);
hull.planes[5] = geom::plane<float>(ftr, fbr, fbl);
geom::sphere<float> 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<float> xy_to_zy = math::quaternion<float>::rotate_y(-math::half_pi<float>);
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<float(float, float)>& 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<float>(node_location_x) * node_size,
0.0f,
node_offset + static_cast<float>(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<float> xy_to_xz = math::quaternion<float>::rotate_x(math::half_pi<float>);
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<float>& 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<float> 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<double(double, double)>& 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<double>(location_x) * node_width;
offset_z += static_cast<double>(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<float>(node_location_x) * node_size,
0.0f,
node_offset + static_cast<float>(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<double>(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<float>(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<float>(patch_subdivisions + 1);
const float half_cell_size = cell_size * 0.5f;
// Init patch bounds
geom::aabb<float> patch_bounds;
patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f;
patch_bounds.min_point.y() = std::numeric_limits<float>::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<float>::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<float> 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

+ 64
- 43
src/game/system/terrain.hpp View File

@ -31,13 +31,14 @@
#include "render/material.hpp"
#include "scene/model-instance.hpp"
#include "scene/collection.hpp"
#include "geom/view-frustum.hpp"
#include <unordered_map>
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<float(float, float)>& 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<quadtree_node_type, terrain_patch*> 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<float>& 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<double(double, double)>& 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<double> face_rotations[6];
struct patch_vertex
{
float3 position;
float2 uv;
float3 normal;
float4 tangent;
};
mutable std::vector<std::vector<patch_vertex>> patch_vertex_buffer;
::render::material* patch_material;
std::function<float(float, float)> elevation_function;
scene::collection* scene_collection;
geom::mesh* patch_base_mesh;
scene::collection* patch_scene_collection;
double max_error;
std::unordered_map<entity::id, terrain_quadsphere*> 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<quadtree_node_type, patch*> patches;
};
} // namespace system

+ 10
- 34
src/game/world.cpp View File

@ -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<game::component::observer>(observer_eid))
{
// Get pointer to observer component
const auto observer = ctx.entity_registry->try_get<game::component::observer>(observer_eid);
// Set observer location
if (observer)
{
observer->elevation = elevation;
observer->latitude = latitude;
observer->longitude = longitude;
ctx.entity_registry->replace<game::component::observer>(observer_eid, *observer);
}
/*
// Update observer location
ctx.entity_registry->patch<game::component::observer>
(
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<game::component::observer>(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<float>(position.x);
*(star_vertex++) = static_cast<float>(position.y);
*(star_vertex++) = static_cast<float>(position.z);
*(star_vertex++) = static_cast<float>(color_acescg.x);
*(star_vertex++) = static_cast<float>(color_acescg.y);
*(star_vertex++) = static_cast<float>(color_acescg.z);
*(star_vertex++) = static_cast<float>(position.x());
*(star_vertex++) = static_cast<float>(position.y());
*(star_vertex++) = static_cast<float>(position.z());
*(star_vertex++) = static_cast<float>(color_acescg.x());
*(star_vertex++) = static_cast<float>(color_acescg.y());
*(star_vertex++) = static_cast<float>(color_acescg.z());
*(star_vertex++) = static_cast<float>(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<game::component::orbit>(earth_eid).parent = ctx.entities["em_bary"];
// Assign earth terrain component
game::component::terrain terrain;
terrain.elevation = [](double, double) -> double
{
//return math::random<double>(0.0, 1.0);
return 0.0;
};
terrain.max_lod = 0;
terrain.patch_material = nullptr;
//ctx.entity_registry->emplace<game::component::terrain>(earth_eid, terrain);
}
catch (const std::exception&)
{

+ 13
- 13
src/geom/aabb.hpp View File

@ -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<T, 4> transformed_corner = math::mul(m, math::vector<T, 4>{corner.x, corner.y, corner.z, T(1)});
math::vector<T, 4> transformed_corner = math::mul(m, math::vector<T, 4>{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 <class T>
bool aabb<T>::intersects(const aabb<T>& 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 <class T>
bool aabb<T>::contains(const sphere<T>& 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 <class T>
bool aabb<T>::contains(const aabb<T>& 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 <class T>
bool aabb<T>::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;
}

+ 4
- 4
src/geom/cartesian.hpp View File

@ -42,13 +42,13 @@ math::vector3 to_spherical(const math::vector3& v);
template <class T>
math::vector3<T> to_spherical(const math::vector3<T>& 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<T>
{
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())
};
}

+ 9
- 9
src/geom/convex-hull.hpp View File

@ -86,9 +86,9 @@ bool convex_hull::intersects(const aabb& aabb) const
math::vector<T, 3> pv;
for (const plane<T>& 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<T>& 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;

+ 2
- 2
src/geom/hyperoctree.hpp View File

@ -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 <std::size_t N, std::size_t D, class T>
inline bool hyperoctree<N, D, T>::contains(node_type node) const
{
return (nodes.find(node) != nodes.end());
return nodes.count(node);
}
template <std::size_t N, std::size_t D, class T>

+ 3
- 3
src/geom/intersection.cpp View File

@ -160,11 +160,11 @@ std::tuple ray_mesh_intersection(c
bool aabb_aabb_intersection(const aabb<float>& a, const aabb<float>& 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;
}

+ 6
- 6
src/geom/mesh-accelerator.cpp View File

@ -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<float>(0.0f, std::min<float>(node_dimensions[0].x - epsilon, transformed_point.x));
transformed_point.y = std::max<float>(0.0f, std::min<float>(node_dimensions[0].y - epsilon, transformed_point.y));
transformed_point.z = std::max<float>(0.0f, std::min<float>(node_dimensions[0].z - epsilon, transformed_point.z));
transformed_point.x() = std::max<float>(0.0f, std::min<float>(node_dimensions[0].x() - epsilon, transformed_point.x()));
transformed_point.y() = std::max<float>(0.0f, std::min<float>(node_dimensions[0].y() - epsilon, transformed_point.y()));
transformed_point.z() = std::max<float>(0.0f, std::min<float>(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<std::uint32_t>(transformed_point.x),
static_cast<std::uint32_t>(transformed_point.y),
static_cast<std::uint32_t>(transformed_point.z));
static_cast<std::uint32_t>(transformed_point.x()),
static_cast<std::uint32_t>(transformed_point.y()),
static_cast<std::uint32_t>(transformed_point.z()));
// Return max depth node at the determined location
return octree32::node(octree32::max_depth, location);

+ 33
- 35
src/geom/mesh-functions.cpp View File

@ -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<mesh::edge*> 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<float>(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<float>(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

+ 6
- 6
src/geom/mesh.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mesh.hpp"
#include "geom/mesh.hpp"
#include <stdexcept>
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;
}

+ 25
- 24
src/geom/mesh.hpp View File

@ -20,8 +20,8 @@
#ifndef ANTKEEPER_GEOM_MESH_HPP
#define ANTKEEPER_GEOM_MESH_HPP
#include <vector>
#include "utility/fundamental-types.hpp"
#include <vector>
namespace geom {
@ -36,8 +36,10 @@ public:
struct vertex;
struct edge;
struct face;
/// List of edges which form a face.
typedef std::vector<edge*> loop;
/**
* Constructs a mesh.
*/
@ -118,61 +120,61 @@ public:
const std::vector<mesh::face*>& 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<mesh::vertex*> vertices;
std::vector<mesh::edge*> edges;
std::vector<mesh::face*> faces;
@ -196,4 +198,3 @@ inline const std::vector& mesh::get_faces() const
} // namespace geom
#endif // ANTKEEPER_GEOM_MESH_HPP

+ 5
- 5
src/geom/meshes/grid.cpp View File

@ -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

+ 6
- 6
src/geom/rect-pack.hpp View File

@ -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};
}

+ 1
- 1
src/geom/rect.hpp View File

@ -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 {

+ 3
- 3
src/geom/sphere.hpp View File

@ -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);
}

+ 1
- 0
src/geom/view-frustum.hpp View File

@ -172,6 +172,7 @@ void view_frustum::recalculate_planes(const matrix_type& view_projection)
template <class T>
void view_frustum<T>::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());

+ 26
- 8
src/math/constants.hpp View File

@ -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 <limits>
namespace math {
/// Positive infinity.
template <class T>
constexpr T inf{std::numeric_limits<T>::infinity()};
/// Pi.
template <class T>
constexpr T pi = T(3.1415926535897932384626433832795);
constexpr T pi = T{3.1415926535897932384626433832795};
/// Pi * 2.
template <class T>
constexpr T two_pi = pi<T> * T(2);
constexpr T two_pi = pi<T> * T{2};
/// Pi * 4.
template <class T>
constexpr T four_pi = pi<T> * T(4);
constexpr T four_pi = pi<T> * T{4};
/// Pi / 2.
template <class T>
constexpr T half_pi = pi<T> * T(0.5);
constexpr T half_pi = pi<T> / T{2};
/// 1 / Pi.
template <class T>
constexpr T inverse_pi = T(1) / pi<T>;
constexpr T inverse_pi = T{1} / pi<T>;
/// sqrt(0.5)
template <class T>
constexpr T sqrt_half = T{0.70710678118654752440084436210485};
/// sqrt(2)
template <class T>
constexpr T sqrt_2 = T{1.4142135623730950488016887242097};
/// sqrt(3)
template <class T>
constexpr T sqrt_3 = T{1.7320508075688772935274463415059};
/// sqrt(5)
template <class T>
constexpr T sqrt_5 = T{2.2360679774997896964091736687313};
} // namespace math

+ 1
- 3
src/math/math.hpp View File

@ -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"

+ 1
- 2
src/math/matrix-functions.hpp View File

@ -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 <type_traits>
namespace math {

+ 1
- 1
src/math/matrix-type.hpp View File

@ -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 <cstddef>
#include <utility>

+ 70
- 0
src/math/noise/fbm.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_NOISE_FBM_HPP
#define ANTKEEPER_MATH_NOISE_FBM_HPP
#include "math/vector.hpp"
#include <functional>
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 <class T, std::size_t N>
T fbm
(
vector<T, N> x,
std::size_t octaves,
T lacunarity,
T gain,
T (*noise)(const vector<T, N>&, std::uint32_t (*)(const vector<T, N>&)),
std::uint32_t (*hash)(const vector<T, N>&)
)
{
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

+ 142
- 0
src/math/noise/hash.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_NOISE_HASH_HPP
#define ANTKEEPER_MATH_NOISE_HASH_HPP
#include "math/vector.hpp"
#include <cstdint>
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 <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 3>& x)
{
math::vector<std::uint32_t, 3> u = math::vector<std::uint32_t, 3>(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 <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 2>& x)
{
return pcg3d_3<T>({x[0], x[1], T{1}});
}
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 1>& x)
{
return pcg3d_3<T>({x[0], T{1}, T{1}});
}
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(T x)
{
return pcg3d_3<T>({x, T{1}, T{1}});
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 3>& x)
{
return pcg3d_3<T>(x)[0];
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 2>& x)
{
return pcg3d_3<T>({x[0], x[1], T{1}})[0];
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 1>& x)
{
return pcg3d_3<T>({x[0], T{1}, T{1}})[0];
}
template <class T>
std::uint32_t pcg3d_1(T x)
{
return pcg3d_3<T>({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 <class T>
math::vector<std::uint32_t, 4> pcg4d_4(const math::vector<T, 4>& x)
{
math::vector<std::uint32_t, 4> u = math::vector<std::uint32_t, 4>(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 <class T>
std::uint32_t pcg4d_1(const math::vector<T, 4>& x)
{
return pcg4d_4<T>(x)[0];
}
/// @}
} // namespace hash
} // namespace noise
} // namespace math
#endif // ANTKEEPER_MATH_NOISE_HASH_HPP

+ 34
- 0
src/math/noise/noise.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

+ 189
- 0
src/math/noise/simplex.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
#define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
#include "math/vector.hpp"
#include <algorithm>
#include <utility>
namespace math {
namespace noise {
/// @private
/// @{
/// Number of corners in an *n*-dimensional simplex lattice cell.
template <std::size_t N>
constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max<std::size_t>(0, N - 1);
/// Number of edges in an *n*-dimensional simplex lattice cell.
template <std::size_t N>
constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count<N - 1> : 2;
/// Returns the simplex lattice cell corner vector for a given dimension and index.
template <class T, std::size_t N, std::size_t... I>
constexpr vector<T, N> make_simplex_corner(std::size_t i, std::index_sequence<I...>)
{
return {((i >> I) % 2) * T{2} - T{1}...};
}
/// Builds an array of simplex lattice cell corner vectors for a given dimension.
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<vector<T, N>, simplex_corner_count<N>> make_simplex_corners(std::index_sequence<I...>)
{
return {make_simplex_corner<T, N>(I, std::make_index_sequence<N>{})...};
}
/// Array of simplex lattice cell corner vectors for a given dimension.
template <class T, std::size_t N>
constexpr auto simplex_corners = make_simplex_corners<T, N>(std::make_index_sequence<simplex_corner_count<N>>{});
/// Returns the simplex lattice cell edge vector for a given dimension and index.
template <class T, std::size_t N, std::size_t... I>
constexpr vector<T, N> make_simplex_edge(std::size_t i, std::index_sequence<I...>)
{
std::size_t j = i / (simplex_edge_count<N> / N);
return
{
I < j ?
simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I]
:
I > j ?
simplex_corners<T, N - 1>[i % simplex_corner_count<N - 1>][I - 1]
:
T{0}
...
};
}
/// Builds an array of simplex lattice cell edge vectors for a given dimension.
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<vector<T, N>, simplex_edge_count<N>> make_simplex_edges(std::index_sequence<I...>)
{
if constexpr (N == 1)
return std::array<vector<T, N>, simplex_edge_count<N>>{vector<T, N>{T{1}}, vector<T, N>{T{-1}}};
else
return {make_simplex_edge<T, N>(I, std::make_index_sequence<N>{})...};
}
/// Array of simplex lattice cell edge vectors for a given dimension.
template <class T, std::size_t N>
constexpr auto simplex_edges = make_simplex_edges<T, N>(std::make_index_sequence<simplex_edge_count<N>>{});
/// @}
/**
* *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 <class T, std::size_t N>
T simplex(const vector<T, N>& x, std::uint32_t (*hash)(const vector<T, N>&))
{
// Skewing (F) and unskewing (G) factors
static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N);
static const T g = f / (T{1} + f * static_cast<T>(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<T>(N) / std::sqrt(static_cast<T>(N + 1))) * falloff(static_cast<T>(N) / (T{4} * static_cast<T>(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<T>(N)) / math::length(simplex_edges<T, N>[0]));
// Skew input position to get the origin vertex of the unit hypercube cell to which they belong
const vector<T, N> origin_vertex = math::floor(x + math::sum(x) * f);
// Displacement vector from origin vertex position to input position
const vector<T, N> dx = x - origin_vertex + math::sum(origin_vertex) * g;
// Find axis traversal order
vector<std::size_t, N> 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<T, N> 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<T, N> d = dx - (current_vertex - origin_vertex) + g * static_cast<T>(i);
// Calculate falloff
T t = falloff(math::length_squared(d));
if (t > T{0})
{
const auto gradient_index = hash(current_vertex) % simplex_edges<T, N>.size();
const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index];
n += math::dot(d, gradient) * t;
}
}
// Normalize value
return n * edge_normalization;
}
} // namespace noise
} // namespace math
#endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP

+ 0
- 1
src/math/operators.hpp View File

@ -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"

+ 4
- 5
src/math/quaternion-functions.hpp View File

@ -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 <cmath>
namespace math {
@ -377,7 +376,7 @@ template
quaternion<T> angle_axis(T angle, const vector<T, 3>& axis)
{
T s = std::sin(angle * T(0.5));
return {static_cast<T>(std::cos(angle * T(0.5))), axis.x * s, axis.y * s, axis.z * s};
return {static_cast<T>(std::cos(angle * T(0.5))), axis.x() * s, axis.y() * s, axis.z() * s};
}
template <class T>
@ -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<T, 3> pa = mul(a, (a.x * q.x + a.y * q.y + a.z * q.z));
qt = normalize(quaternion<T>{q.w, pa.x, pa.y, pa.z});
const vector<T, 3> pa = mul(a, (a.x() * q.x + a.y() * q.y + a.z() * q.z));
qt = normalize(quaternion<T>{q.w, pa.x(), pa.y(), pa.z()});
qs = mul(q, conjugate(qt));
}
else

+ 4
- 5
src/math/se3.hpp View File

@ -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<T>(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;
}

+ 2
- 47
src/math/stream-operators.hpp View File

@ -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 <istream>
#include <ostream>
/**
* 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 <class T, std::size_t N>
std::ostream& operator<<(std::ostream& os, const math::vector<T, N>& 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 <class T>
std::ostream& operator<<(std::ostream& os, const math::quaternion<T>& 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 <class T, std::size_t N>
std::istream& operator>>(std::istream& is, math::vector<T, N>& 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 <class T>
std::istream& operator>>(std::istream& is, const math::quaternion<T>& q);
template <class T, std::size_t N>
std::ostream& operator<<(std::ostream& os, const math::vector<T, N>& v)
{
for (std::size_t i = 0; i < N; ++i)
{
os << v[i];
if (i < N - 1)
{
os << ' ';
}
}
return os;
}
template <class T, std::size_t N, std::size_t M>
std::ostream& operator<<(std::ostream& os, const math::matrix<T, N, M>& m)
{
@ -123,19 +87,10 @@ std::ostream& operator<<(std::ostream& os, const math::matrix& m)
template <class T>
std::ostream& operator<<(std::ostream& os, const math::quaternion<T>& q)
{
os << q.w << ' ' << q.x << ' ' << q.y << ' ' << q.z;
os << q.w << ' ' << q.x() << ' ' << q.y() << ' ' << q.z();
return os;
}
template <class T, std::size_t N>
std::istream& operator>>(std::istream& is, math::vector<T, N>& v)
{
for (std::size_t i = 0; i < N; ++i)
is >> v[i];
return is;
}
template <class T, std::size_t N, std::size_t M>
std::istream& operator>>(std::istream& is, math::matrix<T, N, M>& m)
{

+ 1
- 2
src/math/transform-functions.hpp View File

@ -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<T> inverse(const transform<T>& t)
{
transform<T> 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;

+ 1
- 1
src/math/transform-type.hpp View File

@ -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 {

+ 0
- 645
src/math/vector-functions.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP
#define ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP
#include "math/vector-type.hpp"
#include <algorithm>
#include <cmath>
#include <type_traits>
#include <utility>
namespace math {
/**
* Adds two vectors.
*
* @param x First vector.
* @param y Second vector.
* @return Sum of the two vectors.
*/
template <class T, std::size_t N>
constexpr vector<T, N> add(const vector<T, N>& x, const vector<T, N>& 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 <std::size_t N>
constexpr bool all(const vector<bool, N>& 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 <std::size_t N>
constexpr bool any(const vector<bool, N>& 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 <std::size_t N, typename T>
constexpr vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<T, N> clamp(const vector<T, N>& 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 <class T, std::size_t N>
vector<T, N> clamp_length(const vector<T, N>& 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 <class T>
constexpr vector<T, 3> cross(const vector<T, 3>& x, const vector<T, 3>& 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 <class T, std::size_t N>
T distance(const vector<T, N>& p0, const vector<T, N>& 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 <class T, std::size_t N>
constexpr T distance_squared(const vector<T, N>& p0, const vector<T, N>& p1);
/**
* Divides a vector by another vector.
*
* @param x First vector.
* @param y Second vector.
* @return Result of the division.
*/
template <class T, std::size_t N>
constexpr vector<T, N> div(const vector<T, N>& x, const vector<T, N>& y);
/**
* Divides a vector by a scalar.
*
* @param v Vector.
* @param s Scalar.
* @return Result of the division.
*/
template <class T, std::size_t N>
constexpr vector<T, N> div(const vector<T, N>& 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 <class T, std::size_t N>
constexpr T dot(const vector<T, N>& x, const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> equal(const vector<T, N>& x, const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> greater_than(const vector<T, N>& x, const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> greater_than_equal(const vector<T, N>& x, const vector<T, N>& y);
/**
* Calculates the length of a vector.
*
* @param x Vector of which to calculate the length.
* @return Length of the vector.
*/
template <class T, std::size_t N>
T length(const vector<T, N>& 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 <class T, std::size_t N>
constexpr T length_squared(const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> less_than(const vector<T, N>& x, const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> less_than_equal(const vector<T, N>& x, const vector<T, N>& y);
/**
* Multiplies two vectors.
*
* @param x First vector.
* @param y Second vector.
* @return Product of the two vectors.
*/
template <class T, std::size_t N>
constexpr vector<T, N> mul(const vector<T, N>& x, const vector<T, N>& y);
/**
* Multiplies a vector by a scalar.
*
* @param v Vector.
* @param s Scalar.
* @return Product of the vector and scalar.
*/
template <class T, std::size_t N>
constexpr vector<T, N> mul(const vector<T, N>& v, T s);
/**
* Negates a vector.
*
* @param x Vector to negate.
* @return Negated vector.
*/
template <class T, std::size_t N>
constexpr vector<T, N> negate(const vector<T, N>& x);
/**
* Calculates the unit vector in the same direction as the original vector.
*
* @param x Vector to normalize.
* @return Normalized vector.
*/
template <class T, std::size_t N>
vector<T, N> normalize(const vector<T, N>& x);
/**
* Logically inverts a boolean vector.
*
* @param x Vector to be inverted.
* @return Logically inverted vector.
*/
template <class T, std::size_t N>
constexpr vector<bool, N> not(const vector<T, N>& 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 <class T, std::size_t N>
constexpr vector<bool, N> not_equal(const vector<T, N>& x, const vector<T, N>& y);
/**
* Resizes a vector. Any new elements will be set to `0`.
*
* @param v Vector to resize.
* @return Resized vector.
*/
template <std::size_t N1, class T, std::size_t N0>
constexpr vector<T, N1> resize(const vector<T, N0>& v);
/**
* Subtracts a vector from another vector.
*
* @param x First vector.
* @param y Second vector.
* @return Difference between the two vectors.
*/
template <class T, std::size_t N>
constexpr vector<T, N> sub(const vector<T, N>& x, const vector<T, N>& 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 <std::size_t... Indices, class T, std::size_t N>
constexpr vector<T, sizeof...(Indices)> swizzle(const vector<T, N>& 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 <class T2, class T1, std::size_t N>
constexpr vector<T2, N> type_cast(const vector<T1, N>& v);
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> add(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] + y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> add(const vector<T, N>& x, const vector<T, N>& y)
{
return add(x, y, std::make_index_sequence<N>{});
}
/// @private
template <std::size_t N, std::size_t... I>
constexpr inline bool all(const vector<bool, N>& x, std::index_sequence<I...>)
{
return (x[I] && ...);
}
template <std::size_t N>
constexpr inline bool all(const vector<bool, N>& x)
{
return all(x, std::make_index_sequence<N>{});
}
/// @private
template <std::size_t N, std::size_t... I>
constexpr inline bool any(const vector<bool, N>& x, std::index_sequence<I...>)
{
return (x[I] || ...);
}
template <std::size_t N>
constexpr inline bool any(const vector<bool, N>& x)
{
return any(x, std::make_index_sequence<N>{});
}
template <std::size_t N, typename T>
constexpr inline vector<T, N>& as_vector(T& data)
{
static_assert(std::is_pod<vector<T, N>>::value);
return reinterpret_cast<vector<T, N>&>(data);
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> clamp(const vector<T, N>& x, T min_value, T max_value, std::index_sequence<I...>)
{
return {std::min<T>(max_value, std::max<T>(min_value, x[I]))...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> clamp(const vector<T, N>& x, T min_value, T max_value)
{
return clamp(x, min_value, max_value, std::make_index_sequence<N>{});
}
template <class T, std::size_t N>
vector<T, N> clamp_length(const vector<T, N>& x, T max_length)
{
T length2 = length_squared(x);
return (length2 > max_length * max_length) ? (x * max_length / std::sqrt(length2)) : x;
}
template <class T>
constexpr inline vector<T, 3> cross(const vector<T, 3>& x, const vector<T, 3>& 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 <class T, std::size_t N>
inline T distance(const vector<T, N>& p0, const vector<T, N>& p1)
{
static_assert(std::is_floating_point<T>::value);
return length(sub(p0, p1));
}
template <class T, std::size_t N>
constexpr inline T distance_squared(const vector<T, N>& p0, const vector<T, N>& p1)
{
static_assert(std::is_floating_point<T>::value);
return length_squared(sub(p0, p1));
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> div(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] / y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> div(const vector<T, N>& x, const vector<T, N>& y)
{
return div(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> div(const vector<T, N>& v, T s, std::index_sequence<I...>)
{
return {(v[I] / s)...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> div(const vector<T, N>& v, T s)
{
return div(v, s, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline T dot(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return ((x[I] * y[I]) + ...);
}
template <class T, std::size_t N>
constexpr inline T dot(const vector<T, N>& x, const vector<T, N>& y)
{
return dot(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> equal(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] == y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> equal(const vector<T, N>& x, const vector<T, N>& y)
{
return equal(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> greater_than(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] > y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> greater_than(const vector<T, N>& x, const vector<T, N>& y)
{
return greater_than(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> greater_than_equal(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] >= y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> greater_than_equal(const vector<T, N>& x, const vector<T, N>& y)
{
return greater_than_equal(x, y, std::make_index_sequence<N>{});
}
template <class T, std::size_t N>
inline T length(const vector<T, N>& x)
{
static_assert(std::is_floating_point<T>::value);
return std::sqrt(dot(x, x));
}
template <class T, std::size_t N>
constexpr inline T length_squared(const vector<T, N>& x)
{
return dot(x, x);
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> less_than(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] < y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> less_than(const vector<T, N>& x, const vector<T, N>& y)
{
return less_than(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> less_than_equal(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] <= y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> less_than_equal(const vector<T, N>& x, const vector<T, N>& y)
{
return less_than_equal(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> mul(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] * y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> mul(const vector<T, N>& x, const vector<T, N>& y)
{
return mul(x, y, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> mul(const vector<T, N>& v, T s, std::index_sequence<I...>)
{
return {(v[I] * s)...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> mul(const vector<T, N>& v, T s)
{
return mul(v, s, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> negate(const vector<T, N>& x, std::index_sequence<I...>)
{
return {(-x[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> negate(const vector<T, N>& x)
{
return negate(x, std::make_index_sequence<N>{});
}
template <class T, std::size_t N>
inline vector<T, N> normalize(const vector<T, N>& x)
{
static_assert(std::is_floating_point<T>::value);
return mul(x, T(1) / length(x));
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> not(const vector<T, N>& x, std::index_sequence<I...>)
{
return {!x[I]...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> not(const vector<T, N>& x)
{
return not(x, std::make_index_sequence<N>{});
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<bool, N> not_equal(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] != y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<bool, N> not_equal(const vector<T, N>& x, const vector<T, N>& y)
{
return not_equal(x, y, std::make_index_sequence<N>{});
}
template <std::size_t N1, class T, std::size_t N0>
constexpr vector<T, N1> resize(const vector<T, N0>& v)
{
vector<T, N1> resized;
for (std::size_t i = 0; i < N1; ++i)
{
resized[i] = (i < N0) ? v[i] : T(0);
}
return resized;
}
/// @private
template <class T, std::size_t N, std::size_t... I>
constexpr inline vector<T, N> sub(const vector<T, N>& x, const vector<T, N>& y, std::index_sequence<I...>)
{
return {(x[I] - y[I])...};
}
template <class T, std::size_t N>
constexpr inline vector<T, N> sub(const vector<T, N>& x, const vector<T, N>& y)
{
return sub(x, y, std::make_index_sequence<N>{});
}
template <std::size_t... Indices, class T, std::size_t N>
constexpr inline vector<T, sizeof...(Indices)> swizzle(const vector<T, N>& v)
{
return { v[Indices]... };
}
/// @private
template <class T2, class T1, std::size_t N, std::size_t... I>
constexpr inline vector<T2, N> type_cast(const vector<T1, N>& v, std::index_sequence<I...>)
{
return {static_cast<T2>(v[I])...};
}
template <class T2, class T1, std::size_t N>
constexpr inline vector<T2, N> type_cast(const vector<T1, N>& v)
{
return type_cast<T2>(v, std::make_index_sequence<N>{});
}
} // namespace math
#endif // ANTKEEPER_MATH_VECTOR_FUNCTIONS_HPP

+ 0
- 202
src/math/vector-operators.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<T, N>&, const math::vector<T, N>&)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator+(const math::vector<T, N>& x, const math::vector<T, N>& y);
/// @copydoc math::div(const math::vector<T, N>&, const math::vector<T, N>&)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator/(const math::vector<T, N>& x, const math::vector<T, N>& y);
/// @copydoc math::div(const math::vector<T, N>&, T)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator/(const math::vector<T, N>& v, T s);
/// @copydoc math::mul(const math::vector<T, N>&, const math::vector<T, N>&)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator*(const math::vector<T, N>& x, const math::vector<T, N>& y);
/// @copydoc math::mul(const math::vector<T, N>&, T)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator*(const math::vector<T, N>& v, T s);
/// @copydoc math::mul(const math::vector<T, N>&, T)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator*(T s, const math::vector<T, N>& v);
/// @copydoc math::negate(const math::vector<T, N>&)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator-(const math::vector<T, N>& x);
/// @copydoc math::sub(const math::vector<T, N>&, const math::vector<T, N>&)
template <class T, std::size_t N>
constexpr math::vector<T, N> operator-(const math::vector<T, N>& x, const math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator+=(math::vector<T, N>& x, const math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator-=(math::vector<T, N>& x, const math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator*=(math::vector<T, N>& x, const math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator*=(math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator/=(math::vector<T, N>& x, const math::vector<T, N>& 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 <class T, std::size_t N>
constexpr math::vector<T, N>& operator/=(math::vector<T, N>& v, T s);
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator+(const math::vector<T, N>& x, const math::vector<T, N>& y)
{
return add(x, y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator-(const math::vector<T, N>& x)
{
return negate(x);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator-(const math::vector<T, N>& x, const math::vector<T, N>& y)
{
return sub(x, y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator*(const math::vector<T, N>& x, const math::vector<T, N>& y)
{
return mul(x, y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator*(const math::vector<T, N>& v, T s)
{
return mul(v, s);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator*(T s, const math::vector<T, N>& v)
{
return mul(v, s);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator/(const math::vector<T, N>& x, const math::vector<T, N>& y)
{
return div(x, y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N> operator/(const math::vector<T, N>& v, T s)
{
return div(v, s);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator+=(math::vector<T, N>& x, const math::vector<T, N>& y)
{
return (x = x + y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator-=(math::vector<T, N>& x, const math::vector<T, N>& y)
{
return (x = x - y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator*=(math::vector<T, N>& x, const math::vector<T, N>& y)
{
return (x = x * y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator*=(math::vector<T, N>& v, T s)
{
return (v = v * s);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator/=(math::vector<T, N>& x, const math::vector<T, N>& y)
{
return (x = x * y);
}
template <class T, std::size_t N>
constexpr inline math::vector<T, N>& operator/=(math::vector<T, N>& v, T s)
{
return (v = v / s);
}
#endif // ANTKEEPER_MATH_VECTOR_OPERATORS_HPP

+ 0
- 154
src/math/vector-type.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_VECTOR_TYPE_HPP
#define ANTKEEPER_MATH_VECTOR_TYPE_HPP
#include <cstddef>
namespace math {
/**
* An `N`-dimensional Euclidean vector.
*
* @tparam T Scalar type.
* @tparam N Number of dimensions.
*/
template <typename T, std::size_t N>
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 <typename T>
struct vector<T, 1>
{
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 <typename T>
struct vector<T, 2>
{
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 <typename T>
struct vector<T, 3>
{
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 <typename T>
struct vector<T, 4>
{
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 <typename T>
using vector2 = vector<T, 2>;
/// 3D vector.
template <typename T>
using vector3 = vector<T, 3>;
/// 4D vector.
template <typename T>
using vector4 = vector<T, 4>;
} // namespace math
#endif // ANTKEEPER_MATH_VECTOR_TYPE_HPP

+ 1492
- 0
src/math/vector.hpp
File diff suppressed because it is too large
View File


+ 16
- 16
src/physics/orbit/frame.hpp View File

@ -41,13 +41,13 @@ namespace pqw {
template <class T>
math::vector3<T> spherical(const math::vector3<T>& 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<T>
{
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 <class T>
math::vector3<T> spherical(const math::vector3<T>& 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<T>
{
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 <class T>
math::vector3<T> spherical(const math::vector3<T>& 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<T>
{
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 <class T>
math::vector3<T> spherical(const math::vector3<T>& 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<T>
{
std::sqrt(xx_yy + v.z * v.z),
std::atan2(v.z, std::sqrt(xx_yy)),
math::half_pi<T> - 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<T> - std::atan2(v.y(), v.x())
};
}

+ 4
- 3
src/physics/orbit/trajectory.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP
#include "math/polynomial.hpp"
#include "math/vector.hpp"
#include <vector>
namespace physics {
@ -71,9 +72,9 @@ math::vector trajectory::position(T t) const
t = (t / dt - i) * T(2) - T(1);
math::vector3<T> 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;
}

+ 1
- 1
src/render/passes/ground-pass.cpp View File

@ -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;

+ 1
- 1
src/render/passes/material-pass.cpp View File

@ -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;
}

+ 1
- 1
src/render/passes/outline-pass.cpp View File

@ -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);

+ 12
- 12
src/render/passes/shadow-map-pass.cpp View File

@ -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<float>(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<float>::identity, offset) * math::scale(math::matrix4<float>::identity, scale);

+ 11
- 11
src/render/passes/sky-pass.cpp View File

@ -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

+ 9
- 9
src/resources/entity-archetype-loader.cpp View File

@ -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<double>();
component.airglow_illuminance.y = airglow_illuminance[1].get<double>();
component.airglow_illuminance.z = airglow_illuminance[2].get<double>();
component.airglow_illuminance.x() = airglow_illuminance[0].get<double>();
component.airglow_illuminance.y() = airglow_illuminance[1].get<double>();
component.airglow_illuminance.z() = airglow_illuminance[2].get<double>();
}
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<float>();
component.local.translation.y = translation[1].get<float>();
component.local.translation.z = translation[2].get<float>();
component.local.translation.x() = translation[0].get<float>();
component.local.translation.y() = translation[1].get<float>();
component.local.translation.z() = translation[2].get<float>();
}
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<float>();
component.local.scale.y = translation[1].get<float>();
component.local.scale.z = translation[2].get<float>();
component.local.scale.x() = translation[0].get<float>();
component.local.scale.y() = translation[1].get<float>();
component.local.scale.z() = translation[2].get<float>();
}
component.world = component.local;

+ 2
- 2
src/resources/image-loader.cpp View File

@ -110,7 +110,7 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F
image->resize(static_cast<unsigned int>(exr_image.width), static_cast<unsigned int>(exr_image.height));
// Fill image pixels
float* component = static_cast<float*>(image->get_pixels());
float* component = static_cast<float*>(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<unsigned int>(width), static_cast<unsigned int>(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);

+ 67
- 7
src/resources/image.hpp View File

@ -20,7 +20,9 @@
#ifndef ANTKEEPER_IMAGE_HPP
#define ANTKEEPER_IMAGE_HPP
#include "math/vector.hpp"
#include <cstddef>
#include <type_traits>
/**
* 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 <class T>
constexpr inline T* begin() noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<T*>(pixels);
}
template <class T>
constexpr inline const T* begin() const noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<const T*>(pixels);
}
template <class T>
constexpr inline const T* cbegin() const noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<const T*>(pixels);
}
/// @}
/**
* Returns an iterator to the pixel following the last pixel.
*
* @tparam T Pixel data type.
*/
/// @{
template <class T>
constexpr inline T* end() noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<T*>(static_cast<unsigned char*>(pixels) + size);
}
template <class T>
constexpr inline const T* end() const noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<const T*>(static_cast<const unsigned char*>(pixels) + size);
}
template <class T>
constexpr inline const T* cend() const noexcept
{
static_assert(std::is_standard_layout<T>::value, "Pixel iterator type is not standard-layout.");
static_assert(std::is_trivial<T>::value, "Pixel iterator type is not trivial.");
return static_cast<const T*>(static_cast<const unsigned char*>(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;
}

+ 5
- 5
src/resources/model-loader.cpp View File

@ -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<float>();
}
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<float>();
}
@ -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<float>();
bone_transform.translation.y = (*translation_node)[1].get<float>();
bone_transform.translation.z = (*translation_node)[2].get<float>();
bone_transform.translation.x() = (*translation_node)[0].get<float>();
bone_transform.translation.y() = (*translation_node)[1].get<float>();
bone_transform.translation.z() = (*translation_node)[2].get<float>();
}
}

+ 2
- 2
src/resources/texture-loader.cpp View File

@ -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);

+ 7
- 2
src/scene/camera.cpp View File

@ -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

+ 1
- 1
src/scene/object.hpp View File

@ -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"

+ 1
- 1
src/scene/spot-light.cpp View File

@ -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()

+ 27
- 28
src/scene/text.cpp View File

@ -19,7 +19,6 @@
#include "scene/text.hpp"
#include "render/vertex-attribute.hpp"
#include "math/vector-operators.hpp"
#include "type/unicode/convert.hpp"
#include <cstddef>
@ -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<float>(glyph.position.x), static_cast<float>(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<float>(glyph.position.x()), static_cast<float>(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<float>(font_bitmap.get_width());
uvs[i].y = uvs[i].y / static_cast<float>(font_bitmap.get_height());
uvs[i].x() = uvs[i].x() / static_cast<float>(font_bitmap.get_width());
uvs[i].y() = uvs[i].y() / static_cast<float>(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<char32_t>('\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

+ 3
- 3
src/type/bitmap-font.cpp View File

@ -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

+ 5
- 5
src/type/freetype/typeface.cpp View File

@ -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;
}

+ 1
- 2
src/utility/fundamental-types.hpp View File

@ -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"

Loading…
Cancel
Save