1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2005 by the Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup modifiers
22 */
23
24 /* UV Project modifier: Generates UVs projected from an object */
25
26 #include "BLI_utildefines.h"
27
28 #include "BLI_math.h"
29 #include "BLI_uvproject.h"
30
31 #include "BLT_translation.h"
32
33 #include "DNA_camera_types.h"
34 #include "DNA_defaults.h"
35 #include "DNA_mesh_types.h"
36 #include "DNA_meshdata_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_screen_types.h"
39
40 #include "BKE_camera.h"
41 #include "BKE_context.h"
42 #include "BKE_lib_query.h"
43 #include "BKE_material.h"
44 #include "BKE_mesh.h"
45 #include "BKE_screen.h"
46
47 #include "UI_interface.h"
48 #include "UI_resources.h"
49
50 #include "RNA_access.h"
51
52 #include "MOD_modifiertypes.h"
53 #include "MOD_ui_common.h"
54
55 #include "MEM_guardedalloc.h"
56
57 #include "DEG_depsgraph.h"
58 #include "DEG_depsgraph_build.h"
59 #include "DEG_depsgraph_query.h"
60
initData(ModifierData * md)61 static void initData(ModifierData *md)
62 {
63 UVProjectModifierData *umd = (UVProjectModifierData *)md;
64
65 BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(umd, modifier));
66
67 MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVProjectModifierData), modifier);
68 }
69
requiredDataMask(Object * UNUSED (ob),ModifierData * UNUSED (md),CustomData_MeshMasks * r_cddata_masks)70 static void requiredDataMask(Object *UNUSED(ob),
71 ModifierData *UNUSED(md),
72 CustomData_MeshMasks *r_cddata_masks)
73 {
74 /* ask for UV coordinates */
75 r_cddata_masks->lmask |= CD_MLOOPUV;
76 }
77
foreachIDLink(ModifierData * md,Object * ob,IDWalkFunc walk,void * userData)78 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
79 {
80 UVProjectModifierData *umd = (UVProjectModifierData *)md;
81 for (int i = 0; i < MOD_UVPROJECT_MAXPROJECTORS; i++) {
82 walk(userData, ob, (ID **)&umd->projectors[i], IDWALK_CB_NOP);
83 }
84 }
85
updateDepsgraph(ModifierData * md,const ModifierUpdateDepsgraphContext * ctx)86 static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
87 {
88 UVProjectModifierData *umd = (UVProjectModifierData *)md;
89 bool do_add_own_transform = false;
90 for (int i = 0; i < umd->num_projectors; i++) {
91 if (umd->projectors[i] != NULL) {
92 DEG_add_object_relation(
93 ctx->node, umd->projectors[i], DEG_OB_COMP_TRANSFORM, "UV Project Modifier");
94 do_add_own_transform = true;
95 }
96 }
97 if (do_add_own_transform) {
98 DEG_add_modifier_to_transform_relation(ctx->node, "UV Project Modifier");
99 }
100 }
101
102 typedef struct Projector {
103 Object *ob; /* object this projector is derived from */
104 float projmat[4][4]; /* projection matrix */
105 float normal[3]; /* projector normal in world space */
106 void *uci; /* optional uv-project info (panorama projection) */
107 } Projector;
108
uvprojectModifier_do(UVProjectModifierData * umd,const ModifierEvalContext * UNUSED (ctx),Object * ob,Mesh * mesh)109 static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
110 const ModifierEvalContext *UNUSED(ctx),
111 Object *ob,
112 Mesh *mesh)
113 {
114 float(*coords)[3], (*co)[3];
115 MLoopUV *mloop_uv;
116 int i, numVerts, numPolys, numLoops;
117 MPoly *mpoly, *mp;
118 MLoop *mloop;
119 Projector projectors[MOD_UVPROJECT_MAXPROJECTORS];
120 int num_projectors = 0;
121 char uvname[MAX_CUSTOMDATA_LAYER_NAME];
122 float aspx = umd->aspectx ? umd->aspectx : 1.0f;
123 float aspy = umd->aspecty ? umd->aspecty : 1.0f;
124 float scax = umd->scalex ? umd->scalex : 1.0f;
125 float scay = umd->scaley ? umd->scaley : 1.0f;
126 int free_uci = 0;
127
128 for (i = 0; i < umd->num_projectors; i++) {
129 if (umd->projectors[i] != NULL) {
130 projectors[num_projectors++].ob = umd->projectors[i];
131 }
132 }
133
134 if (num_projectors == 0) {
135 return mesh;
136 }
137
138 /* make sure there are UV Maps available */
139
140 if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) {
141 return mesh;
142 }
143
144 /* make sure we're using an existing layer */
145 CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname);
146
147 /* calculate a projection matrix and normal for each projector */
148 for (i = 0; i < num_projectors; i++) {
149 float tmpmat[4][4];
150 float offsetmat[4][4];
151 Camera *cam = NULL;
152 /* calculate projection matrix */
153 invert_m4_m4(projectors[i].projmat, projectors[i].ob->obmat);
154
155 projectors[i].uci = NULL;
156
157 if (projectors[i].ob->type == OB_CAMERA) {
158 cam = (Camera *)projectors[i].ob->data;
159 if (cam->type == CAM_PANO) {
160 projectors[i].uci = BLI_uvproject_camera_info(projectors[i].ob, NULL, aspx, aspy);
161 BLI_uvproject_camera_info_scale(projectors[i].uci, scax, scay);
162 free_uci = 1;
163 }
164 else {
165 CameraParams params;
166
167 /* setup parameters */
168 BKE_camera_params_init(¶ms);
169 BKE_camera_params_from_object(¶ms, projectors[i].ob);
170
171 /* compute matrix, viewplane, .. */
172 BKE_camera_params_compute_viewplane(¶ms, 1, 1, aspx, aspy);
173
174 /* scale the view-plane */
175 params.viewplane.xmin *= scax;
176 params.viewplane.xmax *= scax;
177 params.viewplane.ymin *= scay;
178 params.viewplane.ymax *= scay;
179
180 BKE_camera_params_compute_matrix(¶ms);
181 mul_m4_m4m4(tmpmat, params.winmat, projectors[i].projmat);
182 }
183 }
184 else {
185 copy_m4_m4(tmpmat, projectors[i].projmat);
186 }
187
188 unit_m4(offsetmat);
189 mul_mat3_m4_fl(offsetmat, 0.5);
190 offsetmat[3][0] = offsetmat[3][1] = offsetmat[3][2] = 0.5;
191
192 mul_m4_m4m4(projectors[i].projmat, offsetmat, tmpmat);
193
194 /* calculate worldspace projector normal (for best projector test) */
195 projectors[i].normal[0] = 0;
196 projectors[i].normal[1] = 0;
197 projectors[i].normal[2] = 1;
198 mul_mat3_m4_v3(projectors[i].ob->obmat, projectors[i].normal);
199 }
200
201 numPolys = mesh->totpoly;
202 numLoops = mesh->totloop;
203
204 /* make sure we are not modifying the original UV map */
205 mloop_uv = CustomData_duplicate_referenced_layer_named(
206 &mesh->ldata, CD_MLOOPUV, uvname, numLoops);
207
208 coords = BKE_mesh_vert_coords_alloc(mesh, &numVerts);
209
210 /* convert coords to world space */
211 for (i = 0, co = coords; i < numVerts; i++, co++) {
212 mul_m4_v3(ob->obmat, *co);
213 }
214
215 /* if only one projector, project coords to UVs */
216 if (num_projectors == 1 && projectors[0].uci == NULL) {
217 for (i = 0, co = coords; i < numVerts; i++, co++) {
218 mul_project_m4_v3(projectors[0].projmat, *co);
219 }
220 }
221
222 mpoly = mesh->mpoly;
223 mloop = mesh->mloop;
224
225 /* apply coords as UVs */
226 for (i = 0, mp = mpoly; i < numPolys; i++, mp++) {
227 if (num_projectors == 1) {
228 if (projectors[0].uci) {
229 uint fidx = mp->totloop - 1;
230 do {
231 uint lidx = mp->loopstart + fidx;
232 uint vidx = mloop[lidx].v;
233 BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], projectors[0].uci);
234 } while (fidx--);
235 }
236 else {
237 /* apply transformed coords as UVs */
238 uint fidx = mp->totloop - 1;
239 do {
240 uint lidx = mp->loopstart + fidx;
241 uint vidx = mloop[lidx].v;
242 copy_v2_v2(mloop_uv[lidx].uv, coords[vidx]);
243 } while (fidx--);
244 }
245 }
246 else {
247 /* multiple projectors, select the closest to face normal direction */
248 float face_no[3];
249 int j;
250 Projector *best_projector;
251 float best_dot;
252
253 /* get the untransformed face normal */
254 BKE_mesh_calc_poly_normal_coords(
255 mp, mloop + mp->loopstart, (const float(*)[3])coords, face_no);
256
257 /* find the projector which the face points at most directly
258 * (projector normal with largest dot product is best)
259 */
260 best_dot = dot_v3v3(projectors[0].normal, face_no);
261 best_projector = &projectors[0];
262
263 for (j = 1; j < num_projectors; j++) {
264 float tmp_dot = dot_v3v3(projectors[j].normal, face_no);
265 if (tmp_dot > best_dot) {
266 best_dot = tmp_dot;
267 best_projector = &projectors[j];
268 }
269 }
270
271 if (best_projector->uci) {
272 uint fidx = mp->totloop - 1;
273 do {
274 uint lidx = mp->loopstart + fidx;
275 uint vidx = mloop[lidx].v;
276 BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], best_projector->uci);
277 } while (fidx--);
278 }
279 else {
280 uint fidx = mp->totloop - 1;
281 do {
282 uint lidx = mp->loopstart + fidx;
283 uint vidx = mloop[lidx].v;
284 mul_v2_project_m4_v3(mloop_uv[lidx].uv, best_projector->projmat, coords[vidx]);
285 } while (fidx--);
286 }
287 }
288 }
289
290 MEM_freeN(coords);
291
292 if (free_uci) {
293 int j;
294 for (j = 0; j < num_projectors; j++) {
295 if (projectors[j].uci) {
296 MEM_freeN(projectors[j].uci);
297 }
298 }
299 }
300
301 mesh->runtime.is_original = false;
302
303 /* Mark tessellated CD layers as dirty. */
304 mesh->runtime.cd_dirty_vert |= CD_MASK_TESSLOOPNORMAL;
305
306 return mesh;
307 }
308
modifyMesh(ModifierData * md,const ModifierEvalContext * ctx,Mesh * mesh)309 static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
310 {
311 Mesh *result;
312 UVProjectModifierData *umd = (UVProjectModifierData *)md;
313
314 result = uvprojectModifier_do(umd, ctx, ctx->object, mesh);
315
316 return result;
317 }
318
panel_draw(const bContext * UNUSED (C),Panel * panel)319 static void panel_draw(const bContext *UNUSED(C), Panel *panel)
320 {
321 uiLayout *sub;
322 uiLayout *layout = panel->layout;
323
324 PointerRNA ob_ptr;
325 PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
326
327 PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
328
329 uiLayoutSetPropSep(layout, true);
330
331 uiItemPointerR(layout, ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
332
333 sub = uiLayoutColumn(layout, true);
334 uiItemR(sub, ptr, "aspect_x", 0, IFACE_("Aspect X"), ICON_NONE);
335 uiItemR(sub, ptr, "aspect_y", 0, IFACE_("Y"), ICON_NONE);
336
337 sub = uiLayoutColumn(layout, true);
338 uiItemR(sub, ptr, "scale_x", 0, IFACE_("Scale X"), ICON_NONE);
339 uiItemR(sub, ptr, "scale_y", 0, IFACE_("Y"), ICON_NONE);
340
341 uiItemR(layout, ptr, "projector_count", 0, IFACE_("Projectors"), ICON_NONE);
342 RNA_BEGIN (ptr, projector_ptr, "projectors") {
343 uiItemR(layout, &projector_ptr, "object", 0, NULL, ICON_NONE);
344 }
345 RNA_END;
346
347 modifier_panel_end(layout, ptr);
348 }
349
panelRegister(ARegionType * region_type)350 static void panelRegister(ARegionType *region_type)
351 {
352 modifier_panel_register(region_type, eModifierType_UVProject, panel_draw);
353 }
354
355 ModifierTypeInfo modifierType_UVProject = {
356 /* name */ "UVProject",
357 /* structName */ "UVProjectModifierData",
358 /* structSize */ sizeof(UVProjectModifierData),
359 /* srna */ &RNA_UVProjectModifier,
360 /* type */ eModifierTypeType_NonGeometrical,
361 /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
362 eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode,
363 /* icon */ ICON_MOD_UVPROJECT,
364
365 /* copyData */ BKE_modifier_copydata_generic,
366
367 /* deformVerts */ NULL,
368 /* deformMatrices */ NULL,
369 /* deformVertsEM */ NULL,
370 /* deformMatricesEM */ NULL,
371 /* modifyMesh */ modifyMesh,
372 /* modifyHair */ NULL,
373 /* modifyPointCloud */ NULL,
374 /* modifyVolume */ NULL,
375
376 /* initData */ initData,
377 /* requiredDataMask */ requiredDataMask,
378 /* freeData */ NULL,
379 /* isDisabled */ NULL,
380 /* updateDepsgraph */ updateDepsgraph,
381 /* dependsOnTime */ NULL,
382 /* dependsOnNormals */ NULL,
383 /* foreachIDLink */ foreachIDLink,
384 /* foreachTexLink */ NULL,
385 /* freeRuntimeData */ NULL,
386 /* panelRegister */ panelRegister,
387 /* blendWrite */ NULL,
388 /* blendRead */ NULL,
389 };
390