53 throw std::invalid_argument(
"Failed to generate terrain from null heightmap");
56 const auto& heightmap_dimensions = heightmap->get_dimensions();
57 if (heightmap_dimensions[0] < 2 || heightmap_dimensions[1] < 2)
60 throw std::runtime_error(
"Heightmap size less than 2x2");
63 if (((heightmap_dimensions[0] - 1) % (subdivisions.
x() + 1)) != 0 ||
64 ((heightmap_dimensions[1] - 1) % (subdivisions.
y() + 1)) != 0)
67 heightmap_dimensions[0],
68 heightmap_dimensions[1],
71 throw std::runtime_error(
"Heightmap subdivision failed");
90 const auto cell_quad_dimensions =
math::uvec2{
static_cast<unsigned int>(heightmap_dimensions[0] - 1) / grid.
dimensions.
x(),
static_cast<unsigned int>(heightmap_dimensions[1] - 1) / grid.
dimensions.
y()};
91 const auto cell_vert_dimensions = cell_quad_dimensions + 1u;
94 const auto scale_ratio = transform.
scale / max_scale;
95 const auto vertex_scale = scale_ratio *
math::fvec3{2.0f /
static_cast<float>(cell_quad_dimensions.x()), 2.0f, 2.0f /
static_cast<float>(cell_quad_dimensions.y())};
96 const auto vertex_translation = -scale_ratio;
99 std::vector<float> heightmap_data(heightmap_dimensions[0] * heightmap_dimensions[1]);
102 return heightmap_data[p.y() * heightmap_dimensions[0] + p.x()];
112 heightmap_dimensions[0],
113 heightmap_dimensions[1],
116 std::as_writable_bytes(std::span{heightmap_data})
123 std::begin(grid.
cells),
124 std::end(grid.
cells),
127 const auto& cell = registry.get<terrain_cell_component>(cell_eid);
130 auto mesh = std::make_shared<geom::brep_mesh>();
131 auto& vertex_positions = static_cast<geom::brep_attribute<math::fvec3>&>(*mesh->vertices().attributes().emplace<math::fvec3>(
"position"));
133 auto cell_pixel_bounds_min = cell.coordinates * cell_quad_dimensions;
134 auto cell_pixel_bounds_max = cell_pixel_bounds_min + cell_quad_dimensions;
137 math::uvec2 pixel_position;
138 for (pixel_position.y() = cell_pixel_bounds_min.y(); pixel_position.y() <= cell_pixel_bounds_max.y(); ++pixel_position.y())
140 for (pixel_position.x() = cell_pixel_bounds_min.x(); pixel_position.x() <= cell_pixel_bounds_max.x(); ++pixel_position.x())
143 auto vertex = mesh->vertices().emplace_back();
146 float height = sample(pixel_position);
149 auto& position = vertex_positions[vertex->index()];
150 position.x() = static_cast<float>(pixel_position.x()) * vertex_scale.x() + vertex_translation.x();
151 position.y() = height * vertex_scale.y() + vertex_translation.y();
152 position.z() = static_cast<float>(pixel_position.y()) * vertex_scale.z() + vertex_translation.z();
157 for (
auto y = 0u; y < cell_quad_dimensions.y(); ++y)
159 for (
auto x = 0u; x < cell_quad_dimensions.x(); ++x)
161 auto a = mesh->vertices()[y * cell_vert_dimensions.x() + x];
162 auto b = mesh->vertices()[a->index() + cell_vert_dimensions.x()];
163 auto c = mesh->vertices()[a->index() + 1];
164 auto d = mesh->vertices()[b->index() + 1];
169 mesh->faces().emplace_back(abc);
170 mesh->faces().emplace_back(cbd);
176 for (pixel_position.y() = cell_pixel_bounds_min.y(); pixel_position.y() <= cell_pixel_bounds_max.y(); ++pixel_position.y())
178 for (pixel_position.x() = cell_pixel_bounds_min.x(); pixel_position.x() <= cell_pixel_bounds_max.x(); ++pixel_position.x())
180 const auto pixel_w = pixel_position.x() >= 1u ? pixel_position -
math::uvec2{1, 0} : pixel_position;
181 const auto pixel_e = pixel_position.
x() < cell_pixel_bounds_max.x() ? pixel_position +
math::uvec2{1, 0} : pixel_position;
182 const auto pixel_s = pixel_position.
y() >= 1u ? pixel_position -
math::uvec2{0, 1} : pixel_position;
183 const auto pixel_n = pixel_position.
y() < cell_pixel_bounds_max.y() ? pixel_position +
math::uvec2{0, 1} : pixel_position;
185 const auto index_c = pixel_position.
y() * (cell_pixel_bounds_max.x() + 1) + pixel_position.x();
186 const auto index_w = pixel_w.y() * (cell_pixel_bounds_max.x() + 1) + pixel_w.x();
187 const auto index_e = pixel_e.y() * (cell_pixel_bounds_max.x() + 1) + pixel_e.x();
188 const auto index_s = pixel_s.y() * (cell_pixel_bounds_max.x() + 1) + pixel_s.x();
189 const auto index_n = pixel_n.y() * (cell_pixel_bounds_max.x() + 1) + pixel_n.x();
191 const auto height_w = vertex_positions[index_w].y();
192 const auto height_e = vertex_positions[index_e].y();
193 const auto height_s = vertex_positions[index_s].y();
194 const auto height_n = vertex_positions[index_n].y();
196 auto& normal_c = vertex_normals[index_c];
202 auto rigid_body = std::make_unique<physics::rigid_body>();
203 rigid_body->set_mass(0.0f);
204 rigid_body->set_collider(std::make_shared<physics::mesh_collider>(mesh));
208 auto model = generate_terrain_model(*mesh, material, cell_quad_dimensions);
210 scene.
object = std::make_shared<scene::static_mesh>(std::move(model));
211 scene.layer_mask = 1;
220 std::unique_ptr<render::model> terrain_system::generate_terrain_model(
const geom::brep_mesh& mesh, std::shared_ptr<render::material> material,
const math::uvec2& quad_dimensions)
const
222 const auto& vertex_positions =
mesh.vertices().attributes().at<
math::fvec3>(
"position");
223 const auto& vertex_normals =
mesh.vertices().attributes().at<
math::fvec3>(
"normal");
226 auto model = std::make_unique<render::model>();
229 auto& bounds = model->get_bounds();
245 3 *
sizeof(std::int16_t)
248 auto& vao = model->get_vertex_array();
249 vao = std::make_unique<gl::vertex_array>(vertex_attributes);
252 const auto vert_dimensions = quad_dimensions + 1u;
253 const std::size_t vertex_count = 2 * (vert_dimensions.x() * quad_dimensions.
y() + quad_dimensions.
y() - 1);
254 constexpr std::size_t vertex_stride = 3 *
sizeof(std::int16_t) + 3 *
sizeof(
float);
255 std::vector<std::byte> vertex_data(vertex_count * vertex_stride);
256 std::byte*
v = vertex_data.data();
261 for (
int j = 0;
j < 3; ++
j)
263 i[
j] =
static_cast<std::int16_t
>(
f[
j] < 0.0f ?
f[
j] * 32768.0f :
f[
j] * 32767.0f);
268 for (
auto y = 0u;
y < quad_dimensions.
y(); ++
y)
270 std::size_t indices[2];
272 for (
auto x = 0u;
x < vert_dimensions.x(); ++
x)
274 indices[0] =
y * vert_dimensions.x() +
x;
275 indices[1] = indices[0] + vert_dimensions.x();
277 for (
auto i: indices)
279 auto position = normalized_int16(vertex_positions[i]);
281 std::memcpy(v, &
position[0],
sizeof(std::int16_t) * 3);
282 v +=
sizeof(std::int16_t) * 3;
283 std::memcpy(v, &vertex_normals[i],
sizeof(
float) * 3);
284 v +=
sizeof(float) * 3;
287 bounds.extend(vertex_positions[i]);
291 if (y < quad_dimensions.
y() - 1)
296 auto position = normalized_int16(vertex_positions[indices[1]]);
297 std::memcpy(v, &
position[0],
sizeof(std::int16_t) * 3);
298 v +=
sizeof(int16_t) * 3;
299 std::memcpy(v, &vertex_normals[indices[1]],
sizeof(
float) * 3);
300 v +=
sizeof(float) * 3;
302 indices[0] = (
y + 1) * vert_dimensions.x();
304 position = normalized_int16(vertex_positions[indices[0]]);
305 std::memcpy(v, &
position[0],
sizeof(std::int16_t) * 3);
306 v +=
sizeof(int16_t) * 3;
307 std::memcpy(v, &vertex_normals[indices[0]],
sizeof(
float) * 3);
308 v +=
sizeof(float) * 3;
313 auto& vbo = model->get_vertex_buffer();
315 model->set_vertex_offset(0);
316 model->set_vertex_stride(vertex_stride);
319 model->get_groups().resize(1);
326 model_group.
vertex_count =
static_cast<std::uint32_t
>(vertex_count);
Boundary representation (B-rep) of a mesh.
Abstract base class for lights, cameras, model instances, and other scene objects.
terrain_system(entity::registry ®istry)
entity::id generate(std::shared_ptr< gl::image_2d > heightmap, const math::uvec2 &subdivisions, const math::transform< float > &transform, std::shared_ptr< render::material > material)
Generates terrain entities from a heightmap.
virtual void update(float t, float dt)
Perform's a system's update() function.
Abstract base class for updatable systems.
entity::registry & registry
Registry on which the system operate.
log_message< log_message_severity::error, Args... > log_error
Formats and logs an error message.
entt::registry registry
Component registry type.
entt::entity id
Entity ID type.
@ static_draw
Data will be modified once, by the application, and used many times, for drawing commands.
@ triangle_strip
Connected triangle primitives with consecutive triangles sharing an edge.
constexpr vector< T, N > max(const vector< T, N > &x, const vector< T, N > &y)
Returns a vector containing the maximum elements of two vectors.
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
@ normal
Vertex normal (vec3)
@ position
Vertex position (vec3)
constexpr element_type & x() noexcept
Returns a reference to the first element.
constexpr element_type & y() noexcept
Returns a reference to the second element.
static constexpr vector infinity() noexcept
Returns a vector of infinities, where every element is equal to infinity.
constexpr element_type & front() noexcept
Returns a reference to the first element.
Part of a model which is associated with exactly one material.
std::shared_ptr< render::material > material
gl::primitive_topology primitive_topology
std::uint32_t vertex_count
std::uint32_t first_vertex
Single cell in a terrain grid.
std::vector< entity::id > cells