MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ generate_explicit_surface()

MAYAFLUX_API Kakshya::MeshData MayaFlux::Kinesis::generate_explicit_surface ( std::span< const float >  heights,
uint32_t  columns,
uint32_t  rows,
const glm::vec2 &  extent,
float  height_scale = 1.0F,
const glm::vec3 &  center = glm::vec3(0.0F) 
)

Generate a mesh from the graph of a function sampled on a grid, y = f(x, z).

heights holds one value per grid point, row-major, columns values along X for each of rows steps along Z. The surface is centred on center and spans extent in X and Z. Normals and tangents come from central differences of the heights, so they follow the surface and are never degenerate. The raw height is stored in each vertex's weight. UV runs (0, 1) at the first row and first column.

Use it where the data is already a table of values: a sound's recent blocks, an image's brightness, a spectrogram, a column of a file. The triangle indices depend only on the grid size and are reused between calls, so regenerating every frame costs one pass over the heights; large grids are filled in parallel.

Parameters
heightscolumns * rows values.
columnsGrid points along X. Minimum 2.
rowsGrid points along Z. Minimum 2.
extentWorld size in X and Z.
height_scaleMultiplier from a height to a world Y offset.
centerWorld position of the middle of the grid at zero height.
Returns
MeshData ready for TRIANGLE_LIST draw, empty and logged if the sizes disagree.

Definition at line 1042 of file GeometryPrimitives.cpp.

1049{
1050 if (columns < 2 || rows < 2 || heights.size() != static_cast<size_t>(columns) * rows) {
1051 MF_ERROR(Journal::Component::Kinesis, Journal::Context::Runtime,
1052 "generate_explicit_surface needs at least 2 columns and rows and exactly columns * rows heights, got {} heights for {} by {}",
1053 heights.size(), columns, rows);
1054 return Kakshya::MeshData::empty();
1055 }
1056
1057 constexpr size_t stride = sizeof(Kakshya::MeshVertex);
1058 const size_t vertex_count = static_cast<size_t>(columns) * rows;
1059
1060 const float inv_columns = 1.0F / static_cast<float>(columns - 1);
1061 const float inv_rows = 1.0F / static_cast<float>(rows - 1);
1062 const float dx = std::max(extent.x, 1e-6F) * inv_columns;
1063 const float dz = std::max(extent.y, 1e-6F) * inv_rows;
1064 const float x0 = center.x - 0.5F * dx * static_cast<float>(columns - 1);
1065 const float z0 = center.z - 0.5F * dz * static_cast<float>(rows - 1);
1066
1067 std::vector<uint8_t> bytes(vertex_count * stride);
1068 uint8_t* const out = bytes.data();
1069 const float* const h = heights.data();
1070
1071 const auto fill_row = [&](size_t row) {
1072 const size_t row_prev = row > 0 ? row - 1 : row;
1073 const size_t row_next = row + 1 < rows ? row + 1 : row;
1074 const float inv_span_z = 1.0F / (static_cast<float>(row_next - row_prev) * dz);
1075
1076 const float* h_row = h + row * columns;
1077 const float* h_prev = h + row_prev * columns;
1078 const float* h_next = h + row_next * columns;
1079
1080 for (size_t col = 0; col < columns; ++col) {
1081 const size_t col_prev = col > 0 ? col - 1 : col;
1082 const size_t col_next = col + 1 < columns ? col + 1 : col;
1083
1084 const float slope_x = (h_row[col_next] - h_row[col_prev]) * height_scale
1085 / (static_cast<float>(col_next - col_prev) * dx);
1086 const float slope_z = (h_next[col] - h_prev[col]) * height_scale * inv_span_z;
1087
1088 const float normal_scale = glm::inversesqrt(1.0F + slope_x * slope_x + slope_z * slope_z);
1089 const float tangent_scale = glm::inversesqrt(1.0F + slope_x * slope_x);
1090
1091 Kakshya::MeshVertex vertex;
1092 vertex.position = { x0 + static_cast<float>(col) * dx,
1093 center.y + h_row[col] * height_scale,
1094 z0 + static_cast<float>(row) * dz };
1095 vertex.weight = h_row[col];
1096 vertex.uv = { static_cast<float>(col) * inv_columns, 1.0F - static_cast<float>(row) * inv_rows };
1097 vertex.normal = { -slope_x * normal_scale, normal_scale, -slope_z * normal_scale };
1098 vertex.tangent = { tangent_scale, slope_x * tangent_scale, 0.0F };
1099
1100 std::memcpy(out + (row * columns + col) * stride, &vertex, stride);
1101 }
1102 };
1103
1104 if (vertex_count >= k_parallel_vertex_threshold) {
1105 std::vector<size_t> row_ids(rows);
1106 std::iota(row_ids.begin(), row_ids.end(), size_t { 0 });
1107 std::for_each(std::execution::par_unseq, row_ids.begin(), row_ids.end(), fill_row);
1108 } else {
1109 for (size_t row = 0; row < rows; ++row) {
1110 fill_row(row);
1111 }
1112 }
1113
1114 auto data = Kakshya::MeshData::empty();
1115 data.vertex_variant = std::move(bytes);
1116 data.index_variant = surface_indices(columns, rows);
1117 data.layout = Kakshya::VertexLayout::for_meshes(stride);
1118 data.layout.vertex_count = static_cast<uint32_t>(vertex_count);
1119 return data;
1120}
#define MF_ERROR(comp, ctx,...)
uint32_t rows
uint32_t columns
uint32_t h
Definition InkPress.cpp:29
std::vector< float > * out
Vertex type for indexed triangle mesh primitives (TRIANGLE_LIST topology)

References columns, generate_explicit_surface(), h, MF_ERROR, MayaFlux::Kakshya::MeshVertex::normal, out, MayaFlux::Kakshya::MeshVertex::position, rows, MayaFlux::Kakshya::MeshVertex::tangent, MayaFlux::Kakshya::MeshVertex::uv, and MayaFlux::Kakshya::MeshVertex::weight.

Referenced by generate_explicit_surface().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: