Blender V4.5
usd_light_convert.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "usd.hh"
8#include "usd_asset_utils.hh"
9#include "usd_private.hh"
10#include "usd_utils.hh"
12
13#include <pxr/base/gf/rotation.h>
14#include <pxr/base/gf/vec3f.h>
15#include <pxr/usd/usdGeom/metrics.h>
16#include <pxr/usd/usdGeom/tokens.h>
17#include <pxr/usd/usdGeom/xformCache.h>
18#include <pxr/usd/usdGeom/xformCommonAPI.h>
19#include <pxr/usd/usdLux/domeLight.h>
20#include <pxr/usd/usdLux/domeLight_1.h>
21#include <pxr/usd/usdLux/tokens.h>
22
23#include "BKE_image.hh"
24#include "BKE_library.hh"
25#include "BKE_main.hh"
26#include "BKE_node.hh"
28#include "BKE_node_runtime.hh"
30
31#include "BLI_fileops.h"
32#include "BLI_math_vector.h"
33#include "BLI_path_utils.hh"
34#include "BLI_string_ref.hh"
35
36#include "DNA_image_types.h"
37#include "DNA_node_types.h"
38#include "DNA_scene_types.h"
39#include "DNA_world_types.h"
40
41#include <string>
42
43#include "CLG_log.h"
44static CLG_LogRef LOG = {"io.usd"};
45
46namespace usdtokens {
47// Attribute values.
48static const pxr::TfToken pole_axis_z("Z", pxr::TfToken::Immortal);
49} // namespace usdtokens
50
51namespace {
52
57struct WorldNtreeSearchResults {
59 pxr::UsdStageRefPtr stage;
60
61 std::string file_path;
62
63 float world_intensity = 0.0f;
64 float world_color[3]{};
65 float mapping_rot[3]{};
66 float color_mult[3]{};
67
68 bool background_found = false;
69 bool env_tex_found = false;
70 bool mult_found = false;
71
72 WorldNtreeSearchResults(const blender::io::usd::USDExportParams &in_params,
73 pxr::UsdStageRefPtr in_stage)
74 : params(in_params), stage(in_stage)
75 {
76 }
77};
78
79} // End anonymous namespace.
80
81namespace blender::io::usd {
82
87static Image *load_image(std::string tex_path, Main *bmain, const USDImportParams &params)
88{
89 /* Optionally copy the asset if it's inside a USDZ package. */
90 const bool import_textures = params.import_textures_mode != USD_TEX_IMPORT_NONE &&
91 should_import_asset(tex_path);
92
93 std::string imported_file_source_path = tex_path;
94
95 if (import_textures) {
96 /* If we are packing the imported textures, we first write them
97 * to a temporary directory. */
98 const char *textures_dir = params.import_textures_mode == USD_TEX_IMPORT_PACK ?
100 params.import_textures_dir;
101
102 const eUSDTexNameCollisionMode name_collision_mode = params.import_textures_mode ==
105 params.tex_name_collision_mode;
106
107 tex_path = import_asset(tex_path.c_str(), textures_dir, name_collision_mode, nullptr);
108 }
109
110 Image *image = BKE_image_load_exists(bmain, tex_path.c_str());
111 if (!image) {
112 return nullptr;
113 }
114
115 if (import_textures && imported_file_source_path != tex_path) {
116 ensure_usd_source_path_prop(imported_file_source_path, &image->id);
117 }
118
119 if (import_textures && params.import_textures_mode == USD_TEX_IMPORT_PACK &&
121 {
122 BKE_image_packfiles(nullptr, image, ID_BLEND_PATH(bmain, &image->id));
124 BLI_delete(temp_textures_dir(), true, true);
125 }
126 }
127
128 return image;
129}
130
131/* Create a new node of type 'new_node_type' and connect it
132 * as an upstream source to 'dst_node' with the given sockets. */
133static bNode *append_node(bNode *dst_node,
134 int16_t new_node_type,
135 const StringRef out_sock,
136 const StringRef in_sock,
137 bNodeTree *ntree,
138 float offset)
139{
140 bNode *src_node = bke::node_add_static_node(nullptr, *ntree, new_node_type);
141 if (!src_node) {
142 return nullptr;
143 }
144
145 bke::node_add_link(*ntree,
146 *src_node,
147 *bke::node_find_socket(*src_node, SOCK_OUT, out_sock),
148 *dst_node,
149 *bke::node_find_socket(*dst_node, SOCK_IN, in_sock));
150
151 src_node->location[0] = dst_node->location[0] - offset;
152 src_node->location[1] = dst_node->location[1];
153
154 return src_node;
155}
156
162static bool node_search(bNode *fromnode,
163 bNode * /*tonode*/,
164 void *userdata,
165 const bool /*reversed*/)
166{
167 if (!(userdata && fromnode)) {
168 return true;
169 }
170
171 WorldNtreeSearchResults *res = reinterpret_cast<WorldNtreeSearchResults *>(userdata);
172
173 if (!res->background_found && fromnode->type_legacy == SH_NODE_BACKGROUND) {
174 /* Get light color and intensity */
175 const bNodeSocketValueRGBA *color_data = bke::node_find_socket(*fromnode, SOCK_IN, "Color")
176 ->default_value_typed<bNodeSocketValueRGBA>();
177 const bNodeSocketValueFloat *strength_data =
178 bke::node_find_socket(*fromnode, SOCK_IN, "Strength")
179 ->default_value_typed<bNodeSocketValueFloat>();
180
181 res->background_found = true;
182 res->world_intensity = strength_data->value;
183 res->world_color[0] = color_data->value[0];
184 res->world_color[1] = color_data->value[1];
185 res->world_color[2] = color_data->value[2];
186 }
187 else if (!res->env_tex_found && fromnode->type_legacy == SH_NODE_TEX_ENVIRONMENT) {
188 /* Get env tex path. */
189
190 res->file_path = get_tex_image_asset_filepath(fromnode, res->stage, res->params);
191
192 if (!res->file_path.empty()) {
193 res->env_tex_found = true;
194 if (res->params.export_textures) {
195 export_texture(fromnode, res->stage, res->params.overwrite_textures);
196 }
197 }
198 }
199 else if (!res->env_tex_found && !res->mult_found && fromnode->type_legacy == SH_NODE_VECTOR_MATH)
200 {
201
202 if (fromnode->custom1 == NODE_VECTOR_MATH_MULTIPLY) {
203 res->mult_found = true;
204
205 bNodeSocket *vec_sock = bke::node_find_socket(*fromnode, SOCK_IN, "Vector");
206 if (vec_sock) {
207 vec_sock = vec_sock->next;
208 }
209
210 if (vec_sock) {
211 copy_v3_v3(res->color_mult, ((bNodeSocketValueVector *)vec_sock->default_value)->value);
212 }
213 }
214 }
215 else if (res->env_tex_found && fromnode->type_legacy == SH_NODE_MAPPING) {
216 copy_v3_fl(res->mapping_rot, 0.0f);
217 if (bNodeSocket *socket = bke::node_find_socket(*fromnode, SOCK_IN, "Rotation")) {
218 const bNodeSocketValueVector *rot_value = static_cast<bNodeSocketValueVector *>(
219 socket->default_value);
220 copy_v3_v3(res->mapping_rot, rot_value->value);
221 }
222 }
223
224 return true;
225}
226
228 const Scene *scene,
229 pxr::UsdStageRefPtr stage)
230{
231 if (!(stage && scene && scene->world)) {
232 return;
233 }
234
235 WorldNtreeSearchResults res(params, stage);
236
237 if (scene->world->use_nodes && scene->world->nodetree) {
238 /* Find the world output. */
239 bNode *output = nullptr;
240 const bNodeTree *ntree = scene->world->nodetree;
241 ntree->ensure_topology_cache();
242 const blender::Span<const bNode *> bsdf_nodes = ntree->nodes_by_type("ShaderNodeOutputWorld");
243 for (const bNode *node : bsdf_nodes) {
244 if (node->flag & NODE_DO_OUTPUT) {
245 output = const_cast<bNode *>(node);
246 break;
247 }
248 }
249
250 if (!output) {
251 /* No output, no valid network to convert. */
252 return;
253 }
254
256 }
257 else {
258 res.world_intensity = 1.0f;
259 copy_v3_v3(res.world_color, &scene->world->horr);
260 res.background_found = !is_zero_v3(res.world_color);
261 }
262
263 if (!(res.background_found || res.env_tex_found)) {
264 /* No nodes to convert */
265 return;
266 }
267
268 /* Create USD dome light. */
269
270 pxr::SdfPath env_light_path = get_unique_path(stage,
271 std::string(params.root_prim_path) + "/env_light");
272
273 pxr::UsdLuxDomeLight dome_light = pxr::UsdLuxDomeLight::Define(stage, env_light_path);
274
275 if (!res.env_tex_found) {
276 /* Like the Hydra delegate, if no texture is found export a solid
277 * color texture as a stand-in so that Hydra renderers don't
278 * throw errors. */
279
280 float fill_color[4] = {res.world_color[0], res.world_color[1], res.world_color[2], 1.0f};
281
282 std::string source_path = cache_image_color(fill_color);
283 const std::string base_path = stage->GetRootLayer()->GetRealPath();
284
285 /* It'll be short, coming from cache_image_color. */
286 char file_path[64];
287 BLI_path_split_file_part(source_path.c_str(), file_path, 64);
288 char dest_path[FILE_MAX];
289 BLI_path_split_dir_part(base_path.c_str(), dest_path, FILE_MAX);
290
291 BLI_path_append_dir(dest_path, FILE_MAX, "textures");
292 BLI_dir_create_recursive(dest_path);
293
294 BLI_path_append(dest_path, FILE_MAX, file_path);
295
296 if (BLI_copy(source_path.c_str(), dest_path) != 0) {
297 CLOG_WARN(&LOG, "USD Export: Couldn't write world color image to %s", dest_path);
298 }
299 else {
300 res.env_tex_found = true;
301 BLI_path_join(dest_path, FILE_MAX, ".", "textures", file_path);
302 res.file_path = dest_path;
303 }
304 }
305
306 if (res.env_tex_found) {
307 pxr::SdfAssetPath path(res.file_path);
308 dome_light.CreateTextureFileAttr().Set(path);
309
310 if (res.mult_found) {
311 pxr::GfVec3f color_val(res.color_mult[0], res.color_mult[1], res.color_mult[2]);
312 dome_light.CreateColorAttr().Set(color_val);
313 }
314 }
315 else {
316 pxr::GfVec3f color_val(res.world_color[0], res.world_color[1], res.world_color[2]);
317 dome_light.CreateColorAttr().Set(color_val);
318 }
319
320 if (res.background_found) {
321 dome_light.CreateIntensityAttr().Set(res.world_intensity);
322 }
323
324 /* We always set a default rotation on the light since res.mapping_rot defaults to zeros. */
325
326 /* Convert radians to degrees. */
327 mul_v3_fl(res.mapping_rot, 180.0f / M_PI);
328
329 pxr::GfMatrix4d xf =
330 pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0)) *
331 pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), 90.0)) *
332 pxr::GfMatrix4d().SetRotate(
333 pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), -res.mapping_rot[2])) *
334 pxr::GfMatrix4d().SetRotate(
335 pxr::GfRotation(pxr::GfVec3d(0.0, 1.0, 0.0), -res.mapping_rot[1])) *
336 pxr::GfMatrix4d().SetRotate(
337 pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), -res.mapping_rot[0]));
338
339 pxr::GfVec3d angles = xf.DecomposeRotation(
340 pxr::GfVec3d::ZAxis(), pxr::GfVec3d::YAxis(), pxr::GfVec3d::XAxis());
341
342 pxr::GfVec3f rot_vec(angles[2], angles[1], angles[0]);
343
344 pxr::UsdGeomXformCommonAPI xform_api(dome_light);
345 xform_api.SetRotate(rot_vec, pxr::UsdGeomXformCommonAPI::RotationOrderXYZ);
346}
347
348/* Import the dome light as a world material. */
349
351 Scene *scene,
352 Main *bmain,
353 const USDImportDomeLightData &dome_light_data,
354 const pxr::UsdPrim &prim,
355 const double motionSampleTime)
356{
357 if (!(scene && scene->world && prim)) {
358 return;
359 }
360
361 if (!scene->world->use_nodes) {
362 scene->world->use_nodes = true;
363 }
364
365 if (!scene->world->nodetree) {
366 scene->world->nodetree = bke::node_tree_add_tree(nullptr, "Shader Nodetree", "ShaderNodeTree");
367 if (!scene->world->nodetree) {
368 CLOG_WARN(&LOG, "Couldn't create world ntree");
369 return;
370 }
371 }
372
373 bNodeTree *ntree = scene->world->nodetree;
374 bNode *output = nullptr;
375 bNode *bgshader = nullptr;
376
377 /* We never delete existing nodes, but we might disconnect them
378 * and move them out of the way. */
379
380 /* Look for the output and background shader nodes, which we will reuse. */
381 for (bNode *node : ntree->all_nodes()) {
382 if (node->type_legacy == SH_NODE_OUTPUT_WORLD) {
383 output = node;
384 }
385 else if (node->type_legacy == SH_NODE_BACKGROUND) {
386 bgshader = node;
387 }
388 else {
389 /* Move existing node out of the way. */
390 node->location[1] += 300;
391 }
392 }
393
394 /* Create the output and background shader nodes, if they don't exist. */
395 if (!output) {
397
398 if (!output) {
399 CLOG_WARN(&LOG, "Couldn't create world output node");
400 return;
401 }
402
403 output->location[0] = 300.0f;
404 output->location[1] = 300.0f;
405 }
406
407 if (!bgshader) {
408 bgshader = append_node(output, SH_NODE_BACKGROUND, "Background", "Surface", ntree, 200);
409
410 if (!bgshader) {
411 CLOG_WARN(&LOG, "Couldn't create world shader node");
412 return;
413 }
414
415 /* Set the default background color. */
416 bNodeSocket *color_sock = bke::node_find_socket(*bgshader, SOCK_IN, "Color");
417 copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value, &scene->world->horr);
418 }
419
420 /* Make sure the first input to the shader node is disconnected. */
421 bNodeSocket *shader_input = bke::node_find_socket(*bgshader, SOCK_IN, "Color");
422
423 if (shader_input && shader_input->link) {
424 bke::node_remove_link(ntree, *shader_input->link);
425 }
426
427 /* Set the background shader intensity. */
428 float intensity = dome_light_data.intensity * params.light_intensity_scale;
429
430 bNodeSocket *strength_sock = bke::node_find_socket(*bgshader, SOCK_IN, "Strength");
431 ((bNodeSocketValueFloat *)strength_sock->default_value)->value = intensity;
432
433 if (!dome_light_data.has_tex) {
434 /* No texture file is authored on the dome light. Set the color, if it was authored,
435 * and return early. */
436 if (dome_light_data.has_color) {
437 bNodeSocket *color_sock = bke::node_find_socket(*bgshader, SOCK_IN, "Color");
438 copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value,
439 dome_light_data.color.data());
440 }
441
444
445 return;
446 }
447
448 /* If the light has authored color, create a color multiply node for the environment
449 * texture output. */
450 bNode *mult = nullptr;
451
452 if (dome_light_data.has_color) {
453 mult = append_node(bgshader, SH_NODE_VECTOR_MATH, "Vector", "Color", ntree, 200);
454
455 if (!mult) {
456 CLOG_WARN(&LOG, "Couldn't create vector multiply node");
457 return;
458 }
459
461
462 /* Set the color in the vector math node's second socket. */
463 bNodeSocket *vec_sock = bke::node_find_socket(*mult, SOCK_IN, "Vector");
464 if (vec_sock) {
465 vec_sock = vec_sock->next;
466 }
467
468 if (vec_sock) {
469 copy_v3_v3(((bNodeSocketValueVector *)vec_sock->default_value)->value,
470 dome_light_data.color.data());
471 }
472 else {
473 CLOG_WARN(&LOG, "Couldn't find vector multiply second vector socket");
474 }
475 }
476
477 bNode *tex = nullptr;
478
479 /* Append an environment texture node to the mult node, if it was created, or directly to
480 * the background shader. */
481 if (mult) {
482 tex = append_node(mult, SH_NODE_TEX_ENVIRONMENT, "Color", "Vector", ntree, 400);
483 }
484 else {
485 tex = append_node(bgshader, SH_NODE_TEX_ENVIRONMENT, "Color", "Color", ntree, 400);
486 }
487
488 if (!tex) {
489 CLOG_WARN(&LOG, "Couldn't create world environment texture node");
490 return;
491 }
492
493 bNode *mapping = append_node(tex, SH_NODE_MAPPING, "Vector", "Vector", ntree, 200);
494 if (!mapping) {
495 CLOG_WARN(&LOG, "Couldn't create mapping node");
496 return;
497 }
498
499 const bNode *tex_coord = append_node(
500 mapping, SH_NODE_TEX_COORD, "Generated", "Vector", ntree, 200);
501 if (!tex_coord) {
502 CLOG_WARN(&LOG, "Couldn't create texture coordinate node");
503 return;
504 }
505
506 /* Load the texture image. */
507 std::string resolved_path = dome_light_data.tex_path.GetResolvedPath();
508
509 if (resolved_path.empty()) {
510 CLOG_WARN(&LOG,
511 "Couldn't get resolved path for asset %s",
512 dome_light_data.tex_path.GetAssetPath().c_str());
513 return;
514 }
515
516 Image *image = load_image(resolved_path, bmain, params);
517 if (!image) {
518 CLOG_WARN(&LOG, "Couldn't load image file %s", resolved_path.c_str());
519 return;
520 }
521
522 tex->id = &image->id;
523
524 /* Set the transform. */
525 pxr::UsdGeomXformCache xf_cache(motionSampleTime);
526 pxr::GfMatrix4d xf = xf_cache.GetLocalToWorldTransform(prim);
527
528 pxr::UsdStageRefPtr stage = prim.GetStage();
529
530 if (!stage) {
531 CLOG_WARN(&LOG, "Couldn't get stage for dome light %s", prim.GetPath().GetText());
532 return;
533 }
534
535 /* Note: This logic tries to produce identical results to `usdview` as of USD 25.05.
536 * However, `usdview` seems to handle Y-Up stages differently; some scenes match while others
537 * do not unless we keep the second conditional below (+90 on x-axis). */
538 const pxr::TfToken stage_up = pxr::UsdGeomGetStageUpAxis(stage);
539 const bool needs_stage_z_adjust = stage_up == pxr::UsdGeomTokens->z &&
540 ELEM(dome_light_data.pole_axis,
541 pxr::UsdLuxTokens->Z,
542 pxr::UsdLuxTokens->scene);
543 const bool needs_stage_y_adjust = stage_up == pxr::UsdGeomTokens->y &&
544 ELEM(dome_light_data.pole_axis, pxr::UsdLuxTokens->Z);
545 if (needs_stage_z_adjust || needs_stage_y_adjust) {
546 xf *= pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 1.0, 0.0), 90.0));
547 }
548 else if (stage_up == pxr::UsdGeomTokens->y) {
549 /* Convert from Y-up to Z-up with a 90 degree rotation about the X-axis. */
550 xf *= pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0));
551 }
552
553 /* Rotate into Blender's frame of reference. */
554 xf = pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), -90.0)) *
555 pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), -90.0)) * xf;
556
557 pxr::GfVec3d angles = xf.DecomposeRotation(
558 pxr::GfVec3d::XAxis(), pxr::GfVec3d::YAxis(), pxr::GfVec3d::ZAxis());
559 pxr::GfVec3f rot_vec(-angles[0], -angles[1], -angles[2]);
560
561 /* Convert degrees to radians. */
562 rot_vec *= M_PI / 180.0f;
563
564 if (bNodeSocket *socket = bke::node_find_socket(*mapping, SOCK_IN, "Rotation")) {
565 bNodeSocketValueVector *rot_value = static_cast<bNodeSocketValueVector *>(
566 socket->default_value);
567 copy_v3_v3(rot_value->value, rot_vec.data());
568 }
569
573}
574
575} // namespace blender::io::usd
void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
Image * BKE_image_load_exists(Main *bmain, const char *filepath, bool *r_exists=nullptr)
bool BKE_image_has_packedfile(const Image *image)
#define SH_NODE_OUTPUT_WORLD
#define SH_NODE_TEX_COORD
#define SH_NODE_VECTOR_MATH
#define SH_NODE_TEX_ENVIRONMENT
#define SH_NODE_BACKGROUND
#define SH_NODE_MAPPING
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
File and directory operations.
int BLI_copy(const char *path_src, const char *path_dst) ATTR_NONNULL()
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:391
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:456
#define M_PI
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_fl(float r[3], float f)
size_t BLI_path_append(char *__restrict dst, size_t dst_maxncpy, const char *__restrict file) ATTR_NONNULL(1
#define FILE_MAX
#define BLI_path_join(...)
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
size_t size_t BLI_path_append_dir(char *__restrict dst, size_t dst_maxncpy, const char *__restrict dir) ATTR_NONNULL(1
#define ELEM(...)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_NTREE_OUTPUT
Definition DNA_ID.h:1063
@ NODE_DO_OUTPUT
@ NODE_VECTOR_MATH_MULTIPLY
@ SOCK_OUT
@ SOCK_IN
SIMD_FORCE_INLINE void mult(const btTransform &t1, const btTransform &t2)
Set the current transform as the value of the product of two transforms.
Definition btTransform.h:76
#define output
#define ID_BLEND_PATH(_bmain, _id)
uint tex_coord
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define LOG(severity)
Definition log.h:32
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:4124
void node_chain_iterator(const bNodeTree *ntree, const bNode *node_start, bool(*callback)(bNode *, bNode *, void *, const bool), void *userdata, bool reversed)
Definition node.cc:3672
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4362
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
void world_material_to_dome_light(const USDExportParams &params, const Scene *scene, pxr::UsdStageRefPtr stage)
const char * temp_textures_dir()
bool should_import_asset(const std::string &path)
static Image * load_image(std::string tex_path, Main *bmain, const USDImportParams &params)
pxr::SdfPath get_unique_path(pxr::UsdStageRefPtr stage, const std::string &path)
Definition usd_utils.cc:61
std::string import_asset(const char *src, const char *import_dir, eUSDTexNameCollisionMode name_collision_mode, ReportList *reports)
static void export_texture(const USDExporterContext &usd_export_context, bNode *node)
@ USD_TEX_IMPORT_NONE
Definition usd.hh:65
@ USD_TEX_IMPORT_PACK
Definition usd.hh:66
std::string cache_image_color(const float color[4])
static bool node_search(bNode *fromnode, bNode *, void *userdata, const bool)
void dome_light_to_world_material(const USDImportParams &params, Scene *scene, Main *bmain, const USDImportDomeLightData &dome_light_data, const pxr::UsdPrim &prim, const double motionSampleTime)
eUSDTexNameCollisionMode
Definition usd.hh:74
@ USD_TEX_NAME_COLLISION_OVERWRITE
Definition usd.hh:76
static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_export_context, bNode *node)
void ensure_usd_source_path_prop(const std::string &path, ID *id)
static bNode * append_node(bNode *dst_node, int16_t new_node_type, const StringRef out_sock, const StringRef in_sock, bNodeTree *ntree, float offset)
static const pxr::TfToken pole_axis_z("Z", pxr::TfToken::Immortal)
struct World * world
struct bNodeTree * nodetree
short use_nodes
float horr
struct bNodeLink * link
struct bNodeSocket * next
void * default_value
float location[2]
int16_t custom1
struct ID * id
int16_t type_legacy