Blender V5.0
obj_import_mesh.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
8
9#include <algorithm>
10
12#include "DNA_material_types.h"
13#include "DNA_meshdata_types.h"
14
15#include "BKE_attribute.hh"
16#include "BKE_deform.hh"
17#include "BKE_lib_id.hh"
18#include "BKE_material.hh"
19#include "BKE_mesh.hh"
21#include "BKE_object.hh"
22#include "BKE_object_deform.h"
23
24#include "BLI_math_vector.h"
25#include "BLI_set.hh"
26
27#include "IO_wavefront_obj.hh"
29#include "obj_export_mtl.hh"
30#include "obj_import_mesh.hh"
31
32#include "CLG_log.h"
33static CLG_LogRef LOG = {"io.obj"};
34
35namespace blender::io::obj {
36
38{
39 const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()};
40 if (tot_verts_object <= 0) {
41 /* Empty mesh */
42 return nullptr;
43 }
44
45 this->fixup_invalid_faces();
46
47 /* Includes explicitly imported edges, not the ones belonging the faces to be created. */
48 Mesh *mesh = BKE_mesh_new_nomain(tot_verts_object,
49 mesh_geometry_.edges_.size(),
50 mesh_geometry_.face_elements_.size(),
51 mesh_geometry_.total_corner_);
52
53 this->create_vertices(mesh);
54 this->create_faces(mesh, import_params.import_vertex_groups && !import_params.use_split_groups);
55 this->create_edges(mesh);
56 this->create_uv_verts(mesh);
57 this->create_normals(mesh);
58 this->create_colors(mesh);
59
60 if (import_params.validate_meshes || mesh_geometry_.has_invalid_faces_) {
61 bool verbose_validate = false;
62#ifndef NDEBUG
63 verbose_validate = true;
64#endif
65 BKE_mesh_validate(mesh, verbose_validate, false);
66 }
67
68 return mesh;
69}
70
72 Main *bmain,
73 Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
74 Map<std::string, Material *> &created_materials,
75 const OBJImportParams &import_params)
76{
77 Mesh *mesh = this->create_mesh(import_params);
78
79 if (mesh == nullptr) {
80 return nullptr;
81 }
82
83 std::string ob_name = get_geometry_name(mesh_geometry_.geometry_name_,
84 import_params.collection_separator);
85 if (ob_name.empty()) {
86 ob_name = "Untitled";
87 }
88
89 Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name.c_str());
90 obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str());
91
92 this->create_materials(bmain,
93 materials,
94 created_materials,
95 obj,
96 import_params.relative_paths,
97 import_params.mtl_name_collision_mode);
98
99 BKE_mesh_nomain_to_mesh(mesh, static_cast<Mesh *>(obj->data), obj);
100
101 transform_object(obj, import_params);
102
103 /* NOTE: vertex groups have to be created after final mesh is assigned to the object. */
104 this->create_vertex_groups(obj);
105
106 return obj;
107}
108
109void MeshFromGeometry::fixup_invalid_faces()
110{
111 for (int64_t face_idx = 0; face_idx < mesh_geometry_.face_elements_.size(); ++face_idx) {
112 const FaceElem &curr_face = mesh_geometry_.face_elements_[face_idx];
113
114 if (curr_face.corner_count_ < 3) {
115 /* Skip and remove faces that have fewer than 3 corners. */
116 mesh_geometry_.total_corner_ -= curr_face.corner_count_;
117 mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
118 --face_idx;
119 continue;
120 }
121
122 /* Check if face is invalid for Blender conventions:
123 * basically whether it has duplicate vertex indices. */
124 bool valid = true;
125 Set<int, 8> used_verts;
126 for (int i = 0; i < curr_face.corner_count_; ++i) {
127 int corner_idx = curr_face.start_index_ + i;
128 int vertex_idx = mesh_geometry_.face_corners_[corner_idx].vert_index;
129 if (used_verts.contains(vertex_idx)) {
130 valid = false;
131 break;
132 }
133 used_verts.add(vertex_idx);
134 }
135 if (valid) {
136 continue;
137 }
138
139 /* We have an invalid face, have to turn it into possibly
140 * multiple valid faces. */
141 Vector<int, 8> face_verts;
142 Vector<int, 8> face_uvs;
143 Vector<int, 8> face_normals;
144 face_verts.reserve(curr_face.corner_count_);
145 face_uvs.reserve(curr_face.corner_count_);
146 face_normals.reserve(curr_face.corner_count_);
147 for (int i = 0; i < curr_face.corner_count_; ++i) {
148 int corner_idx = curr_face.start_index_ + i;
149 const FaceCorner &corner = mesh_geometry_.face_corners_[corner_idx];
150 face_verts.append(corner.vert_index);
151 face_normals.append(corner.vertex_normal_index);
152 face_uvs.append(corner.uv_vert_index);
153 }
154 int face_vertex_group = curr_face.vertex_group_index;
155 int face_material = curr_face.material_index;
156 bool face_shaded_smooth = curr_face.shaded_smooth;
157
158 /* Remove the invalid face. */
159 mesh_geometry_.total_corner_ -= curr_face.corner_count_;
160 mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
161 --face_idx;
162
163 Vector<Vector<int>> new_faces = fixup_invalid_face(global_vertices_.vertices, face_verts);
164
165 /* Create the newly formed faces. */
166 for (Span<int> face : new_faces) {
167 if (face.size() < 3) {
168 continue;
169 }
170 FaceElem new_face{};
171 new_face.vertex_group_index = face_vertex_group;
172 new_face.material_index = face_material;
173 new_face.shaded_smooth = face_shaded_smooth;
174 new_face.start_index_ = mesh_geometry_.face_corners_.size();
175 new_face.corner_count_ = face.size();
176 for (int idx : face) {
177 BLI_assert(idx >= 0 && idx < face_verts.size());
178 mesh_geometry_.face_corners_.append({face_verts[idx], face_uvs[idx], face_normals[idx]});
179 }
180 mesh_geometry_.face_elements_.append(new_face);
181 mesh_geometry_.total_corner_ += face.size();
182 }
183 }
184}
185
186void MeshFromGeometry::create_vertices(Mesh *mesh)
187{
188 MutableSpan<float3> positions = mesh->vert_positions_for_write();
189 /* Go through all the global vertex indices from min to max,
190 * checking which ones are actually and building a global->local
191 * index mapping. Write out the used vertex positions into the Mesh
192 * data. */
193 mesh_geometry_.global_to_local_vertices_.clear();
194 mesh_geometry_.global_to_local_vertices_.reserve(mesh_geometry_.vertices_.size());
195 for (int vi = mesh_geometry_.vertex_index_min_; vi <= mesh_geometry_.vertex_index_max_; ++vi) {
196 BLI_assert(vi >= 0 && vi < global_vertices_.vertices.size());
197 if (!mesh_geometry_.vertices_.contains(vi)) {
198 continue;
199 }
200 int local_vi = int(mesh_geometry_.global_to_local_vertices_.size());
201 BLI_assert(local_vi >= 0 && local_vi < mesh->verts_num);
202 copy_v3_v3(positions[local_vi], global_vertices_.vertices[vi]);
203 mesh_geometry_.global_to_local_vertices_.add_new(vi, local_vi);
204 }
205}
206
207void MeshFromGeometry::create_faces(Mesh *mesh, bool use_vertex_groups)
208{
209 MutableSpan<MDeformVert> dverts;
210 const int64_t total_verts = mesh_geometry_.get_vertex_count();
211 if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) {
212 dverts = mesh->deform_verts_for_write();
213 }
214
215 Span<float3> positions = mesh->vert_positions();
216 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
217 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
218 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
219 bke::SpanAttributeWriter<int> material_indices =
220 attributes.lookup_or_add_for_write_only_span<int>("material_index", bke::AttrDomain::Face);
221
222 const bool set_face_sharpness = !has_normals();
223 bke::SpanAttributeWriter<bool> sharp_faces = attributes.lookup_or_add_for_write_span<bool>(
224 "sharp_face", bke::AttrDomain::Face);
225
226 int corner_index = 0;
227
228 for (int face_idx = 0; face_idx < mesh->faces_num; ++face_idx) {
229 const FaceElem &curr_face = mesh_geometry_.face_elements_[face_idx];
230 if (curr_face.corner_count_ < 3) {
231 /* Don't add single vertex face, or edges. */
232 CLOG_WARN(&LOG, "Face with less than 3 vertices found, skipping.");
233 continue;
234 }
235
236 face_offsets[face_idx] = corner_index;
237 if (set_face_sharpness) {
238 /* If we have no vertex normals, set face sharpness flag based on
239 * whether smooth shading is off. */
240 sharp_faces.span[face_idx] = !curr_face.shaded_smooth;
241 }
242
243 material_indices.span[face_idx] = curr_face.material_index;
244 /* Importing obj files without any materials would result in negative indices, which is not
245 * supported. */
246 material_indices.span[face_idx] = std::max(material_indices.span[face_idx], 0);
247
248 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
249 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
250 corner_verts[corner_index] = mesh_geometry_.global_to_local_vertices_.lookup_default(
251 curr_corner.vert_index, 0);
252
253 /* Setup vertex group data, if needed. */
254 if (!dverts.is_empty()) {
255 const int group_index = curr_face.vertex_group_index;
256 /* NOTE: face might not belong to any group. */
257 if (group_index >= 0 || true) {
258 MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[corner_verts[corner_index]],
259 group_index);
260 dw->weight = 1.0f;
261 }
262 }
263
264 corner_index++;
265 }
266
267 if (!set_face_sharpness) {
268 /* If we do have vertex normals, we do not want to set face sharpness.
269 * Exception is, if degenerate faces (zero area, with co-colocated
270 * vertices) are present in the input data; this confuses custom
271 * corner normals calculation in Blender. Set such faces as sharp,
272 * they will be not shared across smooth vertex face fans. */
273 const float area = bke::mesh::face_area_calc(
274 positions, corner_verts.slice(face_offsets[face_idx], curr_face.corner_count_));
275 if (area < 1.0e-12f) {
276 sharp_faces.span[face_idx] = true;
277 }
278 }
279 }
280
281 material_indices.finish();
282 sharp_faces.finish();
283}
284
285void MeshFromGeometry::create_vertex_groups(Object *obj)
286{
287 Mesh *mesh = static_cast<Mesh *>(obj->data);
288 if (mesh->deform_verts().is_empty()) {
289 return;
290 }
291 for (const std::string &name : mesh_geometry_.group_order_) {
293 }
294}
295
296void MeshFromGeometry::create_edges(Mesh *mesh)
297{
298 MutableSpan<int2> edges = mesh->edges_for_write();
299
300 const int64_t tot_edges{mesh_geometry_.edges_.size()};
301 const int64_t total_verts{mesh_geometry_.get_vertex_count()};
302 UNUSED_VARS_NDEBUG(total_verts);
303 for (int i = 0; i < tot_edges; ++i) {
304 const int2 &src_edge = mesh_geometry_.edges_[i];
305 int2 &dst_edge = edges[i];
306 dst_edge[0] = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge[0], 0);
307 dst_edge[1] = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge[1], 0);
308 BLI_assert(dst_edge[0] < total_verts && dst_edge[1] < total_verts);
309 }
310
311 /* Set argument `update` to true so that existing, explicitly imported edges can be merged
312 * with the new ones created from faces. */
313 bke::mesh_calc_edges(*mesh, true, false);
314}
315
316void MeshFromGeometry::create_uv_verts(Mesh *mesh)
317{
318 if (global_vertices_.uv_vertices.size() <= 0) {
319 return;
320 }
321
322 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
323 bke::SpanAttributeWriter<float2> uv_map = attributes.lookup_or_add_for_write_only_span<float2>(
324 "UVMap", bke::AttrDomain::Corner);
325
326 int corner_index = 0;
327 bool added_uv = false;
328
329 for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
330 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
331 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
332 if (curr_corner.uv_vert_index >= 0 &&
333 curr_corner.uv_vert_index < global_vertices_.uv_vertices.size())
334 {
335 uv_map.span[corner_index] = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
336 added_uv = true;
337 }
338 else {
339 uv_map.span[corner_index] = {0.0f, 0.0f};
340 }
341 corner_index++;
342 }
343 }
344
345 uv_map.finish();
346
347 /* If we have an object without UVs which resides in the same `.obj` file
348 * as an object which *does* have UVs we can end up adding a UV layer
349 * filled with zeroes.
350 * We could maybe check before creating this layer but that would need
351 * iterating over the whole mesh to check for UVs and as this is probably
352 * the exception rather than the rule, just delete it afterwards.
353 */
354 if (!added_uv) {
355 attributes.remove("UVMap");
356 }
357}
358
360 const std::string &name,
361 Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
362 Map<std::string, Material *> &created_materials,
363 bool relative_paths,
364 eOBJMtlNameCollisionMode mtl_name_collision_mode)
365{
366 /* Have we created this material already in this import session? */
367 Material **found_mat = created_materials.lookup_ptr(name);
368 if (found_mat != nullptr) {
369 return *found_mat;
370 }
371
372 /* Check if a material with this name already exists in the main database */
373 Material *existing_mat = (Material *)BKE_libblock_find_name(bmain, ID_MA, name.c_str());
374 if (existing_mat != nullptr &&
375 mtl_name_collision_mode == OBJ_MTL_NAME_COLLISION_REFERENCE_EXISTING)
376 {
377 /* If the collision mode is set to reference existing materials, use the existing one */
378 created_materials.add_new(name, existing_mat);
379 return existing_mat;
380 }
381
382 /* We need to create a new material */
383 const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>());
384
385 /* If we're in MAKE_UNIQUE mode and a material with this name already exists,
386 * BKE_material_add will automatically create a unique name */
387 Material *mat = BKE_material_add(bmain, name.c_str());
388 id_us_min(&mat->id);
389
390 mat->nodetree = create_mtl_node_tree(bmain, mtl, mat, relative_paths);
392
393 created_materials.add_new(name, mat);
394 return mat;
395}
396
397void MeshFromGeometry::create_materials(Main *bmain,
398 Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
399 Map<std::string, Material *> &created_materials,
400 Object *obj,
401 bool relative_paths,
402 eOBJMtlNameCollisionMode mtl_name_collision_mode)
403{
404 for (const std::string &name : mesh_geometry_.material_order_) {
406 bmain, name, materials, created_materials, relative_paths, mtl_name_collision_mode);
407 if (mat == nullptr) {
408 continue;
409 }
410 BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1);
411 }
412 if (obj->totcol > 0) {
413 obj->actcol = 1;
414 }
415}
416
417bool MeshFromGeometry::has_normals() const
418{
419 return !global_vertices_.vert_normals.is_empty() && mesh_geometry_.total_corner_ != 0;
420}
421
422void MeshFromGeometry::create_normals(Mesh *mesh)
423{
424 if (!has_normals()) {
425 return;
426 }
427
428 Array<float3> corner_normals(mesh_geometry_.total_corner_);
429 int corner_index = 0;
430 for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
431 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
432 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
433 int n_index = curr_corner.vertex_normal_index;
434 float3 normal(0, 0, 0);
435 if (n_index >= 0 && n_index < global_vertices_.vert_normals.size()) {
436 normal = global_vertices_.vert_normals[n_index];
437 }
438 corner_normals[corner_index] = normal;
439 corner_index++;
440 }
441 }
442 bke::mesh_set_custom_normals(*mesh, corner_normals);
443}
444
445void MeshFromGeometry::create_colors(Mesh *mesh)
446{
447 /* Nothing to do if we don't have vertex colors at all. */
448 if (global_vertices_.vertex_colors.is_empty()) {
449 return;
450 }
451
452 /* First pass to determine if we need to create a color attribute. */
453 for (int vi : mesh_geometry_.vertices_) {
454 if (!global_vertices_.has_vertex_color(vi)) {
455 return;
456 }
457 }
458
459 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
460 CustomDataLayer *color_layer = BKE_attribute_new(
461 owner, "Color", CD_PROP_COLOR, bke::AttrDomain::Point, nullptr);
462 BKE_id_attributes_active_color_set(&mesh->id, color_layer->name);
463 BKE_id_attributes_default_color_set(&mesh->id, color_layer->name);
464 float4 *colors = (float4 *)color_layer->data;
465
466 /* Second pass to fill out the data. */
467 for (auto item : mesh_geometry_.global_to_local_vertices_.items()) {
468 const int vi = item.key;
469 const int local_vi = item.value;
470 BLI_assert(vi >= 0 && vi < global_vertices_.vertex_colors.size());
471 BLI_assert(local_vi >= 0 && local_vi < mesh->verts_num);
472 const float3 &c = global_vertices_.vertex_colors[vi];
473 colors[local_vi] = float4(c.x, c.y, c.z, 1.0);
474 }
475}
476
477} // namespace blender::io::obj
void BKE_id_attributes_default_color_set(struct ID *id, std::optional< blender::StringRef > name)
struct CustomDataLayer * BKE_attribute_new(AttributeOwner &owner, blender::StringRef name, eCustomDataType type, blender::bke::AttrDomain domain, struct ReportList *reports)
Definition attribute.cc:382
void BKE_id_attributes_active_color_set(struct ID *id, std::optional< blender::StringRef > name)
Definition attribute.cc:985
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:825
ID * BKE_libblock_find_name(Main *bmain, short type, const char *name, const std::optional< Library * > lib=std::nullopt) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition lib_id.cc:1710
void id_us_min(ID *id)
Definition lib_id.cc:366
General operations, lookup, etc. for materials.
void BKE_object_material_assign_single_obdata(Main *bmain, Object *ob, Material *ma, short act)
Material * BKE_material_add(Main *bmain, const char *name)
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_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
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(a)
Definition BLI_assert.h:46
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define UNUSED_VARS_NDEBUG(...)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
@ ID_MA
struct CustomDataLayer CustomDataLayer
@ CD_PROP_COLOR
struct Mesh Mesh
struct MDeformWeight MDeformWeight
@ OB_MESH
struct Object Object
eOBJMtlNameCollisionMode
@ OBJ_MTL_NAME_COLLISION_REFERENCE_EXISTING
long long int int64_t
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr bool is_empty() const
Definition BLI_span.hh:509
int64_t size() const
void append(const T &value)
void reserve(const int64_t min_capacity)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
Mesh * create_mesh(const OBJImportParams &import_params)
Object * create_mesh_object(Main *bmain, Map< std::string, std::unique_ptr< MTLMaterial > > &materials, Map< std::string, Material * > &created_materials, const OBJImportParams &import_params)
#define LOG(level)
Definition log.h:97
float face_area_calc(Span< float3 > vert_positions, Span< int > face_verts)
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 Material * get_or_create_material(Main *bmain, const std::string &name, Map< std::string, std::unique_ptr< MTLMaterial > > &materials, Map< std::string, Material * > &created_materials, bool relative_paths, eOBJMtlNameCollisionMode mtl_name_collision_mode)
bNodeTree * create_mtl_node_tree(Main *bmain, const MTLMaterial &mtl_mat, Material *mat, bool relative_paths)
void transform_object(Object *object, const OBJImportParams &import_params)
Vector< Vector< int > > fixup_invalid_face(Span< float3 > vert_positions, Span< int > face_verts)
std::string get_geometry_name(const std::string &full_name, char separator)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
const char * name
struct bNodeTree * nodetree
void clear(bool preserve_shaders=false) override
int faces_num
eOBJMtlNameCollisionMode mtl_name_collision_mode
Vector< std::string > material_order_
Vector< FaceCorner > face_corners_
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
i
Definition text_draw.cc:230