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(&params);
169         BKE_camera_params_from_object(&params, projectors[i].ob);
170 
171         /* compute matrix, viewplane, .. */
172         BKE_camera_params_compute_viewplane(&params, 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(&params);
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