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