/*
|
|
* 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/>.
|
|
*/
|
|
|
|
#include "entity/systems/terrain.hpp"
|
|
#include "utility/fundamental-types.hpp"
|
|
#include "entity/components/observer.hpp"
|
|
#include "entity/components/terrain.hpp"
|
|
#include "entity/components/celestial-body.hpp"
|
|
#include "geom/quadtree.hpp"
|
|
|
|
|
|
/**
|
|
* A cube with six quadtrees as faces.
|
|
*/
|
|
struct quadtree_cube
|
|
{
|
|
typedef geom::quadtree32 quadtree_type;
|
|
typedef quadtree_type::node_type node_type;
|
|
|
|
void clear();
|
|
|
|
/**
|
|
* Refines the quadtree cube.
|
|
*
|
|
* @param threshold Function object which, given a quadsphere face index and quadtree node, returns `true` if the node should be subdivided, and `false` otherwise.
|
|
*/
|
|
void refine(const std::function<bool(std::uint8_t, node_type)>& threshold);
|
|
|
|
quadtree_type faces[6];
|
|
};
|
|
|
|
void quadtree_cube::clear()
|
|
{
|
|
for (std::uint8_t i = 0; i < 6; ++i)
|
|
faces[i].clear();
|
|
}
|
|
|
|
void quadtree_cube::refine(const std::function<bool(std::uint8_t, node_type)>& threshold)
|
|
{
|
|
for (std::uint8_t i = 0; i < 6; ++i)
|
|
{
|
|
for (auto it = faces[i].begin(); it != faces[i].end(); ++it)
|
|
{
|
|
node_type node = *it;
|
|
|
|
if (threshold(i, node))
|
|
faces[i].insert(quadtree_type::child(node, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
terrain_qtc.refine
|
|
(
|
|
[observer](std::uint8_t face_index, quadtree_cube_type::node_type node) -> bool
|
|
{
|
|
// Extract morton location code
|
|
quadtree_type::node_type location = quadtree_type::location(node);
|
|
quadtree_type::node_type morton_x;
|
|
quadtree_type::node_type morton_y;
|
|
geom::morton::decode(location, morton_x, morton_y);
|
|
|
|
// Extract depth
|
|
quadtree_type::node_type depth = quadtree_type::depth(node);
|
|
|
|
// Determine fractional side length at depth
|
|
float length = 1.0f / std::exp2(depth);
|
|
|
|
// Determine fractional center of node
|
|
float3 center;
|
|
center.x = (static_cast<float>(morton_x) * length + length * 0.5f) * 2.0f - 1.0f;
|
|
center.y = (static_cast<float>(morton_y) * length + length * 0.5f) * 2.0f - 1.0f;
|
|
center.z = 1.0f;
|
|
|
|
// Project node center onto unit sphere
|
|
center = math::normalize(center);
|
|
|
|
// Rotate projected center into sphere space
|
|
center = face_rotations[face_index] * center;
|
|
|
|
// Scale center by body radius
|
|
center *= body_radius;
|
|
|
|
// Calculate distance from observer to node center
|
|
float distance = math::length(projected_center - observer_location);
|
|
|
|
if (depth < 4 && distance < ...)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
);
|
|
*/
|
|
|
|
/**
|
|
* Queries a quad sphere for a list of leaf nodes. Leaf nodes will be inserted in the set
|
|
*
|
|
*
|
|
* 0. If observer position changed more than x amount:
|
|
* 1. Clear quad sphere
|
|
* 2. Insert leaves based on observer distance.
|
|
* 3. Pass quad sphere to tile generation function.
|
|
* 3. Iterate leaves, deriving the face, depth, and morton location from each leaf index.
|
|
* 4. Face, depth, and morton location can be used to determine latitude, longitude, and generate tiles.
|
|
* 5. Generated tiles cached and added to scene.
|
|
* 6. Record position of observer
|
|
*/
|
|
|
|
/**
|
|
* Lat, lon determination:
|
|
*
|
|
* 1. Use morton location and depth to determine the x-y coordinates on a planar cube face.
|
|
* 2. Project x-y coordinates onto sphere.
|
|
* 3. Rotate coordinates according to face index.
|
|
* 4. Convert cartesian coordinates to spherical coordinates.
|
|
*/
|
|
|
|
|
|
namespace entity {
|
|
namespace system {
|
|
|
|
terrain::terrain(entity::registry& registry):
|
|
updatable(registry),
|
|
patch_subdivisions(0),
|
|
patch_vertex_size(0),
|
|
patch_vertex_count(0),
|
|
patch_vertex_data(nullptr)
|
|
{
|
|
// position + uv + normal + tangent + barycentric
|
|
patch_vertex_size = 3 + 2 + 3 + 4 + 3;
|
|
|
|
|
|
set_patch_subdivisions(0);
|
|
|
|
registry.on_construct<component::terrain>().connect<&terrain::on_terrain_construct>(this);
|
|
registry.on_destroy<component::terrain>().connect<&terrain::on_terrain_destroy>(this);
|
|
}
|
|
|
|
terrain::~terrain()
|
|
{}
|
|
|
|
void terrain::update(double t, double dt)
|
|
{
|
|
// Subdivide or collapse quad sphere
|
|
registry.view<component::observer>().each(
|
|
[&](entity::id observer_eid, const auto& observer)
|
|
{
|
|
// Skip observers with null reference body
|
|
if (observer.reference_body_eid == entt::null)
|
|
return;
|
|
|
|
// Skip observers with non-body or non-terrestrial reference bodies
|
|
if (!registry.has<component::celestial_body>(observer.reference_body_eid) ||
|
|
!registry.has<component::terrain>(observer.reference_body_eid))
|
|
return;
|
|
|
|
const auto& celestial_body = registry.get<component::celestial_body>(observer.reference_body_eid);
|
|
const auto& terrain = registry.get<component::terrain>(observer.reference_body_eid);
|
|
|
|
// Haversine distance to all 6 faces, then recursively to children
|
|
});
|
|
}
|
|
|
|
void terrain::set_patch_subdivisions(std::uint8_t n)
|
|
{
|
|
patch_subdivisions = n;
|
|
|
|
// Recalculate number of vertices per patch
|
|
patch_vertex_count = static_cast<std::size_t>(std::pow(std::exp2(patch_subdivisions) + 1, 2));
|
|
|
|
// Resize patch vertex data buffer
|
|
delete[] patch_vertex_data;
|
|
patch_vertex_data = new float[patch_vertex_count * patch_vertex_size];
|
|
}
|
|
|
|
void terrain::on_terrain_construct(entity::registry& registry, entity::id entity_id, component::terrain& component)
|
|
{
|
|
// Build quad sphere
|
|
}
|
|
|
|
void terrain::on_terrain_destroy(entity::registry& registry, entity::id entity_id)
|
|
{
|
|
// Destroy quad sphere
|
|
}
|
|
|
|
void terrain::generate_patch()
|
|
{
|
|
|
|
|
|
}
|
|
|
|
} // namespace system
|
|
} // namespace entity
|