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