1 /* SPDX-FileCopyrightText: 2019 Blender Authors
3 * SPDX-License-Identifier: GPL-2.0-or-later */
5 #include <unordered_map>
7 #include "MEM_guardedalloc.h"
10 #include "field-math.hpp"
12 #include "optimizer.hpp"
13 #include "parametrizer.hpp"
14 #include "quadriflow_capi.hpp"
16 using namespace qflow
;
19 uint32_t p
= (uint32_t)-1;
20 uint32_t n
= (uint32_t)-1;
21 uint32_t uv
= (uint32_t)-1;
25 ObjVertex(uint32_t pi
)
30 bool operator==(const ObjVertex
&v
) const
32 return v
.p
== p
&& v
.n
== n
&& v
.uv
== uv
;
36 struct ObjVertexHash
{
37 std::size_t operator()(const ObjVertex
&v
) const
39 size_t hash
= std::hash
<uint32_t>()(v
.p
);
40 hash
= hash
* 37 + std::hash
<uint32_t>()(v
.uv
);
41 hash
= hash
* 37 + std::hash
<uint32_t>()(v
.n
);
46 typedef std::unordered_map
<ObjVertex
, uint32_t, ObjVertexHash
> VertexMap
;
48 static int check_if_canceled(float progress
,
49 void (*update_cb
)(void *, float progress
, int *cancel
),
53 update_cb(update_cb_data
, progress
, &cancel
);
57 void QFLOW_quadriflow_remesh(QuadriflowRemeshData
*qrd
,
58 void (*update_cb
)(void *, float progress
, int *cancel
),
64 /* Get remeshing parameters. */
65 int faces
= qrd
->target_faces
;
67 if (qrd
->preserve_sharp
) {
68 field
.flag_preserve_sharp
= 1;
70 if (qrd
->preserve_boundary
) {
71 field
.flag_preserve_boundary
= 1;
73 if (qrd
->adaptive_scale
) {
74 field
.flag_adaptive_scale
= 1;
76 if (qrd
->minimum_cost_flow
) {
77 field
.flag_minimum_cost_flow
= 1;
79 if (qrd
->aggresive_sat
) {
80 field
.flag_aggresive_sat
= 1;
83 field
.hierarchy
.rng_seed
= qrd
->rng_seed
;
86 if (check_if_canceled(0.0f
, update_cb
, update_cb_data
) != 0) {
90 /* Copy mesh to quadriflow data structures. */
91 std::vector
<Vector3d
> positions
;
92 std::vector
<uint32_t> indices
;
93 std::vector
<ObjVertex
> vertices
;
95 for (int i
= 0; i
< qrd
->totverts
; i
++) {
96 Vector3d
v(qrd
->verts
[i
* 3], qrd
->verts
[i
* 3 + 1], qrd
->verts
[i
* 3 + 2]);
97 positions
.push_back(v
);
100 for (int q
= 0; q
< qrd
->totfaces
; q
++) {
101 Vector3i
f(qrd
->faces
[q
* 3], qrd
->faces
[q
* 3 + 1], qrd
->faces
[q
* 3 + 2]);
106 tri
[0] = ObjVertex(f
[0]);
107 tri
[1] = ObjVertex(f
[1]);
108 tri
[2] = ObjVertex(f
[2]);
110 for (int i
= 0; i
< nVertices
; ++i
) {
111 const ObjVertex
&v
= tri
[i
];
112 VertexMap::const_iterator it
= vertexMap
.find(v
);
113 if (it
== vertexMap
.end()) {
114 vertexMap
[v
] = (uint32_t)vertices
.size();
115 indices
.push_back((uint32_t)vertices
.size());
116 vertices
.push_back(v
);
119 indices
.push_back(it
->second
);
124 field
.F
.resize(3, indices
.size() / 3);
125 memcpy(field
.F
.data(), indices
.data(), sizeof(uint32_t) * indices
.size());
127 field
.V
.resize(3, vertices
.size());
128 for (uint32_t i
= 0; i
< vertices
.size(); ++i
) {
129 field
.V
.col(i
) = positions
.at(vertices
[i
].p
);
132 if (check_if_canceled(0.1f
, update_cb
, update_cb_data
)) {
136 /* Start processing the input mesh data */
137 field
.NormalizeMesh();
138 field
.Initialize(faces
);
140 if (check_if_canceled(0.2f
, update_cb
, update_cb_data
)) {
144 /* Setup mesh boundary constraints if needed */
145 if (field
.flag_preserve_boundary
) {
146 Hierarchy
&mRes
= field
.hierarchy
;
147 mRes
.clearConstraints();
148 for (uint32_t i
= 0; i
< 3 * mRes
.mF
.cols(); ++i
) {
149 if (mRes
.mE2E
[i
] == -1) {
150 uint32_t i0
= mRes
.mF(i
% 3, i
/ 3);
151 uint32_t i1
= mRes
.mF((i
+ 1) % 3, i
/ 3);
152 Vector3d p0
= mRes
.mV
[0].col(i0
), p1
= mRes
.mV
[0].col(i1
);
153 Vector3d edge
= p1
- p0
;
154 if (edge
.squaredNorm() > 0) {
156 mRes
.mCO
[0].col(i0
) = p0
;
157 mRes
.mCO
[0].col(i1
) = p1
;
158 mRes
.mCQ
[0].col(i0
) = mRes
.mCQ
[0].col(i1
) = edge
;
159 mRes
.mCQw
[0][i0
] = mRes
.mCQw
[0][i1
] = mRes
.mCOw
[0][i0
] = mRes
.mCOw
[0][i1
] = 1.0;
163 mRes
.propagateConstraints();
166 /* Optimize the mesh field orientations (tangental field etc) */
167 Optimizer::optimize_orientations(field
.hierarchy
);
168 field
.ComputeOrientationSingularities();
170 if (check_if_canceled(0.3f
, update_cb
, update_cb_data
)) {
174 if (field
.flag_adaptive_scale
== 1) {
175 field
.EstimateSlope();
178 if (check_if_canceled(0.4f
, update_cb
, update_cb_data
)) {
182 Optimizer::optimize_scale(field
.hierarchy
, field
.rho
, field
.flag_adaptive_scale
);
183 field
.flag_adaptive_scale
= 1;
185 Optimizer::optimize_positions(field
.hierarchy
, field
.flag_adaptive_scale
);
187 field
.ComputePositionSingularities();
189 if (check_if_canceled(0.5f
, update_cb
, update_cb_data
)) {
193 /* Compute the final quad geometry using a maxflow solver */
194 if (!field
.ComputeIndexMap()) {
195 /* Error computing the result. */
199 if (check_if_canceled(0.9f
, update_cb
, update_cb_data
)) {
203 /* Get the output mesh data */
204 qrd
->out_totverts
= field
.O_compact
.size();
205 qrd
->out_totfaces
= field
.F_compact
.size();
207 qrd
->out_verts
= (float *)MEM_malloc_arrayN(qrd
->out_totverts
, sizeof(float[3]), __func__
);
208 qrd
->out_faces
= (int *)MEM_malloc_arrayN(qrd
->out_totfaces
, sizeof(int[4]), __func__
);
210 for (int i
= 0; i
< qrd
->out_totverts
; i
++) {
211 auto t
= field
.O_compact
[i
] * field
.normalize_scale
+ field
.normalize_offset
;
212 qrd
->out_verts
[i
* 3] = t
[0];
213 qrd
->out_verts
[i
* 3 + 1] = t
[1];
214 qrd
->out_verts
[i
* 3 + 2] = t
[2];
217 for (int i
= 0; i
< qrd
->out_totfaces
; i
++) {
218 qrd
->out_faces
[i
* 4] = field
.F_compact
[i
][0];
219 qrd
->out_faces
[i
* 4 + 1] = field
.F_compact
[i
][1];
220 qrd
->out_faces
[i
* 4 + 2] = field
.F_compact
[i
][2];
221 qrd
->out_faces
[i
* 4 + 3] = field
.F_compact
[i
][3];