💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
5.9 KiB

/*
* 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