Blender V5.0
MOD_uvproject.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* UV Project modifier: Generates UVs projected from an object */
10
11#include "BLI_utildefines.h"
12
13#include "BLI_math_matrix.h"
14#include "BLI_math_vector.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_camera_types.h"
19#include "DNA_defaults.h"
20#include "DNA_mesh_types.h"
21#include "DNA_object_types.h"
22#include "DNA_screen_types.h"
23
24#include "BKE_attribute.hh"
25#include "BKE_camera.h"
26#include "BKE_customdata.hh"
27#include "BKE_lib_query.hh"
28#include "BKE_mesh.hh"
29#include "BKE_uvproject.h"
30
32#include "UI_resources.hh"
33
34#include "RNA_access.hh"
35#include "RNA_prototypes.hh"
36
37#include "MOD_modifiertypes.hh"
38#include "MOD_ui_common.hh"
39
40#include "MEM_guardedalloc.h"
41
43
52
53static void required_data_mask(ModifierData * /*md*/, CustomData_MeshMasks *r_cddata_masks)
54{
55 /* ask for UV coordinates */
56 r_cddata_masks->lmask |= CD_MASK_PROP_FLOAT2;
57}
58
59static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
60{
62 for (int i = 0; i < MOD_UVPROJECT_MAXPROJECTORS; i++) {
63 walk(user_data, ob, (ID **)&umd->projectors[i], IDWALK_CB_NOP);
64 }
65}
66
68{
70 bool do_add_own_transform = false;
71 for (int i = 0; i < umd->projectors_num; i++) {
72 if (umd->projectors[i] != nullptr) {
74 ctx->node, umd->projectors[i], DEG_OB_COMP_TRANSFORM, "UV Project Modifier");
75 do_add_own_transform = true;
76 }
77 }
78 if (do_add_own_transform) {
79 DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier");
80 }
81}
82
83struct Projector {
84 Object *ob; /* object this projector is derived from */
85 float projmat[4][4]; /* projection matrix */
86 float normal[3]; /* projector normal in world space */
87 void *uci; /* optional uv-project info (panorama projection) */
88};
89
91 Mesh &mesh, const blender::StringRef md_name)
92{
93 using namespace blender;
94 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
95 if (md_name.is_empty()) {
97 return attributes.lookup_or_add_for_write_span<float2>(name.is_empty() ? "Float2" : name,
99 }
101 md_name, bke::AttrDomain::Corner))
102 {
103 return attribute;
104 }
106 const std::string name = BKE_attribute_calc_unique_name(owner, md_name);
108}
109
111 const ModifierEvalContext * /*ctx*/,
112 Object *ob,
113 Mesh *mesh)
114{
115 using namespace blender;
117 int projectors_num = 0;
118 float aspx = umd->aspectx ? umd->aspectx : 1.0f;
119 float aspy = umd->aspecty ? umd->aspecty : 1.0f;
120 float scax = umd->scalex ? umd->scalex : 1.0f;
121 float scay = umd->scaley ? umd->scaley : 1.0f;
122 bool free_uci = false;
123
124 for (int i = 0; i < umd->projectors_num; i++) {
125 if (umd->projectors[i] != nullptr) {
126 projectors[projectors_num++].ob = umd->projectors[i];
127 }
128 }
129
130 if (projectors_num == 0) {
131 return mesh;
132 }
133
134 bke::SpanAttributeWriter uv_attribute = get_uv_attribute(*mesh, umd->uvlayer_name);
135 if (!uv_attribute) {
136 return mesh;
137 }
138
139 /* calculate a projection matrix and normal for each projector */
140 for (int i = 0; i < projectors_num; i++) {
141 float tmpmat[4][4];
142 float offsetmat[4][4];
143 /* calculate projection matrix */
144 invert_m4_m4(projectors[i].projmat, projectors[i].ob->object_to_world().ptr());
145
146 projectors[i].uci = nullptr;
147
148 if (projectors[i].ob->type == OB_CAMERA) {
149 const Camera *cam = (const Camera *)projectors[i].ob->data;
150 if (cam->type == CAM_PANO) {
151 projectors[i].uci = BKE_uvproject_camera_info(projectors[i].ob, nullptr, aspx, aspy);
153 static_cast<ProjCameraInfo *>(projectors[i].uci), scax, scay);
154 free_uci = true;
155 }
156 else {
158
159 /* setup parameters */
161 BKE_camera_params_from_object(&params, projectors[i].ob);
162
163 /* Compute matrix, view-plane, etc. */
165
166 /* scale the view-plane */
167 params.viewplane.xmin *= scax;
168 params.viewplane.xmax *= scax;
169 params.viewplane.ymin *= scay;
170 params.viewplane.ymax *= scay;
171
173 mul_m4_m4m4(tmpmat, params.winmat, projectors[i].projmat);
174 }
175 }
176 else {
177 copy_m4_m4(tmpmat, projectors[i].projmat);
178 }
179
180 unit_m4(offsetmat);
181 mul_mat3_m4_fl(offsetmat, 0.5);
182 offsetmat[3][0] = offsetmat[3][1] = offsetmat[3][2] = 0.5;
183
184 mul_m4_m4m4(projectors[i].projmat, offsetmat, tmpmat);
185
186 /* Calculate world-space projector normal (for best projector test). */
187 projectors[i].normal[0] = 0;
188 projectors[i].normal[1] = 0;
189 projectors[i].normal[2] = 1;
190 mul_mat3_m4_v3(projectors[i].ob->object_to_world().ptr(), projectors[i].normal);
191 }
192
193 const Span<float3> positions = mesh->vert_positions();
194 const OffsetIndices faces = mesh->faces();
195 const Span<int> corner_verts = mesh->corner_verts();
196 MutableSpan<float2> mloop_uv = uv_attribute.span;
197
198 /* Convert coords to world-space. */
199 Array<float3> coords(positions.size());
200 for (int64_t i = 0; i < positions.size(); i++) {
201 mul_v3_m4v3(coords[i], ob->object_to_world().ptr(), positions[i]);
202 }
203
204 /* if only one projector, project coords to UVs */
205 if (projectors_num == 1 && projectors[0].uci == nullptr) {
206 for (int64_t i = 0; i < coords.size(); i++) {
207 mul_project_m4_v3(projectors[0].projmat, coords[i]);
208 }
209 }
210
211 /* apply coords as UVs */
212 for (const int i : faces.index_range()) {
213 const IndexRange face = faces[i];
214 if (projectors_num == 1) {
215 if (projectors[0].uci) {
216 for (const int corner : face) {
217 const int vert = corner_verts[corner];
219 mloop_uv[corner], coords[vert], static_cast<ProjCameraInfo *>(projectors[0].uci));
220 }
221 }
222 else {
223 /* apply transformed coords as UVs */
224 for (const int corner : face) {
225 const int vert = corner_verts[corner];
226 copy_v2_v2(mloop_uv[corner], coords[vert]);
227 }
228 }
229 }
230 else {
231 /* multiple projectors, select the closest to face normal direction */
232 int j;
233 Projector *best_projector;
234 float best_dot;
235
236 /* get the untransformed face normal */
237 const float3 face_no = blender::bke::mesh::face_normal_calc(positions,
238 corner_verts.slice(face));
239
240 /* find the projector which the face points at most directly
241 * (projector normal with largest dot product is best)
242 */
243 best_dot = dot_v3v3(projectors[0].normal, face_no);
244 best_projector = &projectors[0];
245
246 for (j = 1; j < projectors_num; j++) {
247 float tmp_dot = dot_v3v3(projectors[j].normal, face_no);
248 if (tmp_dot > best_dot) {
249 best_dot = tmp_dot;
250 best_projector = &projectors[j];
251 }
252 }
253
254 if (best_projector->uci) {
255 for (const int corner : face) {
256 const int vert = corner_verts[corner];
258 mloop_uv[corner], coords[vert], static_cast<ProjCameraInfo *>(best_projector->uci));
259 }
260 }
261 else {
262 for (const int corner : face) {
263 const int vert = corner_verts[corner];
264 mul_v2_project_m4_v3(mloop_uv[corner], best_projector->projmat, coords[vert]);
265 }
266 }
267 }
268 }
269
270 if (free_uci) {
271 int j;
272 for (j = 0; j < projectors_num; j++) {
273 if (projectors[j].uci) {
274 MEM_freeN(projectors[j].uci);
275 }
276 }
277 }
278
279 uv_attribute.finish();
280
281 mesh->runtime->is_original_bmesh = false;
282
283 return mesh;
284}
285
286static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
287{
288 Mesh *result;
290
291 result = uvprojectModifier_do(umd, ctx, ctx->object, mesh);
292
293 return result;
294}
295
296static void panel_draw(const bContext * /*C*/, Panel *panel)
297{
298 uiLayout *sub;
299 uiLayout *layout = panel->layout;
300
301 PointerRNA ob_ptr;
303
304 PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
305
306 layout->use_property_split_set(true);
307
308 layout->prop_search(ptr, "uv_layer", &obj_data_ptr, "uv_layers", std::nullopt, ICON_GROUP_UVS);
309
310 /* Aspect and Scale are only used for camera projectors. */
311 bool has_camera = false;
312 RNA_BEGIN (ptr, projector_ptr, "projectors") {
313 PointerRNA ob_projector = RNA_pointer_get(&projector_ptr, "object");
314 if (!RNA_pointer_is_null(&ob_projector) && RNA_enum_get(&ob_projector, "type") == OB_CAMERA) {
315 has_camera = true;
316 break;
317 }
318 }
319 RNA_END;
320
321 sub = &layout->column(true);
322 sub->active_set(has_camera);
323 sub->prop(ptr, "aspect_x", UI_ITEM_NONE, std::nullopt, ICON_NONE);
324 sub->prop(ptr, "aspect_y", UI_ITEM_NONE, IFACE_("Y"), ICON_NONE);
325
326 sub = &layout->column(true);
327 sub->active_set(has_camera);
328 sub->prop(ptr, "scale_x", UI_ITEM_NONE, std::nullopt, ICON_NONE);
329 sub->prop(ptr, "scale_y", UI_ITEM_NONE, IFACE_("Y"), ICON_NONE);
330
331 layout->prop(ptr, "projector_count", UI_ITEM_NONE, IFACE_("Projectors"), ICON_NONE);
332 RNA_BEGIN (ptr, projector_ptr, "projectors") {
333 layout->prop(&projector_ptr, "object", UI_ITEM_NONE, std::nullopt, ICON_NONE);
334 }
335 RNA_END;
336
338}
339
340static void panel_register(ARegionType *region_type)
341{
343}
344
346 /*idname*/ "UVProject",
347 /*name*/ N_("UVProject"),
348 /*struct_name*/ "UVProjectModifierData",
349 /*struct_size*/ sizeof(UVProjectModifierData),
350 /*srna*/ &RNA_UVProjectModifier,
354 /*icon*/ ICON_MOD_UVPROJECT,
355
356 /*copy_data*/ BKE_modifier_copydata_generic,
357
358 /*deform_verts*/ nullptr,
359 /*deform_matrices*/ nullptr,
360 /*deform_verts_EM*/ nullptr,
361 /*deform_matrices_EM*/ nullptr,
362 /*modify_mesh*/ modify_mesh,
363 /*modify_geometry_set*/ nullptr,
364
365 /*init_data*/ init_data,
366 /*required_data_mask*/ required_data_mask,
367 /*free_data*/ nullptr,
368 /*is_disabled*/ nullptr,
369 /*update_depsgraph*/ update_depsgraph,
370 /*depends_on_time*/ nullptr,
371 /*depends_on_normals*/ nullptr,
372 /*foreach_ID_link*/ foreach_ID_link,
373 /*foreach_tex_link*/ nullptr,
374 /*free_runtime_data*/ nullptr,
375 /*panel_register*/ panel_register,
376 /*blend_write*/ nullptr,
377 /*blend_read*/ nullptr,
378 /*foreach_cache*/ nullptr,
379 /*foreach_working_space_color*/ nullptr,
380};
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, blender::StringRef name)
Definition attribute.cc:370
Camera data-block and utility functions.
void BKE_camera_params_init(CameraParams *params)
void BKE_camera_params_from_object(CameraParams *params, const struct Object *cam_ob)
void BKE_camera_params_compute_viewplane(CameraParams *params, int winx, int winy, float aspx, float aspy)
void BKE_camera_params_compute_matrix(CameraParams *params)
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
@ IDWALK_CB_NOP
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
@ eModifierTypeFlag_AcceptsMesh
void BKE_uvproject_from_camera(float target[2], float source[3], struct ProjCameraInfo *uci)
Definition uvproject.cc:32
void BKE_uvproject_camera_info_scale(ProjCameraInfo *uci, float scale_x, float scale_y)
Definition uvproject.cc:193
struct ProjCameraInfo * BKE_uvproject_camera_info(const struct Object *ob, const float rotmat[4][4], float winx, float winy)
#define BLI_assert(a)
Definition BLI_assert.h:46
void mul_v2_project_m4_v3(float r[2], const float mat[4][4], const float vec[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void mul_mat3_m4_fl(float R[4][4], float f)
void mul_project_m4_v3(const float mat[4][4], float vec[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
void unit_m4(float m[4][4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define IFACE_(msgid)
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_TRANSFORM
@ CAM_PANO
#define CD_MASK_PROP_FLOAT2
@ CD_PROP_FLOAT2
#define DNA_struct_default_get(struct_name)
@ eModifierType_UVProject
#define MOD_UVPROJECT_MAXPROJECTORS
Object is a sort of wrapper for general info.
@ OB_CAMERA
Read Guarded memory(de)allocation.
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void required_data_mask(ModifierData *, CustomData_MeshMasks *r_cddata_masks)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:862
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void required_data_mask(ModifierData *, CustomData_MeshMasks *r_cddata_masks)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
ModifierTypeInfo modifierType_UVProject
static blender::bke::SpanAttributeWriter< blender::float2 > get_uv_attribute(Mesh &mesh, const blender::StringRef md_name)
static Mesh * uvprojectModifier_do(UVProjectModifierData *umd, const ModifierEvalContext *, Object *ob, Mesh *mesh)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
#define UI_ITEM_NONE
long long int int64_t
int64_t size() const
Definition BLI_array.hh:256
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
static void update_depsgraph(tGraphSliderOp *gso)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
float3 face_normal_calc(Span< float3 > vert_positions, Span< int > face_verts)
const char * name
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
bool RNA_pointer_is_null(const PointerRNA *ptr)
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition DNA_ID.h:414
MeshRuntimeHandle * runtime
CustomData corner_data
struct uiLayout * layout
Object * ob
float projmat[4][4]
float normal[3]
struct Object * projectors[10]
uiLayout & column(bool align)
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
void active_set(bool active)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238