14 #include <type_traits>
18 # pragma push_macro("emit")
20 # define CV_RESTORE_EMIT_MACRO
22 #include <tbb/parallel_for.h>
41 #ifdef CV_RESTORE_EMIT_MACRO
42 # pragma pop_macro("emit")
43 # undef CV_RESTORE_EMIT_MACRO
53 namespace uvunwrapping {
56 using namespace DirectX;
58 struct UVAtlasPartitionOutput {
60 std::vector<UVAtlasVertex>
vb;
62 std::vector<uint8_t>
ib;
69 const float max_stretch,
71 UVAtlasPartitionOutput& output) {
72 const int64_t num_verts = mesh.GetVertexPositions().GetLength();
74 std::unique_ptr<XMFLOAT3[]> pos(
new XMFLOAT3[num_verts]);
76 core::Tensor vertices = mesh.GetVertexPositions()
79 const float* vertices_ptr = vertices.GetDataPtr<
float>();
80 for (int64_t i = 0; i < num_verts; ++i) {
81 pos[i].x = vertices_ptr[i * 3 + 0];
82 pos[i].y = vertices_ptr[i * 3 + 1];
83 pos[i].z = vertices_ptr[i * 3 + 2];
87 core::Tensor triangles = mesh.GetTriangleIndices()
90 const uint32_t* triangles_ptr = triangles.GetDataPtr<uint32_t>();
91 const int64_t num_triangles = triangles.GetLength();
93 typedef uint64_t Edge_t;
94 typedef std::pair<uint32_t, uint32_t> AdjTriangles_t;
95 const uint32_t
INVALID =
static_cast<uint32_t
>(-1);
96 auto MakeEdge = [](uint32_t idx1, uint32_t idx2) {
102 std::vector<uint32_t> adj(triangles.NumElements());
104 std::unordered_map<Edge_t, AdjTriangles_t> edge_adjtriangle_map;
105 for (int64_t i = 0; i < num_triangles; ++i) {
106 const uint32_t* t_ptr = triangles_ptr + i * 3;
108 for (
int j = 0; j < 3; ++j) {
109 auto e = MakeEdge(t_ptr[j], t_ptr[(j + 1) % 3]);
110 auto it = edge_adjtriangle_map.find(e);
111 if (it != edge_adjtriangle_map.end()) {
112 it->second.second = i;
114 edge_adjtriangle_map[e] = AdjTriangles_t(i,
INVALID);
120 int64_t linear_idx = 0;
121 for (int64_t i = 0; i < num_triangles; ++i) {
122 const uint32_t* t_ptr = triangles_ptr + i * 3;
123 for (
int j = 0; j < 3; ++j, ++linear_idx) {
124 auto e = MakeEdge(t_ptr[j], t_ptr[(j + 1) % 3]);
125 auto& adjacent_tri = edge_adjtriangle_map[e];
126 if (adjacent_tri.first != i) {
127 adj[linear_idx] = adjacent_tri.first;
129 adj[linear_idx] = adjacent_tri.second;
138 std::vector<UVAtlasVertex>
vb;
140 std::vector<uint8_t>
ib;
143 std::vector<uint32_t> remap;
144 std::vector<uint32_t> face_partitioning;
147 HRESULT hr = UVAtlasPartition(
148 pos.get(), num_verts, triangles_ptr, DXGI_FORMAT_R32_UINT,
149 num_triangles, 0, max_stretch, adj.data(),
nullptr,
nullptr,
150 nullptr, UVATLAS_DEFAULT_CALLBACK_FREQUENCY,
151 fast_mode ? UVATLAS_GEODESIC_FAST : UVATLAS_DEFAULT,
vb,
ib,
153 &output.max_stretch_out, &output.num_charts_out);
156 if (hr ==
static_cast<HRESULT
>(0x8007000DL)) {
158 }
else if (hr ==
static_cast<HRESULT
>(0x80070216L)) {
160 }
else if (hr ==
static_cast<HRESULT
>(0x80070032L)) {
164 static_cast<uint32_t
>(hr));
167 output.original_face_idx =
168 mesh.GetTriangleAttr(
"original_idx").ToFlatVector<int64_t>();
169 output.ib = std::move(
ib);
170 output.vb = std::move(
vb);
179 const float max_stretch,
180 int parallel_partitions,
188 if (parallel_partitions > 1) {
189 const int max_points_per_partition =
190 (num_verts - 1) / (parallel_partitions - 1);
191 parallel_partitions = mesh_tmp.
PCAPartition(max_points_per_partition);
199 std::vector<TriangleMesh> mesh_partitions;
200 if (parallel_partitions > 1) {
201 for (
int i = 0; i < parallel_partitions; ++i) {
206 mesh_partitions.emplace_back(mesh_tmp);
209 std::vector<UVAtlasPartitionOutput> uvatlas_partitions(parallel_partitions);
213 const bool fast_mode = parallel_partitions > 1;
214 auto LoopFn = [&](
const tbb::blocked_range<size_t>& range) {
215 for (
size_t i = range.begin(); i < range.end(); ++i) {
216 auto& output = uvatlas_partitions[i];
217 ComputeUVAtlasPartition(mesh_partitions[i], max_stretch, fast_mode,
222 if (parallel_partitions > 1) {
224 tbb::task_arena arena(nthreads);
225 arena.execute([&]() {
227 tbb::blocked_range<size_t>(0, parallel_partitions),
232 tbb::blocked_range<size_t>(0, parallel_partitions), LoopFn);
235 LoopFn(tbb::blocked_range<size_t>(0, parallel_partitions));
239 UVAtlasPartitionOutput& combined_output = uvatlas_partitions.front();
240 for (
int i = 1; i < parallel_partitions; ++i) {
241 auto& output = uvatlas_partitions[i];
244 const uint32_t vidx_offset = combined_output.vb.size();
245 uint32_t* indices_ptr =
reinterpret_cast<uint32_t*
>(output.ib.data());
246 const int64_t num_indices = output.ib.size() /
sizeof(uint32_t);
247 for (int64_t indices_i = 0; indices_i < num_indices; ++indices_i) {
248 indices_ptr[indices_i] += vidx_offset;
250 combined_output.vb.insert(combined_output.vb.end(), output.vb.begin(),
253 const uint32_t fidx_offset =
254 combined_output.ib.size() / (
sizeof(uint32_t) * 3);
256 for (
auto& x : output.partition_result_adjacency) {
261 combined_output.ib.insert(combined_output.ib.end(), output.ib.begin(),
263 combined_output.partition_result_adjacency.insert(
264 combined_output.partition_result_adjacency.end(),
265 output.partition_result_adjacency.begin(),
266 output.partition_result_adjacency.end());
268 combined_output.original_face_idx.insert(
269 combined_output.original_face_idx.end(),
270 output.original_face_idx.begin(),
271 output.original_face_idx.end());
274 combined_output.max_stretch_out =
std::max(
275 combined_output.max_stretch_out, output.max_stretch_out);
276 combined_output.num_charts_out += output.num_charts_out;
279 output = UVAtlasPartitionOutput();
282 HRESULT hr = UVAtlasPack(combined_output.vb, combined_output.ib,
284 combined_output.partition_result_adjacency,
285 nullptr, UVATLAS_DEFAULT_CALLBACK_FREQUENCY);
288 if (hr ==
static_cast<HRESULT
>(0x8007000DL)) {
290 }
else if (hr ==
static_cast<HRESULT
>(0x80070216L)) {
292 }
else if (hr ==
static_cast<HRESULT
>(0x80070032L)) {
296 static_cast<uint32_t
>(hr));
299 auto&
ib = combined_output.ib;
300 auto&
vb = combined_output.vb;
303 const uint32_t* indices_ptr =
reinterpret_cast<uint32_t*
>(
ib.data());
304 if (
ib.size() !=
sizeof(uint32_t) * 3 * num_triangles) {
306 "Unexpected output index buffer size. Got {} expected {}.",
307 ib.size(),
sizeof(uint32_t) * 3 * num_triangles);
312 float* texture_uvs_ptr = texture_uvs.
GetDataPtr<
float>();
313 for (int64_t i = 0; i < num_triangles; ++i) {
315 for (
int j = 0; j < 3; ++j) {
316 const uint32_t& vidx = indices_ptr[i * 3 + j];
317 const auto& vert =
vb[vidx];
318 texture_uvs_ptr[original_i * 6 + j * 2 + 0] = vert.uv.x;
319 texture_uvs_ptr[original_i * 6 + j * 2 + 1] = vert.uv.y;
324 texture_uvs = texture_uvs.To(mesh.
GetDevice());
327 return std::tie(combined_output.max_stretch_out,
328 combined_output.num_charts_out, parallel_partitions);
std::vector< UVAtlasVertex > vb
std::vector< int64_t > original_face_idx
std::vector< uint32_t > partition_result_adjacency
std::vector< uint8_t > ib
Tensor Contiguous() const
static Tensor Arange(const Scalar start, const Scalar stop, const Scalar step=1, const Dtype dtype=core::Int64, const Device &device=core::Device("CPU:0"))
Create a 1D tensor with evenly spaced values in the given interval.
int64_t GetLength() const
Tensor To(Dtype dtype, bool copy=false) const
A triangle mesh contains vertices and triangles.
core::Tensor & GetTriangleIndices()
TriangleMesh SelectFacesByMask(const core::Tensor &mask) const
core::Tensor & GetVertexPositions()
core::Device GetDevice() const override
Returns the device of the geometry.
int PCAPartition(int max_faces)
void SetTriangleAttr(const std::string &key, const core::Tensor &value)
const TensorMap & GetTriangleAttr() const
Getter for triangle_attr_ TensorMap. Used in Pybind.
std::tuple< float, int, int > ComputeUVAtlas(TriangleMesh &mesh, const size_t width, const size_t height, const float gutter, const float max_stretch, int parallel_partitions, int nthreads)
Generic file read and write utility for python interface.