Blender V4.5
fbx_import_mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_attribute.hh"
10#include "BKE_deform.hh"
11#include "BKE_key.hh"
12#include "BKE_lib_id.hh"
13#include "BKE_material.hh"
14#include "BKE_mesh.hh"
15#include "BKE_modifier.hh"
16#include "BKE_object.hh"
17#include "BKE_object_deform.h"
18
19#include "BLI_color.hh"
20#include "BLI_listbase.h"
21#include "BLI_ordered_edge.hh"
22#include "BLI_string.h"
23#include "BLI_task.hh"
24#include "BLI_vector_set.hh"
25
26#include "BLT_translation.hh"
27
28#include "DNA_key_types.h"
29#include "DNA_meshdata_types.h"
30#include "DNA_object_types.h"
31
32#include "IO_fbx.hh"
33
34#include "fbx_import_mesh.hh"
35
36namespace blender::io::fbx {
37
38static constexpr const char *temp_custom_normals_name = "fbx_temp_custom_normals";
39
40static bool is_skin_deformer_usable(const ufbx_mesh *mesh, const ufbx_skin_deformer *skin)
41{
42 return mesh != nullptr && skin != nullptr && skin->clusters.count > 0 &&
43 mesh->num_vertices > 0 && skin->vertices.count == mesh->num_vertices;
44}
45
46static void import_vertex_positions(const ufbx_mesh *fmesh, Mesh *mesh)
47{
48 MutableSpan<float3> positions = mesh->vert_positions_for_write();
49#if 0 // @TODO: "bake" skinned meshes
50 if (skin != nullptr) {
51 /* For a skinned mesh, transform the vertices into bind pose position, in local space. */
52 const ufbx_matrix &geom_to_world = fmesh->instances[0]->geometry_to_world;
53 ufbx_matrix world_to_geom = ufbx_matrix_invert(&geom_to_world);
54 for (int i = 0; i < fmesh->vertex_position.values.count; i++) {
55 ufbx_matrix skin_mat = ufbx_get_skin_vertex_matrix(skin, i, &geom_to_world);
56 skin_mat = ufbx_matrix_mul(&world_to_geom, &skin_mat);
57 ufbx_vec3 val = ufbx_transform_position(&skin_mat, fmesh->vertex_position.values[i]);
58 positions[i] = float3(val.x, val.y, val.z);
59 //@TODO: skin normals
60 }
61 return;
62 }
63#endif
64
65 BLI_assert(positions.size() == fmesh->vertex_position.values.count);
66 for (int i = 0; i < fmesh->vertex_position.values.count; i++) {
67 ufbx_vec3 val = fmesh->vertex_position.values[i];
68 positions[i] = float3(val.x, val.y, val.z);
69 }
70}
71
72static void import_faces(const ufbx_mesh *fmesh, Mesh *mesh)
73{
74 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
75 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
76 BLI_assert((face_offsets.size() == fmesh->num_faces + 1) ||
77 (face_offsets.is_empty() && fmesh->num_faces == 0));
78 for (int face_idx = 0; face_idx < fmesh->num_faces; face_idx++) {
79 //@TODO: skip < 3 vertex faces?
80 const ufbx_face &fface = fmesh->faces[face_idx];
81 face_offsets[face_idx] = fface.index_begin;
82 for (int i = 0; i < fface.num_indices; i++) {
83 int corner_idx = fface.index_begin + i;
84 int vidx = fmesh->vertex_indices[corner_idx];
85 corner_verts[corner_idx] = vidx;
86 }
87 }
88}
89
90static void import_face_material_indices(const ufbx_mesh *fmesh,
92{
93 if (fmesh->face_material.count == fmesh->num_faces) {
95 "material_index", bke::AttrDomain::Face);
96 for (int i = 0; i < fmesh->face_material.count; i++) {
97 materials.span[i] = fmesh->face_material[i];
98 }
99 materials.finish();
100 }
101}
102
103static void import_face_smoothing(const ufbx_mesh *fmesh,
105{
106 if (fmesh->face_smoothing.count > 0 && fmesh->face_smoothing.count == fmesh->num_faces) {
108 "sharp_face", bke::AttrDomain::Face);
109 for (int i = 0; i < fmesh->face_smoothing.count; i++) {
110 smooth.span[i] = !fmesh->face_smoothing[i];
111 }
112 smooth.finish();
113 }
114}
115
116static void import_edges(const ufbx_mesh *fmesh,
117 Mesh *mesh,
119{
120 MutableSpan<int2> edges = mesh->edges_for_write();
121 BLI_assert(edges.size() == fmesh->num_edges);
122 for (int edge_idx = 0; edge_idx < fmesh->num_edges; edge_idx++) {
123 const ufbx_edge &fedge = fmesh->edges[edge_idx];
124 int va = fmesh->vertex_indices[fedge.a];
125 int vb = fmesh->vertex_indices[fedge.b];
126 edges[edge_idx] = int2(va, vb);
127 }
128
129 /* Calculate any remaining edges, and add them to explicitly imported ones.
130 * Note that this clears any per-edge data, so we have to setup edge creases etc.
131 * after that. */
132 bke::mesh_calc_edges(*mesh, true, false);
133
134 const bool has_edge_creases = fmesh->edge_crease.count > 0 &&
135 fmesh->edge_crease.count == fmesh->num_edges;
136 const bool has_edge_smooth = fmesh->edge_smoothing.count > 0 &&
137 fmesh->edge_smoothing.count == fmesh->num_edges;
138 if (has_edge_creases || has_edge_smooth) {
139 /* The total number of edges in mesh now might be different from number of explicitly
140 * imported ones; we have to build mapping from vertex pairs to edge index. */
141 Span<int2> edges = mesh->edges();
142 Map<OrderedEdge, int> edge_map;
143 edge_map.reserve(edges.size());
144 for (const int i : edges.index_range()) {
145 edge_map.add(edges[i], i);
146 }
147
148 if (has_edge_creases) {
150 attributes.lookup_or_add_for_write_only_span<float>("crease_edge",
152 creases.span.fill(0.0f);
153 for (int i = 0; i < fmesh->num_edges; i++) {
154 const ufbx_edge &fedge = fmesh->edges[i];
155 int va = fmesh->vertex_indices[fedge.a];
156 int vb = fmesh->vertex_indices[fedge.b];
157 int edge_i = edge_map.lookup_default({va, vb}, -1);
158 if (edge_i >= 0) {
159 /* Python fbx importer was squaring the incoming crease values. */
160 creases.span[edge_i] = sqrtf(fmesh->edge_crease[i]);
161 }
162 }
163 creases.finish();
164 }
165
166 if (has_edge_smooth) {
168 "sharp_edge", bke::AttrDomain::Edge);
169 sharp.span.fill(false);
170 for (int i = 0; i < fmesh->num_edges; i++) {
171 const ufbx_edge &fedge = fmesh->edges[i];
172 int va = fmesh->vertex_indices[fedge.a];
173 int vb = fmesh->vertex_indices[fedge.b];
174 int edge_i = edge_map.lookup_default({va, vb}, -1);
175 if (edge_i >= 0) {
176 sharp.span[edge_i] = !fmesh->edge_smoothing[i];
177 }
178 }
179 sharp.finish();
180 }
181 }
182}
183
184static void import_uvs(const ufbx_mesh *fmesh,
186 AttributeOwner attr_owner)
187{
188 for (const ufbx_uv_set &fuv_set : fmesh->uv_sets) {
189 std::string attr_name = BKE_attribute_calc_unique_name(attr_owner, fuv_set.name.data);
191 attr_name, bke::AttrDomain::Corner);
192 BLI_assert(fuv_set.vertex_uv.indices.count == uvs.span.size());
193 for (int i = 0; i < fuv_set.vertex_uv.indices.count; i++) {
194 int val_idx = fuv_set.vertex_uv.indices[i];
195 const ufbx_vec2 &uv = fuv_set.vertex_uv.values[val_idx];
196 uvs.span[i] = float2(uv.x, uv.y);
197 }
198 uvs.finish();
199 }
200}
201
202static void import_colors(const ufbx_mesh *fmesh,
203 Mesh *mesh,
205 AttributeOwner attr_owner,
206 eFBXVertexColorMode color_mode)
207{
208 std::string first_color_name;
209 for (const ufbx_color_set &fcol_set : fmesh->color_sets) {
210 std::string attr_name = BKE_attribute_calc_unique_name(attr_owner, fcol_set.name.data);
211 if (first_color_name.empty()) {
212 first_color_name = attr_name;
213 }
214 if (color_mode == eFBXVertexColorMode::sRGB) {
215 /* sRGB colors, use 4 bytes per color. */
219 BLI_assert(fcol_set.vertex_color.indices.count == cols.span.size());
220 for (int i = 0; i < fcol_set.vertex_color.indices.count; i++) {
221 int val_idx = fcol_set.vertex_color.indices[i];
222 const ufbx_vec4 &col = fcol_set.vertex_color.values[val_idx];
223 /* Note: color values are expected to already be in sRGB space. */
224 float4 fcol = float4(col.x, col.y, col.z, col.w);
225 uchar4 bcol;
226 rgba_float_to_uchar(bcol, fcol);
227 cols.span[i] = ColorGeometry4b(bcol);
228 }
229 cols.finish();
230 }
231 else if (color_mode == eFBXVertexColorMode::Linear) {
232 /* Linear colors, use 4 floats per color. */
236 BLI_assert(fcol_set.vertex_color.indices.count == cols.span.size());
237 for (int i = 0; i < fcol_set.vertex_color.indices.count; i++) {
238 int val_idx = fcol_set.vertex_color.indices[i];
239 const ufbx_vec4 &col = fcol_set.vertex_color.values[val_idx];
240 cols.span[i] = ColorGeometry4f(col.x, col.y, col.z, col.w);
241 }
242 cols.finish();
243 }
244 else {
246 }
247 }
248 if (!first_color_name.empty()) {
249 mesh->active_color_attribute = BLI_strdup(first_color_name.c_str());
250 mesh->default_color_attribute = BLI_strdup(first_color_name.c_str());
251 }
252}
253
254static bool import_normals_into_temp_attribute(const ufbx_mesh *fmesh,
255 Mesh *mesh,
257{
258 if (!fmesh->vertex_normal.exists) {
259 return false;
260 }
263 BLI_assert(fmesh->vertex_normal.indices.count == mesh->corners_num);
264 BLI_assert(fmesh->vertex_normal.indices.count == normals.span.size());
265 for (int i = 0; i < mesh->corners_num; i++) {
266 int val_idx = fmesh->vertex_normal.indices[i];
267 const ufbx_vec3 &normal = fmesh->vertex_normal.values[val_idx];
268 normals.span[i] = float3(normal.x, normal.y, normal.z);
269 }
270 normals.finish();
271 return true;
272}
273
275 const ufbx_mesh *fmesh)
276{
277 VectorSet<std::string> name_set;
278 for (const ufbx_skin_deformer *skin : fmesh->skin_deformers) {
279 if (!is_skin_deformer_usable(fmesh, skin)) {
280 continue;
281 }
282
283 for (const ufbx_skin_cluster *cluster : skin->clusters) {
284 if (cluster->num_weights == 0) {
285 continue;
286 }
287
288 std::string bone_name = mapping.node_to_name.lookup_default(cluster->bone_node, "");
289 name_set.add(bone_name);
290 }
291 }
292 return name_set;
293}
294
296 const ufbx_mesh *fmesh,
297 Mesh *mesh)
298{
299 if (fmesh->skin_deformers.count == 0) {
300 return;
301 }
302
303 /* A single mesh can be skinned by several armatures, so we need to build bone (vertex group)
304 * name set, taking all skin deformers into account. */
305 VectorSet<std::string> bone_set = get_skin_bone_name_set(mapping, fmesh);
306 if (bone_set.is_empty()) {
307 return;
308 }
309
310 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
311
312 for (const ufbx_skin_deformer *skin : fmesh->skin_deformers) {
313 if (!is_skin_deformer_usable(fmesh, skin)) {
314 continue;
315 }
316
317 for (const ufbx_skin_cluster *cluster : skin->clusters) {
318 if (cluster->num_weights == 0) {
319 continue;
320 }
321 std::string bone_name = mapping.node_to_name.lookup_default(cluster->bone_node, "");
322 const int group_index = bone_set.index_of_try(bone_name);
323 if (group_index < 0) {
324 continue;
325 }
326
327 for (int i = 0; i < cluster->num_weights; i++) {
328 const int vertex = cluster->vertices[i];
329 if (vertex < dverts.size()) {
330 MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[vertex], group_index);
331 dw->weight = cluster->weights[i];
332 }
333 }
334 }
335 }
336}
337
338static bool import_blend_shapes(Main &bmain,
339 FbxElementMapping &mapping,
340 const ufbx_mesh *fmesh,
341 Mesh *mesh)
342{
343 Key *mesh_key = nullptr;
344 for (const ufbx_blend_deformer *fdeformer : fmesh->blend_deformers) {
345 for (const ufbx_blend_channel *fchan : fdeformer->channels) {
346 /* In theory fbx supports multiple keyframes within one blend shape
347 * channel; we only take the final target keyframe. */
348 if (fchan->target_shape == nullptr) {
349 continue;
350 }
351
352 if (mesh_key == nullptr) {
353 mesh_key = BKE_key_add(&bmain, &mesh->id);
354 mesh_key->type = KEY_RELATIVE;
355 mesh->key = mesh_key;
356
357 KeyBlock *kb = BKE_keyblock_add(mesh_key, nullptr);
358 BKE_keyblock_convert_from_mesh(mesh, mesh_key, kb);
359 }
360
361 KeyBlock *kb = BKE_keyblock_add(mesh_key, fchan->target_shape->name.data);
362 kb->curval = fchan->weight;
363 BKE_keyblock_convert_from_mesh(mesh, mesh_key, kb);
364 if (!kb->data) {
365 /* Nothing to do. This can happen if the mesh has no vertices. */
366 continue;
367 }
368 float3 *kb_data = static_cast<float3 *>(kb->data);
369 for (int i = 0; i < fchan->target_shape->num_offsets; i++) {
370 int idx = fchan->target_shape->offset_vertices[i];
371 const ufbx_vec3 &delta = fchan->target_shape->position_offsets[i];
372 kb_data[idx] += float3(delta.x, delta.y, delta.z);
373 }
374 mapping.el_to_shape_key.add(&fchan->element, mesh_key);
375 }
376 }
377 return mesh_key != nullptr;
378}
379
380/* Handle Blender-specific "FullWeights" that for each blend shape also create
381 * a weighted vertex group for itself. */
383 const ufbx_mesh *fmesh,
384 Mesh *mesh,
385 Object *obj)
386{
387 for (const ufbx_blend_deformer *fdeformer : fmesh->blend_deformers) {
388 for (const ufbx_blend_channel *fchan : fdeformer->channels) {
389 Key *key = mapping.el_to_shape_key.lookup_default(&fchan->element, nullptr);
390 if (fchan->target_shape == nullptr || key == nullptr) {
391 continue;
392 }
393 if (fchan->target_shape->offset_weights.count != fchan->target_shape->num_offsets) {
394 continue;
395 }
396
397 KeyBlock *kb = BKE_keyblock_find_name(key, fchan->target_shape->name.data);
398 if (kb == nullptr) {
399 continue;
400 }
401
402 /* Ignore cases where all weights are 1.0 (group has no effect),
403 * and cases where any weights are outside of 0..1 range (apparently some files have
404 * invalid negative weights and should be ignored). */
405 bool all_one = true;
406 bool all_unorm = true;
407 for (ufbx_real w : fchan->target_shape->offset_weights) {
408 if (w != 1.0) {
409 all_one = false;
410 }
411 if (w < 0.0 || w > 1.0) {
412 all_unorm = false;
413 }
414 }
415 if (all_one || !all_unorm) {
416 continue;
417 }
418
419 int group_index = BKE_defgroup_name_index(&mesh->vertex_group_names, kb->name);
420 if (group_index < 0) {
422 group_index = BKE_defgroup_name_index(&mesh->vertex_group_names, kb->name);
423 if (group_index < 0) {
424 continue;
425 }
426 }
427
428 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
429 for (int i = 0; i < fchan->target_shape->num_offsets; i++) {
430 const int idx = fchan->target_shape->offset_vertices[i];
431 if (idx >= 0 && idx < dverts.size()) {
432 const float w = fchan->target_shape->offset_weights[i];
433 MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[idx], group_index);
434 dw->weight = w;
435 }
436 }
437
438 STRNCPY(kb->vgroup, kb->name);
439 }
440 }
441}
442
443void import_meshes(Main &bmain,
444 const ufbx_scene &fbx,
445 FbxElementMapping &mapping,
446 const FBXImportParams &params)
447{
448 /* Create Mesh objects outside of Main, in parallel. */
449 Vector<Mesh *> meshes(fbx.meshes.count);
450 threading::parallel_for_each(IndexRange(fbx.meshes.count), [&](const int64_t index) {
451 const ufbx_mesh *fmesh = fbx.meshes.data[index];
452 if (fmesh->instances.count == 0) {
453 meshes[index] = nullptr; /* Ignore if not used by any objects. */
454 return;
455 }
456
457 /* Create Mesh outside of main. */
459 fmesh->num_vertices, fmesh->num_edges, fmesh->num_faces, fmesh->num_indices);
460 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
461 AttributeOwner attr_owner = AttributeOwner::from_id(&mesh->id);
462
463 import_vertex_positions(fmesh, mesh);
464 import_faces(fmesh, mesh);
465 import_face_material_indices(fmesh, attributes);
466 import_face_smoothing(fmesh, attributes);
467 import_edges(fmesh, mesh, attributes);
468 import_uvs(fmesh, attributes, attr_owner);
469 if (params.vertex_colors != eFBXVertexColorMode::None) {
470 import_colors(fmesh, mesh, attributes, attr_owner, params.vertex_colors);
471 }
472 bool has_custom_normals = false;
473 if (params.use_custom_normals) {
474 /* Mesh validation below can alter the mesh, so we first write custom normals
475 * into a temporary custom corner domain attribute, and then re-apply that
476 * data as custom normals after the validation. */
477 has_custom_normals = import_normals_into_temp_attribute(fmesh, mesh, attributes);
478 }
479 import_skin_vertex_groups(mapping, fmesh, mesh);
480
481 /* Validate if needed. */
482 if (params.validate_meshes) {
483 bool verbose_validate = false;
484#ifndef NDEBUG
485 verbose_validate = true;
486#endif
487 BKE_mesh_validate(mesh, verbose_validate, false);
488 }
489
490 if (has_custom_normals) {
491 /* Actually set custom normals after the validation. */
493 attributes.lookup_or_add_for_write_only_span<float3>(temp_custom_normals_name,
496 normals.finish();
497 attributes.remove(temp_custom_normals_name);
498 }
499
500 meshes[index] = mesh;
501 });
502
503 /* Create final mesh objects in Main, serially. And do steps that need to be done on the final
504 * objects. */
505 for (int64_t index : meshes.index_range()) {
506 Mesh *mesh = meshes[index];
507 if (mesh == nullptr) {
508 continue;
509 }
510 const ufbx_mesh *fmesh = fbx.meshes[index];
511 BLI_assert(fmesh != nullptr);
512
513 Mesh *mesh_main = static_cast<Mesh *>(
514 BKE_object_obdata_add_from_type(&bmain, OB_MESH, get_fbx_name(fmesh->name, "Mesh")));
515 BKE_mesh_nomain_to_mesh(mesh, mesh_main, nullptr);
516 meshes[index] = mesh_main;
517 mesh = mesh_main;
518 if (params.use_custom_props) {
519 read_custom_properties(fmesh->props, mesh->id, params.props_enum_as_string);
520 }
521
522 const bool any_shapes = import_blend_shapes(bmain, mapping, fmesh, mesh);
523
524 /* Create objects that use this mesh. */
525 for (const ufbx_node *node : fmesh->instances) {
526 std::string name;
527 if (node->is_geometry_transform_helper) {
528 /* Name geometry transform adjustment helpers with parent name and _GeomAdjust suffix. */
529 name = get_fbx_name(node->parent->name) + std::string("_GeomAdjust");
530 }
531 else {
532 name = get_fbx_name(node->name);
533 }
534 Object *obj = BKE_object_add_only_object(&bmain, OB_MESH, name.c_str());
535 obj->data = mesh_main;
536 if (!node->visible) {
538 }
539
540 if (any_shapes) {
541 obj->shapenr = 1;
542 }
543
544 bool matrix_already_set = false;
545
546 /* Skinned mesh. */
547 if (fmesh->skin_deformers.count > 0) {
548 /* Add vertex groups to the object. */
549 VectorSet<std::string> bone_set = get_skin_bone_name_set(mapping, fmesh);
550 for (const std::string &name : bone_set) {
551 BKE_object_defgroup_add_name(obj, name.c_str());
552 }
553
554 /* Add armature modifiers for each skin deformer. */
555 for (const ufbx_skin_deformer *skin : fmesh->skin_deformers) {
556 if (!is_skin_deformer_usable(fmesh, skin)) {
557 continue;
558 }
559 Object *arm_obj = nullptr;
560 for (const ufbx_skin_cluster *cluster : skin->clusters) {
561 if (cluster->num_weights == 0) {
562 continue;
563 }
564 arm_obj = mapping.bone_to_armature.lookup_default(cluster->bone_node, nullptr);
565 if (arm_obj != nullptr) {
566 break;
567 }
568 }
569 /* Add armature modifier. */
570 if (arm_obj != nullptr) {
572 STRNCPY(md->name, BKE_id_name(arm_obj->id));
573 BLI_addtail(&obj->modifiers, md);
575 ArmatureModifierData *ad = reinterpret_cast<ArmatureModifierData *>(md);
576 ad->object = arm_obj;
577
578 if (!matrix_already_set) {
579 matrix_already_set = true;
580 obj->parent = arm_obj;
581
582 /* We are setting mesh parent to the armature, so set the matrix that is
583 * armature-local. Note that the matrix needs to be relative to the FBX
584 * node matrix (not the root bone pose matrix). */
585 ufbx_matrix world_to_arm = mapping.armature_world_to_arm_node_matrix.lookup_default(
586 arm_obj, ufbx_identity_matrix);
587 ufbx_matrix world_to_arm_pose = mapping.armature_world_to_arm_pose_matrix
588 .lookup_default(arm_obj, ufbx_identity_matrix);
589
590 ufbx_matrix mtx = ufbx_matrix_mul(&world_to_arm, &node->geometry_to_world);
591 ufbx_matrix_to_obj(mtx, obj);
592
593 /* Setup parent inverse matrix of the mesh, to account for the mesh possibly being in
594 * different bind pose than what the node is at. */
595 ufbx_matrix mtx_inv = ufbx_matrix_invert(&mtx);
596 ufbx_matrix mtx_world = mapping.get_node_bind_matrix(node);
597 ufbx_matrix mtx_parent_inverse = ufbx_matrix_mul(&mtx_world, &mtx_inv);
598 mtx_parent_inverse = ufbx_matrix_mul(&world_to_arm_pose, &mtx_parent_inverse);
599 matrix_to_m44(mtx_parent_inverse, obj->parentinv);
600 }
601 }
602 }
603 }
604
605 if (any_shapes) {
606 import_blend_shape_full_weights(mapping, fmesh, mesh, obj);
607 }
608
609 /* Assign materials. */
610 if (fmesh->materials.count > 0 && node->materials.count == fmesh->materials.count) {
611 int mat_index = 0;
612 for (int mi = 0; mi < fmesh->materials.count; mi++) {
613 const ufbx_material *mesh_fmat = fmesh->materials[mi];
614 const ufbx_material *node_fmat = node->materials[mi];
615 Material *mesh_mat = mapping.mat_to_material.lookup_default(mesh_fmat, nullptr);
616 Material *node_mat = mapping.mat_to_material.lookup_default(node_fmat, nullptr);
617 if (mesh_mat != nullptr) {
618 mat_index++;
619 /* Assign material to the data block. */
620 BKE_object_material_assign_single_obdata(&bmain, obj, mesh_mat, mat_index);
621
622 /* If object material is different, assign that to object. */
623 if (!ELEM(node_mat, nullptr, mesh_mat)) {
624 BKE_object_material_assign(&bmain, obj, node_mat, mat_index, BKE_MAT_ASSIGN_OBJECT);
625 }
626 }
627 }
628 if (mat_index > 0) {
629 obj->actcol = 1;
630 }
631 }
632
633 /* Subdivision. */
634 if (params.import_subdivision &&
635 fmesh->subdivision_display_mode != UFBX_SUBDIVISION_DISPLAY_DISABLED &&
636 (fmesh->subdivision_preview_levels > 0 || fmesh->subdivision_render_levels > 0))
637 {
639 BLI_addtail(&obj->modifiers, md);
641
642 SubsurfModifierData *ssd = reinterpret_cast<SubsurfModifierData *>(md);
644 ssd->levels = fmesh->subdivision_preview_levels;
645 ssd->renderLevels = fmesh->subdivision_render_levels;
646 ssd->boundary_smooth = fmesh->subdivision_boundary ==
647 UFBX_SUBDIVISION_BOUNDARY_SHARP_CORNERS ?
650 }
651
652 if (params.use_custom_props) {
653 read_custom_properties(node->props, obj->id, params.props_enum_as_string);
654 }
655 if (!matrix_already_set) {
656 node_matrix_to_obj(node, obj, mapping);
657 }
658 mapping.el_to_object.add(&node->element, obj);
659 mapping.imported_objects.add(obj);
660 }
661 }
662}
663
664} // namespace blender::io::fbx
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, blender::StringRef name)
Definition attribute.cc:383
support for deformation groups and hooks.
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:529
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:814
Key * BKE_key_add(Main *bmain, ID *id)
Definition key.cc:258
KeyBlock * BKE_keyblock_add(Key *key, const char *name)
Definition key.cc:1835
void BKE_keyblock_convert_from_mesh(const Mesh *mesh, const Key *key, KeyBlock *kb)
Definition key.cc:2203
KeyBlock * BKE_keyblock_find_name(Key *key, const char name[])
Definition key.cc:1949
const char * BKE_id_name(const ID &id)
General operations, lookup, etc. for materials.
void BKE_object_material_assign_single_obdata(Main *bmain, Object *ob, Material *ma, short act)
@ BKE_MAT_ASSIGN_OBJECT
void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
bool BKE_mesh_validate(Mesh *mesh, bool do_verbose, bool cddata_check_mask)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
ModifierData * BKE_modifier_new(int type)
General operations, lookup, etc. for blender objects.
void * BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) ATTR_NONNULL(1)
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
Functions for dealing with objects and deform verts, used by painting and tools.
struct bDeformGroup * BKE_object_defgroup_add_name(struct Object *ob, const char *name)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define ELEM(...)
@ KEY_RELATIVE
struct Material Material
@ SUBSURF_TYPE_CATMULL_CLARK
@ SUBSURF_BOUNDARY_SMOOTH_ALL
@ SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS
struct ModifierData ModifierData
@ eModifierType_Subsurf
@ eModifierType_Armature
struct SubsurfModifierData SubsurfModifierData
struct ArmatureModifierData ArmatureModifierData
Object is a sort of wrapper for general info.
@ OB_HIDE_VIEWPORT
@ OB_MESH
struct Object Object
eFBXVertexColorMode
Definition IO_fbx.hh:21
long long int int64_t
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void reserve(int64_t n)
Definition BLI_map.hh:1028
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t index_of_try(const Key &key) const
bool add(const Key &key)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
#define sqrtf(x)
static float normals[][3]
uint col
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
void mesh_set_custom_normals(Mesh &mesh, MutableSpan< float3 > corner_normals)
static VectorSet< std::string > get_skin_bone_name_set(const FbxElementMapping &mapping, const ufbx_mesh *fmesh)
void ufbx_matrix_to_obj(const ufbx_matrix &mtx, Object *obj)
static void import_blend_shape_full_weights(const FbxElementMapping &mapping, const ufbx_mesh *fmesh, Mesh *mesh, Object *obj)
static void import_colors(const ufbx_mesh *fmesh, Mesh *mesh, bke::MutableAttributeAccessor &attributes, AttributeOwner attr_owner, eFBXVertexColorMode color_mode)
const char * get_fbx_name(const ufbx_string &name, const char *def)
static bool import_blend_shapes(Main &bmain, FbxElementMapping &mapping, const ufbx_mesh *fmesh, Mesh *mesh)
void read_custom_properties(const ufbx_props &props, ID &id, bool enums_as_strings)
void node_matrix_to_obj(const ufbx_node *node, Object *obj, const FbxElementMapping &mapping)
static bool is_skin_deformer_usable(const ufbx_mesh *mesh, const ufbx_skin_deformer *skin)
static constexpr const char * temp_custom_normals_name
void matrix_to_m44(const ufbx_matrix &src, float dst[4][4])
static void import_skin_vertex_groups(const FbxElementMapping &mapping, const ufbx_mesh *fmesh, Mesh *mesh)
static void import_uvs(const ufbx_mesh *fmesh, bke::MutableAttributeAccessor &attributes, AttributeOwner attr_owner)
static void import_face_smoothing(const ufbx_mesh *fmesh, bke::MutableAttributeAccessor &attributes)
static void import_faces(const ufbx_mesh *fmesh, Mesh *mesh)
static void import_face_material_indices(const ufbx_mesh *fmesh, bke::MutableAttributeAccessor &attributes)
static void import_vertex_positions(const ufbx_mesh *fmesh, Mesh *mesh)
static void import_edges(const ufbx_mesh *fmesh, Mesh *mesh, bke::MutableAttributeAccessor &attributes)
static bool import_normals_into_temp_attribute(const ufbx_mesh *fmesh, Mesh *mesh, bke::MutableAttributeAccessor &attributes)
void import_meshes(Main &bmain, const ufbx_scene &fbx, FbxElementMapping &mapping, const FBXImportParams &params)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:343
char name[64]
float curval
char vgroup[64]
void * data
char type
int corners_num
char * default_color_attribute
ListBase vertex_group_names
struct Key * key
char * active_color_attribute
ListBase modifiers
float parentinv[4][4]
short visibility_flag
struct Object * parent
Map< const ufbx_node *, std::string > node_to_name
Map< const ufbx_element *, Key * > el_to_shape_key
i
Definition text_draw.cc:230