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
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 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
208 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
209 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
210 bke::SpanAttributeWriter<int> material_indices =
211 attributes.lookup_or_add_for_write_only_span<int>("material_index", bke::AttrDomain::Face);
212
213 const bool do_sharp = !has_normals();
214 bke::SpanAttributeWriter<bool> sharp_faces;
215 if (do_sharp) {
216 sharp_faces = attributes.lookup_or_add_for_write_span<bool>("sharp_face",
218 }
219
220 int corner_index = 0;
221
222 for (int face_idx = 0; face_idx < mesh->faces_num; ++face_idx) {
223 const FaceElem &curr_face = mesh_geometry_.face_elements_[face_idx];
224 if (curr_face.corner_count_ < 3) {
225 /* Don't add single vertex face, or edges. */
226 std::cerr << "Face with less than 3 vertices found, skipping." << std::endl;
227 continue;
228 }
229
230 face_offsets[face_idx] = corner_index;
231 if (do_sharp) {
232 sharp_faces.span[face_idx] = !curr_face.shaded_smooth;
233 }
234 material_indices.span[face_idx] = curr_face.material_index;
235 /* Importing obj files without any materials would result in negative indices, which is not
236 * supported. */
237 if (material_indices.span[face_idx] < 0) {
238 material_indices.span[face_idx] = 0;
239 }
240
241 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
242 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
243 corner_verts[corner_index] = mesh_geometry_.global_to_local_vertices_.lookup_default(
244 curr_corner.vert_index, 0);
245
246 /* Setup vertex group data, if needed. */
247 if (!dverts.is_empty()) {
248 const int group_index = curr_face.vertex_group_index;
249 /* NOTE: face might not belong to any group. */
250 if (group_index >= 0 || true) {
251 MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[corner_verts[corner_index]],
252 group_index);
253 dw->weight = 1.0f;
254 }
255 }
256
257 corner_index++;
258 }
259 }
260
261 material_indices.finish();
262 if (do_sharp) {
263 sharp_faces.finish();
264 }
265}
266
267void MeshFromGeometry::create_vertex_groups(Object *obj)
268{
269 Mesh *mesh = static_cast<Mesh *>(obj->data);
270 if (mesh->deform_verts().is_empty()) {
271 return;
272 }
273 for (const std::string &name : mesh_geometry_.group_order_) {
274 BKE_object_defgroup_add_name(obj, name.data());
275 }
276}
277
278void MeshFromGeometry::create_edges(Mesh *mesh)
279{
280 MutableSpan<int2> edges = mesh->edges_for_write();
281
282 const int64_t tot_edges{mesh_geometry_.edges_.size()};
283 const int64_t total_verts{mesh_geometry_.get_vertex_count()};
284 UNUSED_VARS_NDEBUG(total_verts);
285 for (int i = 0; i < tot_edges; ++i) {
286 const int2 &src_edge = mesh_geometry_.edges_[i];
287 int2 &dst_edge = edges[i];
288 dst_edge[0] = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge[0], 0);
289 dst_edge[1] = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge[1], 0);
290 BLI_assert(dst_edge[0] < total_verts && dst_edge[1] < total_verts);
291 }
292
293 /* Set argument `update` to true so that existing, explicitly imported edges can be merged
294 * with the new ones created from faces. */
295 bke::mesh_calc_edges(*mesh, true, false);
296}
297
298void MeshFromGeometry::create_uv_verts(Mesh *mesh)
299{
300 if (global_vertices_.uv_vertices.size() <= 0) {
301 return;
302 }
303
304 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
305 bke::SpanAttributeWriter<float2> uv_map = attributes.lookup_or_add_for_write_only_span<float2>(
306 "UVMap", bke::AttrDomain::Corner);
307
308 int corner_index = 0;
309 bool added_uv = false;
310
311 for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
312 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
313 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
314 if (curr_corner.uv_vert_index >= 0 &&
315 curr_corner.uv_vert_index < global_vertices_.uv_vertices.size())
316 {
317 uv_map.span[corner_index] = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
318 added_uv = true;
319 }
320 else {
321 uv_map.span[corner_index] = {0.0f, 0.0f};
322 }
323 corner_index++;
324 }
325 }
326
327 uv_map.finish();
328
329 /* If we have an object without UVs which resides in the same `.obj` file
330 * as an object which *does* have UVs we can end up adding a UV layer
331 * filled with zeroes.
332 * We could maybe check before creating this layer but that would need
333 * iterating over the whole mesh to check for UVs and as this is probably
334 * the exception rather than the rule, just delete it afterwards.
335 */
336 if (!added_uv) {
337 attributes.remove("UVMap");
338 }
339}
340
342 const std::string &name,
343 Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
344 Map<std::string, Material *> &created_materials,
345 bool relative_paths)
346{
347 /* Have we created this material already? */
348 Material **found_mat = created_materials.lookup_ptr(name);
349 if (found_mat != nullptr) {
350 return *found_mat;
351 }
352
353 /* We have not, will have to create it. Create a new default
354 * MTLMaterial too, in case the OBJ file tries to use a material
355 * that was not in the MTL file. */
356 const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>());
357
358 Material *mat = BKE_material_add(bmain, name.c_str());
359 id_us_min(&mat->id);
360
361 mat->use_nodes = true;
362 mat->nodetree = create_mtl_node_tree(bmain, mtl, mat, relative_paths);
363 BKE_ntree_update_main_tree(bmain, mat->nodetree, nullptr);
364
365 created_materials.add_new(name, mat);
366 return mat;
367}
368
369void MeshFromGeometry::create_materials(Main *bmain,
370 Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
371 Map<std::string, Material *> &created_materials,
372 Object *obj,
373 bool relative_paths)
374{
375 for (const std::string &name : mesh_geometry_.material_order_) {
377 bmain, name, materials, created_materials, relative_paths);
378 if (mat == nullptr) {
379 continue;
380 }
381 BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1);
382 }
383 if (obj->totcol > 0) {
384 obj->actcol = 1;
385 }
386}
387
388bool MeshFromGeometry::has_normals() const
389{
390 return !global_vertices_.vert_normals.is_empty() && mesh_geometry_.total_corner_ != 0;
391}
392
393void MeshFromGeometry::create_normals(Mesh *mesh)
394{
395 if (!has_normals()) {
396 return;
397 }
398
399 Array<float3> corner_normals(mesh_geometry_.total_corner_);
400 int corner_index = 0;
401 for (const FaceElem &curr_face : mesh_geometry_.face_elements_) {
402 for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
403 const FaceCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
404 int n_index = curr_corner.vertex_normal_index;
405 float3 normal(0, 0, 0);
406 if (n_index >= 0 && n_index < global_vertices_.vert_normals.size()) {
407 normal = global_vertices_.vert_normals[n_index];
408 }
409 corner_normals[corner_index] = normal;
410 corner_index++;
411 }
412 }
413 BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(corner_normals.data()));
414}
415
416void MeshFromGeometry::create_colors(Mesh *mesh)
417{
418 /* Nothing to do if we don't have vertex colors at all. */
419 if (global_vertices_.vertex_colors.is_empty()) {
420 return;
421 }
422
423 /* First pass to determine if we need to create a color attribute. */
424 for (int vi : mesh_geometry_.vertices_) {
425 if (!global_vertices_.has_vertex_color(vi)) {
426 return;
427 }
428 }
429
430 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
431 CustomDataLayer *color_layer = BKE_attribute_new(
432 owner, "Color", CD_PROP_COLOR, bke::AttrDomain::Point, nullptr);
433 BKE_id_attributes_active_color_set(&mesh->id, color_layer->name);
434 BKE_id_attributes_default_color_set(&mesh->id, color_layer->name);
435 float4 *colors = (float4 *)color_layer->data;
436
437 /* Second pass to fill out the data. */
438 for (auto item : mesh_geometry_.global_to_local_vertices_.items()) {
439 const int vi = item.key;
440 const int local_vi = item.value;
441 BLI_assert(vi >= 0 && vi < global_vertices_.vertex_colors.size());
442 BLI_assert(local_vi >= 0 && local_vi < mesh->verts_num);
443 const float3 &c = global_vertices_.vertex_colors[vi];
444 colors[local_vi] = float4(c.x, c.y, c.z, 1.0);
445 }
446}
447
448} // 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)
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(...)
@ CD_PROP_COLOR
@ OB_MESH
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
void clear()
Definition BLI_map.hh:989
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
int64_t size() const
Definition BLI_map.hh:927
void reserve(int64_t n)
Definition BLI_map.hh:979
int64_t size() const
Definition BLI_set.hh:564
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
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)
VecBase< float, 4 > float4
__int64 int64_t
Definition stdint.h:89
struct bNodeTree * nodetree
Map< int, int > global_to_local_vertices_
Vector< FaceCorner > face_corners_
bool has_vertex_color(size_t index) const
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27