Blender V5.0
usd_capi_import.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "IO_types.hh"
6#include "usd.hh"
7#include "usd_hook.hh"
9#include "usd_reader_geom.hh"
10#include "usd_reader_prim.hh"
11#include "usd_reader_stage.hh"
12
13#include "BKE_cachefile.hh"
14#include "BKE_collection.hh"
15#include "BKE_context.hh"
16#include "BKE_global.hh"
17#include "BKE_layer.hh"
18#include "BKE_lib_id.hh"
19#include "BKE_main.hh"
20#include "BKE_object.hh"
21#include "BKE_report.hh"
22
23#include "BLI_listbase.h"
24#include "BLI_math_matrix.h"
25#include "BLI_path_utils.hh"
26#include "BLI_string.h"
27#include "BLI_timeit.hh"
28
29#include "BLT_translation.hh"
30
31#include "DEG_depsgraph.hh"
33
34#include "DNA_cachefile_types.h"
36#include "DNA_layer_types.h"
37#include "DNA_listBase.h"
38#include "DNA_object_types.h"
39#include "DNA_scene_types.h"
41
42#include "ED_undo.hh"
43
44#include "MEM_guardedalloc.h"
45
46#include "RNA_access.hh"
47
48#include "WM_api.hh"
49#include "WM_types.hh"
50
51#include <pxr/usd/usd/stage.h>
52#include <pxr/usd/usdGeom/metrics.h>
53
54#include <fmt/core.h>
55
56namespace blender::io::usd {
57
59{
60 return reinterpret_cast<CacheArchiveHandle *>(reader);
61}
62
64{
65 return reinterpret_cast<USDStageReader *>(handle);
66}
67
68static bool gather_objects_paths(const pxr::UsdPrim &object, ListBase *object_paths)
69{
70 if (!object.IsValid()) {
71 return false;
72 }
73
74 for (const pxr::UsdPrim &childPrim : object.GetChildren()) {
75 gather_objects_paths(childPrim, object_paths);
76 }
77
78 CacheObjectPath *usd_path = MEM_callocN<CacheObjectPath>("CacheObjectPath");
79
80 STRNCPY(usd_path->path, object.GetPrimPath().GetString().c_str());
81 BLI_addtail(object_paths, usd_path);
82
83 return true;
84}
85
86enum {
89};
90
115
117{
118 timeit::Nanoseconds duration = timeit::Clock::now() - data->start_time;
119 fmt::print("USD import of '{}' took ", data->filepath);
120 timeit::print_duration(duration);
121 fmt::print("\n");
122}
123
124static void import_startjob(void *customdata, wmJobWorkerStatus *worker_status)
125{
126 ImportJobData *data = static_cast<ImportJobData *>(customdata);
127
128 data->stop = &worker_status->stop;
129 data->do_update = &worker_status->do_update;
130 data->progress = &worker_status->progress;
131 data->was_canceled = false;
132 data->archive = nullptr;
133 data->start_time = timeit::Clock::now();
134 data->cache_file = nullptr;
135
136 data->params.worker_status = worker_status;
137
138 WM_locked_interface_set(data->wm, true);
139 G.is_break = false;
140
141 if (data->params.create_collection) {
142 char display_name[MAX_ID_NAME - 2];
144 display_name, sizeof(display_name), BLI_path_basename(data->filepath));
145 Collection *import_collection = BKE_collection_add(
146 data->bmain, data->scene->master_collection, display_name);
147
148 DEG_id_tag_update(&import_collection->id, ID_RECALC_SYNC_TO_EVAL);
150
151 BKE_view_layer_synced_ensure(data->scene, data->view_layer);
152 data->view_layer->active_collection = BKE_layer_collection_first_from_scene_collection(
153 data->view_layer, import_collection);
154 }
155
157
158 *data->do_update = true;
159 *data->progress = 0.05f;
160
161 if (G.is_break) {
162 data->was_canceled = true;
163 return;
164 }
165
166 *data->do_update = true;
167 *data->progress = 0.1f;
168
169 pxr::UsdStagePopulationMask pop_mask;
170 for (const std::string &mask_token : pxr::TfStringTokenize(data->params.prim_path_mask, ",;")) {
171 pxr::SdfPath prim_path(mask_token);
172 if (!prim_path.IsEmpty()) {
173 pop_mask.Add(prim_path);
174 }
175 }
176
177 pxr::UsdStageRefPtr stage = pop_mask.IsEmpty() ?
178 pxr::UsdStage::Open(data->filepath) :
179 pxr::UsdStage::OpenMasked(data->filepath, pop_mask);
180
181 if (!stage) {
182 BKE_reportf(worker_status->reports,
183 RPT_ERROR,
184 "USD Import: unable to open stage to read %s",
185 data->filepath);
186 data->import_ok = false;
187 data->error_code = USD_ARCHIVE_FAIL;
188 return;
189 }
190
191 double scene_scale = data->params.scale;
192 if (data->params.apply_unit_conversion_scale) {
193 scene_scale *= pxr::UsdGeomGetStageMetersPerUnit(stage);
194 }
195
196 /* Set up the stage for animated data. */
197 if (data->params.set_frame_range) {
198 data->scene->r.sfra = stage->GetStartTimeCode();
199 data->scene->r.efra = stage->GetEndTimeCode();
200 }
201
202 *data->do_update = true;
203 *data->progress = 0.15f;
204
205 /* Callback function to lazily create a cache file when converting
206 * time varying data. */
207 auto get_cache_file = [data, scene_scale]() {
208 if (!data->cache_file) {
209 data->cache_file = static_cast<CacheFile *>(
210 BKE_cachefile_add(data->bmain, BLI_path_basename(data->filepath)));
211
212 /* Decrement the ID ref-count because it is going to be incremented for each
213 * modifier and constraint that it will be attached to, so since currently
214 * it is not used by anyone, its use count will off by one. */
215 id_us_min(&data->cache_file->id);
216
217 data->cache_file->is_sequence = data->params.is_sequence;
218 data->cache_file->scale = scene_scale;
219 STRNCPY(data->cache_file->filepath, data->filepath);
220 if (data->params.relative_path && !BLI_path_is_rel(data->cache_file->filepath)) {
221 BLI_path_rel(data->cache_file->filepath, BKE_main_blendfile_path_from_global());
222 }
223 }
224 return data->cache_file;
225 };
226
227 USDStageReader *archive = new USDStageReader(stage, data->params, get_cache_file);
228
229 /* Ensure Python types for invoking hooks are registered. */
231
233
234 data->archive = archive;
235
236 archive->collect_readers();
237
238 if (data->params.import_lights && data->params.create_world_material &&
239 !archive->dome_light_readers().is_empty())
240 {
241 USDDomeLightReader *dome_light_reader = archive->dome_light_readers().first();
242 dome_light_reader->create_object(data->scene, data->bmain);
243 }
244
245 if (data->params.import_materials && data->params.import_all_materials) {
246 archive->import_all_materials(data->bmain);
247 }
248
249 *data->do_update = true;
250 *data->progress = 0.2f;
251
252 const float size = float(archive->readers().size());
253 size_t i = 0;
254
255 /* Sort readers by name: when creating a lot of objects in Blender,
256 * it is much faster if the order is sorted by name. */
257 archive->sort_readers();
258 *data->do_update = true;
259 *data->progress = 0.25f;
260
261 /* Create blender objects. */
262 for (USDPrimReader *reader : archive->readers()) {
263 if (!reader) {
264 continue;
265 }
266 reader->create_object(data->bmain);
267 if ((++i & 1023) == 0) {
268 *data->do_update = true;
269 *data->progress = 0.25f + 0.25f * (i / size);
270 }
271 }
272
273 /* Setup parenthood and read actual object data. */
274 i = 0;
275 for (USDPrimReader *reader : archive->readers()) {
276 if (!reader) {
277 continue;
278 }
279
280 Object *ob = reader->object();
281 reader->read_object_data(data->bmain, 0.0);
282
283 USDPrimReader *parent = reader->parent();
284 if (parent == nullptr) {
285 ob->parent = nullptr;
286 }
287 else {
288 ob->parent = parent->object();
289 }
290
291 *data->progress = 0.5f + 0.5f * (++i / size);
292 *data->do_update = true;
293
294 if (G.is_break) {
295 data->was_canceled = true;
296 return;
297 }
298 }
299
300 if (data->params.import_skeletons) {
302 }
303
304 data->import_ok = !data->was_canceled;
305
306 worker_status->progress = 1.0f;
307 worker_status->do_update = true;
308}
309
310static void import_endjob(void *customdata)
311{
312 ImportJobData *data = static_cast<ImportJobData *>(customdata);
313
314 /* Delete objects on cancellation. */
315 if (data->was_canceled && data->archive) {
316
317 for (const USDPrimReader *reader : data->archive->readers()) {
318
319 if (!reader) {
320 continue;
321 }
322
323 /* It's possible that cancellation occurred between the creation of
324 * the reader and the creation of the Blender object. */
325 if (Object *ob = reader->object()) {
326 BKE_id_free_us(data->bmain, ob);
327 }
328 }
329 }
330 else if (data->archive) {
331 Base *base;
332 LayerCollection *lc;
333 const Scene *scene = data->scene;
334 ViewLayer *view_layer = data->view_layer;
335
336 BKE_view_layer_base_deselect_all(scene, view_layer);
337
338 lc = BKE_layer_collection_get_active(view_layer);
339
340 /* Create prototype collections for instancing. */
341 data->archive->create_proto_collections(data->bmain, lc->collection);
342
343 /* Add all objects to the collection. */
344 for (const USDPrimReader *reader : data->archive->readers()) {
345 if (!reader) {
346 continue;
347 }
348 if (reader->is_in_proto()) {
349 /* Skip prototype prims, as these are added to prototype collections. */
350 continue;
351 }
352 Object *ob = reader->object();
353 if (!ob) {
354 continue;
355 }
357 }
358
359 /* Sync and do the view layer operations. */
360 BKE_view_layer_synced_ensure(scene, view_layer);
361 for (const USDPrimReader *reader : data->archive->readers()) {
362 if (!reader) {
363 continue;
364 }
365
366 Object *ob = reader->object();
367 if (!ob) {
368 continue;
369 }
370 base = BKE_view_layer_base_find(view_layer, ob);
371 /* TODO: is setting active needed? */
373
376 &ob->id,
379 }
380
383
384 if (data->params.import_materials && data->params.import_all_materials) {
385 data->archive->fake_users_for_unused_materials();
386 }
387
388 data->archive->call_material_import_hooks(data->bmain);
389
390 call_import_hooks(data->archive, data->params.worker_status->reports);
391
392 if (data->is_background_job) {
393 /* Blender already returned from the import operator, so we need to store our own extra undo
394 * step. */
395 ED_undo_push(data->C, "USD Import Finished");
396 }
397 }
398
399 WM_locked_interface_set(data->wm, false);
400
401 switch (data->error_code) {
402 default:
403 case USD_NO_ERROR:
404 data->import_ok = !data->was_canceled;
405 break;
406 case USD_ARCHIVE_FAIL:
407 BKE_report(data->params.worker_status->reports,
408 RPT_ERROR,
409 "Could not open USD archive for reading, see console for detail");
410 break;
411 }
412
415}
416
417static void import_freejob(void *user_data)
418{
419 ImportJobData *data = static_cast<ImportJobData *>(user_data);
420
421 delete data->archive;
422 delete data;
423}
424
426 const char *filepath,
427 const USDImportParams *params,
428 bool as_background_job,
429 ReportList *reports)
430{
431 /* Using new here since `MEM_*` functions do not call constructor to properly initialize data. */
432 ImportJobData *job = new ImportJobData();
433 job->C = const_cast<bContext *>(C);
434 job->bmain = CTX_data_main(C);
435 job->scene = CTX_data_scene(C);
437 job->wm = CTX_wm_manager(C);
438 job->import_ok = false;
439 job->is_background_job = as_background_job;
440 STRNCPY(job->filepath, filepath);
441
443 job->was_canceled = false;
444 job->archive = nullptr;
445
446 job->params = *params;
447
448 G.is_break = false;
449
450 bool import_ok = false;
451 if (as_background_job) {
454 job->scene,
455 "Importing USD...",
458
459 /* setup job */
461 WM_jobs_timer(wm_job, 0.1, NC_SCENE, NC_SCENE);
462 WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob);
463
465 }
466 else {
467 wmJobWorkerStatus worker_status = {};
468 /* Use the operator's reports in non-background case. */
469 worker_status.reports = reports;
470
471 import_startjob(job, &worker_status);
472 import_endjob(job);
473 import_ok = job->import_ok;
474
475 import_freejob(job);
476 }
477
478 return import_ok;
479}
480
481/* TODO(makowalski): Extend this function with basic validation that the
482 * USD reader is compatible with the type of the given (currently unused) 'ob'
483 * Object parameter, similar to the logic in get_abc_reader() in the
484 * Alembic importer code. */
486 const Object * /*ob*/,
487 const char **r_err_str)
488{
489 USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader);
490 pxr::UsdPrim iobject = usd_reader->prim();
491
492 if (!iobject.IsValid()) {
493 *r_err_str = RPT_("Invalid object: verify object path");
494 return nullptr;
495 }
496
497 return usd_reader;
498}
499
500USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
501{
503 params.motion_sample_time = motion_sample_time;
504 params.read_flags = read_flags;
505 return params;
506}
507
509 const Object *ob,
510 blender::bke::GeometrySet &geometry_set,
512 const char **r_err_str)
513{
514 USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, r_err_str));
515
516 if (usd_reader == nullptr) {
517 return;
518 }
519
520 usd_reader->read_geometry(geometry_set, params, r_err_str);
521}
522
524 const Object *ob,
525 const Mesh *existing_mesh,
526 const double time,
527 const char **r_err_str)
528{
529 USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, r_err_str));
530
531 if (usd_reader == nullptr) {
532 return false;
533 }
534
535 return usd_reader->topology_changed(existing_mesh, time);
536}
537
539 CacheReader *reader,
540 Object *object,
541 const char *object_path)
542{
543 if (object_path[0] == '\0') {
544 return reader;
545 }
546
547 USDStageReader *archive = stage_reader_from_handle(handle);
548
549 if (!archive || !archive->valid()) {
550 return reader;
551 }
552
553 if (reader) {
554 USD_CacheReader_free(reader);
555 }
556
557 pxr::UsdPrim prim = archive->stage()->GetPrimAtPath(pxr::SdfPath(object_path));
558
559 if (!prim) {
560 return nullptr;
561 }
562
563 /* TODO(makowalski): The handle does not have the proper import params or settings. */
564 USDPrimReader *usd_reader = archive->create_reader(prim);
565
566 if (usd_reader == nullptr) {
567 /* This object is not supported. */
568 return nullptr;
569 }
570 if (!usd_reader->valid()) {
571 /* This object is invalid for some reason. */
572 return nullptr;
573 }
574 usd_reader->object(object);
575 usd_reader->incref();
576
577 return reinterpret_cast<CacheReader *>(usd_reader);
578}
579
581{
582 USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader);
583 usd_reader->decref();
584
585 if (usd_reader->refcount() == 0) {
586 delete usd_reader;
587 }
588}
589
591 const char *filepath,
592 ListBase *object_paths)
593{
594 pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filepath);
595
596 if (!stage) {
597 return nullptr;
598 }
599
601
602 USDStageReader *stage_reader = new USDStageReader(stage, params);
603
604 if (object_paths) {
605 gather_objects_paths(stage->GetPseudoRoot(), object_paths);
606 }
607
608 return handle_from_stage_reader(stage_reader);
609}
610
612{
613 USDStageReader *stage_reader = stage_reader_from_handle(handle);
614 delete stage_reader;
615}
616
617void USD_get_transform(CacheReader *reader, float r_mat_world[4][4], float time, float scale)
618{
619 if (!reader) {
620 return;
621 }
622 const USDXformReader *usd_reader = reinterpret_cast<USDXformReader *>(reader);
623
624 bool is_constant = false;
625
626 /* Convert from the local matrix we obtain from USD to world coordinates
627 * for Blender. This conversion is done here rather than by Blender due to
628 * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in
629 * BKE_constraint_mat_convertspace(). */
630 Object *object = usd_reader->object();
631 if (object->parent == nullptr) {
632 /* No parent, so local space is the same as world space. */
633 usd_reader->read_matrix(r_mat_world, time, scale, &is_constant);
634 return;
635 }
636
637 float mat_parent[4][4];
638 BKE_object_get_parent_matrix(object, object->parent, mat_parent);
639
640 float mat_local[4][4];
641 usd_reader->read_matrix(mat_local, time, scale, &is_constant);
642 mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv);
643 mul_m4_m4m4(r_mat_world, r_mat_world, mat_local);
644}
645
646} // namespace blender::io::usd
void * BKE_cachefile_add(Main *bmain, const char *name)
Definition cachefile.cc:311
Collection * BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
LayerCollection * BKE_layer_collection_first_from_scene_collection(const ViewLayer *view_layer, const Collection *collection)
LayerCollection * BKE_layer_collection_get_active(ViewLayer *view_layer)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_view_layer_base_select_and_set_active(ViewLayer *view_layer, Base *selbase)
void BKE_id_free_us(Main *bmain, void *idv) ATTR_NONNULL()
void id_us_min(ID *id)
Definition lib_id.cc:366
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
General operations, lookup, etc. for blender objects.
void BKE_object_get_parent_matrix(const Object *ob, Object *par, float r_parentmat[4][4])
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define RPT_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
#define MAX_ID_NAME
Definition DNA_ID.h:373
Object groups, one object can be in many groups at once.
These structs are the foundation for all linked lists in the library system.
Object is a sort of wrapper for general info.
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_USD_IMPORT
Definition WM_api.hh:1798
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define NC_ID
Definition WM_types.hh:395
#define NC_SCENE
Definition WM_types.hh:378
#define NA_ADDED
Definition WM_types.hh:586
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
virtual bool topology_changed(const Mesh *, pxr::UsdTimeCode)
virtual void read_geometry(bke::GeometrySet &geometry_set, USDMeshReadParams params, const char **r_err_str)=0
const pxr::UsdPrim & prim() const
USDPrimReader * parent() const
USDPrimReader * create_reader(const pxr::UsdPrim &prim)
const blender::Vector< USDPrimReader * > & readers() const
const blender::Vector< USDDomeLightReader * > & dome_light_readers() const
void import_all_materials(struct Main *bmain)
void read_matrix(float r_mat[4][4], pxr::UsdTimeCode time, float scale, bool *r_is_constant) const
nullptr float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
#define G(x, y, z)
static void import_endjob(void *customdata)
static USDPrimReader * get_usd_reader(CacheReader *reader, const Object *, const char **r_err_str)
CacheArchiveHandle * USD_create_handle(Main *, const char *filepath, ListBase *object_paths)
CacheReader * CacheReader_open_usd_object(CacheArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path)
static void import_freejob(void *user_data)
static bool gather_objects_paths(const pxr::UsdPrim &object, ListBase *object_paths)
static void report_job_duration(const ExportJobData *data)
bool USD_mesh_topology_changed(CacheReader *reader, const Object *ob, const Mesh *existing_mesh, const double time, const char **r_err_str)
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
void USD_get_transform(CacheReader *reader, float r_mat_world[4][4], float time, float scale)
void USD_read_geometry(CacheReader *reader, const Object *ob, blender::bke::GeometrySet &geometry_set, const USDMeshReadParams params, const char **r_err_str)
bool USD_import(const bContext *C, const char *filepath, const USDImportParams *params, bool as_background_job, ReportList *reports)
void call_import_hooks(USDStageReader *archive, ReportList *reports)
Definition usd_hook.cc:616
void USD_free_handle(CacheArchiveHandle *handle)
void USD_CacheReader_free(CacheReader *reader)
static void import_startjob(void *customdata, wmJobWorkerStatus *worker_status)
static USDStageReader * stage_reader_from_handle(CacheArchiveHandle *handle)
static CacheArchiveHandle * handle_from_stage_reader(USDStageReader *reader)
void register_hook_converters()
Definition usd_hook.cc:298
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:21
Clock::time_point TimePoint
Definition BLI_timeit.hh:20
void print_duration(Nanoseconds duration)
Definition timeit.cc:45
struct Collection * collection
float parentinv[4][4]
struct Object * parent
ReportList * reports
Definition WM_types.hh:1028
i
Definition text_draw.cc:230
void WM_locked_interface_set(wmWindowManager *wm, bool lock)
void WM_main_add_notifier(uint type, void *reference)
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
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360