1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2019 by Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup bke
*/
#include "BKE_subdiv_deform.h"
#include <string.h>
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "BKE_customdata.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_eval.h"
#include "BKE_subdiv_foreach.h"
#include "BKE_subdiv_mesh.h"
#include "MEM_guardedalloc.h"
/* ================================================================================================
* Subdivision context.
*/
typedef struct SubdivDeformContext {
const Mesh *coarse_mesh;
Subdiv *subdiv;
float (*vertex_cos)[3];
int num_verts;
/* Accumulated values.
*
* Averaging is happening for vertices which correspond to the coarse ones.
* This is needed for displacement.
*
* Displacement is being accumulated to a vertices coordinates, since those
* are not needed during traversal of face-vertices vertices. */
/* Per-subdivided vertex counter of averaged values. */
int *accumulated_counters;
bool have_displacement;
} SubdivDeformContext;
static void subdiv_mesh_prepare_accumulator(SubdivDeformContext *ctx, int num_vertices)
{
if (!ctx->have_displacement) {
return;
}
ctx->accumulated_counters = MEM_calloc_arrayN(
sizeof(*ctx->accumulated_counters), num_vertices, "subdiv accumulated counters");
}
static void subdiv_mesh_context_free(SubdivDeformContext *ctx)
{
MEM_SAFE_FREE(ctx->accumulated_counters);
}
/* ================================================================================================
* Accumulation helpers.
*/
static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx,
const int ptex_face_index,
const float u,
const float v,
int vertex_index)
{
Subdiv *subdiv = ctx->subdiv;
float dummy_P[3], dPdu[3], dPdv[3], D[3];
BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
/* Accumulate displacement if needed. */
if (ctx->have_displacement) {
BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
/* NOTE: The storage for vertex coordinates is coming from an external world, not necessarily
* initialized to zeroes. */
if (ctx->accumulated_counters[vertex_index] == 0) {
copy_v3_v3(ctx->vertex_cos[vertex_index], D);
}
else {
add_v3_v3(ctx->vertex_cos[vertex_index], D);
}
}
++ctx->accumulated_counters[vertex_index];
}
/* ================================================================================================
* Subdivision callbacks.
*/
static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context,
const int UNUSED(num_vertices),
const int UNUSED(num_edges),
const int UNUSED(num_loops),
const int UNUSED(num_polygons))
{
SubdivDeformContext *subdiv_context = foreach_context->user_data;
subdiv_mesh_prepare_accumulator(subdiv_context, subdiv_context->coarse_mesh->totvert);
return true;
}
static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int ptex_face_index,
const float u,
const float v,
const int coarse_vertex_index,
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int UNUSED(subdiv_vertex_index))
{
SubdivDeformContext *ctx = foreach_context->user_data;
subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, coarse_vertex_index);
}
static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int ptex_face_index,
const float u,
const float v,
const int coarse_vertex_index,
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int UNUSED(subdiv_vertex_index))
{
SubdivDeformContext *ctx = foreach_context->user_data;
BLI_assert(coarse_vertex_index != ORIGINDEX_NONE);
BLI_assert(coarse_vertex_index < ctx->num_verts);
float inv_num_accumulated = 1.0f;
if (ctx->accumulated_counters != NULL) {
inv_num_accumulated = 1.0f / ctx->accumulated_counters[coarse_vertex_index];
}
/* Displacement is accumulated in subdiv vertex position.
* Needs to be backed up before copying data from original vertex. */
float D[3] = {0.0f, 0.0f, 0.0f};
float *vertex_co = ctx->vertex_cos[coarse_vertex_index];
if (ctx->have_displacement) {
copy_v3_v3(D, vertex_co);
mul_v3_fl(D, inv_num_accumulated);
}
/* Copy custom data and evaluate position. */
BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, vertex_co);
/* Apply displacement. */
add_v3_v3(vertex_co, D);
}
/* ================================================================================================
* Initialization.
*/
static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context,
SubdivForeachContext *foreach_context)
{
memset(foreach_context, 0, sizeof(*foreach_context));
/* General information. */
foreach_context->topology_info = subdiv_mesh_topology_info;
/* Every boundary geometry. Used for displacement and normals averaging. */
if (subdiv_context->have_displacement) {
foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
}
foreach_context->vertex_corner = subdiv_mesh_vertex_corner;
}
/* ================================================================================================
* Public entry point.
*/
void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv,
const struct Mesh *coarse_mesh,
float (*vertex_cos)[3],
int num_verts)
{
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Make sure evaluator is up to date with possible new topology, and that
* is is refined for the new positions of coarse vertices.
*/
if (!BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh)) {
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
* topology.
* In either way, we can't safely continue. */
if (coarse_mesh->totpoly) {
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
return;
}
}
/* Initialize subdivion mesh creation context. */
SubdivDeformContext subdiv_context = {0};
subdiv_context.coarse_mesh = coarse_mesh;
subdiv_context.subdiv = subdiv;
subdiv_context.vertex_cos = vertex_cos;
subdiv_context.num_verts = num_verts;
subdiv_context.have_displacement = (subdiv->displacement_evaluator != NULL);
SubdivForeachContext foreach_context;
setup_foreach_callbacks(&subdiv_context, &foreach_context);
foreach_context.user_data = &subdiv_context;
/* Dummy mesh rasterization settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = 1;
mesh_settings.use_optimal_display = false;
/* Multi-threaded traversal/evaluation. */
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, &mesh_settings, coarse_mesh);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
// BKE_mesh_validate(result, true, true);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Free used memory. */
subdiv_mesh_context_free(&subdiv_context);
}
|