Blender V5.0
object_bake.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstring>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_mesh_types.h"
14#include "DNA_object_types.h"
15#include "DNA_scene_types.h"
16#include "DNA_screen_types.h"
17
18#include "BLI_listbase.h"
19#include "BLI_span.hh"
20#include "BLI_utildefines.h"
21#include "BLI_vector.hh"
22
23#include "BKE_attribute.hh"
24#include "BKE_context.hh"
25#include "BKE_customdata.hh"
26#include "BKE_global.hh"
27#include "BKE_image.hh"
28#include "BKE_modifier.hh"
29#include "BKE_multires.hh"
30#include "BKE_report.hh"
31#include "BKE_scene.hh"
32#include "BKE_subdiv.hh"
33
34#include "RE_multires_bake.h"
35#include "RE_pipeline.h"
36
37#include "IMB_imbuf.hh"
38#include "IMB_imbuf_types.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "ED_screen.hh"
44#include "ED_uvedit.hh"
45
46#include "object_intern.hh"
47
48namespace blender::ed::object {
49
50static Image *bake_object_image_get(Object &object, const int mat_nr)
51{
52 Image *image = nullptr;
53 ED_object_get_active_image(&object, mat_nr + 1, &image, nullptr, nullptr, nullptr);
54 return image;
55}
56
58{
59 Vector<Image *> images;
60 images.reserve(object.totcol);
61 for (int i = 0; i < object.totcol; i++) {
62 images.append(bake_object_image_get(object, i));
63 }
64 return images;
65}
66
67/* ****************** multires BAKING ********************** */
68
69/* holder of per-object data needed for bake job
70 * needed to make job totally thread-safe */
72 MultiresBakerJobData *next = nullptr, *prev = nullptr;
73
74 /* Material aligned image array (for per-face bake image). */
76
77 /* Base mesh at the input of the multiresolution modifier. */
78 Mesh *base_mesh = nullptr;
79
80 /* Multi-resolution modifier which is being baked. */
82
84};
85
86/* data passing to multires-baker job */
102
104{
105 Scene *scene = CTX_data_scene(C);
106 Object *ob;
107 Mesh *mesh;
109 bool ok = true;
110 int a;
111
112 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
113 ob = base->object;
114
115 if (ob->type != OB_MESH) {
117 op->reports, RPT_ERROR, "Baking of multires data only works with an active mesh object");
118
119 ok = false;
120 break;
121 }
122
123 mesh = (Mesh *)ob->data;
124 mmd = get_multires_modifier(scene, ob, false);
125
126 /* Multi-resolution should be and be last in the stack */
127 if (ok && mmd) {
128 ModifierData *md;
129
130 ok = mmd->totlvl > 0;
131
132 for (md = (ModifierData *)mmd->modifier.next; md && ok; md = md->next) {
134 ok = false;
135 }
136 }
137 }
138 else {
139 ok = false;
140 }
141
142 if (!ok) {
143 BKE_report(op->reports, RPT_ERROR, "Multires data baking requires multi-resolution object");
144
145 break;
146 }
147
148 if (!CustomData_has_layer(&mesh->corner_data, CD_PROP_FLOAT2)) {
149 BKE_report(op->reports, RPT_ERROR, "Mesh should be unwrapped before multires data baking");
150
151 ok = false;
152 }
153 else {
154 const bke::AttributeAccessor attributes = mesh->attributes();
155 const VArraySpan material_indices = *attributes.lookup<int>("material_index",
157 a = mesh->faces_num;
158 while (ok && a--) {
159 Image *ima = bake_object_image_get(*ob,
160 material_indices.is_empty() ? 0 : material_indices[a]);
161
162 if (!ima) {
164 op->reports, RPT_ERROR, "You should have active texture to use multires baker");
165
166 ok = false;
167 }
168 else {
170 ImageUser iuser;
171 BKE_imageuser_default(&iuser);
172 iuser.tile = tile->tile_number;
173
174 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
175
176 if (!ibuf) {
178 op->reports, RPT_ERROR, "Baking should happen to image with image buffer");
179
180 ok = false;
181 }
182 else {
183 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.data == nullptr) {
184 ok = false;
185 }
186
187 if (ibuf->float_buffer.data && !ELEM(ibuf->channels, 0, 4)) {
188 ok = false;
189 }
190
191 if (!ok) {
192 BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
193 }
194 }
195
196 BKE_image_release_ibuf(ima, ibuf, nullptr);
197 }
198 }
199 }
200 }
201
202 if (!ok) {
203 break;
204 }
205 }
207
208 return ok;
209}
210
215
217{
218 const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f};
219 const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f};
220 const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
221 const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
222 const float disp_alpha[4] = {0.5f, 0.5f, 0.5f, 0.0f};
223 const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
224
225 if ((image->id.tag & ID_TAG_DOIT) == 0) {
226 LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
227 ImageUser iuser;
228 BKE_imageuser_default(&iuser);
229 iuser.tile = tile->tile_number;
230
231 ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
232
233 if (flag == CLEAR_TANGENT_NORMAL) {
234 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
235 }
236 else if (flag == CLEAR_DISPLACEMENT) {
237 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
238 }
239 else {
240 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
241 }
242
243 image->id.tag |= ID_TAG_DOIT;
244
245 BKE_image_release_ibuf(image, ibuf, nullptr);
246 }
247 }
248}
249
250static void clear_images_poly(const Span<Image *> ob_image_array, const ClearFlag flag)
251{
252 for (Image *image : ob_image_array) {
253 if (image) {
254 image->id.tag &= ~ID_TAG_DOIT;
255 }
256 }
257
258 for (Image *image : ob_image_array) {
259 if (image) {
260 clear_single_image(image, flag);
261 }
262 }
263
264 for (Image *image : ob_image_array) {
265 if (image) {
266 image->id.tag &= ~ID_TAG_DOIT;
267 }
268 }
269}
270
272{
273 Scene *scene = CTX_data_scene(C);
274 int objects_baked = 0;
275
276 if (!multiresbake_check(C, op)) {
277 return OPERATOR_CANCELLED;
278 }
279
280 if (scene->r.bake.flag & R_BAKE_CLEAR) { /* clear images */
281 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
282 Object &object = *base->object;
283 BLI_assert(object.type == OB_MESH);
284
285 ClearFlag clear_flag = ClearFlag(0);
286
287 if (scene->r.bake.type == R_BAKE_NORMALS) {
288 clear_flag = CLEAR_TANGENT_NORMAL;
289 }
290 else if (scene->r.bake.type == R_BAKE_DISPLACEMENT) {
291 clear_flag = CLEAR_DISPLACEMENT;
292 }
293
294 {
295 const Vector<Image *> ob_image_array = bake_object_image_get_array(object);
296 clear_images_poly(ob_image_array, clear_flag);
297 }
298 }
300 }
301
302 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
303 Object &object = *base->object;
304 BLI_assert(object.type == OB_MESH);
305
307
309
310 /* Copy data stored in job descriptor. */
311 bake.bake_margin = scene->r.bake.margin;
312 if (scene->r.bake.type == R_BAKE_NORMALS) {
313 bake.bake_margin_type = R_BAKE_EXTEND;
314 }
315 else {
316 bake.bake_margin_type = eBakeMarginType(scene->r.bake.margin_type);
317 }
318 bake.type = eBakeType(scene->r.bake.type);
319 bake.displacement_space = eBakeSpace(scene->r.bake.displacement_space);
320 bake.use_low_resolution_mesh = scene->r.bake.flag & R_BAKE_LORES_MESH;
321
322 bake.ob_image = bake_object_image_get_array(object);
323
324 bake.base_mesh = static_cast<Mesh *>(object.data);
325 bake.multires_modifier = get_multires_modifier(scene, &object, false);
326
328
329 objects_baked++;
330 }
332
333 if (!objects_baked) {
334 BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
335 }
336
337 return OPERATOR_FINISHED;
338}
339
344{
345 Scene *scene = CTX_data_scene(C);
346
347 /* backup scene settings, so their changing in UI would take no effect on baker */
348 bkj->scene = scene;
349 bkj->bake_margin = scene->r.bake.margin;
350 if (scene->r.bake.type == R_BAKE_NORMALS) {
352 }
353 else {
355 }
356 bkj->type = eBakeType(scene->r.bake.type);
359 bkj->bake_clear = scene->r.bake.flag & R_BAKE_CLEAR;
360
361 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
362 Object &object = *base->object;
363 BLI_assert(object.type == OB_MESH);
364
366
367 MultiresBakerJobData *data = MEM_new<MultiresBakerJobData>(__func__);
368
369 data->ob_image = bake_object_image_get_array(object);
370
371 data->base_mesh = static_cast<Mesh *>(object.data);
372 data->multires_modifier = get_multires_modifier(scene, &object, false);
373
374 BLI_addtail(&bkj->data, data);
375 }
377}
378
379static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
380{
381 MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
382 int baked_objects = 0, tot_obj;
383
384 tot_obj = BLI_listbase_count(&bkj->data);
385
386 if (bkj->bake_clear) { /* clear images */
388 ClearFlag clear_flag = ClearFlag(0);
389
390 if (bkj->type == R_BAKE_NORMALS) {
391 clear_flag = CLEAR_TANGENT_NORMAL;
392 }
393 else if (bkj->type == R_BAKE_DISPLACEMENT) {
394 clear_flag = CLEAR_DISPLACEMENT;
395 }
396
397 clear_images_poly(data->ob_image, clear_flag);
398 }
399 }
400
403
404 /* copy data stored in job descriptor */
405 bake.bake_margin = bkj->bake_margin;
406 bake.bake_margin_type = eBakeMarginType(bkj->bake_margin_type);
407 bake.type = bkj->type;
408 bake.displacement_space = bkj->displacement_space;
409 bake.use_low_resolution_mesh = bkj->use_low_resolution_mesh;
410 bake.ob_image = data->ob_image;
411
412 bake.base_mesh = data->base_mesh;
413 bake.multires_modifier = data->multires_modifier;
414
415 /* needed for proper progress bar */
416 bake.num_total_objects = tot_obj;
417 bake.num_baked_objects = baked_objects;
418
419 bake.stop = &worker_status->stop;
420 bake.do_update = &worker_status->do_update;
421 bake.progress = &worker_status->progress;
422
424
425 data->images = bake.images;
426
427 baked_objects++;
428 }
429}
430
431static void multiresbake_freejob(void *bkv)
432{
433 MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
435
436 data = static_cast<MultiresBakerJobData *>(bkj->data.first);
437 while (data) {
438 next = data->next;
439
440 /* delete here, since this delete will be called from main thread */
441 for (Image *image : data->images) {
443 }
444
445 MEM_delete(data);
446 data = next;
447 }
448
449 MEM_freeN(bkj);
450}
451
453{
454 Scene *scene = CTX_data_scene(C);
455
456 if (!multiresbake_check(C, op)) {
457 return OPERATOR_CANCELLED;
458 }
459
462
463 if (!bkr->data.first) {
464 BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
465 MEM_freeN(bkr);
466 return OPERATOR_CANCELLED;
467 }
468
469 /* setup job */
472 scene,
473 "Baking Multires...",
477 WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO: only draw bake image, can we enforce this. */
478 WM_jobs_callbacks(wm_job, multiresbake_startjob, nullptr, nullptr, nullptr);
479
480 G.is_break = false;
481
483 WM_cursor_wait(false);
484
485 /* add modal handler for ESC */
487
489}
490
491/* ****************** render BAKING ********************** */
492
495 wmOperator * /*op*/,
496 const wmEvent *event)
497{
498 /* no running blender, remove handler and pass through */
501 }
502
503 /* running render */
504 switch (event->type) {
505 case EVT_ESCKEY:
507 default: {
508 break;
509 }
510 }
512}
513
514static bool is_multires_bake(Scene *scene)
515{
516 if (ELEM(scene->r.bake.type,
520 R_BAKE_AO))
521 {
522 return scene->r.bake.flag & R_BAKE_MULTIRES;
523 }
524
525 return false;
526}
527
529 wmOperator *op,
530 const wmEvent * /*event*/)
531{
532 Scene *scene = CTX_data_scene(C);
534
536
538
539 return result;
540}
541
543{
544 Scene *scene = CTX_data_scene(C);
546
547 if (!is_multires_bake(scene)) {
548 BLI_assert(0);
549 return result;
550 }
551
553
555
556 return result;
557}
558
560{
561 /* identifiers */
562 ot->name = "Bake";
563 ot->description = "Bake image textures of selected objects";
564 ot->idname = "OBJECT_OT_bake_image";
565
566 /* API callbacks. */
567 ot->exec = bake_image_exec;
571}
572
573} // namespace blender::ed::object
#define CTX_DATA_BEGIN(C, Type, instance, member)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define CTX_DATA_END
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_imageuser_default(ImageUser *iuser)
void BKE_image_partial_update_mark_full_update(Image *image)
Mark the whole image to be updated.
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
MultiresModifierData * get_multires_modifier(Scene *scene, Object *ob, bool use_first)
Definition multires.cc:178
void multires_flush_sculpt_updates(Object *object)
Definition multires.cc:270
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define ELEM(...)
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
@ CD_PROP_FLOAT2
@ eModifierMode_Realtime
Object is a sort of wrapper for general info.
@ OB_MESH
eBakeType
@ R_BAKE_NORMALS
@ R_BAKE_AO
@ R_BAKE_DISPLACEMENT
@ R_BAKE_VECTOR_DISPLACEMENT
eBakeSpace
eBakeMarginType
@ R_BAKE_EXTEND
@ R_IMF_PLANES_RGBA
@ R_BAKE_LORES_MESH
@ R_BAKE_CLEAR
@ R_BAKE_MULTIRES
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_operator_object_active(bContext *C)
bool ED_object_get_active_image(Object *ob, int mat_nr, Image **r_ima, ImageUser **r_iuser, const bNode **r_node, const bNodeTree **r_ntree)
void IMB_rectfill(ImBuf *drect, const float col[4])
Definition rectop.cc:978
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_OBJECT_BAKE_TEXTURE
Definition WM_api.hh:1784
@ WM_JOB_EXCL_RENDER
Definition WM_api.hh:1765
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
@ WM_JOB_PRIORITY
Definition WM_api.hh:1760
#define ND_RENDER_RESULT
Definition WM_types.hh:446
#define NC_SCENE
Definition WM_types.hh:378
#define NC_IMAGE
Definition WM_types.hh:384
BMesh const char void * data
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
void reserve(const int64_t min_capacity)
GAttributeReader lookup(const StringRef attribute_id) const
const ccl_global KernelWorkTile * tile
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
#define G(x, y, z)
void RE_multires_bake_images(MultiresBakeRender &bake)
static wmOperatorStatus objects_bake_render_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus multiresbake_image_exec(bContext *C, wmOperator *op)
static Image * bake_object_image_get(Object &object, const int mat_nr)
static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *op)
void OBJECT_OT_bake_image(wmOperatorType *ot)
static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
static Vector< Image * > bake_object_image_get_array(Object &object)
static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
static void clear_images_poly(const Span< Image * > ob_image_array, const ClearFlag flag)
static bool is_multires_bake(Scene *scene)
static void multiresbake_freejob(void *bkv)
static void clear_single_image(Image *image, ClearFlag flag)
static wmOperatorStatus bake_image_exec(bContext *C, wmOperator *op)
static bool multiresbake_check(bContext *C, wmOperator *op)
static wmOperatorStatus objects_bake_render_modal(bContext *C, wmOperator *, const wmEvent *event)
char displacement_space
int tag
Definition DNA_ID.h:442
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
ListBase tiles
void * first
struct ModifierData * next
struct BakeData bake
struct RenderData r
wmEventType type
Definition WM_types.hh:757
struct ReportList * reports
i
Definition text_draw.cc:230
void WM_cursor_wait(bool val)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:211
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:388
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:247
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360
uint8_t flag
Definition wm_window.cc:145