Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f906788
refactor: Move VertexInCluster to own file
Martin-cg Jul 11, 2026
f13ba18
refactor: Refactor and extend various terrainlib helpers
Martin-cg Jul 11, 2026
1b45057
fix: Disallow returning spans from rvalue
Martin-cg Jul 11, 2026
bf228b0
fix: Fix and rewrite clustering merging
Martin-cg Jul 11, 2026
3bc05b9
fix: Fix incorrect bounds including children
Martin-cg Jul 11, 2026
1d5d1d3
fix: Do not write debug textures when converting dag nodes into meshes
Martin-cg Jul 11, 2026
2ebb7de
perf: Optimize texture building
Martin-cg Jul 11, 2026
151b9ea
test: Add tests for texture baking
Martin-cg Jul 11, 2026
7991504
fix: Fix reversed loop direction when loading relvant input nodes
Martin-cg Jul 11, 2026
d16d2e1
fix: Various minor bug fixes to dag building
Martin-cg Jul 11, 2026
7dbd024
feat: Wire overwrite to not resume dag building
Martin-cg Jul 11, 2026
1dc9cdd
fix: Add missing EOF newline
Martin-cg Jul 11, 2026
ae2753e
feat: Add helpers for printing and determining texture size
Martin-cg Jul 11, 2026
042de0b
fix: Add missing includes
Martin-cg Jul 11, 2026
ca5fe64
fix: Prefer std::common_type over MoreExact
Martin-cg Jul 11, 2026
5a359bc
chore: Remove unused code from dag-builder
Martin-cg Jul 11, 2026
d751a68
fix: Lock vertices on node boundary not clustering boundary
Martin-cg Jul 11, 2026
d0625a5
style: Prefer limits::max() over -1
Martin-cg Jul 11, 2026
d1a4d04
fix: Increase split factor to promote more spatially coherent clusters
Martin-cg Jul 11, 2026
e13c144
fix: Various minor fixes
Martin-cg Jul 11, 2026
e9dafdb
feat: Make texture bake size dynamic
Martin-cg Jul 12, 2026
cfaf4c9
feat: Add option to bound error by fraction of node bounds
Martin-cg Jul 12, 2026
ee54352
fix: Correctly forward and ensure monotonic errors
Martin-cg Jul 12, 2026
f5cb0d5
fix: Disallow cluster simplifciation into nothing
Martin-cg Jul 12, 2026
2623df7
feat: Add progress bar to tile downloader
Martin-cg Jul 12, 2026
243d973
chore: Ignore extra build-release folder
Martin-cg Jul 12, 2026
02afd55
fix: Fix overload resolution issue
Martin-cg Jul 12, 2026
60b83df
refactor: Move int_div_ceil to int_math.h
Martin-cg Jul 12, 2026
8b73085
feat: Wire in uv algorithm selection
Martin-cg Jul 16, 2026
0ebaaa0
refactor: Move Partitioning to its own file to avoid forward decl
Martin-cg Jul 16, 2026
616d2a7
feat: Rewrite Range helper to mirror Rust's std::range
Martin-cg Jul 16, 2026
75fdb93
fix: Fix copy paste error in merging
Martin-cg Jul 16, 2026
18c3792
feat: Add --resume and default to erroring if no continuation mode is…
Martin-cg Jul 16, 2026
26d18da
feat: Wire in --parallel and --include_mode
Martin-cg Jul 16, 2026
4c1b9ad
fix: Fix wrong calculation regarding error in simplify
Martin-cg Jul 16, 2026
b39bcaf
feat: Add ClusterBatch::is_leaves
Martin-cg Jul 16, 2026
3b3cd13
fix: Check result before noting a node as written
Martin-cg Jul 16, 2026
6d2a5bd
fix: Correctly scan for already built nodes
Martin-cg Jul 16, 2026
043e5fa
docs: Add some more comments
Martin-cg Jul 16, 2026
3d89383
fix: Doc & check that no clusters are discarded while merging
Martin-cg Jul 16, 2026
8dd2d79
feat: Add overload to clustering slicing that returns map
Martin-cg Jul 16, 2026
3672b9d
fix: Lower vertex lock bounds margin
Martin-cg Jul 16, 2026
3074edb
fix: Link new tile-downloader to CLI11
Martin-cg Jul 16, 2026
407cc53
perf: Avoid copying clustering when returning from build_lod
Martin-cg Jul 16, 2026
24e9840
refactor: Rename build_lod input parameter to clusters
Martin-cg Jul 16, 2026
0a6cb3e
docs: Fix typo
Martin-cg Jul 16, 2026
b9a3b06
fix: Slightly increase merge epsilon
Martin-cg Jul 16, 2026
bfd1e65
perf: Avoid copying clustering when returning from load_and_simplify_…
Martin-cg Jul 16, 2026
76c7c28
Merge branch 'main' into dag-builder
adam-ce Jul 17, 2026
941dfe1
fix: Avoid copying enumerated boundary entries
adam-ce Jul 17, 2026
0c3d96d
Merge pull request #28 from adam-ce/dag-builder
adam-ce Jul 17, 2026
905843f
Merge pull request #32 from AlpineMapsOrg/main
adam-ce Jul 26, 2026
bc41ac7
Make unsupported DAG continuation mode explicit
adam-ce Jul 26, 2026
382bc3a
Preserve CMake prefix paths for dependencies
adam-ce Jul 26, 2026
6c23f9c
Update CGAL configuration options
adam-ce Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmake/SetupCMakeProject.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function(_alp_build_and_install NAME SRC_DIR BUILD_DIR INSTALL_DIR)
"-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} ${_alp_sanitizer_flags}"
"-DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS} ${_alp_sanitizer_flags}"
"-DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} ${_alp_sanitizer_flags}"
-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
"-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}"
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
${ARGN}
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ find_package(TBB REQUIRED)
include(../cmake/SetupGDAL.cmake)
alp_setup_gdal(GDAL_VERSION v3.10.3 PROJ_VERSION 9.6.0)

alp_setup_cmake_project(cgal URL https://github.com/CGAL/cgal.git COMMITISH "v6.0.1" CMAKE_ARGUMENTS -DCGAL_HEADER_ONLY=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF)
alp_setup_cmake_project(cgal URL https://github.com/CGAL/cgal.git COMMITISH "v6.0.1" CMAKE_ARGUMENTS -DBUILD_TESTING=OFF -DWITH_examples=OFF)
find_package(CGAL REQUIRED)

include(../cmake/SetupOpenCV.cmake)
Expand Down
2 changes: 1 addition & 1 deletion src/dag_builder/ContinuationMode.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

enum class ContinuationMode {
Error,
Resume,
Overwrite,
Fail
};
10 changes: 10 additions & 0 deletions src/dag_builder/Partitioning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <cstdint>
#include <vector>


struct [[nodiscard]] Partitioning {
uint32_t partition_count = 0;
std::vector<uint32_t> cluster_partitions; // original cluster index -> partition index
};
21 changes: 21 additions & 0 deletions src/dag_builder/VertexInCluster.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <cstdint>

#include "hash_utils.h"

struct VertexInCluster {
uint32_t cluster_index;
uint32_t local_vertex_index;

auto operator<=>(const VertexInCluster &other) const = default;
};

namespace std {
template <>
struct hash<VertexInCluster> {
size_t operator()(const VertexInCluster &v) const noexcept {
return ::hash::combine(v.cluster_index, v.local_vertex_index);
}
};
}
21 changes: 21 additions & 0 deletions src/dag_builder/VertexInClustering.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <cstdint>

#include "hash_utils.h"

struct VertexInClustering {
uint32_t clustering_index;
uint32_t global_vertex_index;

auto operator<=>(const VertexInClustering &other) const = default;
};

namespace std {
template <>
struct hash<VertexInClustering> {
size_t operator()(const VertexInClustering &v) const noexcept {
return ::hash::combine(v.clustering_index, v.global_vertex_index);
}
};
} // namespace std
8 changes: 5 additions & 3 deletions src/dag_builder/atlas/Packer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <xatlas/xatlas.h>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>

#include "log.h"
#include "Packer.h"
Expand Down Expand Up @@ -37,10 +38,13 @@ inline void normalize_uvs(std::span<glm::dvec2> uvs) {

struct Packer::Impl {
std::unique_ptr<xatlas::Atlas, decltype(&xatlas::Destroy)> atlas;
double total_effective_pixel_area = 0.0;

Impl() : atlas(xatlas::Create(), xatlas::Destroy) {}

void add_uv_mesh(const UvMesh& mesh) {
this->total_effective_pixel_area += glm::compMul(glm::dvec2(mesh.texture_size));

std::vector<glm::vec2> scaled_uvs = transform_vector(mesh.uvs, [&](const glm::dvec2 &uv) {
const glm::dvec2 scaled = uv * glm::dvec2(mesh.texture_size);
return glm::vec2(scaled);
Expand Down Expand Up @@ -87,9 +91,7 @@ struct Packer::Impl {

normalize_uvs(uvs.flat());

// TODO: return atlas->width and atlas->height or aspect?

return Packing(uvs);
return Packing(uvs, this->atlas->utilization[0]);
}
};

Expand Down
24 changes: 22 additions & 2 deletions src/dag_builder/atlas/Packer.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#pragma once

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <span>

#include <glm/glm.hpp>

#include "number_utils.h"
#include "SegmentedBuffer.h"

namespace atlas {
Expand All @@ -17,8 +21,10 @@ struct UvMesh {
class Packing {
public:
Packing() = default;
Packing(std::vector<glm::dvec2> uvs) : Packing(SegmentedBuffer<glm::dvec2>(std::move(uvs))) {}
Packing(SegmentedBuffer<glm::dvec2> uvs) : _uvs(std::move(uvs)) {}
Packing(std::vector<glm::dvec2> uvs, const double effective_pixel_area, const float utilization = 1.0f)
: Packing(SegmentedBuffer<glm::dvec2>(std::move(uvs)), effective_pixel_area, utilization) {}
Packing(SegmentedBuffer<glm::dvec2> uvs, const double effective_pixel_area, const float utilization = 1.0f)
: _uvs(std::move(uvs)), _effective_pixel_area(effective_pixel_area), _utilization(utilization) {}

std::span<const glm::dvec2> uvs_for_mesh(const uint32_t mesh_index) const {
return this->_uvs.segment(mesh_index);
Expand All @@ -31,8 +37,22 @@ class Packing {
return this->_uvs.backing();
}

// Sum of effective input pixels (source-texture pixels actually covered by UVs) across all packed meshes.
double effective_pixel_area() const {
return this->_effective_pixel_area;
}

// Fraction of the packed atlas actually covered by charts. 1.0 for
// packings that were not produced by the chart packer (e.g. a single
// mesh's own UVs), since there is no wasted atlas space to account for.
float utilization() const {
return this->_utilization;
}

private:
SegmentedBuffer<glm::dvec2> _uvs;
double _effective_pixel_area = 0.0;
float _utilization = 1.0f;
};

class Packer {
Expand Down
136 changes: 84 additions & 52 deletions src/dag_builder/atlas/TextureBaker.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <algorithm>
#include <cmath>
#include <functional>
#include <cstddef>
Expand All @@ -12,10 +13,12 @@

#include <opencv2/opencv.hpp>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>

#include "atlas/Packer.h"
#include "atlas/pull_reproject_texture.h"
#include "mesh/bounds.h"
#include "number_utils.h"
#include "opencv_utils.h"
#include "range_utils.h"
#include "TextureSet.h"
Expand Down Expand Up @@ -84,13 +87,25 @@ inline glm::uvec2 get_effective_texture_size(const TextureMap &map) {
inline glm::uvec2 get_effective_texture_size(const TextureComposition &comp) {
double area = 0;
for (const auto &map : comp.maps) {
const auto size = get_effective_texture_size(map);
area += size.x * size.y;
area += glm::compMul(glm::dvec2(get_effective_texture_size(map)));
}
return glm::uvec2(std::ceil(std::sqrt(area)));
}
} // namespace detail

// Resolution needed to give a compression_ratio fraction of effective_pixel_area
// worth of resolution, after accounting for atlas packing waste (unused texels
// between packed charts), capped at max_texture_size.
inline glm::uvec2 compute_bake_texture_size(const double effective_pixel_area, const float utilization, const double compression_ratio, const uint32_t max_texture_size) {
const double target_area = effective_pixel_area * compression_ratio / std::max<double>(utilization, 1e-3);
const uint32_t side = static_cast<uint32_t>(std::ceil(std::sqrt(target_area)));
return glm::uvec2(std::min(next_power_of_two(side), max_texture_size));
}

inline glm::uvec2 compute_bake_texture_size(const atlas::Packing &packing, const double compression_ratio, const uint32_t max_texture_size) {
return compute_bake_texture_size(packing.effective_pixel_area(), packing.utilization(), compression_ratio, max_texture_size);
}

class TextureBaker {
public:
TextureMapId add_mesh(TexturedMesh mesh) {
Expand Down Expand Up @@ -128,36 +143,72 @@ class TextureBaker {
return id;
}

TextureCake bake(const glm::uvec2 texture_size) {
// Exact output resolution.
TextureCake bake(const glm::uvec2 texture_size) const {
return this->bake(this->pack(), texture_size);
}

// Runs atlas packing (chart computation for multi-mesh atlases; a cheap
// passthrough for a single entry). Callers that need packing.utilization()
// or packing.effective_pixel_area() to choose an output resolution should
// call this once and pass the result to bake(packing, texture_size) below,
// rather than letting bake(texture_size) compute it again.
atlas::Packing pack() const {
if (this->_entries.size() == 1) {
return this->bake_single(this->_entries.front(), texture_size);
} else {
return this->bake_multi(texture_size);
return this->pack_single(this->_entries.front());
}

atlas::Packer packer;

std::vector<glm::uvec3> target_triangles;
for (const Entry& entry : this->_entries) {
visit_entry(entry,
[&](const TexturedMesh& mesh) {
packer.add_uv_mesh(mesh.triangles, mesh.map.uvs, detail::get_effective_texture_size(mesh.map));
},
[&](const TextureComposition& comp) {
target_triangles.clear();
target_triangles.reserve(comp.triangles.size());
for (const MappedTriangle &triangle : comp.triangles) {
target_triangles.push_back(triangle.target);
}
packer.add_uv_mesh(
target_triangles,
comp.target_uvs,
detail::get_effective_texture_size(comp)
);
});
}

return packer.pack();
}

private:
using Entry = std::variant<TexturedMesh, TextureComposition>;
// Bakes using an already-computed packing (e.g. from pack()), so no atlas repacking happens.
TextureCake bake(const atlas::Packing &packing, const glm::uvec2 texture_size) const {
if (this->_entries.size() == 1) {
return visit_entry<TextureCake>(this->_entries.front(),
[&](const TexturedMesh &mesh) {
TextureCake cake;
cake._packing = packing;
cake._texture = rescale_texture(mesh.map.texture, texture_size);
return cake;
},
[&](const TextureComposition &) {
TextureCake cake;
cake._packing = packing;
cake._texture = this->bake_texture(cake._packing, texture_size);
return cake;
});
}

TextureCake bake_multi(const glm::uvec2 texture_size) const {
TextureCake cake;
cake._packing = this->pack();
cake._packing = packing;
cake._texture = this->bake_texture(cake._packing, texture_size);
return cake;
}

TextureCake bake_single(const Entry &entry, const glm::uvec2 texture_size) const {
return visit_entry<TextureCake>(entry,
[&](const TexturedMesh &mesh) {
TextureCake cake;
cake._packing = this->pack_single(mesh);
cake._texture = rescale_texture(mesh.map.texture, texture_size);
return cake;
},
[&](const TextureComposition &) {
return this->bake_multi(texture_size);
});
}
private:
using Entry = std::variant<TexturedMesh, TextureComposition>;

Texture bake_texture(const atlas::Packing &packing, const glm::uvec2 texture_size) const {
TextureReprojector reprojector(texture_size, CV_8UC3);
Expand Down Expand Up @@ -233,43 +284,24 @@ class TextureBaker {
}

atlas::Packing pack_single(const Entry &entry) const {
const double area = effective_pixel_area(entry);
return visit_entry<atlas::Packing>(entry,
[&](const TexturedMesh &mesh) {
return atlas::Packing(mesh.map.uvs);
return atlas::Packing(mesh.map.uvs, area);
},
[&](const TextureComposition &comp) {
return atlas::Packing(comp.target_uvs);
return atlas::Packing(comp.target_uvs, area);
});
}

atlas::Packing pack() const {
if (this->_entries.size() == 1) {
return this->pack_single(this->_entries.front());
}

atlas::Packer packer;

std::vector<glm::uvec3> target_triangles;
for (const Entry& entry : this->_entries) {
visit_entry(entry,
[&](const TexturedMesh& mesh) {
packer.add_uv_mesh(mesh.triangles, mesh.map.uvs, detail::get_effective_texture_size(mesh.map));
},
[&](const TextureComposition& comp) {
target_triangles.clear();
target_triangles.reserve(comp.triangles.size());
for (const MappedTriangle &triangle : comp.triangles) {
target_triangles.push_back(triangle.target);
}
packer.add_uv_mesh(
target_triangles,
comp.target_uvs,
detail::get_effective_texture_size(comp)
);
});
}

return packer.pack();
static double effective_pixel_area(const Entry &entry) {
return visit_entry<double>(entry,
[](const TexturedMesh &mesh) {
return glm::compMul(glm::dvec2(detail::get_effective_texture_size(mesh.map)));
},
[](const TextureComposition &comp) {
return glm::compMul(glm::dvec2(detail::get_effective_texture_size(comp)));
});
}

template <class R = void, class MeshFn, class CompFn>
Expand Down
Loading
Loading