Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/io')
-rw-r--r--source/blender/io/alembic/ABC_alembic.h30
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc13
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc52
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc13
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc92
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.h9
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc6
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc2
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc26
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp4
-rw-r--r--source/blender/io/collada/ArmatureExporter.cpp2
-rw-r--r--source/blender/io/collada/BCAnimationCurve.cpp4
-rw-r--r--source/blender/io/collada/BlenderContext.cpp28
-rw-r--r--source/blender/io/collada/BlenderContext.h8
-rw-r--r--source/blender/io/collada/ControllerExporter.cpp7
-rw-r--r--source/blender/io/collada/DocumentImporter.cpp11
-rw-r--r--source/blender/io/collada/EffectExporter.cpp6
-rw-r--r--source/blender/io/collada/ExportSettings.h7
-rw-r--r--source/blender/io/collada/GeometryExporter.cpp89
-rw-r--r--source/blender/io/collada/ImportSettings.h1
-rw-r--r--source/blender/io/collada/Materials.cpp2
-rw-r--r--source/blender/io/collada/MeshImporter.cpp135
-rw-r--r--source/blender/io/collada/MeshImporter.h7
-rw-r--r--source/blender/io/collada/SceneExporter.cpp8
-rw-r--r--source/blender/io/collada/collada.cpp4
-rw-r--r--source/blender/io/collada/collada_utils.cpp3
-rw-r--r--source/blender/io/common/CMakeLists.txt4
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h6
-rw-r--r--source/blender/io/gpencil/gpencil_io.h2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc10
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh6
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.cc17
-rw-r--r--source/blender/io/stl/CMakeLists.txt8
-rw-r--r--source/blender/io/stl/importer/stl_import.cc3
-rw-r--r--source/blender/io/stl/importer/stl_import_mesh.cc25
-rw-r--r--source/blender/io/usd/intern/usd_capi_import.cc28
-rw-r--r--source/blender/io/usd/intern/usd_reader_material.cc86
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc132
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.h11
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.cc15
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.h2
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc64
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.cc2
-rw-r--r--source/blender/io/usd/usd.h2
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.cc17
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.h13
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc349
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh34
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_io.hh404
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc116
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh5
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc214
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh99
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc11
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc222
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.cc145
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.hh3
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.cc335
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.hh88
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_objects.hh71
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc28
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh14
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_importer.cc16
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc47
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh1
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc23
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_importer_tests.cc450
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc259
71 files changed, 2282 insertions, 1682 deletions
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index ded3258ff18..05025861857 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -60,6 +60,28 @@ struct AlembicExportParams {
float global_scale;
};
+struct AlembicImportParams {
+ /* Multiplier for the cached data scale. Mostly useful if the data is stored in a different unit
+ * as what Blender expects (e.g. centimeters instead of meters). */
+ float global_scale;
+
+ /* Number of consecutive files to expect if the cached animation is split in a sequence. */
+ int sequence_len;
+ /* Start frame of the sequence, offset from 0. */
+ int sequence_offset;
+ /* True if the cache is split in multiple files. */
+ bool is_sequence;
+
+ /* True if the importer should set the current scene's start and end frame based on the start and
+ * end frames of the cached animation. */
+ bool set_frame_range;
+ /* True if imported meshes should be validated. Error messages are sent to the console. */
+ bool validate_meshes;
+ /* True if a cache reader should be added regardless of whether there is animated data in the
+ * cached file. */
+ bool always_add_cache_reader;
+};
+
/* The ABC_export and ABC_import functions both take a as_background_job
* parameter, and return a boolean.
*
@@ -78,13 +100,7 @@ bool ABC_export(struct Scene *scene,
bool ABC_import(struct bContext *C,
const char *filepath,
- float scale,
- bool is_sequence,
- bool set_frame_range,
- int sequence_len,
- int offset,
- bool validate_meshes,
- bool always_add_cache_reader,
+ const struct AlembicImportParams *params,
bool as_background_job);
struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain,
diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
index 514ce389e36..712b04f3992 100644
--- a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
@@ -14,6 +14,7 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
+#include "BKE_layer.h"
#include "BKE_modifier.h"
namespace blender::io::alembic {
@@ -34,7 +35,8 @@ void SubdivModifierDisabler::disable_modifiers()
Scene *scene = DEG_get_input_scene(depsgraph_);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_MESH) {
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index 99c609b0235..4f09aee3ea9 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -120,9 +120,9 @@ void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, context.object->obmat);
- MTFace *mtface = mesh->mtface;
- MFace *mface = mesh->mface;
- MVert *mverts = mesh->mvert;
+ MTFace *mtface = (MTFace *)CustomData_get_layer(&mesh->fdata, CD_MTFACE);
+ MFace *mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ const MVert *mverts = BKE_mesh_verts(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
if ((!mtface || !mface) && !uv_warning_shown_) {
@@ -243,8 +243,9 @@ void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, context.object->obmat);
- MTFace *mtface = mesh->mtface;
- MVert *mverts = mesh->mvert;
+ MFace *mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ MTFace *mtface = (MTFace *)CustomData_get_layer(&mesh->fdata, CD_MTFACE);
+ const MVert *mverts = BKE_mesh_verts(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
ParticleSystem *psys = context.particle_system;
@@ -269,7 +270,7 @@ void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
continue;
}
- MFace *face = &mesh->mface[num];
+ MFace *face = &mface[num];
MTFace *tface = mtface + num;
float r_uv[2], tmpnor[3], mapfw[4], vec[3];
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 07b185ffd64..7d38cd1ec88 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -12,6 +12,7 @@
#include "BLI_math_vector.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
@@ -175,8 +176,8 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context)
m_custom_data_config.pack_uvs = args_.export_params->packuv;
m_custom_data_config.mesh = mesh;
- m_custom_data_config.mpoly = mesh->mpoly;
- m_custom_data_config.mloop = mesh->mloop;
+ m_custom_data_config.mpoly = mesh->polys_for_write().data();
+ m_custom_data_config.mloop = mesh->loops_for_write().data();
m_custom_data_config.totpoly = mesh->totpoly;
m_custom_data_config.totloop = mesh->totloop;
m_custom_data_config.totvert = mesh->totvert;
@@ -366,7 +367,7 @@ bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::
{
/* Export velocity attribute output by fluid sim, sequence cache modifier
* and geometry nodes. */
- CustomDataLayer *velocity_layer = BKE_id_attribute_find(
+ const CustomDataLayer *velocity_layer = BKE_id_attribute_find(
&mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT);
if (velocity_layer == nullptr) {
@@ -390,12 +391,12 @@ void ABCGenericMeshWriter::get_geo_groups(Object *object,
struct Mesh *mesh,
std::map<std::string, std::vector<int32_t>> &geo_groups)
{
- const int num_poly = mesh->totpoly;
- MPoly *polygons = mesh->mpoly;
+ const bke::AttributeAccessor attributes = mesh->attributes();
+ const VArraySpan<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
- for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
- short mnr = current_poly.mat_nr;
+ for (const int i : material_indices.index_range()) {
+ short mnr = material_indices[i];
Material *mat = BKE_object_material_get(object, mnr + 1);
@@ -435,8 +436,7 @@ static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
points.clear();
points.resize(mesh->totvert);
- MVert *verts = mesh->mvert;
-
+ const Span<MVert> verts = mesh->verts();
for (int i = 0, e = mesh->totvert; i < e; i++) {
copy_yup_from_zup(points[i].getValue(), verts[i].co);
}
@@ -447,25 +447,23 @@ static void get_topology(struct Mesh *mesh,
std::vector<int32_t> &loop_counts,
bool &r_has_flat_shaded_poly)
{
- const int num_poly = mesh->totpoly;
- const int num_loops = mesh->totloop;
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
r_has_flat_shaded_poly = false;
poly_verts.clear();
loop_counts.clear();
- poly_verts.reserve(num_loops);
- loop_counts.reserve(num_poly);
+ poly_verts.reserve(loops.size());
+ loop_counts.reserve(polys.size());
/* NOTE: data needs to be written in the reverse order. */
- for (int i = 0; i < num_poly; i++) {
- MPoly &poly = mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
loop_counts.push_back(poly.totloop);
r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
- MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+ const MLoop *loop = &loops[poly.loopstart + (poly.totloop - 1)];
for (int j = 0; j < poly.totloop; j++, loop--) {
poly_verts.push_back(loop->v);
@@ -484,14 +482,14 @@ static void get_edge_creases(struct Mesh *mesh,
lengths.clear();
sharpnesses.clear();
- MEdge *edge = mesh->medge;
+ const Span<MEdge> edges = mesh->edges();
- for (int i = 0, e = mesh->totedge; i < e; i++) {
- const float sharpness = static_cast<float>(edge[i].crease) * factor;
+ for (const int i : edges.index_range()) {
+ const float sharpness = static_cast<float>(edges[i].crease) * factor;
if (sharpness != 0.0f) {
- indices.push_back(edge[i].v1);
- indices.push_back(edge[i].v2);
+ indices.push_back(edges[i].v1);
+ indices.push_back(edges[i].v2);
sharpnesses.push_back(sharpness);
}
}
@@ -543,8 +541,10 @@ static void get_loop_normals(struct Mesh *mesh,
/* NOTE: data needs to be written in the reverse order. */
int abc_index = 0;
- MPoly *mp = mesh->mpoly;
- for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
+ const Span<MPoly> polys = mesh->polys();
+
+ for (const int i : polys.index_range()) {
+ const MPoly *mp = &polys[i];
for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
int blender_index = mp->loopstart + j;
copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 2820a128072..5494bfaa6e8 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -57,7 +57,7 @@ static void get_uvs(const CDStreamConfig &config,
}
const int num_poly = config.totpoly;
- MPoly *polygons = config.mpoly;
+ MPoly *mpoly = config.mpoly;
MLoop *mloop = config.mloop;
if (!config.pack_uvs) {
@@ -67,7 +67,7 @@ static void get_uvs(const CDStreamConfig &config,
/* Iterate in reverse order to match exported polygons. */
for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
+ MPoly &current_poly = mpoly[i];
const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
for (int j = 0; j < current_poly.totloop; j++, count++) {
@@ -85,7 +85,7 @@ static void get_uvs(const CDStreamConfig &config,
int idx_count = 0;
for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
+ MPoly &current_poly = mpoly[i];
MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop;
const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
@@ -540,7 +540,7 @@ void read_generated_coordinates(const ICompoundProperty &prop,
cd_data = CustomData_get_layer(&mesh->vdata, CD_ORCO);
}
else {
- cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+ cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CONSTRUCT, nullptr, totvert);
}
float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
@@ -564,7 +564,6 @@ void read_custom_data(const std::string &iobject_full_name,
}
int num_uvs = 0;
- int num_colors = 0;
const size_t num_props = prop.getNumProperties();
@@ -583,10 +582,6 @@ void read_custom_data(const std::string &iobject_full_name,
/* Read vertex colors according to convention. */
if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
- if (++num_colors > MAX_MCOL) {
- continue;
- }
-
read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss);
continue;
}
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index df3559c108c..c07aaa37988 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -20,11 +20,13 @@
#include "DNA_object_types.h"
#include "BLI_compiler_compat.h"
+#include "BLI_edgehash.h"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_geom.h"
-#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -92,6 +94,7 @@ static void assign_materials(Main *bmain,
mat_iter = matname_to_material.find(mat_name);
if (mat_iter == matname_to_material.end()) {
assigned_mat = BKE_material_add(bmain, mat_name.c_str());
+ id_us_min(&assigned_mat->id);
matname_to_material[mat_name] = assigned_mat;
}
else {
@@ -100,6 +103,9 @@ static void assign_materials(Main *bmain,
BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, mat_index);
}
+ if (ob->totcol > 0) {
+ ob->actcol = 1;
+ }
}
} /* namespace utils */
@@ -129,8 +135,6 @@ static void read_mverts_interp(MVert *mverts,
interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), static_cast<float>(weight));
copy_zup_from_yup(mvert.co, tmp);
-
- mvert.bweight = 0;
}
}
@@ -151,13 +155,12 @@ static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
void read_mverts(Mesh &mesh, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
for (int i = 0; i < positions->size(); i++) {
- MVert &mvert = mesh.mvert[i];
+ MVert &mvert = verts[i];
Imath::V3f pos_in = (*positions)[i];
copy_zup_from_yup(mvert.co, pos_in.getValue());
-
- mvert.bweight = 0;
}
if (normals) {
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(&mesh);
@@ -268,7 +271,7 @@ static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
+ MPoly *mpoly = mesh->polys_for_write().data();
const N3fArraySample &loop_normals = *loop_normals_ptr;
int abc_index = 0;
for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) {
@@ -303,7 +306,7 @@ static void process_vertex_normals(CDStreamConfig &config,
}
config.mesh->flag |= ME_AUTOSMOOTH;
- BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors);
+ BKE_mesh_set_custom_normals_from_verts(config.mesh, vnors);
MEM_freeN(vnors);
}
@@ -390,7 +393,7 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
/* Create a new layer. */
int numloops = mesh->totloop;
cd_ptr = CustomData_add_layer_named(
- &mesh->ldata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
+ &mesh->ldata, cd_data_type, CD_SET_DEFAULT, nullptr, numloops, name);
return cd_ptr;
}
@@ -513,13 +516,10 @@ static void read_mesh_sample(const std::string &iobject_full_name,
CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
{
CDStreamConfig config;
-
- BLI_assert(mesh->mvert || mesh->totvert == 0);
-
config.mesh = mesh;
- config.mvert = mesh->mvert;
- config.mloop = mesh->mloop;
- config.mpoly = mesh->mpoly;
+ config.mvert = mesh->verts_for_write().data();
+ config.mloop = mesh->loops_for_write().data();
+ config.mpoly = mesh->polys_for_write().data();
config.totvert = mesh->totvert;
config.totloop = mesh->totloop;
config.totpoly = mesh->totpoly;
@@ -615,11 +615,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
if (read_mesh != mesh) {
- /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */
- /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
- uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
- mesh->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
}
if (m_settings->validate_meshes) {
@@ -765,7 +761,11 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
size_t num_polys = new_mesh->totpoly;
if (num_polys > 0) {
std::map<std::string, int> mat_map;
- assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map);
+ bke::MutableAttributeAccessor attributes = new_mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
+ material_indices.finish();
}
return new_mesh;
@@ -774,10 +774,9 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
return existing_mesh;
}
-void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
- MPoly *mpoly,
- int totpoly,
- std::map<std::string, int> &r_mat_map)
+void AbcMeshReader::assign_facesets_to_material_indices(const ISampleSelector &sample_sel,
+ MutableSpan<int> material_indices,
+ std::map<std::string, int> &r_mat_map)
{
std::vector<std::string> face_sets;
m_schema.getFaceSetNames(face_sets);
@@ -810,13 +809,12 @@ void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
for (size_t l = 0; l < num_group_faces; l++) {
size_t pos = (*group_faces)[l];
- if (pos >= totpoly) {
+ if (pos >= material_indices.size()) {
std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
break;
}
- MPoly &poly = mpoly[pos];
- poly.mat_nr = assigned_mat - 1;
+ material_indices[pos] = assigned_mat - 1;
}
}
}
@@ -824,25 +822,16 @@ void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel)
{
std::map<std::string, int> mat_map;
- assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map);
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
+ material_indices.finish();
utils::assign_materials(bmain, m_object, mat_map);
}
/* ************************************************************************** */
-BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
-{
- for (int i = 0, e = totedge; i < e; i++) {
- MEdge &edge = edges[i];
-
- if (edge.v1 == v1 && edge.v2 == v2) {
- return &edge;
- }
- }
-
- return nullptr;
-}
-
static void read_subd_sample(const std::string &iobject_full_name,
ImportSettings *settings,
const ISubDSchema &schema,
@@ -902,7 +891,7 @@ static void read_vertex_creases(Mesh *mesh,
}
float *vertex_crease_data = (float *)CustomData_add_layer(
- &mesh->vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh->totvert);
+ &mesh->vdata, CD_CREASE, CD_SET_DEFAULT, nullptr, mesh->totvert);
const int totvert = mesh->totvert;
for (int i = 0, v = indices->size(); i < v; ++i) {
@@ -926,8 +915,13 @@ static void read_edge_creases(Mesh *mesh,
return;
}
- MEdge *edges = mesh->medge;
- int totedge = mesh->totedge;
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, edges.size());
+
+ for (const int i : edges.index_range()) {
+ MEdge *edge = &edges[i];
+ BLI_edgehash_insert(edge_hash, edge->v1, edge->v2, edge);
+ }
for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
int v1 = (*indices)[i];
@@ -939,9 +933,9 @@ static void read_edge_creases(Mesh *mesh,
std::swap(v1, v2);
}
- MEdge *edge = find_edge(edges, totedge, v1, v2);
+ MEdge *edge = static_cast<MEdge *>(BLI_edgehash_lookup(edge_hash, v1, v2));
if (edge == nullptr) {
- edge = find_edge(edges, totedge, v2, v1);
+ edge = static_cast<MEdge *>(BLI_edgehash_lookup(edge_hash, v2, v1));
}
if (edge) {
@@ -949,6 +943,8 @@ static void read_edge_creases(Mesh *mesh,
}
}
+ BLI_edgehash_free(edge_hash, nullptr);
+
mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
@@ -999,7 +995,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
}
ISubDSchema::Sample sample;
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h
index f97525297b7..151f4d82226 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.h
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.h
@@ -5,6 +5,8 @@
* \ingroup balembic
*/
+#include "BLI_span.hh"
+
#include "abc_customdata.h"
#include "abc_reader_object.h"
@@ -38,10 +40,9 @@ class AbcMeshReader final : public AbcObjectReader {
Mesh *mesh,
const Alembic::AbcGeom::ISampleSelector &sample_sel);
- void assign_facesets_to_mpoly(const Alembic::Abc::ISampleSelector &sample_sel,
- MPoly *mpoly,
- int totpoly,
- std::map<std::string, int> &r_mat_map);
+ void assign_facesets_to_material_indices(const Alembic::Abc::ISampleSelector &sample_sel,
+ MutableSpan<int> material_indices,
+ std::map<std::string, int> &r_mat_map);
};
class AbcSubDReader final : public AbcObjectReader {
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index a698eeca8f1..db056c0eef6 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -218,12 +218,12 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
const chrono_t time,
const float scale,
- bool &is_constant)
+ bool &r_is_constant)
{
IXform ixform = xform();
if (!ixform) {
unit_m4(r_mat);
- is_constant = true;
+ r_is_constant = true;
return;
}
@@ -254,7 +254,7 @@ void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
mul_m4_m4m4(r_mat, scale_mat, r_mat);
}
- is_constant = schema.isConstant();
+ r_is_constant = schema.isConstant();
}
void AbcObjectReader::addCacheModifier()
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index ff189bc92dc..54ae71ad7a6 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -69,7 +69,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
}
if (m_settings->validate_meshes) {
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index 27df23b38c6..11c26fd2f72 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -601,9 +601,10 @@ static void import_endjob(void *user_data)
else {
Base *base;
LayerCollection *lc;
+ const Scene *scene = data->scene;
ViewLayer *view_layer = data->view_layer;
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
lc = BKE_layer_collection_get_active(view_layer);
@@ -616,6 +617,7 @@ static void import_endjob(void *user_data)
/* Sync the collection, and do view layer operations. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(data->bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
for (AbcObjectReader *reader : data->readers) {
Object *ob = reader->object();
base = BKE_view_layer_base_find(view_layer, ob);
@@ -672,13 +674,7 @@ static void import_freejob(void *user_data)
bool ABC_import(bContext *C,
const char *filepath,
- float scale,
- bool is_sequence,
- bool set_frame_range,
- int sequence_len,
- int offset,
- bool validate_meshes,
- bool always_add_cache_reader,
+ const AlembicImportParams *params,
bool as_background_job)
{
/* Using new here since MEM_* functions do not call constructor to properly initialize data. */
@@ -691,13 +687,13 @@ bool ABC_import(bContext *C,
job->import_ok = false;
BLI_strncpy(job->filename, filepath, 1024);
- job->settings.scale = scale;
- job->settings.is_sequence = is_sequence;
- job->settings.set_frame_range = set_frame_range;
- job->settings.sequence_len = sequence_len;
- job->settings.sequence_offset = offset;
- job->settings.validate_meshes = validate_meshes;
- job->settings.always_add_cache_reader = always_add_cache_reader;
+ job->settings.scale = params->global_scale;
+ job->settings.is_sequence = params->is_sequence;
+ job->settings.set_frame_range = params->set_frame_range;
+ job->settings.sequence_len = params->sequence_len;
+ job->settings.sequence_offset = params->sequence_offset;
+ job->settings.validate_meshes = params->validate_meshes;
+ job->settings.always_add_cache_reader = params->always_add_cache_reader;
job->error_code = ABC_NO_ERROR;
job->was_cancelled = false;
job->archive = nullptr;
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index 923a392dbde..cc91c3eeac9 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -64,7 +64,7 @@ void AnimationImporter::add_bezt(FCurve *fcu,
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
@@ -132,7 +132,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
}
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
fcurves.push_back(fcu);
unused_curves.push_back(fcu);
diff --git a/source/blender/io/collada/ArmatureExporter.cpp b/source/blender/io/collada/ArmatureExporter.cpp
index 87dd2fbd816..3cc98917116 100644
--- a/source/blender/io/collada/ArmatureExporter.cpp
+++ b/source/blender/io/collada/ArmatureExporter.cpp
@@ -76,7 +76,7 @@ bool ArmatureExporter::add_instance_controller(Object *ob)
ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
Mesh *me = (Mesh *)ob->data;
- if (!me->dvert) {
+ if (BKE_mesh_deform_verts(me) == nullptr) {
return false;
}
diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp
index fbb2ba499a5..fe90dc5d5fa 100644
--- a/source/blender/io/collada/BCAnimationCurve.cpp
+++ b/source/blender/io/collada/BCAnimationCurve.cpp
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2008 Blender Foundation. All rights reserved. */
+#include "RNA_path.h"
+
#include "BCAnimationCurve.h"
BCAnimationCurve::BCAnimationCurve()
@@ -96,7 +98,7 @@ void BCAnimationCurve::create_bezt(float frame, float output)
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
BCAnimationCurve::~BCAnimationCurve()
diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp
index 5f54f38a0ab..807488233ce 100644
--- a/source/blender/io/collada/BlenderContext.cpp
+++ b/source/blender/io/collada/BlenderContext.cpp
@@ -9,21 +9,25 @@
#include "BlenderContext.h"
#include "ExportSettings.h"
+#include "BKE_layer.h"
#include "BKE_scene.h"
-bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer)
+#include "BLI_listbase.h"
+
+bool bc_is_base_node(LinkNode *export_set, Object *ob, const Scene *scene, ViewLayer *view_layer)
{
- Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, view_layer);
+ Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, scene, view_layer);
return (root == ob);
}
Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
Object *ob,
+ const Scene *scene,
ViewLayer *view_layer)
{
Object *ancestor = ob;
while (ob->parent) {
- if (bc_is_in_Export_set(export_set, ob->parent, view_layer)) {
+ if (bc_is_in_Export_set(export_set, ob->parent, scene, view_layer)) {
ancestor = ob->parent;
}
ob = ob->parent;
@@ -31,10 +35,13 @@ Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
return ancestor;
}
-void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *view_layer)
+void bc_get_children(std::vector<Object *> &child_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
- Base *base;
- for (base = (Base *)view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *cob = base->object;
if (cob->parent == ob) {
switch (ob->type) {
@@ -51,7 +58,10 @@ void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *vi
}
}
-bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer)
+bool bc_is_in_Export_set(LinkNode *export_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
bool to_export = (BLI_linklist_index(export_set, ob) != -1);
@@ -60,9 +70,9 @@ bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer
* export list, but it contains children to export. */
std::vector<Object *> children;
- bc_get_children(children, ob, view_layer);
+ bc_get_children(children, ob, scene, view_layer);
for (Object *child : children) {
- if (bc_is_in_Export_set(export_set, child, view_layer)) {
+ if (bc_is_in_Export_set(export_set, child, scene, view_layer)) {
to_export = true;
break;
}
diff --git a/source/blender/io/collada/BlenderContext.h b/source/blender/io/collada/BlenderContext.h
index 6fdb043b3dc..8a782b74e52 100644
--- a/source/blender/io/collada/BlenderContext.h
+++ b/source/blender/io/collada/BlenderContext.h
@@ -22,8 +22,11 @@ extern "C" {
static const BC_global_forward_axis BC_DEFAULT_FORWARD = BC_GLOBAL_FORWARD_Y;
static const BC_global_up_axis BC_DEFAULT_UP = BC_GLOBAL_UP_Z;
-bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
-bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
+bool bc_is_in_Export_set(LinkNode *export_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer);
+bool bc_is_base_node(LinkNode *export_set, Object *ob, const Scene *scene, ViewLayer *view_layer);
/**
* Returns the highest selected ancestor
* returns NULL if no ancestor is selected
@@ -32,6 +35,7 @@ bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
*/
Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
Object *ob,
+ const Scene *scene,
ViewLayer *view_layer);
int bc_is_marked(Object *ob);
void bc_remove_mark(Object *ob);
diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp
index 38ad0e42d0f..6bf8d904a41 100644
--- a/source/blender/io/collada/ControllerExporter.cpp
+++ b/source/blender/io/collada/ControllerExporter.cpp
@@ -63,7 +63,7 @@ bool ControllerExporter::add_instance_controller(Object *ob)
ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
Mesh *me = (Mesh *)ob->data;
- if (!me->dvert) {
+ if (BKE_mesh_deform_verts(me) == nullptr) {
return false;
}
@@ -160,7 +160,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
bool use_instantiation = this->export_settings.get_use_object_instantiation();
Mesh *me;
- if (((Mesh *)ob->data)->dvert == nullptr) {
+ if (BKE_mesh_deform_verts((Mesh *)ob->data) == nullptr) {
return;
}
@@ -203,9 +203,10 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
}
}
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
int oob_counter = 0;
for (i = 0; i < me->totvert; i++) {
- MDeformVert *vert = &me->dvert[i];
+ const MDeformVert *vert = &dvert[i];
std::map<int, float> jw;
/* We're normalizing the weights later */
diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp
index 2ce97bc8b5d..00c69a5fb8a 100644
--- a/source/blender/io/collada/DocumentImporter.cpp
+++ b/source/blender/io/collada/DocumentImporter.cpp
@@ -90,8 +90,12 @@ DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_set
CTX_data_scene(C),
view_layer,
import_settings),
- mesh_importer(
- &unit_converter, &armature_importer, CTX_data_main(C), CTX_data_scene(C), view_layer),
+ mesh_importer(&unit_converter,
+ import_settings->custom_normals,
+ &armature_importer,
+ CTX_data_main(C),
+ CTX_data_scene(C),
+ view_layer),
anim_importer(C, &unit_converter, &armature_importer, CTX_data_scene(C))
{
}
@@ -116,7 +120,7 @@ bool DocumentImporter::import()
loader.registerExtraDataCallbackHandler(ehandler);
/* deselect all to select new objects */
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(CTX_data_scene(mContext), view_layer);
std::string mFilename = std::string(this->import_settings->filepath);
const std::string encodedFilename = bc_url_encode(mFilename);
@@ -743,6 +747,7 @@ bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat)
const std::string &str_mat_id = cmat->getName().empty() ? cmat->getOriginalId() :
cmat->getName();
Material *ma = BKE_material_add(bmain, (char *)str_mat_id.c_str());
+ id_us_min(&ma->id);
this->uid_effect_map[cmat->getInstantiatedEffect()] = ma;
this->uid_material_map[cmat->getUniqueId()] = ma;
diff --git a/source/blender/io/collada/EffectExporter.cpp b/source/blender/io/collada/EffectExporter.cpp
index 71a54e3a7c9..40ce20617fc 100644
--- a/source/blender/io/collada/EffectExporter.cpp
+++ b/source/blender/io/collada/EffectExporter.cpp
@@ -46,6 +46,7 @@ EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw,
bool EffectsExporter::hasEffects(Scene *sce)
{
+ bool result = false;
FOREACH_SCENE_OBJECT_BEGIN (sce, ob) {
int a;
for (a = 0; a < ob->totcol; a++) {
@@ -56,11 +57,12 @@ bool EffectsExporter::hasEffects(Scene *sce)
continue;
}
- return true;
+ result = true;
+ break;
}
}
FOREACH_SCENE_OBJECT_END;
- return false;
+ return result;
}
void EffectsExporter::exportEffects(bContext *C, Scene *sce)
diff --git a/source/blender/io/collada/ExportSettings.h b/source/blender/io/collada/ExportSettings.h
index b3f192fdac6..e4c1b41fdde 100644
--- a/source/blender/io/collada/ExportSettings.h
+++ b/source/blender/io/collada/ExportSettings.h
@@ -81,7 +81,10 @@ typedef struct ExportSettings {
#ifdef __cplusplus
}
-void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *view_layer);
+void bc_get_children(std::vector<Object *> &child_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer);
class BCExportSettings {
@@ -271,7 +274,7 @@ class BCExportSettings {
bool is_export_root(Object *ob)
{
- return bc_is_base_node(get_export_set(), ob, get_view_layer());
+ return bc_is_base_node(get_export_set(), ob, get_scene(), get_view_layer());
}
};
diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp
index 7e2a24aeb41..e60900ccdb6 100644
--- a/source/blender/io/collada/GeometryExporter.cpp
+++ b/source/blender/io/collada/GeometryExporter.cpp
@@ -17,6 +17,7 @@
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
@@ -26,6 +27,8 @@
#include "collada_internal.h"
#include "collada_utils.h"
+using blender::Span;
+
void GeometryExporter::exportGeom()
{
Scene *sce = blender_context.get_scene();
@@ -116,11 +119,12 @@ void GeometryExporter::operator()(Object *ob)
if (this->export_settings.get_include_shapekeys()) {
Key *key = BKE_key_from_object(ob);
if (key) {
+ blender::MutableSpan<MVert> verts = me->verts_for_write();
KeyBlock *kb = (KeyBlock *)key->block.first;
/* skip the basis */
kb = kb->next;
for (; kb; kb = kb->next) {
- BKE_keyblock_convert_to_mesh(kb, me->mvert, me->totvert);
+ BKE_keyblock_convert_to_mesh(kb, verts.data(), me->totvert);
export_key_mesh(ob, me, kb);
}
}
@@ -196,8 +200,7 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id)
{
-
- MEdge *medges = me->medge;
+ const Span<MEdge> edges = me->edges();
int totedges = me->totedge;
int edges_in_linelist = 0;
std::vector<unsigned int> edge_list;
@@ -206,7 +209,7 @@ void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &ge
/* Find all loose edges in Mesh
* and save vertex indices in edge_list */
for (index = 0; index < totedges; index++) {
- MEdge *edge = &medges[index];
+ const MEdge *edge = &edges[index];
if (edge->flag & ME_LOOSEEDGE) {
edges_in_linelist += 1;
@@ -284,16 +287,17 @@ static bool collect_vertex_counts_per_poly(Mesh *me,
int material_index,
std::vector<unsigned long> &vcount_list)
{
- MPoly *mpolys = me->mpoly;
- int totpolys = me->totpoly;
+ const Span<MPoly> polys = me->polys();
+ const blender::bke::AttributeAccessor attributes = me->attributes();
+ const blender::VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
bool is_triangulated = true;
- int i;
- /* Expecting that p->mat_nr is always 0 if the mesh has no materials assigned */
- for (i = 0; i < totpolys; i++) {
- MPoly *p = &mpolys[i];
- if (p->mat_nr == material_index) {
- int vertex_count = p->totloop;
+ /* Expecting that the material index is always 0 if the mesh has no materials assigned */
+ for (const int i : polys.index_range()) {
+ if (material_indices[i] == material_index) {
+ const MPoly &poly = polys[i];
+ const int vertex_count = poly.totloop;
vcount_list.push_back(vertex_count);
if (vertex_count != 3) {
is_triangulated = false;
@@ -318,10 +322,8 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
std::string &geom_id,
std::vector<BCPolygonNormalsIndices> &norind)
{
-
- MPoly *mpolys = me->mpoly;
- MLoop *mloops = me->mloop;
- int totpolys = me->totpoly;
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
std::vector<unsigned long> vcount_list;
@@ -397,14 +399,18 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
/* performs the actual writing */
prepareToAppendValues(is_triangulated, *primitive_list, vcount_list);
+ const blender::bke::AttributeAccessor attributes = me->attributes();
+ const blender::VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
/* <p> */
int texindex = 0;
- for (int i = 0; i < totpolys; i++) {
- MPoly *p = &mpolys[i];
+ for (const int i : polys.index_range()) {
+ const MPoly *p = &polys[i];
int loop_count = p->totloop;
- if (p->mat_nr == material_index) {
- MLoop *l = &mloops[p->loopstart];
+ if (material_indices[i] == material_index) {
+ const MLoop *l = &loops[p->loopstart];
BCPolygonNormalsIndices normal_indices = norind[i];
for (int j = 0; j < loop_count; j++) {
@@ -428,18 +434,13 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
{
-#if 0
- int totverts = dm->getNumVerts(dm);
- MVert *verts = dm->getVertArray(dm);
-#endif
- int totverts = me->totvert;
- MVert *verts = me->mvert;
+ const Span<MVert> verts = me->verts();
COLLADASW::FloatSourceF source(mSW);
source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) +
ARRAY_ID_SUFFIX);
- source.setAccessorCount(totverts);
+ source.setAccessorCount(verts.size());
source.setAccessorStride(3);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@@ -450,8 +451,7 @@ void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
* count = ""> */
source.prepareToAppendValues();
/* appends data to <float_array> */
- int i = 0;
- for (i = 0; i < totverts; i++) {
+ for (const int i : verts.index_range()) {
Vector co;
if (export_settings.get_apply_global_orientation()) {
bc_add_global_transform(co, verts[i].co, export_settings.get_global_transform());
@@ -500,11 +500,11 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me)
source.prepareToAppendValues();
- MPoly *mpoly;
- int i;
- for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) {
- const MLoopCol *mlc = mloopcol + mpoly->loopstart;
- for (int j = 0; j < mpoly->totloop; j++, mlc++) {
+ const Span<MPoly> polys = me->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const MLoopCol *mlc = mloopcol + poly.loopstart;
+ for (int j = 0; j < poly.totloop; j++, mlc++) {
source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f);
}
}
@@ -529,10 +529,8 @@ std::string GeometryExporter::makeTexcoordSourceId(std::string &geom_id,
void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
{
-
- int totpoly = me->totpoly;
int totuv = me->totloop;
- MPoly *mpolys = me->mpoly;
+ const Span<MPoly> polys = me->polys();
int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
@@ -558,8 +556,8 @@ void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
source.prepareToAppendValues();
- for (int index = 0; index < totpoly; index++) {
- MPoly *mpoly = mpolys + index;
+ for (const int i : polys.index_range()) {
+ const MPoly *mpoly = &polys[i];
MLoopUV *mloop = mloops + mpoly->loopstart;
for (int j = 0; j < mpoly->totloop; j++) {
source.appendValues(mloop[j].uv[0], mloop[j].uv[1]);
@@ -617,9 +615,10 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
std::map<Normal, unsigned int> shared_normal_indices;
int last_normal_index = -1;
- MVert *verts = me->mvert;
+ const Span<MVert> verts = me->verts();
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me);
- MLoop *mloops = me->mloop;
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
const float(*lnors)[3] = nullptr;
bool use_custom_normals = false;
@@ -629,15 +628,15 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
use_custom_normals = true;
}
- for (int poly_index = 0; poly_index < me->totpoly; poly_index++) {
- MPoly *mpoly = &me->mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly *mpoly = &polys[poly_index];
bool use_vertex_normals = use_custom_normals || mpoly->flag & ME_SMOOTH;
if (!use_vertex_normals) {
/* For flat faces use face normal as vertex normal: */
float vector[3];
- BKE_mesh_calc_poly_normal(mpoly, mloops + mpoly->loopstart, verts, vector);
+ BKE_mesh_calc_poly_normal(mpoly, &loops[mpoly->loopstart], verts.data(), vector);
Normal n = {vector[0], vector[1], vector[2]};
normals.push_back(n);
@@ -654,7 +653,7 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
normalize_v3_v3(normalized, lnors[loop_idx]);
}
else {
- copy_v3_v3(normalized, vert_normals[mloops[loop_index].v]);
+ copy_v3_v3(normalized, vert_normals[loops[loop_index].v]);
normalize_v3(normalized);
}
Normal n = {normalized[0], normalized[1], normalized[2]};
diff --git a/source/blender/io/collada/ImportSettings.h b/source/blender/io/collada/ImportSettings.h
index c92cf580112..2772314900c 100644
--- a/source/blender/io/collada/ImportSettings.h
+++ b/source/blender/io/collada/ImportSettings.h
@@ -8,6 +8,7 @@
typedef struct ImportSettings {
bool import_units;
+ bool custom_normals;
bool find_chains;
bool auto_connect;
bool fix_orientation;
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index b5d89d8d1cf..997da31b939 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -86,7 +86,7 @@ bNodeTree *MaterialNode::prepare_material_nodetree()
return nullptr;
}
- material->nodetree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
+ ntreeAddTreeEmbedded(nullptr, &material->id, "Shader Nodetree", "ShaderNodeTree");
material->use_nodes = true;
ntree = material->nodetree;
return ntree;
diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp
index fa0348fbcf2..719ac752413 100644
--- a/source/blender/io/collada/MeshImporter.cpp
+++ b/source/blender/io/collada/MeshImporter.cpp
@@ -33,6 +33,8 @@
#include "MeshImporter.h"
#include "collada_utils.h"
+using blender::MutableSpan;
+
/* get node name, or fall back to original id if not present (name is optional) */
template<class T> static std::string bc_get_dae_name(T *node)
{
@@ -189,9 +191,14 @@ void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol)
}
}
-MeshImporter::MeshImporter(
- UnitConverter *unitconv, ArmatureImporter *arm, Main *bmain, Scene *sce, ViewLayer *view_layer)
+MeshImporter::MeshImporter(UnitConverter *unitconv,
+ bool use_custom_normals,
+ ArmatureImporter *arm,
+ Main *bmain,
+ Scene *sce,
+ ViewLayer *view_layer)
: unitconverter(unitconv),
+ use_custom_normals(use_custom_normals),
m_bmain(bmain),
scene(sce),
view_layer(view_layer),
@@ -341,13 +348,10 @@ void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me)
}
me->totvert = pos.getFloatValues()->getCount() / stride;
- me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert);
-
- MVert *mvert;
- int i;
-
- for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
- get_vector(mvert->co, pos, i, stride);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, me->totvert);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ for (const int i : verts.index_range()) {
+ get_vector(verts[i].co, pos, i, stride);
}
}
@@ -448,10 +452,8 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
if (total_poly_count > 0) {
me->totpoly = total_poly_count;
me->totloop = total_loop_count;
- me->mpoly = (MPoly *)CustomData_add_layer(
- &me->pdata, CD_MPOLY, CD_CALLOC, nullptr, me->totpoly);
- me->mloop = (MLoop *)CustomData_add_layer(
- &me->ldata, CD_MLOOP, CD_CALLOC, nullptr, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop);
unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount();
for (int i = 0; i < totuvset; i++) {
@@ -468,10 +470,10 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
COLLADAFW::String &uvname = info->mName;
/* Allocate space for UV_data */
CustomData_add_layer_named(
- &me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, uvname.c_str());
+ &me->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, me->totloop, uvname.c_str());
}
/* activate the first uv map */
- me->mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0);
+ CustomData_set_layer_active(&me->ldata, CD_MLOOPUV, 0);
}
int totcolset = collada_mesh->getColors().getInputInfosArray().getCount();
@@ -481,9 +483,9 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
collada_mesh->getColors().getInputInfosArray()[i];
COLLADAFW::String colname = extract_vcolname(info->mName);
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, colname.c_str());
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, me->totloop, colname.c_str());
}
- me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, 0);
+ CustomData_set_layer_active(&me->ldata, CD_PROP_BYTE_COLOR, 0);
}
}
}
@@ -546,19 +548,20 @@ void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
totedge = mesh->totedge + len;
/* Update custom-data. */
- CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge);
+ CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
- CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
+ CustomData_add_layer(&edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, totedge);
}
CustomData_free(&mesh->edata, mesh->totedge);
mesh->edata = edata;
- BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
+
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
/* set default flags */
- medge = &mesh->medge[mesh->totedge];
+ medge = &edges[mesh->totedge];
for (int i = 0; i < len; i++, medge++) {
medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT;
}
@@ -575,7 +578,8 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
/* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */
mesh_add_edges(me, loose_edge_count);
- MEdge *med = me->medge + face_edge_count;
+ MutableSpan<MEdge> edges = me->edges_for_write();
+ MEdge *med = edges.data() + face_edge_count;
COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives();
@@ -588,7 +592,6 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
unsigned int *indices = mp->getPositionIndices().getData();
for (int j = 0; j < edge_count; j++, med++) {
- med->bweight = 0;
med->crease = 0;
med->flag |= ME_LOOSEEDGE;
med->v1 = indices[2 * j];
@@ -599,7 +602,9 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
}
}
-void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
+void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
+ Mesh *me,
+ blender::Vector<blender::float3> &loop_normals)
{
unsigned int i;
@@ -608,12 +613,17 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
UVDataWrapper uvs(collada_mesh->getUVCoords());
VCOLDataWrapper vcol(collada_mesh->getColors());
- MPoly *mpoly = me->mpoly;
- MLoop *mloop = me->mloop;
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+ MPoly *mpoly = polys.data();
+ MLoop *mloop = loops.data();
int loop_index = 0;
MaterialIdPrimitiveArrayMap mat_prim_map;
+ int *material_indices = (int *)CustomData_add_layer_named(
+ &me->pdata, CD_PROP_INT32, CD_SET_DEFAULT, nullptr, me->totpoly, "material_index");
+
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals();
@@ -632,12 +642,13 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
int collada_meshtype = mp->getPrimitiveType();
/* since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive */
- Primitive prim = {mpoly, 0};
+ Primitive prim = {mpoly, material_indices, 0};
/* If MeshPrimitive is TRIANGLE_FANS we split it into triangles
* The first triangle-fan vertex will be the first vertex in every triangle
* XXX The proper function of TRIANGLE_FANS is not tested!!!
- * XXX In particular the handling of the normal_indices looks very wrong to me */
+ * XXX In particular the handling of the normal_indices is very wrong */
+ /* TODO: UV, vertex color and custom normal support */
if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) {
unsigned int grouped_vertex_count = mp->getGroupedVertexElementsCount();
for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) {
@@ -662,6 +673,9 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
}
mpoly++;
+ if (material_indices) {
+ material_indices++;
+ }
mloop += 3;
loop_index += 3;
prim.totpoly++;
@@ -721,9 +735,22 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
}
if (mp_has_normals) {
+ /* If it turns out that we have complete custom normals for each MPoly
+ * and we want to use custom normals, this will be overridden. */
if (!is_flat_face(normal_indices, nor, vcount)) {
mpoly->flag |= ME_SMOOTH;
}
+
+ if (use_custom_normals) {
+ /* Store the custom normals for later application. */
+ float vert_normal[3];
+ unsigned int *cur_normal = normal_indices;
+ for (int k = 0; k < vcount; k++, cur_normal++) {
+ get_vector(vert_normal, nor, *cur_normal, 3);
+ normalize_v3(vert_normal);
+ loop_normals.append(vert_normal);
+ }
+ }
}
if (mp->hasColorIndices()) {
@@ -868,6 +895,16 @@ std::string *MeshImporter::get_geometry_name(const std::string &mesh_name)
return nullptr;
}
+static bool bc_has_out_of_bound_indices(Mesh *me)
+{
+ for (const MLoop &loop : me->loops()) {
+ if (loop.v >= me->totvert) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* this function checks if both objects have the same
* materials assigned to Object (in the same order)
@@ -897,11 +934,9 @@ static bool bc_has_same_material_configuration(Object *ob1, Object *ob2)
}
/**
- *
* Caution here: This code assumes that all materials are assigned to Object
* and no material is assigned to Data.
* That is true right after the objects have been imported.
- *
*/
static void bc_copy_materials_to_data(Object *ob, Mesh *me)
{
@@ -912,9 +947,7 @@ static void bc_copy_materials_to_data(Object *ob, Mesh *me)
}
/**
- *
- * Remove all references to materials from the object
- *
+ * Remove all references to materials from the object.
*/
static void bc_remove_materials_from_object(Object *ob, Mesh *me)
{
@@ -1010,10 +1043,9 @@ void MeshImporter::assign_material_to_geom(
for (it = prims.begin(); it != prims.end(); it++) {
Primitive &prim = *it;
- MPoly *mpoly = prim.mpoly;
- for (int i = 0; i < prim.totpoly; i++, mpoly++) {
- mpoly->mat_nr = mat_index;
+ for (int i = 0; i < prim.totpoly; i++) {
+ prim.material_indices[i] = mat_index;
}
}
}
@@ -1119,8 +1151,37 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
this->mesh_geom_map[std::string(me->id.name)] = str_geom_id;
read_vertices(mesh, me);
- read_polys(mesh, me);
+
+ blender::Vector<blender::float3> loop_normals;
+ read_polys(mesh, me, loop_normals);
+
BKE_mesh_calc_edges(me, false, false);
+
+ /* We must apply custom normals after edges have been calculated, because
+ * BKE_mesh_set_custom_normals()'s internals expect me->medge to be populated
+ * and for the MLoops to have correct edge indices. */
+ if (use_custom_normals && !loop_normals.is_empty()) {
+ /* BKE_mesh_set_custom_normals()'s internals also expect that each MLoop
+ * has a valid vertex index, which may not be the case due to the existing
+ * logic in read_polys(). This check isn't necessary in the no-custom-normals
+ * case because the invalid MLoops get stripped in a later step. */
+ if (bc_has_out_of_bound_indices(me)) {
+ fprintf(stderr, "Can't apply custom normals, encountered invalid loop vert indices!\n");
+ }
+ /* There may be a mismatch in lengths if one or more of the MeshPrimitives in
+ * the Geometry had missing or otherwise invalid normals. */
+ else if (me->totloop != loop_normals.size()) {
+ fprintf(stderr,
+ "Can't apply custom normals, me->totloop != loop_normals.size() (%d != %d)\n",
+ me->totloop,
+ (int)loop_normals.size());
+ }
+ else {
+ BKE_mesh_set_custom_normals(me, reinterpret_cast<float(*)[3]>(loop_normals.data()));
+ me->flag |= ME_AUTOSMOOTH;
+ }
+ }
+
/* read_lines() must be called after the face edges have been generated.
* Otherwise the loose edges will be silently deleted again. */
read_lines(mesh, me);
diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h
index 416b5728b66..a59b24d4f24 100644
--- a/source/blender/io/collada/MeshImporter.h
+++ b/source/blender/io/collada/MeshImporter.h
@@ -24,6 +24,7 @@
#include "collada_utils.h"
#include "BLI_edgehash.h"
+#include "BLI_math_vec_types.hh"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
@@ -63,6 +64,7 @@ class VCOLDataWrapper {
class MeshImporter : public MeshImporterBase {
private:
UnitConverter *unitconverter;
+ bool use_custom_normals;
Main *m_bmain;
Scene *scene;
@@ -80,6 +82,7 @@ class MeshImporter : public MeshImporterBase {
* (<triangles>, <polylist>, etc.) */
struct Primitive {
MPoly *mpoly;
+ int *material_indices;
unsigned int totpoly;
};
typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive>> MaterialIdPrimitiveArrayMap;
@@ -155,7 +158,7 @@ class MeshImporter : public MeshImporterBase {
*
* TODO: import uv set names.
*/
- void read_polys(COLLADAFW::Mesh *mesh, Mesh *me);
+ void read_polys(COLLADAFW::Mesh *mesh, Mesh *me, blender::Vector<blender::float3> &loop_normals);
/**
* Read all loose edges.
* IMPORTANT: This function assumes that all edges from existing
@@ -178,6 +181,7 @@ class MeshImporter : public MeshImporterBase {
public:
MeshImporter(UnitConverter *unitconv,
+ bool use_custom_normals,
ArmatureImporter *arm,
Main *bmain,
Scene *sce,
@@ -203,7 +207,6 @@ class MeshImporter : public MeshImporterBase {
* if the check is positive:
* Add the materials of the first user to the geometry
* adjust all other users accordingly.
- *
*/
void optimize_material_assignements();
diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp
index 1b1da110573..b98ff27c89e 100644
--- a/source/blender/io/collada/SceneExporter.cpp
+++ b/source/blender/io/collada/SceneExporter.cpp
@@ -82,11 +82,13 @@ void SceneExporter::writeNodeList(std::vector<Object *> &child_objects, Object *
void SceneExporter::writeNode(Object *ob)
{
+ const Scene *scene = blender_context.get_scene();
ViewLayer *view_layer = blender_context.get_view_layer();
std::vector<Object *> child_objects;
- bc_get_children(child_objects, ob, view_layer);
- bool can_export = bc_is_in_Export_set(this->export_settings.get_export_set(), ob, view_layer);
+ bc_get_children(child_objects, ob, scene, view_layer);
+ bool can_export = bc_is_in_Export_set(
+ this->export_settings.get_export_set(), ob, scene, view_layer);
/* Add associated armature first if available */
bool armature_exported = false;
@@ -94,7 +96,7 @@ void SceneExporter::writeNode(Object *ob)
if (ob_arm != nullptr) {
armature_exported = bc_is_in_Export_set(
- this->export_settings.get_export_set(), ob_arm, view_layer);
+ this->export_settings.get_export_set(), ob_arm, scene, view_layer);
if (armature_exported && bc_is_marked(ob_arm)) {
writeNode(ob_arm);
bc_remove_mark(ob_arm);
diff --git a/source/blender/io/collada/collada.cpp b/source/blender/io/collada/collada.cpp
index d559c0b4962..c33363ef205 100644
--- a/source/blender/io/collada/collada.cpp
+++ b/source/blender/io/collada/collada.cpp
@@ -29,6 +29,7 @@ static void print_import_header(ImportSettings &import_settings)
fprintf(stderr, "+-- Collada Import parameters------\n");
fprintf(stderr, "| input file : %s\n", import_settings.filepath);
fprintf(stderr, "| use units : %s\n", (import_settings.import_units) ? "yes" : "no");
+ fprintf(stderr, "| custom normals : %s\n", (import_settings.custom_normals) ? "yes" : "no");
fprintf(stderr, "| autoconnect : %s\n", (import_settings.auto_connect) ? "yes" : "no");
fprintf(stderr, "+-- Armature Import parameters ----\n");
fprintf(stderr, "| find bone chains: %s\n", (import_settings.find_chains) ? "yes" : "no");
@@ -57,6 +58,7 @@ int collada_import(bContext *C, ImportSettings *import_settings)
int collada_export(bContext *C, ExportSettings *export_settings)
{
BlenderContext blender_context(C);
+ const Scene *scene = blender_context.get_scene();
ViewLayer *view_layer = blender_context.get_view_layer();
int includeFilter = OB_REL_NONE;
@@ -72,7 +74,7 @@ int collada_export(bContext *C, ExportSettings *export_settings)
*/
eObjectSet objectSet = (export_settings->selected) ? OB_SET_SELECTED : OB_SET_ALL;
export_settings->export_set = BKE_object_relational_superset(
- view_layer, objectSet, (eObRelationTypes)includeFilter);
+ scene, view_layer, objectSet, (eObRelationTypes)includeFilter);
int export_count = BLI_linklist_count(export_settings->export_set);
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index 75842734b08..03bc14c6d13 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -197,6 +197,7 @@ Object *bc_add_object(Main *bmain, Scene *scene, ViewLayer *view_layer, int type
LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
BKE_collection_object_add(bmain, layer_collection->collection, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
@@ -1108,7 +1109,7 @@ static std::string bc_get_uvlayer_name(Mesh *me, int layer)
static bNodeTree *prepare_material_nodetree(Material *ma)
{
if (ma->nodetree == nullptr) {
- ma->nodetree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
+ ntreeAddTreeEmbedded(nullptr, &ma->id, "Shader Nodetree", "ShaderNodeTree");
ma->use_nodes = true;
}
return ma->nodetree;
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index a6818c0bf5d..ee5c6a0a47f 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -19,15 +19,15 @@ set(SRC
intern/dupli_parent_finder.cc
intern/dupli_persistent_id.cc
intern/object_identifier.cc
- intern/path_util.cc
intern/orientation.c
+ intern/path_util.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
+ IO_orientation.h
IO_path_util.hh
IO_path_util_types.h
IO_types.h
- IO_orientation.h
intern/dupli_parent_finder.hh
)
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index a67cfe6a9d6..966eb640264 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -228,7 +228,7 @@ class AbstractHierarchyIterator {
* writer is created it will also write the current iteration, to ensure the hierarchy is
* complete. The `export_subset` option is only in effect when the writer already existed from a
* previous iteration. */
- void set_export_subset(ExportSubset export_subset_);
+ void set_export_subset(ExportSubset export_subset);
/* Convert the given name to something that is valid for the exported file format.
* This base implementation is a no-op; override in a concrete subclass. */
@@ -267,7 +267,7 @@ class AbstractHierarchyIterator {
/* These three functions create writers and call their write() method. */
void make_writers(const HierarchyContext *parent_context);
void make_writer_object_data(const HierarchyContext *context);
- void make_writers_particle_systems(const HierarchyContext *context);
+ void make_writers_particle_systems(const HierarchyContext *transform_context);
/* Return the appropriate HierarchyContext for the data of the object represented by
* object_context. */
@@ -332,7 +332,7 @@ class AbstractHierarchyIterator {
virtual void release_writer(AbstractHierarchyWriter *writer) = 0;
AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
- ExportChildren &graph_children(const HierarchyContext *parent_context);
+ ExportChildren &graph_children(const HierarchyContext *context);
};
} // namespace blender::io
diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h
index 215891e3e48..eb811fa2de8 100644
--- a/source/blender/io/gpencil/gpencil_io.h
+++ b/source/blender/io/gpencil/gpencil_io.h
@@ -35,6 +35,8 @@ typedef struct GpencilIOParams {
/** Stroke sampling factor. */
float stroke_sample;
int32_t resolution;
+ /** Filename to be used in new objects. */
+ char filename[128];
} GpencilIOParams;
/* GpencilIOParams->flag. */
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index 05f1158c57d..bcf9a36c200 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -20,6 +20,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_scene.h"
@@ -91,8 +92,7 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara
/* Camera rectangle. */
if ((rv3d_->persp == RV3D_CAMOB) || (force_camera_view)) {
- render_x_ = (scene_->r.xsch * scene_->r.size) / 100;
- render_y_ = (scene_->r.ysch * scene_->r.size) / 100;
+ BKE_render_resolution(&scene->r, false, &render_x_, &render_y_);
ED_view3d_calc_camera_border(CTX_data_scene(params_.C),
depsgraph_,
@@ -128,13 +128,15 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara
void GpencilIO::create_object_list()
{
+ Scene *scene = CTX_data_scene(params_.C);
ViewLayer *view_layer = CTX_data_view_layer(params_.C);
float3 camera_z_axis;
copy_v3_v3(camera_z_axis, rv3d_->viewinv[2]);
ob_list_.clear();
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_GPENCIL) {
@@ -258,7 +260,7 @@ float GpencilIO::stroke_point_radius_get(bGPDlayer *gpl, bGPDstroke *gps)
/* Radius. */
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps, 3, diff_mat_.values, 0.0f);
pt = &gps_perimeter->points[0];
const float2 screen_ex = gpencil_3D_point_to_2D(&pt->x);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index a89b723ed6c..f712ed839d9 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.hh
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -58,8 +58,8 @@ class GpencilIO {
struct Scene *scene_;
struct RegionView3D *rv3d_;
- int16_t winx_, winy_;
- int16_t render_x_, render_y_;
+ int winx_, winy_;
+ int render_x_, render_y_;
float camera_ratio_;
rctf camera_rect_;
@@ -70,7 +70,7 @@ class GpencilIO {
float stroke_color_[4], fill_color_[4];
/* Geometry functions. */
- /** Convert to screenspace. */
+ /** Convert to screen-space. */
bool gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co);
/** Convert to render space. */
float2 gpencil_3D_point_to_render_space(const float3 co);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
index 700d91791a8..463032ebb9d 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -192,7 +192,7 @@ void GpencilExporterPDF::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
index 2601ad05ea7..58f12e9b8b1 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
@@ -217,7 +217,7 @@ void GpencilExporterSVG::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
index 9b00fbaa027..6d4439243fd 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
@@ -14,6 +14,7 @@
#include "BKE_material.h"
#include "ED_gpencil.h"
+#include "ED_object.h"
#include "gpencil_io_import_base.hh"
@@ -27,10 +28,22 @@ GpencilImporter::GpencilImporter(const GpencilIOParams *iparams) : GpencilIO(ipa
Object *GpencilImporter::create_object()
{
- const float *cur = scene_->cursor.location;
+ const float *cur_loc = scene_->cursor.location;
+ const float rot[3] = {0.0f};
ushort local_view_bits = (params_.v3d && params_.v3d->localvd) ? params_.v3d->local_view_uuid :
(ushort)0;
- Object *ob_gpencil = ED_gpencil_add_object(params_.C, cur, local_view_bits);
+
+ Object *ob_gpencil = ED_object_add_type(params_.C,
+ OB_GPENCIL,
+ (params_.filename[0] != '\0') ? params_.filename :
+ nullptr,
+ cur_loc,
+ rot,
+ false,
+ local_view_bits);
+
+ /* Set object defaults. */
+ ED_gpencil_add_defaults(params_.C, ob_gpencil);
return ob_gpencil;
}
diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt
index e0c48bbbf7e..3a21da5c579 100644
--- a/source/blender/io/stl/CMakeLists.txt
+++ b/source/blender/io/stl/CMakeLists.txt
@@ -24,16 +24,16 @@ set(INC_SYS
set(SRC
IO_stl.cc
- importer/stl_import_mesh.cc
+ importer/stl_import.cc
importer/stl_import_ascii_reader.cc
importer/stl_import_binary_reader.cc
- importer/stl_import.cc
+ importer/stl_import_mesh.cc
IO_stl.h
- importer/stl_import_mesh.hh
+ importer/stl_import.hh
importer/stl_import_ascii_reader.hh
importer/stl_import_binary_reader.hh
- importer/stl_import.hh
+ importer/stl_import_mesh.hh
)
set(LIB
diff --git a/source/blender/io/stl/importer/stl_import.cc b/source/blender/io/stl/importer/stl_import.cc
index 097d14b038c..e5fde6658ab 100644
--- a/source/blender/io/stl/importer/stl_import.cc
+++ b/source/blender/io/stl/importer/stl_import.cc
@@ -99,11 +99,12 @@ void importer_main(Main *bmain,
BKE_mesh_validate(mesh, verbose_validate, false);
}
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
BKE_mesh_assign_object(bmain, obj, mesh);
BKE_collection_object_add(bmain, lc->collection, obj);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, obj);
BKE_view_layer_base_select_and_set_active(view_layer, base);
diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc
index b9ed441f0d9..de993cd2f27 100644
--- a/source/blender/io/stl/importer/stl_import_mesh.cc
+++ b/source/blender/io/stl/importer/stl_import_mesh.cc
@@ -76,27 +76,26 @@ Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name)
id_us_min(&mesh->id);
mesh->totvert = verts_.size();
- mesh->mvert = static_cast<MVert *>(
- CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert));
+ CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, mesh->totvert);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
for (int i = 0; i < mesh->totvert; i++) {
- copy_v3_v3(mesh->mvert[i].co, verts_[i]);
+ copy_v3_v3(verts[i].co, verts_[i]);
}
mesh->totpoly = tris_.size();
mesh->totloop = tris_.size() * 3;
- mesh->mpoly = static_cast<MPoly *>(
- CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly));
- mesh->mloop = static_cast<MLoop *>(
- CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop));
-
+ CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, mesh->totpoly);
+ CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, mesh->totloop);
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
threading::parallel_for(tris_.index_range(), 2048, [&](IndexRange tris_range) {
for (const int i : tris_range) {
- mesh->mpoly[i].loopstart = 3 * i;
- mesh->mpoly[i].totloop = 3;
+ polys[i].loopstart = 3 * i;
+ polys[i].totloop = 3;
- mesh->mloop[3 * i].v = tris_[i].v1;
- mesh->mloop[3 * i + 1].v = tris_[i].v2;
- mesh->mloop[3 * i + 2].v = tris_[i].v3;
+ loops[3 * i].v = tris_[i].v1;
+ loops[3 * i + 1].v = tris_[i].v2;
+ loops[3 * i + 2].v = tris_[i].v3;
}
});
diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc
index 13ae6f4d4c0..54ad7ef5410 100644
--- a/source/blender/io/usd/intern/usd_capi_import.cc
+++ b/source/blender/io/usd/intern/usd_capi_import.cc
@@ -218,6 +218,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
data->scene->r.efra = stage->GetEndTimeCode();
}
+ *data->do_update = true;
*data->progress = 0.15f;
USDStageReader *archive = new USDStageReader(stage, data->params, data->settings);
@@ -226,13 +227,32 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
archive->collect_readers(data->bmain);
+ *data->do_update = true;
*data->progress = 0.2f;
const float size = static_cast<float>(archive->readers().size());
size_t i = 0;
- /* Setup parenthood */
+ /* Sort readers by name: when creating a lot of objects in Blender,
+ * it is much faster if the order is sorted by name. */
+ archive->sort_readers();
+ *data->do_update = true;
+ *data->progress = 0.25f;
+
+ /* Create blender objects. */
+ for (USDPrimReader *reader : archive->readers()) {
+ if (!reader) {
+ continue;
+ }
+ reader->create_object(data->bmain, 0.0);
+ if ((++i & 1023) == 0) {
+ *data->do_update = true;
+ *data->progress = 0.25f + 0.25f * (i / size);
+ }
+ }
+ /* Setup parenthood and read actual object data. */
+ i = 0;
for (USDPrimReader *reader : archive->readers()) {
if (!reader) {
@@ -252,7 +272,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
ob->parent = parent->object();
}
- *data->progress = 0.2f + 0.8f * (++i / size);
+ *data->progress = 0.5f + 0.5f * (++i / size);
*data->do_update = true;
if (G.is_break) {
@@ -290,9 +310,10 @@ static void import_endjob(void *customdata)
else if (data->archive) {
Base *base;
LayerCollection *lc;
+ const Scene *scene = data->scene;
ViewLayer *view_layer = data->view_layer;
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
lc = BKE_layer_collection_get_active(view_layer);
@@ -312,6 +333,7 @@ static void import_endjob(void *customdata)
/* Sync the collection, and do view layer operations. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(data->bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
for (USDPrimReader *reader : data->archive->readers()) {
if (!reader) {
continue;
diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc
index 8feceee55ed..3546beb022c 100644
--- a/source/blender/io/usd/intern/usd_reader_material.cc
+++ b/source/blender/io/usd/intern/usd_reader_material.cc
@@ -4,13 +4,17 @@
#include "usd_reader_material.h"
#include "BKE_image.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
+#include "BLI_fileops.h"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BLI_vector.hh"
#include "DNA_material_types.h"
@@ -94,6 +98,60 @@ static void link_nodes(
nodeAddLink(ntree, source, source_socket, dest, dest_socket);
}
+/* Returns a layer handle retrieved from the given attribute's property specs.
+ * Note that the returned handle may be invalid if no layer could be found. */
+static pxr::SdfLayerHandle get_layer_handle(const pxr::UsdAttribute &attribute)
+{
+ for (auto PropertySpec : attribute.GetPropertyStack(pxr::UsdTimeCode::EarliestTime())) {
+ if (PropertySpec->HasDefaultValue() ||
+ PropertySpec->GetLayer()->GetNumTimeSamplesForPath(PropertySpec->GetPath()) > 0) {
+ return PropertySpec->GetLayer();
+ }
+ }
+
+ return pxr::SdfLayerHandle();
+}
+
+static bool is_udim_path(const std::string &path)
+{
+ return path.find("<UDIM>") != std::string::npos;
+}
+
+/* For the given UDIM path (assumed to contain the UDIM token), returns an array
+ * containing valid tile indices. */
+static blender::Vector<int> get_udim_tiles(const std::string &file_path)
+{
+ char base_udim_path[FILE_MAX];
+ BLI_strncpy(base_udim_path, file_path.c_str(), sizeof(base_udim_path));
+
+ blender::Vector<int> udim_tiles;
+
+ /* Extract the tile numbers from all files on disk. */
+ ListBase tiles = {nullptr, nullptr};
+ int tile_start, tile_range;
+ bool result = BKE_image_get_tile_info(base_udim_path, &tiles, &tile_start, &tile_range);
+ if (result) {
+ LISTBASE_FOREACH (LinkData *, tile, &tiles) {
+ int tile_number = POINTER_AS_INT(tile->data);
+ udim_tiles.append(tile_number);
+ }
+ }
+
+ BLI_freelistN(&tiles);
+
+ return udim_tiles;
+}
+
+/* Add tiles with the given indices to the given image. */
+static void add_udim_tiles(Image *image, const blender::Vector<int> &indices)
+{
+ image->source = IMA_SRC_TILED;
+
+ for (int tile_number : indices) {
+ BKE_image_add_tile(image, tile_number, nullptr);
+ }
+}
+
/* Returns true if the given shader may have opacity < 1.0, based
* on heuristics. */
static bool needs_blend(const pxr::UsdShadeShader &usd_shader)
@@ -266,6 +324,7 @@ Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_mater
/* Create the material. */
Material *mtl = BKE_material_add(bmain_, mtl_name.c_str());
+ id_us_min(&mtl->id);
/* Get the UsdPreviewSurface shader source for the material,
* if there is one. */
@@ -294,8 +353,7 @@ void USDMaterialReader::import_usd_preview(Material *mtl,
* and output shaders. */
/* Add the node tree. */
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
- mtl->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(nullptr, &mtl->id, "Shader Nodetree", "ShaderNodeTree");
mtl->use_nodes = true;
/* Create the Principled BSDF shader node. */
@@ -601,11 +659,31 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>();
std::string file_path = asset_path.GetResolvedPath();
if (file_path.empty()) {
+ /* No resolved path, so use the asset path (usually
+ * necessary for UDIM paths). */
+ file_path = asset_path.GetAssetPath();
+
+ /* Texture paths are frequently relative to the USD, so get
+ * the absolute path. */
+ if (pxr::SdfLayerHandle layer_handle = get_layer_handle(file_input.GetAttr())) {
+ file_path = layer_handle->ComputeAbsolutePath(file_path);
+ }
+ }
+
+ if (file_path.empty()) {
std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path
<< "' for Texture Image node." << std::endl;
return;
}
+ /* If this is a UDIM texture, this will store the
+ * UDIM tile indices. */
+ blender::Vector<int> udim_tiles;
+
+ if (is_udim_path(file_path)) {
+ udim_tiles = get_udim_tiles(file_path);
+ }
+
const char *im_file = file_path.c_str();
Image *image = BKE_image_load_exists(bmain_, im_file);
if (!image) {
@@ -614,6 +692,10 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
return;
}
+ if (udim_tiles.size() > 0) {
+ add_udim_tiles(image, udim_tiles);
+ }
+
tex_image->id = &image->id;
/* Set texture color space.
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index 45657525527..7cb4c65f166 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -6,6 +6,7 @@
#include "usd_reader_mesh.h"
#include "usd_reader_material.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -64,12 +65,26 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
{
- return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial();
+ pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim);
+
+ /* Compute generically bound ('allPurpose') materials. */
+ pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial();
+
+ /* If no generic material could be resolved, also check for 'preview' and
+ * 'full' purpose materials as fallbacks. */
+ if (!mtl) {
+ mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
+ }
+
+ if (!mtl) {
+ mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->full);
+ }
+
+ return mtl;
}
-/* Returns an existing Blender material that corresponds to the USD
- * material with with the given path. Returns null if no such material
- * exists. */
+/* Returns an existing Blender material that corresponds to the USD material with the given path.
+ * Returns null if no such material exists. */
static Material *find_existing_material(
const pxr::SdfPath &usd_mat_path,
const USDImportParams &params,
@@ -88,7 +103,7 @@ static Material *find_existing_material(
return mat_iter->second;
}
/* We can't find the Blender material which was previously created for this USD
- * material, which should never happen. */
+ * material, which should never happen. */
BLI_assert_unreachable();
}
}
@@ -168,6 +183,9 @@ static void assign_materials(Main *bmain,
std::cout << "WARNING: Couldn't assign material " << it->first << std::endl;
}
}
+ if (ob->totcol > 0) {
+ ob->actcol = 1;
+ }
}
} // namespace utils
@@ -193,7 +211,8 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type
/* Create a new layer. */
numloops = mesh->totloop;
- cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
+ cd_ptr = CustomData_add_layer_named(
+ loopdata, cd_data_type, CD_SET_DEFAULT, nullptr, numloops, name);
return cd_ptr;
}
@@ -229,11 +248,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
is_initial_load_ = false;
if (read_mesh != mesh) {
- /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
- /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
- uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true);
- mesh->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_);
}
readFaceSetsSample(bmain, mesh, motionSampleTime);
@@ -293,18 +308,17 @@ bool USDMeshReader::topology_changed(const Mesh *existing_mesh, const double mot
void USDMeshReader::read_mpolys(Mesh *mesh)
{
- MPoly *mpolys = mesh->mpoly;
- MLoop *mloops = mesh->mloop;
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
int loop_index = 0;
for (int i = 0; i < face_counts_.size(); i++) {
const int face_size = face_counts_[i];
- MPoly &poly = mpolys[i];
+ MPoly &poly = polys[i];
poly.loopstart = loop_index;
poly.totloop = face_size;
- poly.mat_nr = 0;
/* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded,
* this is encoded in custom loop normals. */
@@ -313,12 +327,12 @@ void USDMeshReader::read_mpolys(Mesh *mesh)
if (is_left_handed_) {
int loop_end_index = loop_index + (face_size - 1);
for (int f = 0; f < face_size; ++f, ++loop_index) {
- mloops[loop_index].v = face_indices_[loop_end_index - f];
+ loops[loop_index].v = face_indices_[loop_end_index - f];
}
}
else {
for (int f = 0; f < face_size; ++f, ++loop_index) {
- mloops[loop_index].v = face_indices_[loop_index];
+ loops[loop_index].v = face_indices_[loop_index];
}
}
}
@@ -382,6 +396,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo
}
}
+ const Span<MLoop> loops = mesh->loops();
for (int i = 0; i < face_counts_.size(); i++) {
const int face_size = face_counts_[i];
@@ -417,7 +432,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo
/* For Vertex interpolation, use the vertex index. */
int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ?
- mesh->mloop[loop_index].v :
+ loops[loop_index].v :
loop_index;
if (usd_uv_index >= sample.uvs.size()) {
@@ -497,24 +512,23 @@ void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime)
MLoopCol *colors = static_cast<MLoopCol *>(cd_ptr);
- mesh->mloopcol = colors;
-
- MPoly *poly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) {
- for (int j = 0; j < poly->totloop; ++j) {
- int loop_index = poly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; ++j) {
+ int loop_index = poly.loopstart + j;
/* Default for constant varying interpolation. */
int usd_index = 0;
if (interp == pxr::UsdGeomTokens->vertex) {
- usd_index = mesh->mloop[loop_index].v;
+ usd_index = loops[loop_index].v;
}
else if (interp == pxr::UsdGeomTokens->faceVarying) {
- usd_index = poly->loopstart;
+ usd_index = poly.loopstart;
if (is_left_handed_) {
- usd_index += poly->totloop - 1 - j;
+ usd_index += poly.totloop - 1 - j;
}
else {
usd_index += j;
@@ -562,7 +576,7 @@ void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTim
}
float *creases = static_cast<float *>(
- CustomData_add_layer(&mesh->vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh->totvert));
+ CustomData_add_layer(&mesh->vdata, CD_CREASE, CD_SET_DEFAULT, nullptr, mesh->totvert));
for (size_t i = 0; i < corner_indices.size(); i++) {
creases[corner_indices[i]] = corner_sharpnesses[i];
@@ -612,15 +626,15 @@ void USDMeshReader::process_normals_face_varying(Mesh *mesh)
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
- for (int j = 0; j < mpoly->totloop; j++) {
- int blender_index = mpoly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; j++) {
+ int blender_index = poly.loopstart + j;
- int usd_index = mpoly->loopstart;
+ int usd_index = poly.loopstart;
if (is_left_handed_) {
- usd_index += mpoly->totloop - 1 - j;
+ usd_index += poly.totloop - 1 - j;
}
else {
usd_index += j;
@@ -653,12 +667,11 @@ void USDMeshReader::process_normals_uniform(Mesh *mesh)
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
-
- for (int j = 0; j < mpoly->totloop; j++) {
- int loop_index = mpoly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; j++) {
+ int loop_index = poly.loopstart + j;
lnors[loop_index][0] = normals_[i][0];
lnors[loop_index][1] = normals_[i][1];
lnors[loop_index][2] = normals_[i][2];
@@ -681,8 +694,9 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings,
* in code that expect this data to be there. */
if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ MutableSpan<MVert> verts = mesh->verts_for_write();
for (int i = 0; i < positions_.size(); i++) {
- MVert &mvert = mesh->mvert[i];
+ MVert &mvert = verts[i];
mvert.co[0] = positions_[i][0];
mvert.co[1] = positions_[i][1];
mvert.co[2] = positions_[i][2];
@@ -720,10 +734,9 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings,
}
}
-void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
- MPoly *mpoly,
- const int /* totpoly */,
- std::map<pxr::SdfPath, int> *r_mat_map)
+void USDMeshReader::assign_facesets_to_material_indices(double motionSampleTime,
+ MutableSpan<int> material_indices,
+ std::map<pxr::SdfPath, int> *r_mat_map)
{
if (r_mat_map == nullptr) {
return;
@@ -763,9 +776,8 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
pxr::VtIntArray indices;
indicesAttribute.Get(&indices, motionSampleTime);
- for (int i = 0; i < indices.size(); i++) {
- MPoly &poly = mpoly[indices[i]];
- poly.mat_nr = mat_idx;
+ for (const int i : indices) {
+ material_indices[i] = mat_idx;
}
}
}
@@ -790,7 +802,12 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot
}
std::map<pxr::SdfPath, int> mat_map;
- assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map);
+
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ this->assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map);
+ material_indices.finish();
/* Build material name map if it's not built yet. */
if (this->settings_->mat_name_to_mat.empty()) {
utils::build_mat_map(bmain, &this->settings_->mat_name_to_mat);
@@ -882,8 +899,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size());
for (pxr::TfToken token : uv_tokens) {
- void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
- active_mesh->mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
}
}
@@ -893,10 +909,14 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
/* Here we assume that the number of materials doesn't change, i.e. that
* the material slots that were created when the object was loaded from
* USD are still valid now. */
- size_t num_polys = active_mesh->totpoly;
- if (num_polys > 0 && import_params_.import_materials) {
+ MutableSpan<MPoly> polys = active_mesh->polys_for_write();
+ if (!polys.is_empty() && import_params_.import_materials) {
std::map<pxr::SdfPath, int> mat_map;
- assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map);
+ bke::MutableAttributeAccessor attributes = active_mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map);
+ material_indices.finish();
}
}
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h
index 5e33ce8b5e8..181fd5ebf79 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.h
+++ b/source/blender/io/usd/intern/usd_reader_mesh.h
@@ -3,13 +3,13 @@
* Modifications Copyright 2021 Tangent Animation and. NVIDIA Corporation. All rights reserved. */
#pragma once
+#include "BLI_span.hh"
+
#include "usd.h"
#include "usd_reader_geom.h"
#include "pxr/usd/usdGeom/mesh.h"
-struct MPoly;
-
namespace blender::io::usd {
class USDMeshReader : public USDGeomReader {
@@ -61,10 +61,9 @@ class USDMeshReader : public USDGeomReader {
/** Set USD uniform (per-face) normals as Blender loop normals. */
void process_normals_uniform(Mesh *mesh);
void readFaceSetsSample(Main *bmain, Mesh *mesh, double motionSampleTime);
- void assign_facesets_to_mpoly(double motionSampleTime,
- struct MPoly *mpoly,
- int totpoly,
- std::map<pxr::SdfPath, int> *r_mat_map);
+ void assign_facesets_to_material_indices(double motionSampleTime,
+ MutableSpan<int> material_indices,
+ std::map<pxr::SdfPath, int> *r_mat_map);
void read_mpolys(Mesh *mesh);
void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false);
diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc
index 583c58a1356..df75be849e2 100644
--- a/source/blender/io/usd/intern/usd_reader_stage.cc
+++ b/source/blender/io/usd/intern/usd_reader_stage.cc
@@ -28,6 +28,9 @@
#include <iostream>
+#include "BLI_sort.hh"
+#include "BLI_string.h"
+
namespace blender::io::usd {
USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage,
@@ -252,8 +255,6 @@ USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &
return nullptr;
}
- reader->create_object(bmain, 0.0);
-
readers_.push_back(reader);
reader->incref();
@@ -310,4 +311,14 @@ void USDStageReader::clear_readers()
readers_.clear();
}
+void USDStageReader::sort_readers()
+{
+ blender::parallel_sort(
+ readers_.begin(), readers_.end(), [](const USDPrimReader *a, const USDPrimReader *b) {
+ const char *na = a ? a->name().c_str() : "";
+ const char *nb = b ? b->name().c_str() : "";
+ return BLI_strcasecmp(na, nb) < 0;
+ });
+}
+
} // Namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h
index 0ed964c7679..5f4a343f874 100644
--- a/source/blender/io/usd/intern/usd_reader_stage.h
+++ b/source/blender/io/usd/intern/usd_reader_stage.h
@@ -63,6 +63,8 @@ class USDStageReader {
return readers_;
};
+ void sort_readers();
+
private:
USDPrimReader *collect_readers(Main *bmain, const pxr::UsdPrim &prim);
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc
index b76f74cfd3d..a39f74c6420 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -11,6 +11,7 @@
#include "BLI_math_vector.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
@@ -245,8 +246,8 @@ static void get_vertices(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
usd_mesh_data.points.reserve(mesh->totvert);
- const MVert *verts = mesh->mvert;
- for (int i = 0; i < mesh->totvert; ++i) {
+ const Span<MVert> verts = mesh->verts();
+ for (const int i : verts.index_range()) {
usd_mesh_data.points.push_back(pxr::GfVec3f(verts[i].co));
}
}
@@ -255,22 +256,27 @@ static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
/* Only construct face groups (a.k.a. geometry subsets) when we need them for material
* assignments. */
- bool construct_face_groups = mesh->totcol > 1;
+ const bke::AttributeAccessor attributes = mesh->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (!material_indices.is_single() && mesh->totcol > 1) {
+ const VArraySpan<int> indices_span(material_indices);
+ for (const int i : indices_span.index_range()) {
+ usd_mesh_data.face_groups[indices_span[i]].push_back(i);
+ }
+ }
usd_mesh_data.face_vertex_counts.reserve(mesh->totpoly);
usd_mesh_data.face_indices.reserve(mesh->totloop);
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
- for (int i = 0; i < mesh->totpoly; ++i, ++mpoly) {
- MLoop *loop = mloop + mpoly->loopstart;
- usd_mesh_data.face_vertex_counts.push_back(mpoly->totloop);
- for (int j = 0; j < mpoly->totloop; ++j, ++loop) {
- usd_mesh_data.face_indices.push_back(loop->v);
- }
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
- if (construct_face_groups) {
- usd_mesh_data.face_groups[mpoly->mat_nr].push_back(i);
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ usd_mesh_data.face_vertex_counts.push_back(poly.totloop);
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ usd_mesh_data.face_indices.push_back(loop.v);
}
}
}
@@ -279,22 +285,23 @@ static void get_edge_creases(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
const float factor = 1.0f / 255.0f;
- MEdge *edge = mesh->medge;
+ const Span<MEdge> edges = mesh->edges();
float sharpness;
- for (int edge_idx = 0, totedge = mesh->totedge; edge_idx < totedge; ++edge_idx, ++edge) {
- if (edge->crease == 0) {
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ if (edge.crease == 0) {
continue;
}
- if (edge->crease == 255) {
+ if (edge.crease == 255) {
sharpness = pxr::UsdGeomMesh::SHARPNESS_INFINITE;
}
else {
- sharpness = static_cast<float>(edge->crease) * factor;
+ sharpness = static_cast<float>(edge.crease) * factor;
}
- usd_mesh_data.crease_vertex_indices.push_back(edge->v1);
- usd_mesh_data.crease_vertex_indices.push_back(edge->v2);
+ usd_mesh_data.crease_vertex_indices.push_back(edge.v1);
+ usd_mesh_data.crease_vertex_indices.push_back(edge.v2);
usd_mesh_data.crease_lengths.push_back(2);
usd_mesh_data.crease_sharpnesses.push_back(sharpness);
}
@@ -392,6 +399,8 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
{
pxr::UsdTimeCode timecode = get_export_time_code();
const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
pxr::VtVec3fArray loop_normals;
loop_normals.reserve(mesh->totloop);
@@ -406,21 +415,20 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
/* Compute the loop normals based on the 'smooth' flag. */
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
const float(*face_normals)[3] = BKE_mesh_poly_normals_ensure(mesh);
- MPoly *mpoly = mesh->mpoly;
- for (int poly_idx = 0, totpoly = mesh->totpoly; poly_idx < totpoly; ++poly_idx, ++mpoly) {
- MLoop *mloop = mesh->mloop + mpoly->loopstart;
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
- if ((mpoly->flag & ME_SMOOTH) == 0) {
+ if ((poly.flag & ME_SMOOTH) == 0) {
/* Flat shaded, use common normal for all verts. */
- pxr::GfVec3f pxr_normal(face_normals[poly_idx]);
- for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx) {
+ pxr::GfVec3f pxr_normal(face_normals[i]);
+ for (int loop_idx = 0; loop_idx < poly.totloop; ++loop_idx) {
loop_normals.push_back(pxr_normal);
}
}
else {
/* Smooth shaded, use individual vert normals. */
- for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx, ++mloop) {
- loop_normals.push_back(pxr::GfVec3f(vert_normals[mloop->v]));
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ loop_normals.push_back(pxr::GfVec3f(vert_normals[loop.v]));
}
}
}
diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc
index 6300e5c657c..12db6d73901 100644
--- a/source/blender/io/usd/intern/usd_writer_volume.cc
+++ b/source/blender/io/usd/intern/usd_writer_volume.cc
@@ -152,7 +152,7 @@ std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume
strcat(vdb_file_name, ".vdb");
char vdb_file_path[FILE_MAX];
- BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL);
+ BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, nullptr);
return vdb_file_path;
}
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index a07315d8b4e..3494d8ffdc3 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -52,7 +52,7 @@ struct USDImportParams {
bool import_materials;
bool import_meshes;
bool import_volumes;
- char *prim_path_mask;
+ char prim_path_mask[1024];
bool import_subdiv;
bool import_instance_proxies;
bool create_collection;
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
index fb0b4a1aca9..2fd2973ee73 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
@@ -4,6 +4,7 @@
* \ingroup obj
*/
+#include "BLI_path_util.h"
#include "BLI_timeit.hh"
#include "IO_wavefront_obj.h"
@@ -11,14 +12,26 @@
#include "obj_exporter.hh"
#include "obj_importer.hh"
+using namespace blender::timeit;
+
+static void report_duration(const char *job, const TimePoint &start_time, const char *path)
+{
+ Nanoseconds duration = Clock::now() - start_time;
+ std::cout << "OBJ " << job << " of '" << BLI_path_basename(path) << "' took ";
+ print_duration(duration);
+ std::cout << '\n';
+}
+
void OBJ_export(bContext *C, const OBJExportParams *export_params)
{
- SCOPED_TIMER("OBJ export");
+ TimePoint start_time = Clock::now();
blender::io::obj::exporter_main(C, *export_params);
+ report_duration("export", start_time, export_params->filepath);
}
void OBJ_import(bContext *C, const OBJImportParams *import_params)
{
- SCOPED_TIMER(__func__);
+ TimePoint start_time = Clock::now();
blender::io::obj::importer_main(C, *import_params);
+ report_duration("import", start_time, import_params->filepath);
}
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
index b4a00deb99c..0a92bbca477 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
@@ -16,8 +16,6 @@
extern "C" {
#endif
-static const int TOTAL_AXES = 3;
-
struct OBJExportParams {
/** Full path to the destination .OBJ file. */
char filepath[FILE_MAX];
@@ -50,18 +48,15 @@ struct OBJExportParams {
bool export_triangulated_mesh;
bool export_curves_as_nurbs;
ePathReferenceMode path_mode;
+ bool export_pbr_extensions;
/* Grouping options. */
bool export_object_groups;
bool export_material_groups;
bool export_vertex_groups;
- /**
- * Calculate smooth groups from sharp edges.
- */
+ /* Calculate smooth groups from sharp edges. */
bool export_smooth_groups;
- /**
- * Create bitflags instead of the default "0"/"1" group IDs.
- */
+ /* Create bitflags instead of the default "0"/"1" group IDs. */
bool smooth_groups_bitflags;
};
@@ -74,6 +69,8 @@ struct OBJImportParams {
eIOAxis up_axis;
bool import_vertex_groups;
bool validate_meshes;
+ bool relative_paths;
+ bool clear_selection;
};
/**
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index 731587bfcea..f2547e6fc14 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -44,7 +44,7 @@ static const char *DEFORM_GROUP_DISABLED = "off";
* So an empty material name is written. */
static const char *MATERIAL_GROUP_DISABLED = "";
-void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_uv_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -57,12 +57,12 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
const int uv_offset = offsets.uv_vertex_offset + 1;
const int normal_offset = offsets.normal_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
+ uv_indices[j] + uv_offset,
+ normal_indices[j] + normal_offset);
}
}
else {
@@ -71,15 +71,15 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
* then go backwards. Same logic in other write_*_indices functions below. */
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
+ uv_indices[j] + uv_offset,
+ normal_indices[j] + normal_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -90,24 +90,24 @@ void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
const int vertex_offset = offsets.vertex_offset + 1;
const int normal_offset = offsets.normal_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
+ normal_indices[j] + normal_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
+ normal_indices[j] + normal_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_uv_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -118,24 +118,22 @@ void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
const int vertex_offset = offsets.vertex_offset + 1;
const int uv_offset = offsets.uv_vertex_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset);
+ fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset);
+ fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -144,27 +142,27 @@ void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
{
const int vertex_offset = offsets.vertex_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
+ fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
+ fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
void OBJWriter::write_header() const
{
using namespace std::string_literals;
- FormatHandler<eFileType::OBJ> fh;
- fh.write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + "\n");
- fh.write<eOBJSyntaxElement::string>("# www.blender.org\n");
+ FormatHandler fh;
+ fh.write_string("# Blender "s + BKE_blender_version_string());
+ fh.write_string("# www.blender.org");
fh.write_to_file(outfile_);
}
@@ -174,22 +172,27 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
char mtl_file_name[FILE_MAXFILE];
char mtl_dir_name[FILE_MAXDIR];
BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
- FormatHandler<eFileType::OBJ> fh;
- fh.write<eOBJSyntaxElement::mtllib>(mtl_file_name);
+ FormatHandler fh;
+ fh.write_obj_mtllib(mtl_file_name);
fh.write_to_file(outfile_);
}
-void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh,
- const OBJMesh &obj_mesh_data) const
+static void spaces_to_underscores(std::string &r_name)
{
- const char *object_name = obj_mesh_data.get_object_name();
+ std::replace(r_name.begin(), r_name.end(), ' ', '_');
+}
+
+void OBJWriter::write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const
+{
+ std::string object_name = obj_mesh_data.get_object_name();
+ spaces_to_underscores(object_name);
if (export_params_.export_object_groups) {
- const std::string object_name = obj_mesh_data.get_object_name();
- const char *mesh_name = obj_mesh_data.get_object_mesh_name();
- fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mesh_name);
+ std::string mesh_name = obj_mesh_data.get_object_mesh_name();
+ spaces_to_underscores(mesh_name);
+ fh.write_obj_group(object_name + "_" + mesh_name);
return;
}
- fh.write<eOBJSyntaxElement::object_name>(object_name);
+ fh.write_obj_object(object_name);
}
/* Split up large meshes into multi-threaded jobs; each job processes
@@ -207,9 +210,7 @@ static int calc_chunk_count(int count)
* will be written into the final /fh/ buffer at the end.
*/
template<typename Function>
-void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
- int tot_count,
- const Function &function)
+void obj_parallel_chunked_output(FormatHandler &fh, int tot_count, const Function &function)
{
if (tot_count <= 0) {
return;
@@ -225,7 +226,7 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
return;
}
/* Give each chunk its own temporary output buffer, and process them in parallel. */
- std::vector<FormatHandler<eFileType::OBJ>> buffers(chunk_count);
+ std::vector<FormatHandler> buffers(chunk_count);
blender::threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) {
for (const int r : range) {
int i_start = r * chunk_size;
@@ -242,58 +243,57 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
}
}
-void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vertex_coords(FormatHandler &fh,
const OBJMesh &obj_mesh_data,
bool write_colors) const
{
const int tot_count = obj_mesh_data.tot_vertices();
Mesh *mesh = obj_mesh_data.get_mesh();
- CustomDataLayer *colors_layer = nullptr;
+ const CustomDataLayer *colors_layer = nullptr;
if (write_colors) {
colors_layer = BKE_id_attributes_active_color_get(&mesh->id);
}
if (write_colors && (colors_layer != nullptr)) {
- const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
+ const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f});
BLI_assert(tot_count == attribute.size());
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
ColorGeometry4f linear = attribute.get(i);
float srgb[3];
linearrgb_to_srgb_v3_v3(srgb, linear);
- buf.write<eOBJSyntaxElement::vertex_coords_color>(
- vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
+ buf.write_obj_vertex_color(vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
});
}
else {
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
- buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
+ buf.write_obj_vertex(vertex[0], vertex[1], vertex[2]);
});
}
}
-void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const
+void OBJWriter::write_uv_coords(FormatHandler &fh, OBJMesh &r_obj_mesh_data) const
{
const Vector<float2> &uv_coords = r_obj_mesh_data.get_uv_coords();
const int tot_count = uv_coords.size();
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
const float2 &uv_vertex = uv_coords[i];
- buf.write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
+ buf.write_obj_uv(uv_vertex[0], uv_vertex[1]);
});
}
-void OBJWriter::write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data)
+void OBJWriter::write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
{
/* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
const Vector<float3> &normal_coords = obj_mesh_data.get_normal_coords();
const int tot_count = normal_coords.size();
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
const float3 &normal = normal_coords[i];
- buf.write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]);
+ buf.write_obj_normal(normal[0], normal[1], normal[2]);
});
}
@@ -328,7 +328,7 @@ static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params,
return group;
}
-void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_poly_elements(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn)
@@ -340,7 +340,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
const int tot_deform_groups = obj_mesh_data.tot_deform_groups();
threading::EnumerableThreadSpecific<Vector<float>> group_weights;
- obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) {
+ obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler &buf, int idx) {
/* Polygon order for writing into the file is not necessarily the same
* as order in the mesh; it will be sorted by material indices. Remap current
* and previous indices here according to the order. */
@@ -356,7 +356,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i);
const int group = get_smooth_group(obj_mesh_data, export_params_, i);
if (group != prev_group) {
- buf.write<eOBJSyntaxElement::smooth_group>(group);
+ buf.write_obj_smooth(group);
}
}
@@ -369,19 +369,22 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
prev_i, local_weights);
const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights);
if (group != prev_group) {
- buf.write<eOBJSyntaxElement::object_group>(
- group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
- obj_mesh_data.get_poly_deform_group_name(group));
+ buf.write_obj_group(group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
+ obj_mesh_data.get_poly_deform_group_name(group));
}
}
+ const bke::AttributeAccessor attributes = obj_mesh_data.get_mesh()->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
/* Write material name and material group if different from previous. */
if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) {
- const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(prev_i);
- const int16_t mat = obj_mesh_data.ith_poly_matnr(i);
+ const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : std::max(0, material_indices[prev_i]);
+ const int16_t mat = std::max(0, material_indices[i]);
if (mat != prev_mat) {
if (mat == NOT_FOUND) {
- buf.write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
+ buf.write_obj_usemtl(MATERIAL_GROUP_DISABLED);
}
else {
const char *mat_name = matname_fn(mat);
@@ -389,10 +392,11 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
mat_name = MATERIAL_GROUP_DISABLED;
}
if (export_params_.export_material_groups) {
- const std::string object_name = obj_mesh_data.get_object_name();
- fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name);
+ std::string object_name = obj_mesh_data.get_object_name();
+ spaces_to_underscores(object_name);
+ fh.write_obj_group(object_name + "_" + mat_name);
}
- buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name);
+ buf.write_obj_usemtl(mat_name);
}
}
}
@@ -407,7 +411,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
});
}
-void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_edges_indices(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data) const
{
@@ -419,13 +423,12 @@ void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
if (!vertex_indices) {
continue;
}
- fh.write<eOBJSyntaxElement::edge>((*vertex_indices)[0] + offsets.vertex_offset + 1,
- (*vertex_indices)[1] + offsets.vertex_offset + 1);
+ fh.write_obj_edge((*vertex_indices)[0] + offsets.vertex_offset + 1,
+ (*vertex_indices)[1] + offsets.vertex_offset + 1);
}
}
-void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
- const OBJCurve &obj_nurbs_data) const
+void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const
{
const int total_splines = obj_nurbs_data.total_splines();
for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
@@ -433,15 +436,14 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
spline_idx, vertex_idx, export_params_.scaling_factor);
- fh.write<eOBJSyntaxElement::vertex_coords>(
- vertex_coords[0], vertex_coords[1], vertex_coords[2]);
+ fh.write_obj_vertex(vertex_coords[0], vertex_coords[1], vertex_coords[2]);
}
const char *nurbs_name = obj_nurbs_data.get_curve_name();
const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
- fh.write<eOBJSyntaxElement::object_group>(nurbs_name);
- fh.write<eOBJSyntaxElement::cstype>();
- fh.write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
+ fh.write_obj_group(nurbs_name);
+ fh.write_obj_cstype();
+ fh.write_obj_nurbs_degree(nurbs_degree);
/**
* The numbers written here are indices into the vertex coordinates written
* earlier, relative to the line that is going to be written.
@@ -450,13 +452,13 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
* 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices.
*/
const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
- fh.write<eOBJSyntaxElement::curve_element_begin>();
+ fh.write_obj_curve_begin();
for (int i = 0; i < total_control_points; i++) {
/* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
* last vertex coordinate, -2 second last. */
- fh.write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
+ fh.write_obj_poly_v(-((i % total_vertices) + 1));
}
- fh.write<eOBJSyntaxElement::curve_element_end>();
+ fh.write_obj_curve_end();
/**
* In `parm u 0 0.1 ..` line:, (total control points + 2) equidistant numbers in the
@@ -467,7 +469,7 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx);
const bool cyclic = flagsu & CU_NURB_CYCLIC;
const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT);
- fh.write<eOBJSyntaxElement::nurbs_parameter_begin>();
+ fh.write_obj_nurbs_parm_begin();
for (int i = 1; i <= total_control_points + 2; i++) {
float parm = 1.0f * i / (total_control_points + 2 + 1);
if (endpoint) {
@@ -478,11 +480,10 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
parm = 1;
}
}
- fh.write<eOBJSyntaxElement::nurbs_parameters>(parm);
+ fh.write_obj_nurbs_parm(parm);
}
- fh.write<eOBJSyntaxElement::nurbs_parameter_end>();
-
- fh.write<eOBJSyntaxElement::nurbs_group_end>();
+ fh.write_obj_nurbs_parm_end();
+ fh.write_obj_nurbs_group_end();
}
}
@@ -490,6 +491,21 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
/** \name .MTL writers.
* \{ */
+static const char *tex_map_type_to_string[] = {
+ "map_Kd",
+ "map_Pm",
+ "map_Ks",
+ "map_Ns",
+ "map_Pr",
+ "map_Ps",
+ "map_refl",
+ "map_Ke",
+ "map_d",
+ "map_Bump",
+};
+BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_string) == (int)MTLTexMapType::Count,
+ "array size mismatch");
+
/**
* Convert #float3 to string of space-separated numbers, with no leading or trailing space.
* Only to be used in NON-performance-critical code.
@@ -530,9 +546,9 @@ void MTLWriter::write_header(const char *blen_filepath)
const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
BLI_path_basename(blen_filepath) :
"None";
- fmt_handler_.write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
- " MTL File: '" + blen_basename + "'\n");
- fmt_handler_.write<eMTLSyntaxElement::string>("# www.blender.org\n");
+ fmt_handler_.write_string("# Blender "s + BKE_blender_version_string() + " MTL File: '" +
+ blen_basename + "'");
+ fmt_handler_.write_string("# www.blender.org");
}
StringRefNull MTLWriter::mtl_file_path() const
@@ -540,66 +556,109 @@ StringRefNull MTLWriter::mtl_file_path() const
return mtl_filepath_;
}
-void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
+void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl, bool write_pbr)
{
- fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
- fmt_handler_.write<eMTLSyntaxElement::Ka>(
- mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
- fmt_handler_.write<eMTLSyntaxElement::Kd>(
- mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
- fmt_handler_.write<eMTLSyntaxElement::Ks>(
- mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
- fmt_handler_.write<eMTLSyntaxElement::Ke>(
- mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
- fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
- fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d);
- fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum);
+ /* For various material properties, we only capture information
+ * coming from the texture, or the default value of the socket.
+ * When the texture is present, do not emit the default value. */
+
+ /* Do not write Ns & Ka when writing in PBR mode. */
+ if (!write_pbr) {
+ if (!mtl.tex_map_of_type(MTLTexMapType::SpecularExponent).is_valid()) {
+ fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent);
+ }
+ fmt_handler_.write_mtl_float3(
+ "Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Color).is_valid()) {
+ fmt_handler_.write_mtl_float3("Kd", mtl.color.x, mtl.color.y, mtl.color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Specular).is_valid()) {
+ fmt_handler_.write_mtl_float3("Ks", mtl.spec_color.x, mtl.spec_color.y, mtl.spec_color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Emission).is_valid()) {
+ fmt_handler_.write_mtl_float3(
+ "Ke", mtl.emission_color.x, mtl.emission_color.y, mtl.emission_color.z);
+ }
+ fmt_handler_.write_mtl_float("Ni", mtl.ior);
+ if (!mtl.tex_map_of_type(MTLTexMapType::Alpha).is_valid()) {
+ fmt_handler_.write_mtl_float("d", mtl.alpha);
+ }
+ fmt_handler_.write_mtl_illum(mtl.illum_mode);
+
+ if (write_pbr) {
+ if (!mtl.tex_map_of_type(MTLTexMapType::Roughness).is_valid() && mtl.roughness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pr", mtl.roughness);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Metallic).is_valid() && mtl.metallic >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pm", mtl.metallic);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Sheen).is_valid() && mtl.sheen >= 0.0f) {
+ fmt_handler_.write_mtl_float("Ps", mtl.sheen);
+ }
+ if (mtl.cc_thickness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pc", mtl.cc_thickness);
+ }
+ if (mtl.cc_roughness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pcr", mtl.cc_roughness);
+ }
+ if (mtl.aniso >= 0.0f) {
+ fmt_handler_.write_mtl_float("aniso", mtl.aniso);
+ }
+ if (mtl.aniso_rot >= 0.0f) {
+ fmt_handler_.write_mtl_float("anisor", mtl.aniso_rot);
+ }
+ if (mtl.transmit_color.x > 0.0f || mtl.transmit_color.y > 0.0f ||
+ mtl.transmit_color.z > 0.0f) {
+ fmt_handler_.write_mtl_float3(
+ "Tf", mtl.transmit_color.x, mtl.transmit_color.y, mtl.transmit_color.z);
+ }
+ }
}
-void MTLWriter::write_texture_map(
- const MTLMaterial &mtl_material,
- const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map,
- const char *blen_filedir,
- const char *dest_dir,
- ePathReferenceMode path_mode,
- Set<std::pair<std::string, std::string>> &copy_set)
+void MTLWriter::write_texture_map(const MTLMaterial &mtl_material,
+ MTLTexMapType texture_key,
+ const MTLTexMap &texture_map,
+ const char *blen_filedir,
+ const char *dest_dir,
+ ePathReferenceMode path_mode,
+ Set<std::pair<std::string, std::string>> &copy_set)
{
std::string options;
/* Option strings should have their own leading spaces. */
- if (texture_map.value.translation != float3{0.0f, 0.0f, 0.0f}) {
- options.append(" -o ").append(float3_to_string(texture_map.value.translation));
+ if (texture_map.translation != float3{0.0f, 0.0f, 0.0f}) {
+ options.append(" -o ").append(float3_to_string(texture_map.translation));
}
- if (texture_map.value.scale != float3{1.0f, 1.0f, 1.0f}) {
- options.append(" -s ").append(float3_to_string(texture_map.value.scale));
+ if (texture_map.scale != float3{1.0f, 1.0f, 1.0f}) {
+ options.append(" -s ").append(float3_to_string(texture_map.scale));
}
- if (texture_map.key == eMTLSyntaxElement::map_Bump && mtl_material.map_Bump_strength > 0.0001f) {
- options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
+ if (texture_key == MTLTexMapType::Normal && mtl_material.normal_strength > 0.0001f) {
+ options.append(" -bm ").append(std::to_string(mtl_material.normal_strength));
}
-#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
- if (texture_map.key == eMTLSyntaxElement) { \
- std::string path = path_reference( \
- texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set); \
- /* Always emit forward slashes for cross-platform compatibility. */ \
- std::replace(path.begin(), path.end(), '\\', '/'); \
- fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \
- return; \
- }
+ std::string path = path_reference(
+ texture_map.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set);
+ /* Always emit forward slashes for cross-platform compatibility. */
+ std::replace(path.begin(), path.end(), '\\', '/');
+
+ fmt_handler_.write_mtl_map(tex_map_type_to_string[(int)texture_key], options, path);
+}
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Kd);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ks);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ns);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_d);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump);
+static bool is_pbr_map(MTLTexMapType type)
+{
+ return type == MTLTexMapType::Metallic || type == MTLTexMapType::Roughness ||
+ type == MTLTexMapType::Sheen;
+}
- BLI_assert(!"This map type was not written to the file.");
+static bool is_non_pbr_map(MTLTexMapType type)
+{
+ return type == MTLTexMapType::SpecularExponent || type == MTLTexMapType::Reflection;
}
void MTLWriter::write_materials(const char *blen_filepath,
ePathReferenceMode path_mode,
- const char *dest_dir)
+ const char *dest_dir,
+ bool write_pbr)
{
if (mtlmaterials_.size() == 0) {
return;
@@ -615,14 +674,22 @@ void MTLWriter::write_materials(const char *blen_filepath,
[](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
Set<std::pair<std::string, std::string>> copy_set;
for (const MTLMaterial &mtlmat : mtlmaterials_) {
- fmt_handler_.write<eMTLSyntaxElement::string>("\n");
- fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name);
- write_bsdf_properties(mtlmat);
- for (const auto &tex : mtlmat.texture_maps.items()) {
- if (tex.value.image_path.empty()) {
+ fmt_handler_.write_string("");
+ fmt_handler_.write_mtl_newmtl(mtlmat.name);
+ write_bsdf_properties(mtlmat, write_pbr);
+ for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
+ const MTLTexMap &tex = mtlmat.texture_maps[key];
+ if (!tex.is_valid()) {
+ continue;
+ }
+ if (!write_pbr && is_pbr_map((MTLTexMapType)key)) {
+ continue;
+ }
+ if (write_pbr && is_non_pbr_map((MTLTexMapType)key)) {
continue;
}
- write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set);
+ write_texture_map(
+ mtlmat, (MTLTexMapType)key, tex, blen_filedir, dest_dir, path_mode, copy_set);
}
}
path_reference_copy(copy_set);
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
index 97c23484426..eda4576297b 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
@@ -66,7 +66,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write object's name or group.
*/
- void write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const;
+ void write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write file name of Material Library in .OBJ file.
*/
@@ -74,19 +74,19 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write vertex coordinates for all vertices as "v x y z" or "v x y z r g b".
*/
- void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
+ void write_vertex_coords(FormatHandler &fh,
const OBJMesh &obj_mesh_data,
bool write_colors) const;
/**
* Write UV vertex coordinates for all vertices as `vt u v`.
* \note UV indices are stored here, but written with polygons later.
*/
- void write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data) const;
+ void write_uv_coords(FormatHandler &fh, OBJMesh &obj_mesh_data) const;
/**
* Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
* \note Normal indices ares stored here, but written with polygons later.
*/
- void write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data);
+ void write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data);
/**
* Write polygon elements with at least vertex indices, and conditionally with UV vertex
* indices and polygon normal indices. Also write groups: smooth, vertex, material.
@@ -94,23 +94,23 @@ class OBJWriter : NonMovable, NonCopyable {
* name used in the .obj file.
* \note UV indices were stored while writing UV vertices.
*/
- void write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+ void write_poly_elements(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn);
/**
* Write loose edges of a mesh as "l v1 v2".
*/
- void write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_edges_indices(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data) const;
/**
* Write a NURBS curve to the .OBJ file in parameter form.
*/
- void write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh, const OBJCurve &obj_nurbs_data) const;
+ void write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const;
private:
- using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler<eFileType::OBJ> &fh,
+ using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -124,7 +124,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
*/
- void write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_uv_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -133,7 +133,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
*/
- void write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -142,7 +142,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
*/
- void write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_uv_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -151,7 +151,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1 v2 ...".
*/
- void write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -164,7 +164,7 @@ class OBJWriter : NonMovable, NonCopyable {
*/
class MTLWriter : NonMovable, NonCopyable {
private:
- FormatHandler<eFileType::MTL> fmt_handler_;
+ FormatHandler fmt_handler_;
FILE *outfile_;
std::string mtl_filepath_;
Vector<MTLMaterial> mtlmaterials_;
@@ -186,7 +186,8 @@ class MTLWriter : NonMovable, NonCopyable {
*/
void write_materials(const char *blen_filepath,
ePathReferenceMode path_mode,
- const char *dest_dir);
+ const char *dest_dir,
+ bool write_pbr);
StringRefNull mtl_file_path() const;
/**
* Add the materials of the given object to #MTLWriter, de-duplicating
@@ -203,12 +204,13 @@ class MTLWriter : NonMovable, NonCopyable {
/**
* Write properties sourced from p-BSDF node or #Object.Material.
*/
- void write_bsdf_properties(const MTLMaterial &mtl_material);
+ void write_bsdf_properties(const MTLMaterial &mtl_material, bool write_pbr);
/**
* Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image".
*/
void write_texture_map(const MTLMaterial &mtl_material,
- const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map,
+ MTLTexMapType texture_key,
+ const MTLTexMap &texture_map,
const char *blen_filedir,
const char *dest_dir,
ePathReferenceMode mode,
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
index 5413c9969e3..59ee7bd32c0 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
@@ -23,268 +23,24 @@
namespace blender::io::obj {
-enum class eFileType {
- OBJ,
- MTL,
-};
-
-enum class eOBJSyntaxElement {
- vertex_coords,
- vertex_coords_color,
- uv_vertex_coords,
- normal,
- poly_element_begin,
- vertex_uv_normal_indices,
- vertex_normal_indices,
- vertex_uv_indices,
- vertex_indices,
- poly_element_end,
- poly_usemtl,
- edge,
- cstype,
- nurbs_degree,
- curve_element_begin,
- curve_element_end,
- nurbs_parameter_begin,
- nurbs_parameters,
- nurbs_parameter_end,
- nurbs_group_end,
- new_line,
- mtllib,
- smooth_group,
- object_group,
- object_name,
- /* Use rarely. New line is NOT included for string. */
- string,
-};
-
-enum class eMTLSyntaxElement {
- newmtl,
- Ni,
- d,
- Ns,
- illum,
- Ka,
- Kd,
- Ks,
- Ke,
- map_Kd,
- map_Ks,
- map_Ns,
- map_d,
- map_refl,
- map_Ke,
- map_Bump,
- /* Use rarely. New line is NOT included for string. */
- string,
-};
-
-template<eFileType filetype> struct FileTypeTraits;
-
-/* Used to prevent mixing of say OBJ file format with MTL syntax elements. */
-template<> struct FileTypeTraits<eFileType::OBJ> {
- using SyntaxType = eOBJSyntaxElement;
-};
-
-template<> struct FileTypeTraits<eFileType::MTL> {
- using SyntaxType = eMTLSyntaxElement;
-};
-
-struct FormattingSyntax {
- /* Formatting syntax with the file format key like `newmtl %s\n`. */
- const char *fmt = nullptr;
- /* Number of arguments needed by the syntax. */
- const int total_args = 0;
- /* Whether types of the given arguments are accepted by the syntax above. Fail to compile by
- * default.
- */
- const bool are_types_valid = false;
-};
-
-/**
- * Type dependent but always false. Use to add a `constexpr` conditional compile-time error.
- */
-template<typename T> struct always_false : std::false_type {
-};
-
-template<typename... T>
-constexpr bool is_type_float = (... && std::is_floating_point_v<std::decay_t<T>>);
-
-template<typename... T>
-constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>);
-
-template<typename... T>
-constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>);
-
-/* GCC (at least 9.3) while compiling the obj_exporter_tests.cc with optimizations on,
- * results in "obj_export_io.hh:205:18: warning: ‘%s’ directive output truncated writing 34 bytes
- * into a region of size 6" and similar warnings. Yes the output is truncated, and that is covered
- * as an edge case by tests on purpose. */
-#if defined(__GNUC__) && !defined(__clang__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wformat-truncation"
-#endif
-template<typename... T>
-constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key)
-{
- switch (key) {
- case eOBJSyntaxElement::vertex_coords: {
- return {"v {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eOBJSyntaxElement::vertex_coords_color: {
- return {"v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", 6, is_type_float<T...>};
- }
- case eOBJSyntaxElement::uv_vertex_coords: {
- return {"vt {:.6f} {:.6f}\n", 2, is_type_float<T...>};
- }
- case eOBJSyntaxElement::normal: {
- return {"vn {:.4f} {:.4f} {:.4f}\n", 3, is_type_float<T...>};
- }
- case eOBJSyntaxElement::poly_element_begin: {
- return {"f", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::vertex_uv_normal_indices: {
- return {" {}/{}/{}", 3, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_normal_indices: {
- return {" {}//{}", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_uv_indices: {
- return {" {}/{}", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_indices: {
- return {" {}", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::poly_usemtl: {
- return {"usemtl {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::edge: {
- return {"l {} {}\n", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::cstype: {
- return {"cstype bspline\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_degree: {
- return {"deg {}\n", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::curve_element_begin: {
- return {"curv 0.0 1.0", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameter_begin: {
- return {"parm u 0.0", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameters: {
- return {" {:.6f}", 1, is_type_float<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameter_end: {
- return {" 1.0\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_group_end: {
- return {"end\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::poly_element_end: {
- ATTR_FALLTHROUGH;
- }
- case eOBJSyntaxElement::curve_element_end: {
- ATTR_FALLTHROUGH;
- }
- case eOBJSyntaxElement::new_line: {
- return {"\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::mtllib: {
- return {"mtllib {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::smooth_group: {
- return {"s {}\n", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::object_group: {
- return {"g {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::object_name: {
- return {"o {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::string: {
- return {"{}", 1, is_type_string_related<T...>};
- }
- }
-}
-
-template<typename... T>
-constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key)
-{
- switch (key) {
- case eMTLSyntaxElement::newmtl: {
- return {"newmtl {}\n", 1, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::Ni: {
- return {"Ni {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::d: {
- return {"d {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ns: {
- return {"Ns {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::illum: {
- return {"illum {}\n", 1, is_type_integral<T...>};
- }
- case eMTLSyntaxElement::Ka: {
- return {"Ka {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Kd: {
- return {"Kd {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ks: {
- return {"Ks {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ke: {
- return {"Ke {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- /* NOTE: first texture map related argument, if present, will have its own leading space. */
- case eMTLSyntaxElement::map_Kd: {
- return {"map_Kd{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ks: {
- return {"map_Ks{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ns: {
- return {"map_Ns{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_d: {
- return {"map_d{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_refl: {
- return {"map_refl{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ke: {
- return {"map_Ke{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Bump: {
- return {"map_Bump{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::string: {
- return {"{}", 1, is_type_string_related<T...>};
- }
- }
-}
-#if defined(__GNUC__) && !defined(__clang__)
-# pragma GCC diagnostic pop
-#endif
-
/**
- * File format and syntax agnostic file buffer writer.
+ * File buffer writer.
* All writes are done into an internal chunked memory buffer
* (list of default 64 kilobyte blocks).
* Call write_fo_file once in a while to write the memory buffer(s)
* into the given file.
*/
-template<eFileType filetype, size_t buffer_chunk_size = 64 * 1024>
class FormatHandler : NonCopyable, NonMovable {
private:
typedef std::vector<char> VectorChar;
std::vector<VectorChar> blocks_;
+ size_t buffer_chunk_size_;
public:
+ FormatHandler(size_t buffer_chunk_size = 64 * 1024) : buffer_chunk_size_(buffer_chunk_size)
+ {
+ }
+
/* Write contents to the buffer(s) into a file, and clear the buffers. */
void write_to_file(FILE *f)
{
@@ -305,7 +61,7 @@ class FormatHandler : NonCopyable, NonMovable {
return blocks_.size();
}
- void append_from(FormatHandler<filetype, buffer_chunk_size> &v)
+ void append_from(FormatHandler &v)
{
blocks_.insert(blocks_.end(),
std::make_move_iterator(v.blocks_.begin()),
@@ -313,24 +69,132 @@ class FormatHandler : NonCopyable, NonMovable {
v.blocks_.clear();
}
- /**
- * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`.
- *
- * \param key: Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for
- * `eFileType::MTL`.
- */
- template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
- constexpr void write(T &&...args)
- {
- /* Get format syntax, number of arguments expected and whether types of given arguments are
- * valid.
- */
- constexpr FormattingSyntax fmt_nargs_valid = syntax_elem_to_formatting<T...>(key);
- BLI_STATIC_ASSERT(fmt_nargs_valid.are_types_valid &&
- (sizeof...(T) == fmt_nargs_valid.total_args),
- "Types of all arguments and the number of arguments should match what the "
- "formatting specifies.");
- write_impl(fmt_nargs_valid.fmt, std::forward<T>(args)...);
+ void write_obj_vertex(float x, float y, float z)
+ {
+ write_impl("v {:.6f} {:.6f} {:.6f}\n", x, y, z);
+ }
+ void write_obj_vertex_color(float x, float y, float z, float r, float g, float b)
+ {
+ write_impl("v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", x, y, z, r, g, b);
+ }
+ void write_obj_uv(float x, float y)
+ {
+ write_impl("vt {:.6f} {:.6f}\n", x, y);
+ }
+ void write_obj_normal(float x, float y, float z)
+ {
+ write_impl("vn {:.4f} {:.4f} {:.4f}\n", x, y, z);
+ }
+ void write_obj_poly_begin()
+ {
+ write_impl("f");
+ }
+ void write_obj_poly_end()
+ {
+ write_obj_newline();
+ }
+ void write_obj_poly_v_uv_normal(int v, int uv, int n)
+ {
+ write_impl(" {}/{}/{}", v, uv, n);
+ }
+ void write_obj_poly_v_normal(int v, int n)
+ {
+ write_impl(" {}//{}", v, n);
+ }
+ void write_obj_poly_v_uv(int v, int uv)
+ {
+ write_impl(" {}/{}", v, uv);
+ }
+ void write_obj_poly_v(int v)
+ {
+ write_impl(" {}", v);
+ }
+ void write_obj_usemtl(StringRef s)
+ {
+ write_impl("usemtl {}\n", s);
+ }
+ void write_obj_mtllib(StringRef s)
+ {
+ write_impl("mtllib {}\n", s);
+ }
+ void write_obj_smooth(int s)
+ {
+ write_impl("s {}\n", s);
+ }
+ void write_obj_group(StringRef s)
+ {
+ write_impl("g {}\n", s);
+ }
+ void write_obj_object(StringRef s)
+ {
+ write_impl("o {}\n", s);
+ }
+ void write_obj_edge(int a, int b)
+ {
+ write_impl("l {} {}\n", a, b);
+ }
+ void write_obj_cstype()
+ {
+ write_impl("cstype bspline\n");
+ }
+ void write_obj_nurbs_degree(int deg)
+ {
+ write_impl("deg {}\n", deg);
+ }
+ void write_obj_curve_begin()
+ {
+ write_impl("curv 0.0 1.0");
+ }
+ void write_obj_curve_end()
+ {
+ write_obj_newline();
+ }
+ void write_obj_nurbs_parm_begin()
+ {
+ write_impl("parm u 0.0");
+ }
+ void write_obj_nurbs_parm(float v)
+ {
+ write_impl(" {:.6f}", v);
+ }
+ void write_obj_nurbs_parm_end()
+ {
+ write_impl(" 1.0\n");
+ }
+ void write_obj_nurbs_group_end()
+ {
+ write_impl("end\n");
+ }
+ void write_obj_newline()
+ {
+ write_impl("\n");
+ }
+
+ void write_mtl_newmtl(StringRef s)
+ {
+ write_impl("newmtl {}\n", s);
+ }
+ void write_mtl_float(const char *type, float v)
+ {
+ write_impl("{} {:.6f}\n", type, v);
+ }
+ void write_mtl_float3(const char *type, float r, float g, float b)
+ {
+ write_impl("{} {:.6f} {:.6f} {:.6f}\n", type, r, g, b);
+ }
+ void write_mtl_illum(int mode)
+ {
+ write_impl("illum {}\n", mode);
+ }
+ /* NOTE: options, if present, will have its own leading space. */
+ void write_mtl_map(const char *type, StringRef options, StringRef value)
+ {
+ write_impl("{}{} {}\n", type, options, value);
+ }
+
+ void write_string(StringRef s)
+ {
+ write_impl("{}\n", s);
}
private:
@@ -340,7 +204,7 @@ class FormatHandler : NonCopyable, NonMovable {
{
if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
VectorChar &b = blocks_.emplace_back(VectorChar());
- b.reserve(std::max(at_least, buffer_chunk_size));
+ b.reserve(std::max(at_least, buffer_chunk_size_));
}
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index e2ecda32717..10880b016fb 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -6,6 +6,7 @@
/* Silence warnings from copying deprecated fields. Needed for an Object copy constructor use. */
#define DNA_DEPRECATED_ALLOW
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_lib_id.h"
@@ -47,7 +48,7 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
/* Since a new mesh been allocated, it needs to be freed in the destructor. */
mesh_eval_needs_free_ = true;
}
- if (export_params.export_triangulated_mesh && ELEM(export_object_eval_.type, OB_MESH, OB_SURF)) {
+ if (export_params.export_triangulated_mesh && export_object_eval_.type == OB_MESH) {
std::tie(export_mesh_eval_, mesh_eval_needs_free_) = triangulate_mesh_eval();
}
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
@@ -133,7 +134,7 @@ void OBJMesh::set_world_axes_transform(const eIOAxis forward, const eIOAxis up)
copy_m3_m4(normal_matrix, world_and_axes_transform_);
invert_m3_m3(world_and_axes_normal_transform_, normal_matrix);
transpose_m3(world_and_axes_normal_transform_);
- mirrored_transform_ = determinant_m3_array(world_and_axes_normal_transform_) < 0;
+ mirrored_transform_ = is_negative_m3(world_and_axes_normal_transform_);
}
int OBJMesh::tot_vertices() const
@@ -187,28 +188,38 @@ void OBJMesh::ensure_mesh_edges() const
void OBJMesh::calc_smooth_groups(const bool use_bitflags)
{
- poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(export_mesh_eval_->medge,
- export_mesh_eval_->totedge,
- export_mesh_eval_->mpoly,
- export_mesh_eval_->totpoly,
- export_mesh_eval_->mloop,
- export_mesh_eval_->totloop,
+ const Span<MEdge> edges = export_mesh_eval_->edges();
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(edges.data(),
+ edges.size(),
+ polys.data(),
+ polys.size(),
+ loops.data(),
+ loops.size(),
&tot_smooth_groups_,
use_bitflags);
}
void OBJMesh::calc_poly_order()
{
- const int tot_polys = tot_polygons();
- poly_order_.resize(tot_polys);
- for (int i = 0; i < tot_polys; ++i) {
+ const bke::AttributeAccessor attributes = export_mesh_eval_->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material_indices.is_single() && material_indices.get_internal_single() == 0) {
+ return;
+ }
+ const VArraySpan<int> material_indices_span(material_indices);
+
+ poly_order_.resize(material_indices_span.size());
+ for (const int i : material_indices_span.index_range()) {
poly_order_[i] = i;
}
- const MPoly *mpolys = export_mesh_eval_->mpoly;
+
/* Sort polygons by their material index. */
blender::parallel_sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) {
- int mat_a = mpolys[a].mat_nr;
- int mat_b = mpolys[b].mat_nr;
+ int mat_a = material_indices_span[a];
+ int mat_b = material_indices_span[b];
if (mat_a != mat_b) {
return mat_a < mat_b;
}
@@ -219,26 +230,20 @@ void OBJMesh::calc_poly_order()
const Material *OBJMesh::get_object_material(const int16_t mat_nr) const
{
/**
- * The const_cast is safe here because BKE_object_material_get won't change the object
+ * The const_cast is safe here because #BKE_object_material_get_eval won't change the object
* but it is a big can of worms to fix the declaration of that function right now.
*
* The call uses "+ 1" as material getter needs one-based indices.
*/
Object *obj = const_cast<Object *>(&export_object_eval_);
- const Material *r_mat = BKE_object_material_get(obj, mat_nr + 1);
+ const Material *r_mat = BKE_object_material_get_eval(obj, mat_nr + 1);
return r_mat;
}
bool OBJMesh::is_ith_poly_smooth(const int poly_index) const
{
- return export_mesh_eval_->mpoly[poly_index].flag & ME_SMOOTH;
-}
-
-int16_t OBJMesh::ith_poly_matnr(const int poly_index) const
-{
- BLI_assert(poly_index < export_mesh_eval_->totpoly);
- const int16_t r_mat_nr = export_mesh_eval_->mpoly[poly_index].mat_nr;
- return r_mat_nr >= 0 ? r_mat_nr : NOT_FOUND;
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ return polys[poly_index].flag & ME_SMOOTH;
}
const char *OBJMesh::get_object_name() const
@@ -263,7 +268,8 @@ const char *OBJMesh::get_object_material_name(const int16_t mat_nr) const
float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_factor) const
{
float3 r_coords;
- copy_v3_v3(r_coords, export_mesh_eval_->mvert[vert_index].co);
+ const Span<MVert> verts = export_mesh_eval_->verts();
+ copy_v3_v3(r_coords, verts[vert_index].co);
mul_m4_v3(world_and_axes_transform_, r_coords);
mul_v3_fl(r_coords, scaling_factor);
return r_coords;
@@ -271,8 +277,10 @@ float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_fac
Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
{
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const MPoly &mpoly = polys[poly_index];
+ const MLoop *mloop = &loops[mpoly.loopstart];
const int totloop = mpoly.totloop;
Vector<int> r_poly_vertex_indices(totloop);
for (int loop_index = 0; loop_index < totloop; loop_index++) {
@@ -283,9 +291,8 @@ Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
void OBJMesh::store_uv_coords_and_indices()
{
- const MPoly *mpoly = export_mesh_eval_->mpoly;
- const MLoop *mloop = export_mesh_eval_->mloop;
- const int totpoly = export_mesh_eval_->totpoly;
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
const int totvert = export_mesh_eval_->totvert;
const MLoopUV *mloopuv = static_cast<const MLoopUV *>(
CustomData_get_layer(&export_mesh_eval_->ldata, CD_MLOOPUV));
@@ -296,9 +303,9 @@ void OBJMesh::store_uv_coords_and_indices()
const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
- mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false);
+ polys.data(), nullptr, loops.data(), mloopuv, polys.size(), totvert, limit, false, false);
- uv_indices_.resize(totpoly);
+ uv_indices_.resize(polys.size());
/* At least total vertices of a mesh will be present in its texture map. So
* reserve minimum space early. */
uv_coords_.reserve(totvert);
@@ -310,16 +317,16 @@ void OBJMesh::store_uv_coords_and_indices()
if (uv_vert->separate) {
tot_uv_vertices_ += 1;
}
- const int vertices_in_poly = mpoly[uv_vert->poly_index].totloop;
+ const int verts_in_poly = polys[uv_vert->poly_index].totloop;
/* Store UV vertex coordinates. */
uv_coords_.resize(tot_uv_vertices_);
- const int loopstart = mpoly[uv_vert->poly_index].loopstart;
+ const int loopstart = polys[uv_vert->poly_index].loopstart;
Span<float> vert_uv_coords(mloopuv[loopstart + uv_vert->loop_of_poly_index].uv, 2);
uv_coords_[tot_uv_vertices_ - 1] = float2(vert_uv_coords[0], vert_uv_coords[1]);
/* Store UV vertex indices. */
- uv_indices_[uv_vert->poly_index].resize(vertices_in_poly);
+ uv_indices_[uv_vert->poly_index].resize(verts_in_poly);
/* Keep indices zero-based and let the writer handle the "+ 1" as per OBJ spec. */
uv_indices_[uv_vert->poly_index][uv_vert->loop_of_poly_index] = tot_uv_vertices_ - 1;
}
@@ -340,10 +347,11 @@ Span<int> OBJMesh::calc_poly_uv_indices(const int poly_index) const
float3 OBJMesh::calc_poly_normal(const int poly_index) const
{
float3 r_poly_normal;
- const MPoly &poly = export_mesh_eval_->mpoly[poly_index];
- const MLoop &mloop = export_mesh_eval_->mloop[poly.loopstart];
- const MVert &mvert = *(export_mesh_eval_->mvert);
- BKE_mesh_calc_poly_normal(&poly, &mloop, &mvert, r_poly_normal);
+ const Span<MVert> verts = export_mesh_eval_->verts();
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const MPoly &poly = polys[poly_index];
+ BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), r_poly_normal);
mul_m3_v3(world_and_axes_normal_transform_, r_poly_normal);
normalize_v3(r_poly_normal);
return r_poly_normal;
@@ -367,6 +375,8 @@ static float3 round_float3_to_n_digits(const float3 &v, int round_digits)
void OBJMesh::store_normal_coords_and_indices()
{
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+
/* We'll round normal components to 4 digits.
* This will cover up some minor differences
* between floating point calculations on different platforms.
@@ -382,7 +392,7 @@ void OBJMesh::store_normal_coords_and_indices()
const float(*lnors)[3] = static_cast<const float(*)[3]>(
CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
for (int poly_index = 0; poly_index < export_mesh_eval_->totpoly; ++poly_index) {
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+ const MPoly &mpoly = polys[poly_index];
bool need_per_loop_normals = lnors != nullptr || (mpoly.flag & ME_SMOOTH);
if (need_per_loop_normals) {
for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; ++loop_of_poly) {
@@ -426,7 +436,8 @@ Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const
if (loop_to_normal_index_.is_empty()) {
return {};
}
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const MPoly &mpoly = polys[poly_index];
const int totloop = mpoly.totloop;
Vector<int> r_poly_normal_indices(totloop);
for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
@@ -449,23 +460,23 @@ int16_t OBJMesh::get_poly_deform_group_index(const int poly_index,
{
BLI_assert(poly_index < export_mesh_eval_->totpoly);
BLI_assert(group_weights.size() == BKE_object_defgroup_count(&export_object_eval_));
-
- const MDeformVert *dvert_layer = static_cast<const MDeformVert *>(
- CustomData_get_layer(&export_mesh_eval_->vdata, CD_MDEFORMVERT));
- if (!dvert_layer) {
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const Span<MDeformVert> dverts = export_mesh_eval_->deform_verts();
+ if (dverts.is_empty()) {
return NOT_FOUND;
}
group_weights.fill(0);
bool found_any_group = false;
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
+ const MPoly &mpoly = polys[poly_index];
+ const MLoop *mloop = &loops[mpoly.loopstart];
for (int loop_i = 0; loop_i < mpoly.totloop; ++loop_i, ++mloop) {
- const MDeformVert &dvert = dvert_layer[mloop->v];
- for (int weight_i = 0; weight_i < dvert.totweight; ++weight_i) {
- const auto group = dvert.dw[weight_i].def_nr;
+ const MDeformVert &dv = dverts[mloop->v];
+ for (int weight_i = 0; weight_i < dv.totweight; ++weight_i) {
+ const auto group = dv.dw[weight_i].def_nr;
if (group < group_weights.size()) {
- group_weights[group] += dvert.dw[weight_i].weight;
+ group_weights[group] += dv.dw[weight_i].weight;
found_any_group = true;
}
}
@@ -489,7 +500,8 @@ const char *OBJMesh::get_poly_deform_group_name(const int16_t def_group_index) c
std::optional<std::array<int, 2>> OBJMesh::calc_loose_edge_vert_indices(const int edge_index) const
{
- const MEdge &edge = export_mesh_eval_->medge[edge_index];
+ const Span<MEdge> edges = export_mesh_eval_->edges();
+ const MEdge &edge = edges[edge_index];
if (edge.flag & ME_LOOSEEDGE) {
return std::array<int, 2>{static_cast<int>(edge.v1), static_cast<int>(edge.v2)};
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index ee2e6227700..db29f5651ed 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -130,11 +130,6 @@ class OBJMesh : NonCopyable {
* Return mat_nr-th material of the object. The given index should be zero-based.
*/
const Material *get_object_material(int16_t mat_nr) const;
- /**
- * Returns a zero-based index of a polygon's material indexing into
- * the Object's material slots.
- */
- int16_t ith_poly_matnr(int poly_index) const;
void ensure_mesh_normals() const;
void ensure_mesh_edges() const;
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
index 4ed148ec64e..f8c7da75a70 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
@@ -6,6 +6,7 @@
#include "BKE_image.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BLI_map.hh"
#include "BLI_math_vector.h"
@@ -15,13 +16,26 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
-#include "NOD_node_tree_ref.hh"
-
#include "obj_export_mesh.hh"
#include "obj_export_mtl.hh"
namespace blender::io::obj {
+const char *tex_map_type_to_socket_id[] = {
+ "Base Color",
+ "Metallic",
+ "Specular",
+ "Roughness", /* Map specular exponent to roughness. */
+ "Roughness",
+ "Sheen",
+ "Metallic", /* Map reflection to metallic. */
+ "Emission",
+ "Alpha",
+ "Normal",
+};
+BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_socket_id) == (int)MTLTexMapType::Count,
+ "array size mismatch");
+
/**
* Copy a float property of the given type from the bNode to given buffer.
*/
@@ -72,25 +86,25 @@ static void copy_property_from_node(const eNodeSocketDatatype property_type,
* Collect all the source sockets linked to the destination socket in a destination node.
*/
static void linked_sockets_to_dest_id(const bNode *dest_node,
- const nodes::NodeTreeRef &node_tree,
- StringRefNull dest_socket_id,
- Vector<const nodes::OutputSocketRef *> &r_linked_sockets)
+ const bNodeTree &node_tree,
+ const char *dest_socket_id,
+ Vector<const bNodeSocket *> &r_linked_sockets)
{
r_linked_sockets.clear();
if (!dest_node) {
return;
}
- Span<const nodes::NodeRef *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname);
- Span<const nodes::InputSocketRef *> dest_inputs = object_dest_nodes.first()->inputs();
- const nodes::InputSocketRef *dest_socket = nullptr;
- for (const nodes::InputSocketRef *curr_socket : dest_inputs) {
- if (STREQ(curr_socket->bsocket()->identifier, dest_socket_id.c_str())) {
+ Span<const bNode *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname);
+ Span<const bNodeSocket *> dest_inputs = object_dest_nodes.first()->input_sockets();
+ const bNodeSocket *dest_socket = nullptr;
+ for (const bNodeSocket *curr_socket : dest_inputs) {
+ if (STREQ(curr_socket->identifier, dest_socket_id)) {
dest_socket = curr_socket;
break;
}
}
if (dest_socket) {
- Span<const nodes::OutputSocketRef *> linked_sockets = dest_socket->directly_linked_sockets();
+ Span<const bNodeSocket *> linked_sockets = dest_socket->directly_linked_sockets();
r_linked_sockets.resize(linked_sockets.size());
r_linked_sockets = linked_sockets;
}
@@ -99,40 +113,52 @@ static void linked_sockets_to_dest_id(const bNode *dest_node,
/**
* From a list of sockets, get the parent node which is of the given node type.
*/
-static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> sockets_list,
- const int node_type)
+static const bNode *get_node_of_type(Span<const bNodeSocket *> sockets_list, const int node_type)
{
- for (const nodes::SocketRef *socket : sockets_list) {
- const bNode *parent_node = socket->bnode();
- if (parent_node->typeinfo->type == node_type) {
- return parent_node;
+ for (const bNodeSocket *socket : sockets_list) {
+ const bNode &parent_node = socket->owner_node();
+ if (parent_node.typeinfo->type == node_type) {
+ return &parent_node;
}
}
return nullptr;
}
-/**
+/*
* From a texture image shader node, get the image's filepath.
* If packed image is found, only the file "name" is returned.
*/
-static const char *get_image_filepath(const bNode *tex_node)
+static std::string get_image_filepath(const bNode *tex_node)
{
if (!tex_node) {
- return nullptr;
+ return "";
}
Image *tex_image = reinterpret_cast<Image *>(tex_node->id);
if (!tex_image || !BKE_image_has_filepath(tex_image)) {
- return nullptr;
+ return "";
}
- const char *path = tex_image->filepath;
+
if (BKE_image_has_packedfile(tex_image)) {
/* Put image in the same directory as the .MTL file. */
- path = BLI_path_slash_rfind(path) + 1;
+ const char *filename = BLI_path_slash_rfind(tex_image->filepath) + 1;
fprintf(stderr,
"Packed image found:'%s'. Unpack and place the image in the same "
"directory as the .MTL file.\n",
- path);
+ filename);
+ return filename;
}
+
+ char path[FILE_MAX];
+ BLI_strncpy(path, tex_image->filepath, FILE_MAX);
+
+ if (tex_image->source == IMA_SRC_SEQUENCE) {
+ char head[FILE_MAX], tail[FILE_MAX];
+ unsigned short numlen;
+ int framenr = static_cast<NodeTexImage *>(tex_node->storage)->iuser.framenr;
+ BLI_path_sequence_decode(path, head, tail, &numlen);
+ BLI_path_sequence_encode(path, head, tail, numlen, framenr);
+ }
+
return path;
}
@@ -141,16 +167,16 @@ static const char *get_image_filepath(const bNode *tex_node)
* We only want one that feeds directly into a Material Output node
* (that is the behavior of the legacy Python exporter).
*/
-static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree)
+static const bNode *find_bsdf_node(const bNodeTree *nodetree)
{
if (!nodetree) {
return nullptr;
}
- for (const nodes::NodeRef *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) {
- const nodes::InputSocketRef *node_input_socket0 = node->inputs()[0];
- for (const nodes::OutputSocketRef *out_sock : node_input_socket0->directly_linked_sockets()) {
- const nodes::NodeRef &in_node = out_sock->node();
- if (in_node.typeinfo()->type == SH_NODE_BSDF_PRINCIPLED) {
+ for (const bNode *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) {
+ const bNodeSocket &node_input_socket0 = node->input_socket(0);
+ for (const bNodeSocket *out_sock : node_input_socket0.directly_linked_sockets()) {
+ const bNode &in_node = out_sock->owner_node();
+ if (in_node.typeinfo->type == SH_NODE_BSDF_PRINCIPLED) {
return &in_node;
}
}
@@ -161,58 +187,68 @@ static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree)
/**
* Store properties found either in bNode or material into r_mtl_mat.
*/
-static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
+static void store_bsdf_properties(const bNode *bsdf_node,
const Material *material,
MTLMaterial &r_mtl_mat)
{
- const bNode *bnode = nullptr;
- if (bsdf_node) {
- bnode = bsdf_node->bnode();
- }
-
- /* If p-BSDF is not present, fallback to #Object.Material. */
float roughness = material->roughness;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Roughness", {&roughness, 1});
}
/* Empirical approximation. Importer should use the inverse of this method. */
float spec_exponent = (1.0f - roughness);
spec_exponent *= spec_exponent * 1000.0f;
float specular = material->spec;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Specular", {&specular, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Specular", {&specular, 1});
}
float metallic = material->metallic;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Metallic", {&metallic, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Metallic", {&metallic, 1});
}
float refraction_index = 1.0f;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "IOR", {&refraction_index, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "IOR", {&refraction_index, 1});
}
- float dissolved = material->a;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Alpha", {&dissolved, 1});
+ float alpha = material->a;
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Alpha", {&alpha, 1});
}
- const bool transparent = dissolved != 1.0f;
+ const bool transparent = alpha != 1.0f;
float3 diffuse_col = {material->r, material->g, material->b};
- if (bnode) {
- copy_property_from_node(SOCK_RGBA, bnode, "Base Color", {diffuse_col, 3});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_RGBA, bsdf_node, "Base Color", {diffuse_col, 3});
}
float3 emission_col{0.0f};
float emission_strength = 0.0f;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
- copy_property_from_node(SOCK_RGBA, bnode, "Emission", {emission_col, 3});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1});
+ copy_property_from_node(SOCK_RGBA, bsdf_node, "Emission", {emission_col, 3});
}
mul_v3_fl(emission_col, emission_strength);
+ float sheen = -1.0f;
+ float clearcoat = -1.0f;
+ float clearcoat_roughness = -1.0f;
+ float aniso = -1.0f;
+ float aniso_rot = -1.0f;
+ float transmission = -1.0f;
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Sheen", {&sheen, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Clearcoat", {&clearcoat, 1});
+ copy_property_from_node(
+ SOCK_FLOAT, bsdf_node, "Clearcoat Roughness", {&clearcoat_roughness, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic", {&aniso, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic Rotation", {&aniso_rot, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Transmission", {&transmission, 1});
+ }
+
/* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of `illum`. */
/* Highlight on. */
int illum = 2;
@@ -235,26 +271,34 @@ static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
/* Transparency: Glass on, Reflection: Ray trace off */
illum = 9;
}
- r_mtl_mat.Ns = spec_exponent;
+ r_mtl_mat.spec_exponent = spec_exponent;
if (metallic != 0.0f) {
- r_mtl_mat.Ka = {metallic, metallic, metallic};
+ r_mtl_mat.ambient_color = {metallic, metallic, metallic};
}
else {
- r_mtl_mat.Ka = {1.0f, 1.0f, 1.0f};
+ r_mtl_mat.ambient_color = {1.0f, 1.0f, 1.0f};
}
- r_mtl_mat.Kd = diffuse_col;
- r_mtl_mat.Ks = {specular, specular, specular};
- r_mtl_mat.Ke = emission_col;
- r_mtl_mat.Ni = refraction_index;
- r_mtl_mat.d = dissolved;
- r_mtl_mat.illum = illum;
+ r_mtl_mat.color = diffuse_col;
+ r_mtl_mat.spec_color = {specular, specular, specular};
+ r_mtl_mat.emission_color = emission_col;
+ r_mtl_mat.ior = refraction_index;
+ r_mtl_mat.alpha = alpha;
+ r_mtl_mat.illum_mode = illum;
+ r_mtl_mat.roughness = roughness;
+ r_mtl_mat.metallic = metallic;
+ r_mtl_mat.sheen = sheen;
+ r_mtl_mat.cc_thickness = clearcoat;
+ r_mtl_mat.cc_roughness = clearcoat_roughness;
+ r_mtl_mat.aniso = aniso;
+ r_mtl_mat.aniso_rot = aniso_rot;
+ r_mtl_mat.transmit_color = {transmission, transmission, transmission};
}
/**
* Store image texture options and file-paths in `r_mtl_mat`.
*/
-static void store_image_textures(const nodes::NodeRef *bsdf_node,
- const nodes::NodeTreeRef *node_tree,
+static void store_image_textures(const bNode *bsdf_node,
+ const bNodeTree *node_tree,
const Material *material,
MTLMaterial &r_mtl_mat)
{
@@ -262,21 +306,20 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
/* No nodetree, no images, or no Principled BSDF node. */
return;
}
- const bNode *bnode = bsdf_node->bnode();
/* Normal Map Texture has two extra tasks of:
* - finding a Normal Map node before finding a texture node.
* - finding "Strength" property of the node for `-bm` option.
*/
- for (Map<const eMTLSyntaxElement, tex_map_XX>::MutableItem texture_map :
- r_mtl_mat.texture_maps.items()) {
- Vector<const nodes::OutputSocketRef *> linked_sockets;
+ for (int key = 0; key < (int)MTLTexMapType::Count; ++key) {
+ MTLTexMap &value = r_mtl_mat.texture_maps[key];
+ Vector<const bNodeSocket *> linked_sockets;
const bNode *normal_map_node{nullptr};
- if (texture_map.key == eMTLSyntaxElement::map_Bump) {
+ if (key == (int)MTLTexMapType::Normal) {
/* Find sockets linked to destination "Normal" socket in P-BSDF node. */
- linked_sockets_to_dest_id(bnode, *node_tree, "Normal", linked_sockets);
+ linked_sockets_to_dest_id(bsdf_node, *node_tree, "Normal", linked_sockets);
/* Among the linked sockets, find Normal Map shader node. */
normal_map_node = get_node_of_type(linked_sockets, SH_NODE_NORMAL_MAP);
@@ -285,16 +328,17 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
}
else {
/* Skip emission map if emission strength is zero. */
- if (texture_map.key == eMTLSyntaxElement::map_Ke) {
+ if (key == (int)MTLTexMapType::Emission) {
float emission_strength = 0.0f;
- copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
+ copy_property_from_node(
+ SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1});
if (emission_strength == 0.0f) {
continue;
}
}
/* Find sockets linked to the destination socket of interest, in P-BSDF node. */
linked_sockets_to_dest_id(
- bnode, *node_tree, texture_map.value.dest_socket_id, linked_sockets);
+ bsdf_node, *node_tree, tex_map_type_to_socket_id[key], linked_sockets);
}
/* Among the linked sockets, find Image Texture shader node. */
@@ -302,8 +346,8 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
if (!tex_node) {
continue;
}
- const char *tex_image_filepath = get_image_filepath(tex_node);
- if (!tex_image_filepath) {
+ const std::string tex_image_filepath = get_image_filepath(tex_node);
+ if (tex_image_filepath.empty()) {
continue;
}
@@ -313,14 +357,14 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
if (normal_map_node) {
copy_property_from_node(
- SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.map_Bump_strength, 1});
+ SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.normal_strength, 1});
}
/* Texture transform options. Only translation (origin offset, "-o") and scale
* ("-o") are supported. */
- copy_property_from_node(SOCK_VECTOR, mapping, "Location", {texture_map.value.translation, 3});
- copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {texture_map.value.scale, 3});
+ copy_property_from_node(SOCK_VECTOR, mapping, "Location", {value.translation, 3});
+ copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {value.scale, 3});
- texture_map.value.image_path = tex_image_filepath;
+ value.image_path = tex_image_filepath;
}
}
@@ -330,14 +374,14 @@ MTLMaterial mtlmaterial_for_material(const Material *material)
MTLMaterial mtlmat;
mtlmat.name = std::string(material->id.name + 2);
std::replace(mtlmat.name.begin(), mtlmat.name.end(), ' ', '_');
- const nodes::NodeTreeRef *nodetree = nullptr;
- if (material->nodetree) {
- nodetree = new nodes::NodeTreeRef(material->nodetree);
+ const bNodeTree *nodetree = material->nodetree;
+ if (nodetree != nullptr) {
+ nodetree->ensure_topology_cache();
}
- const nodes::NodeRef *bsdf_node = find_bsdf_node(nodetree);
+
+ const bNode *bsdf_node = find_bsdf_node(nodetree);
store_bsdf_properties(bsdf_node, material, mtlmat);
store_image_textures(bsdf_node, nodetree, material, mtlmat);
- delete nodetree;
return mtlmat;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
index 80e3127f69f..9c1bc2f0f8f 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
@@ -6,38 +6,38 @@
#pragma once
-#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
#include "DNA_node_types.h"
-#include "obj_export_io.hh"
-
-namespace blender {
-template<> struct DefaultHash<io::obj::eMTLSyntaxElement> {
- uint64_t operator()(const io::obj::eMTLSyntaxElement value) const
- {
- return static_cast<uint64_t>(value);
- }
-};
-
-} // namespace blender
+struct Material;
namespace blender::io::obj {
-class OBJMesh;
-/**
- * Generic container for texture node properties.
- */
-struct tex_map_XX {
- tex_map_XX(StringRef to_socket_id) : dest_socket_id(to_socket_id){};
+enum class MTLTexMapType {
+ Color = 0,
+ Metallic,
+ Specular,
+ SpecularExponent,
+ Roughness,
+ Sheen,
+ Reflection,
+ Emission,
+ Alpha,
+ Normal,
+ Count
+};
+extern const char *tex_map_type_to_socket_id[];
+
+struct MTLTexMap {
+ bool is_valid() const
+ {
+ return !image_path.empty();
+ }
- /** Target socket which this texture node connects to. */
- const std::string dest_socket_id;
+ /* Target socket which this texture node connects to. */
float3 translation{0.0f};
float3 scale{1.0f};
- /* Only Flat and Smooth projections are supported. */
+ /* Only Flat and Sphere projections are supported. */
int projection_type = SHD_PROJ_FLAT;
std::string image_path;
std::string mtl_dir_path;
@@ -47,43 +47,38 @@ struct tex_map_XX {
* Container suited for storing Material data for/from a .MTL file.
*/
struct MTLMaterial {
- MTLMaterial()
+ const MTLTexMap &tex_map_of_type(MTLTexMapType key) const
{
- texture_maps.add(eMTLSyntaxElement::map_Kd, tex_map_XX("Base Color"));
- texture_maps.add(eMTLSyntaxElement::map_Ks, tex_map_XX("Specular"));
- texture_maps.add(eMTLSyntaxElement::map_Ns, tex_map_XX("Roughness"));
- texture_maps.add(eMTLSyntaxElement::map_d, tex_map_XX("Alpha"));
- texture_maps.add(eMTLSyntaxElement::map_refl, tex_map_XX("Metallic"));
- texture_maps.add(eMTLSyntaxElement::map_Ke, tex_map_XX("Emission"));
- texture_maps.add(eMTLSyntaxElement::map_Bump, tex_map_XX("Normal"));
+ return texture_maps[(int)key];
}
-
- /**
- * Caller must ensure that the given lookup key exists in the Map.
- * \return Texture map corresponding to the given ID.
- */
- tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key)
+ MTLTexMap &tex_map_of_type(MTLTexMapType key)
{
- {
- BLI_assert(texture_maps.contains_as(key));
- return texture_maps.lookup_as(key);
- }
+ return texture_maps[(int)key];
}
std::string name;
/* Always check for negative values while importing or exporting. Use defaults if
* any value is negative. */
- float Ns{-1.0f};
- float3 Ka{-1.0f};
- float3 Kd{-1.0f};
- float3 Ks{-1.0f};
- float3 Ke{-1.0f};
- float Ni{-1.0f};
- float d{-1.0f};
- int illum{-1};
- Map<const eMTLSyntaxElement, tex_map_XX> texture_maps;
- /** Only used for Normal Map node: "map_Bump". */
- float map_Bump_strength{-1.0f};
+ float spec_exponent{-1.0f}; /* `Ns` */
+ float3 ambient_color{-1.0f}; /* `Ka` */
+ float3 color{-1.0f}; /* `Kd` */
+ float3 spec_color{-1.0f}; /* `Ks` */
+ float3 emission_color{-1.0f}; /* `Ke` */
+ float ior{-1.0f}; /* `Ni` */
+ float alpha{-1.0f}; /* `d` */
+ float3 transmit_color{-1.0f}; /* `Kt` / `Tf` */
+ float roughness{-1.0f}; /* `Pr` */
+ float metallic{-1.0f}; /* `Pm` */
+ float sheen{-1.0f}; /* `Ps` */
+ float cc_thickness{-1.0f}; /* `Pc` */
+ float cc_roughness{-1.0f}; /* `Pcr` */
+ float aniso{-1.0f}; /* `aniso` */
+ float aniso_rot{-1.0f}; /* `anisor` */
+
+ int illum_mode{-1};
+ MTLTexMap texture_maps[(int)MTLTexMapType::Count];
+ /* Only used for Normal Map node: `map_Bump`. */
+ float normal_strength{-1.0f};
};
MTLMaterial mtlmaterial_for_material(const Material *material);
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 77d4f6268bc..a51c017f81d 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -143,7 +143,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
* we have to have the output text buffer for each object,
* and write them all into the file at the end. */
size_t count = exportable_as_mesh.size();
- std::vector<FormatHandler<eFileType::OBJ>> buffers(count);
+ std::vector<FormatHandler> buffers(count);
/* Serial: gather material indices, ensure normals & edges. */
Vector<Vector<int>> mtlindices;
@@ -242,7 +242,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
const OBJWriter &obj_writer)
{
- FormatHandler<eFileType::OBJ> fh;
+ FormatHandler fh;
/* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
* to wait for #blender::Vector to clean the objects up. */
for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
@@ -268,7 +268,7 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
std::unique_ptr<MTLWriter> mtl_writer = nullptr;
if (export_params.export_materials) {
try {
- mtl_writer = std::make_unique<MTLWriter>(export_params.filepath);
+ mtl_writer = std::make_unique<MTLWriter>(filepath);
}
catch (const std::system_error &ex) {
print_exception_error(ex);
@@ -293,7 +293,10 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
}
BLI_path_slash_native(dest_dir);
BLI_path_normalize(nullptr, dest_dir);
- mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir);
+ mtl_writer->write_materials(export_params.blen_filepath,
+ export_params.path_mode,
+ dest_dir,
+ export_params.export_pbr_extensions);
}
write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
index c2aa96713b1..f92f9894f75 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
@@ -10,9 +10,11 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
#include "obj_import_string_utils.hh"
+#include <algorithm>
#include <charconv>
namespace blender::io::obj {
@@ -22,32 +24,24 @@ using std::string;
/**
* Based on the properties of the given Geometry instance, create a new Geometry instance
* or return the previous one.
- *
- * Also update index offsets which should always happen if a new Geometry instance is created.
*/
static Geometry *create_geometry(Geometry *const prev_geometry,
const eGeometryType new_type,
StringRef name,
- const GlobalVertices &global_vertices,
- Vector<std::unique_ptr<Geometry>> &r_all_geometries,
- VertexIndexOffset &r_offset)
+ Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
auto new_geometry = [&]() {
r_all_geometries.append(std::make_unique<Geometry>());
Geometry *g = r_all_geometries.last().get();
g->geom_type_ = new_type;
g->geometry_name_ = name.is_empty() ? "New object" : name;
- g->vertex_start_ = global_vertices.vertices.size();
- g->vertex_color_start_ = global_vertices.vertex_colors.size();
- r_offset.set_index_offset(g->vertex_start_);
return g;
};
if (prev_geometry && prev_geometry->geom_type_ == GEOM_MESH) {
/* After the creation of a Geometry instance, at least one element has been found in the OBJ
- * file that indicates that it is a mesh (basically anything but the vertex positions). */
- if (!prev_geometry->face_elements_.is_empty() || prev_geometry->has_vertex_normals_ ||
- !prev_geometry->edges_.is_empty()) {
+ * file that indicates that it is a mesh (faces or edges). */
+ if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) {
return new_geometry();
}
if (new_type == GEOM_MESH) {
@@ -70,15 +64,11 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
return new_geometry();
}
-static void geom_add_vertex(Geometry *geom,
- const char *p,
- const char *end,
- GlobalVertices &r_global_vertices)
+static void geom_add_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices)
{
float3 vert;
p = parse_floats(p, end, 0.0f, vert, 3);
r_global_vertices.vertices.append(vert);
- geom->vertex_count_++;
/* OBJ extension: `xyzrgb` vertex colors, when the vertex position
* is followed by 3 more RGB color components. See
* http://paulbourke.net/dataformats/obj/colour.html */
@@ -88,16 +78,22 @@ static void geom_add_vertex(Geometry *geom,
if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) {
float3 linear;
srgb_to_linearrgb_v3_v3(linear, srgb);
- r_global_vertices.vertex_colors.append(linear);
- geom->vertex_color_count_++;
+
+ auto &blocks = r_global_vertices.vertex_colors;
+ /* If we don't have vertex colors yet, or the previous vertex
+ * was without color, we need to start a new vertex colors block. */
+ if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() !=
+ r_global_vertices.vertices.size() - 1)) {
+ GlobalVertices::VertexColorsBlock block;
+ block.start_vertex_index = r_global_vertices.vertices.size() - 1;
+ blocks.append(block);
+ }
+ blocks.last().colors.append(linear);
}
}
}
-static void geom_add_mrgb_colors(Geometry *geom,
- const char *p,
- const char *end,
- GlobalVertices &r_global_vertices)
+static void geom_add_mrgb_colors(const char *p, const char *end, GlobalVertices &r_global_vertices)
{
/* MRGB color extension, in the form of
* "#MRGB MMRRGGBBMMRRGGBB ..."
@@ -117,14 +113,26 @@ static void geom_add_mrgb_colors(Geometry *geom,
srgb[3] = 0xFF;
float linear[4];
srgb_to_linearrgb_uchar4(linear, srgb);
- r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]});
- geom->vertex_color_count_++;
+
+ auto &blocks = r_global_vertices.vertex_colors;
+ /* If we don't have vertex colors yet, or the previous vertex
+ * was without color, we need to start a new vertex colors block. */
+ if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() !=
+ r_global_vertices.vertices.size())) {
+ GlobalVertices::VertexColorsBlock block;
+ block.start_vertex_index = r_global_vertices.vertices.size();
+ blocks.append(block);
+ }
+ blocks.last().colors.append({linear[0], linear[1], linear[2]});
+ /* MRGB colors are specified after vertex positions; each new color
+ * "pushes" the vertex colors block further back into which vertices it is for. */
+ blocks.last().start_vertex_index--;
+
p += mrgb_length;
}
}
-static void geom_add_vertex_normal(Geometry *geom,
- const char *p,
+static void geom_add_vertex_normal(const char *p,
const char *end,
GlobalVertices &r_global_vertices)
{
@@ -135,7 +143,6 @@ static void geom_add_vertex_normal(Geometry *geom,
* normalized. */
normalize_v3(normal);
r_global_vertices.vertex_normals.append(normal);
- geom->has_vertex_normals_ = true;
}
static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices)
@@ -148,24 +155,24 @@ static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r
static void geom_add_edge(Geometry *geom,
const char *p,
const char *end,
- const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices)
{
int edge_v1, edge_v2;
p = parse_int(p, end, -1, edge_v1);
p = parse_int(p, end, -1, edge_v2);
/* Always keep stored indices non-negative and zero-based. */
- edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
- edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
+ edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -1;
+ edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -1;
BLI_assert(edge_v1 >= 0 && edge_v2 >= 0);
geom->edges_.append({static_cast<uint>(edge_v1), static_cast<uint>(edge_v2)});
+ geom->track_vertex_index(edge_v1);
+ geom->track_vertex_index(edge_v2);
}
static void geom_add_polygon(Geometry *geom,
const char *p,
const char *end,
const GlobalVertices &global_vertices,
- const VertexIndexOffset &offsets,
const int material_index,
const int group_index,
const bool shaded_smooth)
@@ -204,8 +211,7 @@ static void geom_add_polygon(Geometry *geom,
}
}
/* Always keep stored indices non-negative and zero-based. */
- corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() :
- -offsets.get_index_offset() - 1;
+ corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -1;
if (corner.vert_index < 0 || corner.vert_index >= global_vertices.vertices.size()) {
fprintf(stderr,
"Invalid vertex index %i (valid range [0, %zu)), ignoring face\n",
@@ -213,6 +219,9 @@ static void geom_add_polygon(Geometry *geom,
(size_t)global_vertices.vertices.size());
face_valid = false;
}
+ else {
+ geom->track_vertex_index(corner.vert_index);
+ }
if (got_uv) {
corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1;
if (corner.uv_vert_index < 0 || corner.uv_vert_index >= global_vertices.uv_vertices.size()) {
@@ -226,7 +235,7 @@ static void geom_add_polygon(Geometry *geom,
/* Ignore corner normal index, if the geometry does not have any normals.
* Some obj files out there do have face definitions that refer to normal indices,
* without any normals being present (T98782). */
- if (got_normal && geom->has_vertex_normals_) {
+ if (got_normal && !global_vertices.vertex_normals.is_empty()) {
corner.vertex_normal_index += corner.vertex_normal_index < 0 ?
global_vertices.vertex_normals.size() :
-1;
@@ -260,9 +269,7 @@ static void geom_add_polygon(Geometry *geom,
static Geometry *geom_set_curve_type(Geometry *geom,
const char *p,
const char *end,
- const GlobalVertices &global_vertices,
const StringRef group_name,
- VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
p = drop_whitespace(p, end);
@@ -270,8 +277,7 @@ static Geometry *geom_set_curve_type(Geometry *geom,
std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl;
return geom;
}
- geom = create_geometry(
- geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets);
+ geom = create_geometry(geom, GEOM_CURVE, group_name, r_all_geometries);
geom->nurbs_element_.group_ = group_name;
return geom;
}
@@ -390,6 +396,22 @@ static bool parse_keyword(const char *&p, const char *end, StringRef keyword)
return true;
}
+/* Special case: if there were no faces/edges in any geometries,
+ * treat all the vertices as a point cloud. */
+static void use_all_vertices_if_no_faces(Geometry *geom,
+ const Vector<std::unique_ptr<Geometry>> &all_geometries,
+ const GlobalVertices &global_vertices)
+{
+ if (!global_vertices.vertices.is_empty() && geom && geom->geom_type_ == GEOM_MESH) {
+ if (std::all_of(
+ all_geometries.begin(), all_geometries.end(), [](const std::unique_ptr<Geometry> &g) {
+ return g->get_vertex_count() == 0;
+ })) {
+ geom->track_all_vertices(global_vertices.vertices.size());
+ }
+ }
+}
+
void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices)
{
@@ -402,9 +424,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE);
BLI_path_extension_replace(ob_name, FILE_MAXFILE, "");
- VertexIndexOffset offsets;
- Geometry *curr_geom = create_geometry(
- nullptr, GEOM_MESH, ob_name, r_global_vertices, r_all_geometries, offsets);
+ Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries);
/* State variables: once set, they remain the same for the remaining
* elements in the object. */
@@ -427,6 +447,11 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
break; /* No more data to read. */
}
+ /* Take care of line continuations now (turn them into spaces);
+ * the rest of the parsing code does not need to worry about them anymore. */
+ fixup_line_continuations(buffer.data() + buffer_offset,
+ buffer.data() + buffer_offset + bytes_read);
+
/* Ensure buffer ends in a newline. */
if (bytes_read < read_buffer_size_) {
if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') {
@@ -445,9 +470,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
while (last_nl > 0) {
--last_nl;
if (buffer[last_nl] == '\n') {
- if (last_nl < 1 || buffer[last_nl - 1] != '\\') {
- break;
- }
+ break;
}
}
if (buffer[last_nl] != '\n') {
@@ -474,10 +497,10 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
/* Most common things that start with 'v': vertices, normals, UVs. */
if (*p == 'v') {
if (parse_keyword(p, end, "v")) {
- geom_add_vertex(curr_geom, p, end, r_global_vertices);
+ geom_add_vertex(p, end, r_global_vertices);
}
else if (parse_keyword(p, end, "vn")) {
- geom_add_vertex_normal(curr_geom, p, end, r_global_vertices);
+ geom_add_vertex_normal(p, end, r_global_vertices);
}
else if (parse_keyword(p, end, "vt")) {
geom_add_uv_vertex(p, end, r_global_vertices);
@@ -489,26 +512,21 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
p,
end,
r_global_vertices,
- offsets,
state_material_index,
- state_group_index, /* TODO was wrongly material name! */
+ state_group_index,
state_shaded_smooth);
}
/* Faces. */
else if (parse_keyword(p, end, "l")) {
- geom_add_edge(curr_geom, p, end, offsets, r_global_vertices);
+ geom_add_edge(curr_geom, p, end, r_global_vertices);
}
/* Objects. */
else if (parse_keyword(p, end, "o")) {
state_shaded_smooth = false;
state_group_name = "";
state_material_name = "";
- curr_geom = create_geometry(curr_geom,
- GEOM_MESH,
- StringRef(p, end).trim(),
- r_global_vertices,
- r_all_geometries,
- offsets);
+ curr_geom = create_geometry(
+ curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries);
}
/* Groups. */
else if (parse_keyword(p, end, "g")) {
@@ -537,7 +555,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
add_mtl_library(StringRef(p, end).trim());
}
else if (parse_keyword(p, end, "#MRGB")) {
- geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices);
+ geom_add_mrgb_colors(p, end, r_global_vertices);
}
/* Comments. */
else if (*p == '#') {
@@ -545,8 +563,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
}
/* Curve related things. */
else if (parse_keyword(p, end, "cstype")) {
- curr_geom = geom_set_curve_type(
- curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries);
+ curr_geom = geom_set_curve_type(curr_geom, p, end, state_group_name, r_all_geometries);
}
else if (parse_keyword(p, end, "deg")) {
geom_set_curve_degree(curr_geom, p, end);
@@ -572,39 +589,44 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
buffer_offset = left_size;
}
+ use_all_vertices_if_no_faces(curr_geom, r_all_geometries, r_global_vertices);
add_default_mtl_library();
}
-static eMTLSyntaxElement mtl_line_start_to_enum(const char *&p, const char *end)
+static MTLTexMapType mtl_line_start_to_texture_type(const char *&p, const char *end)
{
if (parse_keyword(p, end, "map_Kd")) {
- return eMTLSyntaxElement::map_Kd;
+ return MTLTexMapType::Color;
}
if (parse_keyword(p, end, "map_Ks")) {
- return eMTLSyntaxElement::map_Ks;
+ return MTLTexMapType::Specular;
}
if (parse_keyword(p, end, "map_Ns")) {
- return eMTLSyntaxElement::map_Ns;
+ return MTLTexMapType::SpecularExponent;
}
if (parse_keyword(p, end, "map_d")) {
- return eMTLSyntaxElement::map_d;
+ return MTLTexMapType::Alpha;
}
- if (parse_keyword(p, end, "refl")) {
- return eMTLSyntaxElement::map_refl;
- }
- if (parse_keyword(p, end, "map_refl")) {
- return eMTLSyntaxElement::map_refl;
+ if (parse_keyword(p, end, "refl") || parse_keyword(p, end, "map_refl")) {
+ return MTLTexMapType::Reflection;
}
if (parse_keyword(p, end, "map_Ke")) {
- return eMTLSyntaxElement::map_Ke;
+ return MTLTexMapType::Emission;
+ }
+ if (parse_keyword(p, end, "bump") || parse_keyword(p, end, "map_Bump") ||
+ parse_keyword(p, end, "map_bump")) {
+ return MTLTexMapType::Normal;
}
- if (parse_keyword(p, end, "bump")) {
- return eMTLSyntaxElement::map_Bump;
+ if (parse_keyword(p, end, "map_Pr")) {
+ return MTLTexMapType::Roughness;
}
- if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) {
- return eMTLSyntaxElement::map_Bump;
+ if (parse_keyword(p, end, "map_Pm")) {
+ return MTLTexMapType::Metallic;
}
- return eMTLSyntaxElement::string;
+ if (parse_keyword(p, end, "map_Ps")) {
+ return MTLTexMapType::Sheen;
+ }
+ return MTLTexMapType::Count;
}
static const std::pair<StringRef, int> unsupported_texture_options[] = {
@@ -622,7 +644,7 @@ static const std::pair<StringRef, int> unsupported_texture_options[] = {
static bool parse_texture_option(const char *&p,
const char *end,
MTLMaterial *material,
- tex_map_XX &tex_map)
+ MTLTexMap &tex_map)
{
p = drop_whitespace(p, end);
if (parse_keyword(p, end, "-o")) {
@@ -634,7 +656,7 @@ static bool parse_texture_option(const char *&p,
return true;
}
if (parse_keyword(p, end, "-bm")) {
- p = parse_float(p, end, 1.0f, material->map_Bump_strength, true, true);
+ p = parse_float(p, end, 1.0f, material->normal_strength, true, true);
return true;
}
if (parse_keyword(p, end, "-type")) {
@@ -676,13 +698,13 @@ static void parse_texture_map(const char *p,
if (!is_map && !is_refl && !is_bump) {
return;
}
- eMTLSyntaxElement key = mtl_line_start_to_enum(p, end);
- if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) {
+ MTLTexMapType key = mtl_line_start_to_texture_type(p, end);
+ if (key == MTLTexMapType::Count) {
/* No supported texture map found. */
std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl;
return;
}
- tex_map_XX &tex_map = material->texture_maps.lookup(key);
+ MTLTexMap &tex_map = material->tex_map_of_type(key);
tex_map.mtl_dir_path = mtl_dir_path;
/* Parse texture map options. */
@@ -731,7 +753,7 @@ MTLParser::MTLParser(StringRefNull mtl_library, StringRefNull obj_filepath)
{
char obj_file_dir[FILE_MAXDIR];
BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR);
- BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL);
+ BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), nullptr);
BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR);
}
@@ -767,31 +789,55 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat
}
else if (material != nullptr) {
if (parse_keyword(p, end, "Ns")) {
- parse_float(p, end, 324.0f, material->Ns);
+ parse_float(p, end, 324.0f, material->spec_exponent);
}
else if (parse_keyword(p, end, "Ka")) {
- parse_floats(p, end, 0.0f, material->Ka, 3);
+ parse_floats(p, end, 0.0f, material->ambient_color, 3);
}
else if (parse_keyword(p, end, "Kd")) {
- parse_floats(p, end, 0.8f, material->Kd, 3);
+ parse_floats(p, end, 0.8f, material->color, 3);
}
else if (parse_keyword(p, end, "Ks")) {
- parse_floats(p, end, 0.5f, material->Ks, 3);
+ parse_floats(p, end, 0.5f, material->spec_color, 3);
}
else if (parse_keyword(p, end, "Ke")) {
- parse_floats(p, end, 0.0f, material->Ke, 3);
+ parse_floats(p, end, 0.0f, material->emission_color, 3);
}
else if (parse_keyword(p, end, "Ni")) {
- parse_float(p, end, 1.45f, material->Ni);
+ parse_float(p, end, 1.45f, material->ior);
}
else if (parse_keyword(p, end, "d")) {
- parse_float(p, end, 1.0f, material->d);
+ parse_float(p, end, 1.0f, material->alpha);
}
else if (parse_keyword(p, end, "illum")) {
/* Some files incorrectly use a float (T60135). */
float val;
parse_float(p, end, 1.0f, val);
- material->illum = val;
+ material->illum_mode = val;
+ }
+ else if (parse_keyword(p, end, "Pr")) {
+ parse_float(p, end, 0.5f, material->roughness);
+ }
+ else if (parse_keyword(p, end, "Pm")) {
+ parse_float(p, end, 0.0f, material->metallic);
+ }
+ else if (parse_keyword(p, end, "Ps")) {
+ parse_float(p, end, 0.0f, material->sheen);
+ }
+ else if (parse_keyword(p, end, "Pc")) {
+ parse_float(p, end, 0.0f, material->cc_thickness);
+ }
+ else if (parse_keyword(p, end, "Pcr")) {
+ parse_float(p, end, 0.0f, material->cc_roughness);
+ }
+ else if (parse_keyword(p, end, "aniso")) {
+ parse_float(p, end, 0.0f, material->aniso);
+ }
+ else if (parse_keyword(p, end, "anisor")) {
+ parse_float(p, end, 0.0f, material->aniso_rot);
+ }
+ else if (parse_keyword(p, end, "Kt") || parse_keyword(p, end, "Tf")) {
+ parse_floats(p, end, 0.0f, material->transmit_color, 3);
}
else {
parse_texture_map(p, end, material, mtl_dir_path_);
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
index 8cef0700b3b..ef05534928a 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
@@ -8,7 +8,7 @@
#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
-#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_material.h"
@@ -22,6 +22,7 @@
#include "IO_wavefront_obj.h"
#include "importer_mesh_utils.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_mesh.hh"
namespace blender::io::obj {
@@ -31,13 +32,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
Map<std::string, Material *> &created_materials,
const OBJImportParams &import_params)
{
+ const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()};
+ if (tot_verts_object <= 0) {
+ /* Empty mesh */
+ return nullptr;
+ }
std::string ob_name{mesh_geometry_.geometry_name_};
if (ob_name.empty()) {
ob_name = "Untitled";
}
fixup_invalid_faces();
- const int64_t tot_verts_object{mesh_geometry_.vertex_count_};
/* Total explicitly imported edges, not the ones belonging the polygons to be created. */
const int64_t tot_edges{mesh_geometry_.edges_.size()};
const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()};
@@ -53,7 +58,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
create_uv_verts(mesh);
create_normals(mesh);
create_colors(mesh);
- create_materials(bmain, materials, created_materials, obj);
+ create_materials(bmain, materials, created_materials, obj, import_params.relative_paths);
if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) {
bool verbose_validate = false;
@@ -64,11 +69,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
}
transform_object(obj, import_params);
- /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
- const uint16_t autosmooth = (mesh->flag & ME_AUTOSMOOTH);
- Mesh *dst = static_cast<Mesh *>(obj->data);
- BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true);
- dst->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(mesh, static_cast<Mesh *>(obj->data), obj);
/* NOTE: vertex groups have to be created after final mesh is assigned to the object. */
create_vertex_groups(obj);
@@ -153,29 +154,39 @@ void MeshFromGeometry::fixup_invalid_faces()
void MeshFromGeometry::create_vertices(Mesh *mesh)
{
- const int tot_verts_object{mesh_geometry_.vertex_count_};
- for (int i = 0; i < tot_verts_object; ++i) {
- int vi = mesh_geometry_.vertex_start_ + i;
- if (vi < global_vertices_.vertices.size()) {
- copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]);
- }
- else {
- std::cerr << "Vertex index:" << vi
- << " larger than total vertices:" << global_vertices_.vertices.size() << " ."
- << std::endl;
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ /* Go through all the global vertex indices from min to max,
+ * checking which ones are actually and building a global->local
+ * index mapping. Write out the used vertex positions into the Mesh
+ * data. */
+ mesh_geometry_.global_to_local_vertices_.clear();
+ mesh_geometry_.global_to_local_vertices_.reserve(mesh_geometry_.vertices_.size());
+ for (int vi = mesh_geometry_.vertex_index_min_; vi <= mesh_geometry_.vertex_index_max_; ++vi) {
+ BLI_assert(vi >= 0 && vi < global_vertices_.vertices.size());
+ if (!mesh_geometry_.vertices_.contains(vi)) {
+ continue;
}
+ int local_vi = (int)mesh_geometry_.global_to_local_vertices_.size();
+ BLI_assert(local_vi >= 0 && local_vi < mesh->totvert);
+ copy_v3_v3(verts[local_vi].co, global_vertices_.vertices[vi]);
+ mesh_geometry_.global_to_local_vertices_.add_new(vi, local_vi);
}
}
void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
{
- mesh->dvert = nullptr;
- const int64_t total_verts = mesh_geometry_.vertex_count_;
+ MutableSpan<MDeformVert> dverts;
+ const int64_t total_verts = mesh_geometry_.get_vertex_count();
if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) {
- mesh->dvert = static_cast<MDeformVert *>(
- CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts));
+ dverts = mesh->deform_verts_for_write();
}
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ mesh->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index",
+ ATTR_DOMAIN_FACE);
+
const int64_t tot_face_elems{mesh->totpoly};
int tot_loop_idx = 0;
@@ -187,40 +198,42 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
continue;
}
- MPoly &mpoly = mesh->mpoly[poly_idx];
+ MPoly &mpoly = polys[poly_idx];
mpoly.totloop = curr_face.corner_count_;
mpoly.loopstart = tot_loop_idx;
if (curr_face.shaded_smooth) {
mpoly.flag |= ME_SMOOTH;
}
- mpoly.mat_nr = curr_face.material_index;
+ material_indices.span[poly_idx] = curr_face.material_index;
/* Importing obj files without any materials would result in negative indices, which is not
* supported. */
- if (mpoly.mat_nr < 0) {
- mpoly.mat_nr = 0;
+ if (material_indices.span[poly_idx] < 0) {
+ material_indices.span[poly_idx] = 0;
}
for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
- MLoop &mloop = mesh->mloop[tot_loop_idx];
+ MLoop &mloop = loops[tot_loop_idx];
tot_loop_idx++;
- mloop.v = curr_corner.vert_index;
+ mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0);
/* Setup vertex group data, if needed. */
- if (!mesh->dvert) {
+ if (dverts.is_empty()) {
continue;
}
const int group_index = curr_face.vertex_group_index;
- MDeformWeight *dw = BKE_defvert_ensure_index(mesh->dvert + mloop.v, group_index);
+ MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[mloop.v], group_index);
dw->weight = 1.0f;
}
}
+
+ material_indices.finish();
}
void MeshFromGeometry::create_vertex_groups(Object *obj)
{
Mesh *mesh = static_cast<Mesh *>(obj->data);
- if (mesh->dvert == nullptr) {
+ if (mesh->deform_verts().is_empty()) {
return;
}
for (const std::string &name : mesh_geometry_.group_order_) {
@@ -230,15 +243,17 @@ void MeshFromGeometry::create_vertex_groups(Object *obj)
void MeshFromGeometry::create_edges(Mesh *mesh)
{
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+
const int64_t tot_edges{mesh_geometry_.edges_.size()};
- const int64_t total_verts{mesh_geometry_.vertex_count_};
+ const int64_t total_verts{mesh_geometry_.get_vertex_count()};
UNUSED_VARS_NDEBUG(total_verts);
for (int i = 0; i < tot_edges; ++i) {
const MEdge &src_edge = mesh_geometry_.edges_[i];
- MEdge &dst_edge = mesh->medge[i];
- BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts);
- dst_edge.v1 = src_edge.v1;
- dst_edge.v2 = src_edge.v2;
+ MEdge &dst_edge = edges[i];
+ dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0);
+ dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0);
+ BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts);
dst_edge.flag = ME_LOOSEEDGE;
}
@@ -254,7 +269,7 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
return;
}
MLoopUV *mluv_dst = static_cast<MLoopUV *>(CustomData_add_layer(
- &mesh->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, mesh_geometry_.total_loops_));
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh_geometry_.total_loops_));
int tot_loop_idx = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
@@ -273,7 +288,8 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
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)
+ Map<std::string, Material *> &created_materials,
+ bool relative_paths)
{
/* Have we created this material already? */
Material **found_mat = created_materials.lookup_ptr(name);
@@ -287,9 +303,10 @@ static Material *get_or_create_material(Main *bmain,
const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>());
Material *mat = BKE_material_add(bmain, name.c_str());
- ShaderNodetreeWrap mat_wrap{bmain, mtl, mat};
+ id_us_min(&mat->id);
+
mat->use_nodes = true;
- mat->nodetree = mat_wrap.get_nodetree();
+ mat->nodetree = create_mtl_node_tree(bmain, mtl, mat, relative_paths);
BKE_ntree_update_main_tree(bmain, mat->nodetree, nullptr);
created_materials.add_new(name, mat);
@@ -299,23 +316,30 @@ static Material *get_or_create_material(Main *bmain,
void MeshFromGeometry::create_materials(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials,
- Object *obj)
+ Object *obj,
+ bool relative_paths)
{
for (const std::string &name : mesh_geometry_.material_order_) {
- Material *mat = get_or_create_material(bmain, name, materials, created_materials);
+ Material *mat = get_or_create_material(
+ bmain, name, materials, created_materials, relative_paths);
if (mat == nullptr) {
continue;
}
BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1);
}
+ if (obj->totcol > 0) {
+ obj->actcol = 1;
+ }
}
void MeshFromGeometry::create_normals(Mesh *mesh)
{
- /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */
-
/* No normal data: nothing to do. */
- if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) {
+ if (global_vertices_.vertex_normals.is_empty()) {
+ return;
+ }
+ /* Custom normals can only be stored on face corners. */
+ if (mesh_geometry_.total_loops_ == 0) {
return;
}
@@ -341,23 +365,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh)
void MeshFromGeometry::create_colors(Mesh *mesh)
{
- /* Nothing to do if we don't have vertex colors. */
- if (mesh_geometry_.vertex_color_count_ < 1) {
- return;
- }
- if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) {
- std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_
- << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '"
- << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl;
+ /* Nothing to do if we don't have vertex colors at all. */
+ if (global_vertices_.vertex_colors.is_empty()) {
return;
}
- CustomDataLayer *color_layer = BKE_id_attribute_new(
- &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr);
- float4 *colors = (float4 *)color_layer->data;
- for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) {
- float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i];
- colors[i] = float4(c.x, c.y, c.z, 1.0f);
+ /* Find which vertex color block is for this mesh (if any). */
+ for (const auto &block : global_vertices_.vertex_colors) {
+ if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index &&
+ mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) {
+ /* This block is suitable, use colors from it. */
+ CustomDataLayer *color_layer = BKE_id_attribute_new(
+ &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr);
+ float4 *colors = (float4 *)color_layer->data;
+ int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index;
+ for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) {
+ float3 c = block.colors[offset + i];
+ colors[i] = float4(c.x, c.y, c.z, 1.0f);
+ }
+ return;
+ }
}
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
index 591a7b81e63..cdc88528f1e 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
@@ -62,7 +62,8 @@ class MeshFromGeometry : NonMovable, NonCopyable {
void create_materials(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials,
- Object *obj);
+ Object *obj,
+ bool relative_paths);
void create_normals(Mesh *mesh);
void create_colors(Mesh *mesh);
void create_vertex_groups(Object *obj);
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
index 60e419728f3..c471b2002de 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
@@ -5,18 +5,19 @@
*/
#include "BKE_image.h"
+#include "BKE_main.h"
#include "BKE_node.h"
#include "BLI_map.hh"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "NOD_shader.h"
-/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
-#include "obj_export_io.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_mtl.hh"
#include "obj_import_string_utils.hh"
@@ -27,12 +28,12 @@ namespace blender::io::obj {
* Only float value(s) can be set using this method.
*/
static void set_property_of_socket(eNodeSocketDatatype property_type,
- StringRef socket_id,
+ const char *socket_id,
Span<float> value,
bNode *r_node)
{
BLI_assert(r_node);
- bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id.data())};
+ bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id)};
BLI_assert(socket && socket->type == property_type);
switch (property_type) {
case SOCK_FLOAT: {
@@ -60,136 +61,124 @@ static void set_property_of_socket(eNodeSocketDatatype property_type,
}
}
-static bool load_texture_image_at_path(Main *bmain,
- const tex_map_XX &tex_map,
- bNode *r_node,
- const std::string &path)
+static Image *load_image_at_path(Main *bmain, const std::string &path, bool relative_paths)
{
- Image *tex_image = BKE_image_load(bmain, path.c_str());
- if (!tex_image) {
+ Image *image = BKE_image_load_exists(bmain, path.c_str());
+ if (!image) {
fprintf(stderr, "Cannot load image file: '%s'\n", path.c_str());
- return false;
+ return nullptr;
}
fprintf(stderr, "Loaded image from: '%s'\n", path.c_str());
- r_node->id = reinterpret_cast<ID *>(tex_image);
- NodeTexImage *image = static_cast<NodeTexImage *>(r_node->storage);
- image->projection = tex_map.projection_type;
- return true;
+ if (relative_paths) {
+ BLI_path_rel(image->filepath, BKE_main_blendfile_path(bmain));
+ }
+ return image;
}
-/**
- * Load image for Image Texture node and set the node properties.
- * Return success if Image can be loaded successfully.
- */
-static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_node)
+static Image *create_placeholder_image(Main *bmain, const std::string &path)
{
- BLI_assert(r_node && r_node->type == SH_NODE_TEX_IMAGE);
+ const float color[4] = {0, 0, 0, 1};
+ Image *image = BKE_image_add_generated(bmain,
+ 32,
+ 32,
+ BLI_path_basename(path.c_str()),
+ 24,
+ false,
+ IMA_GENTYPE_BLANK,
+ color,
+ false,
+ false,
+ false);
+ STRNCPY(image->filepath, path.c_str());
+ image->source = IMA_SRC_FILE;
+ return image;
+}
+
+static Image *load_texture_image(Main *bmain, const MTLTexMap &tex_map, bool relative_paths)
+{
+ Image *image = nullptr;
/* First try treating texture path as relative. */
std::string tex_path{tex_map.mtl_dir_path + tex_map.image_path};
- if (load_texture_image_at_path(bmain, tex_map, r_node, tex_path)) {
- return true;
+ image = load_image_at_path(bmain, tex_path, relative_paths);
+ if (image != nullptr) {
+ return image;
}
/* Then try using it directly as absolute path. */
std::string raw_path{tex_map.image_path};
- if (load_texture_image_at_path(bmain, tex_map, r_node, raw_path)) {
- return true;
+ image = load_image_at_path(bmain, raw_path, relative_paths);
+ if (image != nullptr) {
+ return image;
}
/* Try removing quotes. */
std::string no_quote_path{tex_path};
auto end_pos = std::remove(no_quote_path.begin(), no_quote_path.end(), '"');
no_quote_path.erase(end_pos, no_quote_path.end());
- if (no_quote_path != tex_path &&
- load_texture_image_at_path(bmain, tex_map, r_node, no_quote_path)) {
- return true;
+ if (no_quote_path != tex_path) {
+ image = load_image_at_path(bmain, no_quote_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
}
/* Try replacing underscores with spaces. */
std::string no_underscore_path{no_quote_path};
std::replace(no_underscore_path.begin(), no_underscore_path.end(), '_', ' ');
- if (no_underscore_path != no_quote_path && no_underscore_path != tex_path &&
- load_texture_image_at_path(bmain, tex_map, r_node, no_underscore_path)) {
- return true;
+ if (no_underscore_path != no_quote_path && no_underscore_path != tex_path) {
+ image = load_image_at_path(bmain, no_underscore_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
+ }
+ /* Try taking just the basename from input path. */
+ std::string base_path{tex_map.mtl_dir_path + BLI_path_basename(tex_map.image_path.c_str())};
+ if (base_path != tex_path) {
+ image = load_image_at_path(bmain, base_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
}
- return false;
-}
-
-ShaderNodetreeWrap::ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat, Material *mat)
- : mtl_mat_(mtl_mat)
-{
- nodetree_.reset(ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname));
- bsdf_ = add_node_to_tree(SH_NODE_BSDF_PRINCIPLED);
- shader_output_ = add_node_to_tree(SH_NODE_OUTPUT_MATERIAL);
-
- set_bsdf_socket_values(mat);
- add_image_textures(bmain, mat);
- link_sockets(bsdf_, "BSDF", shader_output_, "Surface", 4);
-
- nodeSetActive(nodetree_.get(), shader_output_);
+ image = create_placeholder_image(bmain, tex_path);
+ return image;
}
-/**
- * Assert if caller hasn't acquired nodetree.
- */
-ShaderNodetreeWrap::~ShaderNodetreeWrap()
-{
- if (nodetree_) {
- /* nodetree's ownership must be acquired by the caller. */
- nodetree_.reset();
- BLI_assert(0);
- }
-}
+/* Nodes are arranged in columns by type, with manually placed x coordinates
+ * based on node widths. */
+const float node_locx_texcoord = -880.0f;
+const float node_locx_mapping = -680.0f;
+const float node_locx_image = -480.0f;
+const float node_locx_normalmap = -200.0f;
+const float node_locx_bsdf = 0.0f;
+const float node_locx_output = 280.0f;
-bNodeTree *ShaderNodetreeWrap::get_nodetree()
-{
- /* If this function has been reached, we know that nodes and the nodetree
- * can be added to the scene safely. */
- return nodetree_.release();
-}
+/* Nodes are arranged in rows; one row for each image being used. */
+const float node_locy_top = 300.0f;
+const float node_locy_step = 300.0f;
-bNode *ShaderNodetreeWrap::add_node_to_tree(const int node_type)
+/* Add a node of the given type at the given location. */
+static bNode *add_node(bNodeTree *ntree, int type, float x, float y)
{
- return nodeAddStaticNode(nullptr, nodetree_.get(), node_type);
+ bNode *node = nodeAddStaticNode(nullptr, ntree, type);
+ node->locx = x;
+ node->locy = y;
+ return node;
}
-std::pair<float, float> ShaderNodetreeWrap::set_node_locations(const int pos_x)
+static void link_sockets(bNodeTree *ntree,
+ bNode *from_node,
+ const char *from_node_id,
+ bNode *to_node,
+ const char *to_node_id)
{
- int pos_y = 0;
- bool found = false;
- while (true) {
- for (Span<int> location : node_locations) {
- if (location[0] == pos_x && location[1] == pos_y) {
- pos_y += 1;
- found = true;
- }
- else {
- found = false;
- }
- }
- if (!found) {
- node_locations.append({pos_x, pos_y});
- return {pos_x * node_size_, pos_y * node_size_ * 2.0 / 3.0};
- }
- }
-}
-
-void ShaderNodetreeWrap::link_sockets(bNode *from_node,
- StringRef from_node_id,
- bNode *to_node,
- StringRef to_node_id,
- const int from_node_pos_x)
-{
- std::tie(from_node->locx, from_node->locy) = set_node_locations(from_node_pos_x);
- std::tie(to_node->locx, to_node->locy) = set_node_locations(from_node_pos_x + 1);
- bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id.data())};
- bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id.data())};
+ bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id)};
+ bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id)};
BLI_assert(from_sock && to_sock);
- nodeAddLink(nodetree_.get(), from_node, from_sock, to_node, to_sock);
+ nodeAddLink(ntree, from_node, from_sock, to_node, to_sock);
}
-void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
+static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat)
{
- const int illum = mtl_mat_.illum;
+ const int illum = mtl_mat.illum_mode;
bool do_highlight = false;
bool do_tranparency = false;
bool do_reflection = false;
@@ -255,21 +244,23 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
/* Approximations for trying to map obj/mtl material model into
* Principled BSDF: */
/* Specular: average of Ks components. */
- float specular = (mtl_mat_.Ks[0] + mtl_mat_.Ks[1] + mtl_mat_.Ks[2]) / 3;
+ float specular = (mtl_mat.spec_color[0] + mtl_mat.spec_color[1] + mtl_mat.spec_color[2]) / 3;
if (specular < 0.0f) {
specular = do_highlight ? 1.0f : 0.0f;
}
/* Roughness: map 0..1000 range to 1..0 and apply non-linearity. */
float roughness;
- if (mtl_mat_.Ns < 0.0f) {
+ if (mtl_mat.spec_exponent < 0.0f) {
roughness = do_highlight ? 0.0f : 1.0f;
}
else {
- float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat_.Ns));
+ float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat.spec_exponent));
roughness = 1.0f - sqrt(clamped_ns / 1000.0f);
}
- /* Metallic: average of Ka components. */
- float metallic = (mtl_mat_.Ka[0] + mtl_mat_.Ka[1] + mtl_mat_.Ka[2]) / 3;
+ /* Metallic: average of `Ka` components. */
+ float metallic = (mtl_mat.ambient_color[0] + mtl_mat.ambient_color[1] +
+ mtl_mat.ambient_color[2]) /
+ 3;
if (do_reflection) {
if (metallic < 0.0f) {
metallic = 1.0f;
@@ -279,7 +270,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
metallic = 0.0f;
}
- float ior = mtl_mat_.Ni;
+ float ior = mtl_mat.ior;
if (ior < 0) {
if (do_tranparency) {
ior = 1.0f;
@@ -288,89 +279,153 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
ior = 1.5f;
}
}
- float alpha = mtl_mat_.d;
+ float alpha = mtl_mat.alpha;
if (do_tranparency && alpha < 0) {
alpha = 1.0f;
}
- float3 base_color = {mtl_mat_.Kd[0], mtl_mat_.Kd[1], mtl_mat_.Kd[2]};
+ /* PBR values, when present, override the ones calculated above. */
+ if (mtl_mat.roughness >= 0) {
+ roughness = mtl_mat.roughness;
+ }
+ if (mtl_mat.metallic >= 0) {
+ metallic = mtl_mat.metallic;
+ }
+
+ float3 base_color = mtl_mat.color;
if (base_color.x >= 0 && base_color.y >= 0 && base_color.z >= 0) {
- set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf_);
+ set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf);
/* Viewport shading uses legacy r,g,b base color. */
mat->r = base_color.x;
mat->g = base_color.y;
mat->b = base_color.z;
}
- float3 emission_color = {mtl_mat_.Ke[0], mtl_mat_.Ke[1], mtl_mat_.Ke[2]};
+ float3 emission_color = mtl_mat.emission_color;
if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) {
- set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_);
+ set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf);
}
- if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) {
- set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_);
+ if (mtl_mat.tex_map_of_type(MTLTexMapType::Emission).is_valid()) {
+ set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf);
}
- set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_);
- set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf);
+ set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf);
mat->roughness = roughness;
- set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf);
mat->metallic = metallic;
if (ior != -1) {
- set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf);
}
if (alpha != -1) {
- set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf);
}
if (do_tranparency || (alpha >= 0.0f && alpha < 1.0f)) {
mat->blend_method = MA_BM_BLEND;
}
+
+ if (mtl_mat.sheen >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Sheen", {mtl_mat.sheen}, bsdf);
+ }
+ if (mtl_mat.cc_thickness >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Clearcoat", {mtl_mat.cc_thickness}, bsdf);
+ }
+ if (mtl_mat.cc_roughness >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Clearcoat Roughness", {mtl_mat.cc_roughness}, bsdf);
+ }
+ if (mtl_mat.aniso >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Anisotropic", {mtl_mat.aniso}, bsdf);
+ }
+ if (mtl_mat.aniso_rot >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Anisotropic Rotation", {mtl_mat.aniso_rot}, bsdf);
+ }
+
+ /* Transmission: average of transmission color. */
+ float transmission = (mtl_mat.transmit_color[0] + mtl_mat.transmit_color[1] +
+ mtl_mat.transmit_color[2]) /
+ 3;
+ if (transmission >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Transmission", {transmission}, bsdf);
+ }
}
-void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat)
+static void add_image_textures(Main *bmain,
+ bNodeTree *ntree,
+ bNode *bsdf,
+ Material *mat,
+ const MTLMaterial &mtl_mat,
+ bool relative_paths)
{
- for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map :
- mtl_mat_.texture_maps.items()) {
- if (texture_map.value.image_path.empty()) {
+ float node_locy = node_locy_top;
+ for (int key = 0; key < (int)MTLTexMapType::Count; ++key) {
+ const MTLTexMap &value = mtl_mat.texture_maps[key];
+ if (!value.is_valid()) {
/* No Image texture node of this map type can be added to this material. */
continue;
}
- bNode *image_texture = add_node_to_tree(SH_NODE_TEX_IMAGE);
- if (!load_texture_image(bmain, texture_map.value, image_texture)) {
- /* Image could not be added, so don't add or link further nodes. */
+ Image *image = load_texture_image(bmain, value, relative_paths);
+ if (image == nullptr) {
continue;
}
+ bNode *image_node = add_node(ntree, SH_NODE_TEX_IMAGE, node_locx_image, node_locy);
+ BLI_assert(image_node);
+ image_node->id = &image->id;
+ static_cast<NodeTexImage *>(image_node->storage)->projection = value.projection_type;
+
/* Add normal map node if needed. */
bNode *normal_map = nullptr;
- if (texture_map.key == eMTLSyntaxElement::map_Bump) {
- normal_map = add_node_to_tree(SH_NODE_NORMAL_MAP);
- const float bump = std::max(0.0f, mtl_mat_.map_Bump_strength);
+ if (key == (int)MTLTexMapType::Normal) {
+ normal_map = add_node(ntree, SH_NODE_NORMAL_MAP, node_locx_normalmap, node_locy);
+ const float bump = std::max(0.0f, mtl_mat.normal_strength);
set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map);
}
/* Add UV mapping & coordinate nodes only if needed. */
- if (texture_map.value.translation != float3(0, 0, 0) ||
- texture_map.value.scale != float3(1, 1, 1)) {
- bNode *mapping = add_node_to_tree(SH_NODE_MAPPING);
- bNode *texture_coordinate = add_node_to_tree(SH_NODE_TEX_COORD);
- set_property_of_socket(SOCK_VECTOR, "Location", {texture_map.value.translation, 3}, mapping);
- set_property_of_socket(SOCK_VECTOR, "Scale", {texture_map.value.scale, 3}, mapping);
-
- link_sockets(texture_coordinate, "UV", mapping, "Vector", 0);
- link_sockets(mapping, "Vector", image_texture, "Vector", 1);
+ if (value.translation != float3(0, 0, 0) || value.scale != float3(1, 1, 1)) {
+ bNode *texcoord = add_node(ntree, SH_NODE_TEX_COORD, node_locx_texcoord, node_locy);
+ bNode *mapping = add_node(ntree, SH_NODE_MAPPING, node_locx_mapping, node_locy);
+ set_property_of_socket(SOCK_VECTOR, "Location", {value.translation, 3}, mapping);
+ set_property_of_socket(SOCK_VECTOR, "Scale", {value.scale, 3}, mapping);
+
+ link_sockets(ntree, texcoord, "UV", mapping, "Vector");
+ link_sockets(ntree, mapping, "Vector", image_node, "Vector");
}
if (normal_map) {
- link_sockets(image_texture, "Color", normal_map, "Color", 2);
- link_sockets(normal_map, "Normal", bsdf_, "Normal", 3);
+ link_sockets(ntree, image_node, "Color", normal_map, "Color");
+ link_sockets(ntree, normal_map, "Normal", bsdf, "Normal");
}
- else if (texture_map.key == eMTLSyntaxElement::map_d) {
- link_sockets(image_texture, "Alpha", bsdf_, texture_map.value.dest_socket_id, 2);
+ else if (key == (int)MTLTexMapType::Alpha) {
+ link_sockets(ntree, image_node, "Alpha", bsdf, tex_map_type_to_socket_id[key]);
mat->blend_method = MA_BM_BLEND;
}
else {
- link_sockets(image_texture, "Color", bsdf_, texture_map.value.dest_socket_id, 2);
+ link_sockets(ntree, image_node, "Color", bsdf, tex_map_type_to_socket_id[key]);
}
+
+ /* Next layout row: goes downwards on the screen. */
+ node_locy -= node_locy_step;
}
}
+
+bNodeTree *create_mtl_node_tree(Main *bmain,
+ const MTLMaterial &mtl,
+ Material *mat,
+ bool relative_paths)
+{
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, &mat->id, "Shader Nodetree", ntreeType_Shader->idname);
+
+ bNode *bsdf = add_node(ntree, SH_NODE_BSDF_PRINCIPLED, node_locx_bsdf, node_locy_top);
+ bNode *output = add_node(ntree, SH_NODE_OUTPUT_MATERIAL, node_locx_output, node_locy_top);
+
+ set_bsdf_socket_values(bsdf, mat, mtl);
+ add_image_textures(bmain, ntree, bsdf, mat, mtl, relative_paths);
+ link_sockets(ntree, bsdf, "BSDF", output, "Surface");
+ nodeSetActive(ntree, output);
+
+ return ntree;
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
index 12bea1cdc5a..a3ba803e921 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
@@ -1,93 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-/** \file
- * \ingroup obj
- */
-
#pragma once
-#include <array>
-
-#include "BLI_map.hh"
-#include "BLI_math_vec_types.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
-
#include "DNA_node_types.h"
-#include "MEM_guardedalloc.h"
-
-#include "obj_export_mtl.hh"
+struct Main;
+struct Material;
namespace blender::io::obj {
-struct UniqueNodetreeDeleter {
- void operator()(bNodeTree *node)
- {
- MEM_freeN(node);
- }
-};
-
-using unique_nodetree_ptr = std::unique_ptr<bNodeTree, UniqueNodetreeDeleter>;
-
-class ShaderNodetreeWrap {
- private:
- /* Node arrangement:
- * Texture Coordinates -> Mapping -> Image Texture -> (optional) Normal Map -> p-BSDF -> Material
- * Output. */
- unique_nodetree_ptr nodetree_;
- bNode *bsdf_;
- bNode *shader_output_;
- const MTLMaterial &mtl_mat_;
-
- /* List of all locations occupied by nodes. */
- Vector<std::array<int, 2>> node_locations;
- const float node_size_{300.f};
-
- public:
- /**
- * Initializes a nodetree with a p-BSDF node's BSDF socket connected to shader output node's
- * surface socket.
- */
- ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat, Material *mat);
- ~ShaderNodetreeWrap();
-
- /**
- * Release nodetree for materials to own it. nodetree has its unique deleter
- * if destructor is not reached for some reason.
- */
- bNodeTree *get_nodetree();
+struct MTLMaterial;
- private:
- /**
- * Add a new static node to the tree.
- * No two nodes are linked here.
- */
- bNode *add_node_to_tree(const int node_type);
- /**
- * Return x-y coordinates for a node where y is determined by other nodes present in
- * the same vertical column.
- */
- std::pair<float, float> set_node_locations(const int pos_x);
- /**
- * Link two nodes by the sockets of given IDs.
- * Also releases the ownership of the "from" node for nodetree to free it.
- * \param from_node_pos_x: 0 to 4 value as per nodetree arrangement.
- */
- void link_sockets(bNode *from_node,
- StringRef from_node_id,
- bNode *to_node,
- StringRef to_node_id,
- const int from_node_pos_x);
- /**
- * Set values of sockets in p-BSDF node of the nodetree.
- */
- void set_bsdf_socket_values(Material *mat);
- /**
- * Create image texture, vector and normal mapping nodes from MTL materials and link the
- * nodes to p-BSDF node.
- */
- void add_image_textures(Main *bmain, Material *mat);
-};
+bNodeTree *create_mtl_node_tree(Main *bmain,
+ const MTLMaterial &mtl_mat,
+ Material *mat,
+ bool relative_paths);
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
index 3d6733d661e..04d9a665588 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
@@ -9,9 +9,10 @@
#include "BKE_lib_id.h"
#include "BLI_map.hh"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
#include "BLI_vector.hh"
-#include "BLI_vector_set.hh"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -19,35 +20,24 @@
namespace blender::io::obj {
/**
- * List of all vertex and UV vertex coordinates in an OBJ file accessible to any
- * Geometry instance at any time.
+ * All vertex positions, normals, UVs, colors in the OBJ file.
*/
struct GlobalVertices {
Vector<float3> vertices;
Vector<float2> uv_vertices;
Vector<float3> vertex_normals;
- Vector<float3> vertex_colors;
-};
-/**
- * Keeps track of the vertices that belong to other Geometries.
- * Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total
- * vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices
- * in the global list).
- */
-struct VertexIndexOffset {
- private:
- int offset_ = 0;
-
- public:
- void set_index_offset(const int64_t total_vertices)
- {
- offset_ = total_vertices;
- }
- int64_t get_index_offset() const
- {
- return offset_;
- }
+ /**
+ * Vertex colors might not be present in the file at all, or only
+ * provided for some meshes. Store them in chunks as they are
+ * spelled out in the file, e.g. if there are 10 vertices in sequence, all
+ * with `xyzrgb` colors, they will be one block.
+ */
+ struct VertexColorsBlock {
+ Vector<float3> colors;
+ int start_vertex_index;
+ };
+ Vector<VertexColorsBlock> vertex_colors;
};
/**
@@ -101,21 +91,42 @@ struct Geometry {
Map<std::string, int> material_indices_;
Vector<std::string> material_order_;
- int vertex_start_ = 0;
- int vertex_count_ = 0;
- int vertex_color_start_ = 0;
- int vertex_color_count_ = 0;
- /** Edges written in the file in addition to (or even without polygon) elements. */
+ int vertex_index_min_ = INT_MAX;
+ int vertex_index_max_ = -1;
+ /* Global vertex indices used by this geometry. */
+ Set<int> vertices_;
+ /* Mapping from global vertex index to geometry-local vertex index. */
+ Map<int, int> global_to_local_vertices_;
+ /* Loose edges in the file. */
Vector<MEdge> edges_;
Vector<PolyCorner> face_corners_;
Vector<PolyElem> face_elements_;
bool has_invalid_polys_ = false;
- bool has_vertex_normals_ = false;
bool has_vertex_groups_ = false;
NurbsElement nurbs_element_;
int total_loops_ = 0;
+
+ int get_vertex_count() const
+ {
+ return (int)vertices_.size();
+ }
+ void track_vertex_index(int index)
+ {
+ vertices_.add(index);
+ math::min_inplace(vertex_index_min_, index);
+ math::max_inplace(vertex_index_max_, index);
+ }
+ void track_all_vertices(int count)
+ {
+ vertices_.reserve(count);
+ for (int i = 0; i < count; ++i) {
+ vertices_.add(i);
+ }
+ vertex_index_min_ = 0;
+ vertex_index_max_ = count - 1;
+ }
};
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
index bc9006e1051..7e282b164b0 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
@@ -18,14 +18,12 @@ StringRef read_next_line(StringRef &buffer)
const char *start = buffer.begin();
const char *end = buffer.end();
size_t len = 0;
- char prev = 0;
const char *ptr = start;
while (ptr < end) {
char c = *ptr++;
- if (c == '\n' && prev != '\\') {
+ if (c == '\n') {
break;
}
- prev = c;
++len;
}
@@ -35,7 +33,29 @@ StringRef read_next_line(StringRef &buffer)
static bool is_whitespace(char c)
{
- return c <= ' ' || c == '\\';
+ return c <= ' ';
+}
+
+void fixup_line_continuations(char *p, char *end)
+{
+ while (true) {
+ /* Find next backslash, if any. */
+ char *backslash = std::find(p, end, '\\');
+ if (backslash == end) {
+ break;
+ }
+ /* Skip over possible whitespace right after it. */
+ p = backslash + 1;
+ while (p < end && is_whitespace(*p) && *p != '\n') {
+ ++p;
+ }
+ /* If then we have a newline, turn both backslash
+ * and the newline into regular spaces. */
+ if (p < end && *p == '\n') {
+ *backslash = ' ';
+ *p = ' ';
+ }
+ }
}
const char *drop_whitespace(const char *p, const char *end)
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
index f6dd1a6b675..e42f5080d25 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
@@ -6,9 +6,6 @@
/*
* Various text parsing utilities used by OBJ importer.
- * The utilities are not directly usable by other formats, since
- * they treat backslash (\) as a whitespace character (OBJ format
- * allows backslashes to function as a line-continuation character).
*
* Many of these functions take two pointers (p, end) indicating
* which part of a string to operate on, and return a possibly
@@ -27,21 +24,22 @@ namespace blender::io::obj {
* The returned line will not have '\n' characters at the end;
* the `buffer` is modified to contain remaining text without
* the input line.
- *
- * Note that backslash (\) character is treated as a line
- * continuation.
*/
StringRef read_next_line(StringRef &buffer);
/**
+ * Fix up OBJ line continuations by replacing backslash (\) and the
+ * following newline with spaces.
+ */
+void fixup_line_continuations(char *p, char *end);
+
+/**
* Drop leading white-space from a string part.
- * Note that backslash character is considered white-space.
*/
const char *drop_whitespace(const char *p, const char *end);
/**
* Drop leading non-white-space from a string part.
- * Note that backslash character is considered white-space.
*/
const char *drop_non_whitespace(const char *p, const char *end);
diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc
index f2051d195c8..ccbcce64d65 100644
--- a/source/blender/io/wavefront_obj/importer/obj_importer.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc
@@ -9,6 +9,7 @@
#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_set.hh"
+#include "BLI_sort.hh"
#include "BLI_string_ref.hh"
#include "BKE_layer.h"
@@ -18,6 +19,7 @@
#include "DNA_collection_types.h"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
#include "obj_import_mesh.hh"
#include "obj_import_nurbs.hh"
@@ -38,12 +40,20 @@ static void geometry_to_blender_objects(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials)
{
- BKE_view_layer_base_deselect_all(view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
/* Don't do collection syncs for each object, will do once after the loop. */
BKE_layer_collection_resync_forbid();
+ /* Sort objects by name: creating many objects is much faster if the creation
+ * order is sorted by name. */
+ blender::parallel_sort(
+ all_geometries.begin(), all_geometries.end(), [](const auto &a, const auto &b) {
+ const char *na = a ? a->geometry_name_.c_str() : "";
+ const char *nb = b ? b->geometry_name_.c_str() : "";
+ return BLI_strcasecmp(na, nb) < 0;
+ });
+
/* Create all the objects. */
Vector<Object *> objects;
objects.reserve(all_geometries.size());
@@ -66,6 +76,7 @@ static void geometry_to_blender_objects(Main *bmain,
/* Sync the collection after all objects are created. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* After collection sync, select objects in the view layer and do DEG updates. */
for (Object *obj : objects) {
@@ -112,6 +123,9 @@ void importer_main(Main *bmain,
mtl_parser.parse_and_store(materials);
}
+ if (import_params.clear_selection) {
+ BKE_view_layer_base_deselect_all(scene, view_layer);
+ }
geometry_to_blender_objects(bmain,
scene,
view_layer,
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
index 6aec848573f..dcba78ac99e 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
@@ -60,7 +60,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
return;
}
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
- EXPECT_EQ(objmeshes.size(), 20);
+ EXPECT_EQ(objmeshes.size(), 21);
EXPECT_EQ(objcurves.size(), 0);
}
@@ -185,17 +185,17 @@ TEST(obj_exporter_writer, mtllib)
TEST(obj_exporter_writer, format_handler_buffer_chunking)
{
/* Use a tiny buffer chunk size, so that the test below ends up creating several blocks. */
- FormatHandler<eFileType::OBJ, 16> h;
- h.write<eOBJSyntaxElement::object_name>("abc");
- h.write<eOBJSyntaxElement::object_name>("abcd");
- h.write<eOBJSyntaxElement::object_name>("abcde");
- h.write<eOBJSyntaxElement::object_name>("abcdef");
- h.write<eOBJSyntaxElement::object_name>("012345678901234567890123456789abcd");
- h.write<eOBJSyntaxElement::object_name>("123");
- h.write<eOBJSyntaxElement::curve_element_begin>();
- h.write<eOBJSyntaxElement::new_line>();
- h.write<eOBJSyntaxElement::nurbs_parameter_begin>();
- h.write<eOBJSyntaxElement::new_line>();
+ FormatHandler h(16);
+ h.write_obj_object("abc");
+ h.write_obj_object("abcd");
+ h.write_obj_object("abcde");
+ h.write_obj_object("abcdef");
+ h.write_obj_object("012345678901234567890123456789abcd");
+ h.write_obj_object("123");
+ h.write_obj_curve_begin();
+ h.write_obj_newline();
+ h.write_obj_nurbs_parm_begin();
+ h.write_obj_newline();
size_t got_blocks = h.get_block_count();
ASSERT_EQ(got_blocks, 7);
@@ -527,4 +527,27 @@ TEST_F(obj_exporter_regression_test, all_objects_mat_groups)
_export.params);
}
+TEST_F(obj_exporter_regression_test, materials_without_pbr)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_normals = false;
+ _export.params.path_mode = PATH_REFERENCE_RELATIVE;
+ compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
+ "io_tests/obj/materials_without_pbr.obj",
+ "io_tests/obj/materials_without_pbr.mtl",
+ _export.params);
+}
+
+TEST_F(obj_exporter_regression_test, materials_pbr)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_normals = false;
+ _export.params.path_mode = PATH_REFERENCE_RELATIVE;
+ _export.params.export_pbr_extensions = true;
+ compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
+ "io_tests/obj/materials_pbr.obj",
+ "io_tests/obj/materials_pbr.mtl",
+ _export.params);
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
index 7d3b41ed527..006d86312b6 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
@@ -31,6 +31,7 @@ struct OBJExportParamsDefault {
params.path_mode = PATH_REFERENCE_AUTO;
params.export_triangulated_mesh = false;
params.export_curves_as_nurbs = false;
+ params.export_pbr_extensions = false;
params.export_object_groups = false;
params.export_material_groups = false;
diff --git a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
index e9747b437cc..dc1cfd2b449 100644
--- a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
@@ -10,17 +10,34 @@ namespace blender::io::obj {
TEST(obj_import_string_utils, read_next_line)
{
- std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na";
+ std::string str = "abc\n \n\nline with \t spaces\nCRLF ending:\r\na";
StringRef s = str;
EXPECT_STRREF_EQ("abc", read_next_line(s));
EXPECT_STRREF_EQ(" ", read_next_line(s));
EXPECT_STRREF_EQ("", read_next_line(s));
- EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s));
+ EXPECT_STRREF_EQ("line with \t spaces", read_next_line(s));
EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s));
EXPECT_STRREF_EQ("a", read_next_line(s));
EXPECT_TRUE(s.is_empty());
}
+TEST(obj_import_string_utils, fixup_line_continuations)
+{
+ const char *str =
+ "backslash \\\n eol\n"
+ "backslash spaces \\ \n eol\n"
+ "without eol \\ is \\\\ \\ left intact\n"
+ "\\";
+ const char *exp =
+ "backslash eol\n"
+ "backslash spaces eol\n"
+ "without eol \\ is \\\\ \\ left intact\n"
+ "\\";
+ std::string buf(str);
+ fixup_line_continuations(buf.data(), buf.data() + buf.size());
+ EXPECT_STRREF_EQ(exp, buf);
+}
+
static StringRef drop_whitespace(StringRef s)
{
return StringRef(drop_whitespace(s.begin(), s.end()), s.end());
@@ -54,7 +71,7 @@ TEST(obj_import_string_utils, drop_whitespace)
/* No leading whitespace */
EXPECT_STRREF_EQ("c", drop_whitespace("c"));
/* Case with backslash, should be treated as whitespace */
- EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d"));
+ EXPECT_STRREF_EQ("d", drop_whitespace(" \t d"));
}
TEST(obj_import_string_utils, parse_int_valid)
diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
index eeb81f5e23e..99e12aed99c 100644
--- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
@@ -8,11 +8,12 @@
#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_main.h"
+#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BLI_listbase.h"
-#include "BLI_math_base.h"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "BLO_readfile.h"
@@ -47,7 +48,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
void import_and_check(const char *path,
const Expectation *expect,
size_t expect_count,
- int expect_mat_count)
+ int expect_mat_count,
+ int expect_image_count = 0)
{
if (!blendfile_load("io_tests/blend_geometry/all_quads.blend")) {
ADD_FAILURE();
@@ -60,6 +62,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
params.up_axis = IO_AXIS_Y;
params.validate_meshes = true;
params.import_vertex_groups = false;
+ params.relative_paths = true;
+ params.clear_selection = true;
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
@@ -92,8 +96,9 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
EXPECT_EQ(mesh->totedge, exp.mesh_totedge_or_curve_endp);
EXPECT_EQ(mesh->totpoly, exp.mesh_totpoly_or_curve_order);
EXPECT_EQ(mesh->totloop, exp.mesh_totloop_or_curve_cyclic);
- EXPECT_V3_NEAR(mesh->mvert[0].co, exp.vert_first, 0.0001f);
- EXPECT_V3_NEAR(mesh->mvert[mesh->totvert - 1].co, exp.vert_last, 0.0001f);
+ const Span<MVert> verts = mesh->verts();
+ EXPECT_V3_NEAR(verts.first().co, exp.vert_first, 0.0001f);
+ EXPECT_V3_NEAR(verts.last().co, exp.vert_last, 0.0001f);
const float3 *lnors = (const float3 *)(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
float3 normal_first = lnors != nullptr ? lnors[0] : float3(0, 0, 0);
EXPECT_V3_NEAR(normal_first, exp.normal_first, 0.0001f);
@@ -132,12 +137,12 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
DEG_OBJECT_ITER_END;
EXPECT_EQ(object_index, expect_count);
- /* Count number of materials. */
- int mat_count = 0;
- LISTBASE_FOREACH (ID *, id, &bfile->main->materials) {
- ++mat_count;
- }
+ /* Check number of materials & textures. */
+ const int mat_count = BLI_listbase_count(&bfile->main->materials);
EXPECT_EQ(mat_count, expect_mat_count);
+
+ const int ima_count = BLI_listbase_count(&bfile->main->images);
+ EXPECT_EQ(ima_count, expect_image_count);
}
};
@@ -158,6 +163,36 @@ TEST_F(obj_importer_test, import_cube)
import_and_check("cube.obj", expect, std::size(expect), 1);
}
+TEST_F(obj_importer_test, import_cube_o_after_verts)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {
+ "OBActualCube",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(-1, -1, 1),
+ float3(1, -1, -1),
+ float3(0, 0, 1),
+ },
+ {
+ "OBSparseTri",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(1, -1, 1),
+ float3(-2, -2, 2),
+ float3(-0.2357f, 0.9428f, 0.2357f),
+ },
+ };
+ import_and_check("cube_o_after_verts.obj", expect, std::size(expect), 2);
+}
+
TEST_F(obj_importer_test, import_suzanne_all_data)
{
Expectation expect[] = {
@@ -196,7 +231,9 @@ TEST_F(obj_importer_test, import_nurbs_curves)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBCurveDeg3", OB_CURVES_LEGACY, 4, 0, 3, 0, float3(10, -2, 0), float3(6, -2, 0)},
{"OBnurbs_curves", OB_CURVES_LEGACY, 4, 0, 4, 0, float3(2, -2, 0), float3(-2, -2, 0)},
+ {"OBNurbsCurveCyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, -2, 0), float3(-6, 2, 0)},
{"OBNurbsCurveDiffWeights",
OB_CURVES_LEGACY,
4,
@@ -205,7 +242,6 @@ TEST_F(obj_importer_test, import_nurbs_curves)
0,
float3(6, -2, 0),
float3(2, -2, 0)},
- {"OBNurbsCurveCyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, -2, 0), float3(-6, 2, 0)},
{"OBNurbsCurveEndpoint",
OB_CURVES_LEGACY,
4,
@@ -214,7 +250,6 @@ TEST_F(obj_importer_test, import_nurbs_curves)
0,
float3(-6, -2, 0),
float3(-10, -2, 0)},
- {"OBCurveDeg3", OB_CURVES_LEGACY, 4, 0, 3, 0, float3(10, -2, 0), float3(6, -2, 0)},
};
import_and_check("nurbs_curves.obj", expect, std::size(expect), 0);
}
@@ -239,7 +274,8 @@ TEST_F(obj_importer_test, import_nurbs_manual)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCurve_Uniform_Parm", OB_CURVES_LEGACY, 5, 0, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
+ {"OBCurve_Cyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, 0, 2), float3(2, 0, -2)},
+ {"OBCurve_Endpoints", OB_CURVES_LEGACY, 5, 1, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
{"OBCurve_NonUniform_Parm",
OB_CURVES_LEGACY,
5,
@@ -248,8 +284,7 @@ TEST_F(obj_importer_test, import_nurbs_manual)
0,
float3(-2, 0, 2),
float3(-2, 0, 2)},
- {"OBCurve_Endpoints", OB_CURVES_LEGACY, 5, 1, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
- {"OBCurve_Cyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, 0, 2), float3(2, 0, -2)},
+ {"OBCurve_Uniform_Parm", OB_CURVES_LEGACY, 5, 0, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
};
import_and_check("nurbs_manual.obj", expect, std::size(expect), 0);
}
@@ -258,7 +293,7 @@ TEST_F(obj_importer_test, import_nurbs_mesh)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBTorus Knot",
+ {"OBTorus_Knot",
OB_MESH,
108,
108,
@@ -276,30 +311,69 @@ TEST_F(obj_importer_test, import_materials)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(-1, -1, 1), float3(1, -1, -1)},
};
- import_and_check("materials.obj", expect, std::size(expect), 4);
+ import_and_check("materials.obj", expect, std::size(expect), 4, 8);
}
-TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
+TEST_F(obj_importer_test, import_cubes_with_textures_rel)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBFaceWithHole_BecomesTwoFacesFormingAHole",
+ {"OBCube4Tex",
OB_MESH,
8,
- 10,
- 2,
12,
- float3(-2, 0, -2),
- float3(1, 0, -1)},
- {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts",
+ 6,
+ 24,
+ float3(1, 1, -1),
+ float3(-1, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTexMul",
OB_MESH,
8,
- 4,
+ 12,
+ 6,
+ 24,
+ float3(4, -2, -1),
+ float3(2, -4, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTiledTex",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(4, 1, -1),
+ float3(2, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTiledTexFromAnotherFolder",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(7, 1, -1),
+ float3(5, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ };
+ import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 4, 4);
+}
+
+TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBFaceAllVerts_BecomesOneOverlappingFaceUsingAllVerts",
+ OB_MESH,
+ 8,
+ 8,
1,
- 4,
- float3(3, 0, -2),
- float3(6, 0, -1)},
- {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 8, 3, 1, 3, float3(-2, 0, 3), float3(1, 0, 4)},
+ 8,
+ float3(8, 0, -2),
+ float3(11, 0, -1)},
{"OBFaceAllVertsDup_BecomesOneOverlappingFaceUsingAllVerts",
OB_MESH,
8,
@@ -308,15 +382,24 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
8,
float3(3, 0, 3),
float3(6, 0, 4)},
- {"OBFaceAllVerts_BecomesOneOverlappingFaceUsingAllVerts",
+ {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 2, 0, 0, 0, float3(8, 0, 3), float3(8, 0, 7)},
+ {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts",
OB_MESH,
- 8,
- 8,
+ 4,
+ 4,
1,
+ 4,
+ float3(3, 0, -2),
+ float3(7, 0, -2)},
+ {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 3, 3, 1, 3, float3(-2, 0, 3), float3(2, 0, 7)},
+ {"OBFaceWithHole_BecomesTwoFacesFormingAHole",
+ OB_MESH,
8,
- float3(8, 0, -2),
- float3(11, 0, -1)},
- {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 8, 0, 0, 0, float3(8, 0, 3), float3(11, 0, 4)},
+ 10,
+ 2,
+ 12,
+ float3(-2, 0, -2),
+ float3(1, 0, -1)},
};
import_and_check("faces_invalid_or_with_holes.obj", expect, std::size(expect), 0);
}
@@ -327,12 +410,12 @@ TEST_F(obj_importer_test, import_invalid_indices)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBQuad",
OB_MESH,
- 4,
+ 3,
3,
1,
3,
float3(-2, 0, -2),
- float3(2, 0, -2),
+ float3(2, 0, 2),
float3(0, 1, 0),
float2(0.5f, 0.25f)},
};
@@ -345,12 +428,12 @@ TEST_F(obj_importer_test, import_invalid_syntax)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon",
OB_MESH,
- 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */
+ 3,
3,
1,
3,
float3(1, 2, 3),
- float3(10, 11, 12),
+ float3(7, 8, 9),
float3(0, 1, 0),
float2(0.5f, 0.25f)},
};
@@ -362,6 +445,63 @@ TEST_F(obj_importer_test, import_all_objects)
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
/* .obj file has empty EmptyText and EmptyMesh objects; these are ignored and skipped */
+ {"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
+ {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
+ {"OBMaterialCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(28, 1, -1),
+ float3(26, 1, 1),
+ float3(-1, 0, 0)},
+ {"OBNurbsCircle",
+ OB_MESH,
+ 96,
+ 96,
+ 0,
+ 0,
+ float3(3.292893f, -2.707107f, 0),
+ float3(3.369084f, -2.77607f, 0)},
+ {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
+ {"OBParticleCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(22, 1, -1),
+ float3(20, 1, 1),
+ float3(0, 0, 1)},
+ {"OBShapeKeyCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(19, 1, -1),
+ float3(17, 1, 1),
+ float3(-0.4082f, -0.4082f, 0.8165f)},
+ {"OBSmoothCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(4, 1, -1),
+ float3(2, 1, 1),
+ float3(0.5774f, 0.5773f, 0.5774f)},
+ {"OBSurface",
+ OB_MESH,
+ 256,
+ 480,
+ 224,
+ 896,
+ float3(7.292893f, -2.707107f, -1),
+ float3(7.525872f, -2.883338f, 1),
+ float3(-0.7071f, -0.7071f, 0),
+ float2(0, 0.142857f)},
{"OBSurfPatch",
OB_MESH,
256,
@@ -382,24 +522,16 @@ TEST_F(obj_importer_test, import_all_objects)
float3(11, -2, 1),
float3(-0.0541f, -0.0541f, -0.9971f),
float2(0, 1)},
- {"OBSmoothCube",
- OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(4, 1, -1),
- float3(2, 1, 1),
- float3(0.5774f, 0.5773f, 0.5774f)},
- {"OBMaterialCube",
+ {"OBSurfTorus.001",
OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(28, 1, -1),
- float3(26, 1, 1),
- float3(-1, 0, 0)},
+ 1024,
+ 2048,
+ 1024,
+ 4096,
+ float3(5.34467f, -2.65533f, -0.176777f),
+ float3(5.232792f, -2.411795f, -0.220835f),
+ float3(-0.5042f, -0.5042f, -0.7011f),
+ float2(0, 1)},
{"OBTaperCube",
OB_MESH,
106,
@@ -409,24 +541,26 @@ TEST_F(obj_importer_test, import_all_objects)
float3(24.444445f, 0.502543f, -0.753814f),
float3(23.790743f, 0.460522f, -0.766546f),
float3(-0.0546f, 0.1716f, 0.9837f)},
- {"OBParticleCube",
+ {"OBText",
OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(22, 1, -1),
- float3(20, 1, 1),
- float3(0, 0, 1)},
- {"OBShapeKeyCube",
+ 177,
+ 345,
+ 171,
+ 513,
+ float3(1.75f, -9.458f, 0),
+ float3(0.587f, -9.406f, 0),
+ float3(0, 0, 1),
+ float2(0.017544f, 0)},
+ {"OBUVCube",
OB_MESH,
8,
13,
7,
26,
- float3(19, 1, -1),
- float3(17, 1, 1),
- float3(-0.4082f, -0.4082f, 0.8165f)},
+ float3(7, 1, -1),
+ float3(5, 1, 1),
+ float3(0, 0, 1),
+ float2(0.654526f, 0.579873f)},
{"OBUVImageCube",
OB_MESH,
8,
@@ -437,15 +571,6 @@ TEST_F(obj_importer_test, import_all_objects)
float3(8, 1, 1),
float3(0, 0, 1),
float2(0.654526f, 0.579873f)},
- {"OBVGroupCube",
- OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(16, 1, -1),
- float3(14, 1, 1),
- float3(0, 0, 1)},
{"OBVColCube",
OB_MESH,
8,
@@ -457,57 +582,15 @@ TEST_F(obj_importer_test, import_all_objects)
float3(0, 0, 1),
float2(0, 0),
float4(0.0f, 0.002125f, 1.0f, 1.0f)},
- {"OBUVCube",
+ {"OBVGroupCube",
OB_MESH,
8,
13,
7,
26,
- float3(7, 1, -1),
- float3(5, 1, 1),
- float3(0, 0, 1),
- float2(0.654526f, 0.579873f)},
- {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
- {"OBSurface",
- OB_MESH,
- 256,
- 480,
- 224,
- 896,
- float3(7.292893f, -2.707107f, -1),
- float3(7.525872f, -2.883338f, 1),
- float3(-0.7071f, -0.7071f, 0),
- float2(0, 0.142857f)},
- {"OBText",
- OB_MESH,
- 177,
- 345,
- 171,
- 513,
- float3(1.75f, -9.458f, 0),
- float3(0.587f, -9.406f, 0),
- float3(0, 0, 1),
- float2(0.017544f, 0)},
- {"OBSurfTorus.001",
- OB_MESH,
- 1024,
- 2048,
- 1024,
- 4096,
- float3(5.34467f, -2.65533f, -0.176777f),
- float3(5.232792f, -2.411795f, -0.220835f),
- float3(-0.5042f, -0.5042f, -0.7011f),
- float2(0, 1)},
- {"OBNurbsCircle",
- OB_MESH,
- 96,
- 96,
- 0,
- 0,
- float3(3.292893f, -2.707107f, 0),
- float3(3.369084f, -2.77607f, 0)},
- {"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
- {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
+ float3(16, 1, -1),
+ float3(14, 1, 1),
+ float3(0, 0, 1)},
};
import_and_check("all_objects.obj", expect, std::size(expect), 7);
}
@@ -516,28 +599,6 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCubeVertexByte",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(1.0f, 1.0f, -1.0f),
- float3(-1.0f, -1.0f, 1.0f),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.846873f, 0.027321f, 0.982123f, 1.0f)},
- {"OBCubeVertexFloat",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(3.392028f, 1.0f, -1.0f),
- float3(1.392028f, -1.0f, 1.0f),
- float3(0, 0, 0),
- float2(0, 0),
- float4(49.99467f, 0.027321f, 0.982123f, 1.0f)},
{"OBCubeCornerByte",
OB_MESH,
8,
@@ -579,36 +640,91 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
24,
float3(-4.550208f, -1.0f, -1.918042f),
float3(-2.550208f, 1.0f, -3.918042f)},
+ {"OBCubeVertexByte",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(1.0f, 1.0f, -1.0f),
+ float3(-1.0f, -1.0f, 1.0f),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.846873f, 0.027321f, 0.982123f, 1.0f)},
+ {"OBCubeVertexFloat",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(3.392028f, 1.0f, -1.0f),
+ float3(1.392028f, -1.0f, 1.0f),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(49.99467f, 0.027321f, 0.982123f, 1.0f)},
};
import_and_check("cubes_vertex_colors.obj", expect, std::size(expect), 0);
}
TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb)
{
- Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCubeXYZRGB",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(1, 1, -1),
- float3(-1, -1, 1),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.6038f, 0.3185f, 0.1329f, 1.0f)},
- {"OBCubeMRGB",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(4, 1, -1),
- float3(2, -1, 1),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}};
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBCubeMRGB",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(4, 1, -1),
+ float3(2, -1, 1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.8714f, 0.6308f, 0.5271f, 1.0f)},
+ {"OBCubeXYZRGB",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(1, 1, -1),
+ float3(-1, -1, 1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.6038f, 0.3185f, 0.1329f, 1.0f)},
+ {"OBTriMRGB",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(12, 1, -1),
+ float3(10, 0, -1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(1.0f, 0.0f, 0.0f, 1.0f)},
+ {
+ "OBTriNoColors",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(8, 1, -1),
+ float3(6, 0, -1),
+ },
+ };
import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0);
}
+TEST_F(obj_importer_test, import_vertices)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ /* Loose vertices without faces or edges. */
+ {"OBCube.001", OB_MESH, 8, 0, 0, 0, float3(1, 1, -1), float3(-1, 1, 1)},
+ };
+ import_and_check("vertices.obj", expect, std::size(expect), 0);
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
index 08050ac34c9..e473d629673 100644
--- a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
@@ -6,6 +6,7 @@
#include "testing/testing.h"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
namespace blender::io::obj {
@@ -49,18 +50,26 @@ class obj_mtl_parser_test : public testing::Test {
}
const MTLMaterial &got = *materials.lookup(exp.name);
const float tol = 0.0001f;
- EXPECT_V3_NEAR(exp.Ka, got.Ka, tol);
- EXPECT_V3_NEAR(exp.Kd, got.Kd, tol);
- EXPECT_V3_NEAR(exp.Ks, got.Ks, tol);
- EXPECT_V3_NEAR(exp.Ke, got.Ke, tol);
- EXPECT_NEAR(exp.Ns, got.Ns, tol);
- EXPECT_NEAR(exp.Ni, got.Ni, tol);
- EXPECT_NEAR(exp.d, got.d, tol);
- EXPECT_NEAR(exp.map_Bump_strength, got.map_Bump_strength, tol);
- EXPECT_EQ(exp.illum, got.illum);
- for (const auto &it : exp.texture_maps.items()) {
- const tex_map_XX &exp_tex = it.value;
- const tex_map_XX &got_tex = got.texture_maps.lookup(it.key);
+ EXPECT_V3_NEAR(exp.ambient_color, got.ambient_color, tol);
+ EXPECT_V3_NEAR(exp.color, got.color, tol);
+ EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol);
+ EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol);
+ EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol);
+ EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol);
+ EXPECT_NEAR(exp.ior, got.ior, tol);
+ EXPECT_NEAR(exp.alpha, got.alpha, tol);
+ EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol);
+ EXPECT_EQ(exp.illum_mode, got.illum_mode);
+ EXPECT_NEAR(exp.roughness, got.roughness, tol);
+ EXPECT_NEAR(exp.metallic, got.metallic, tol);
+ EXPECT_NEAR(exp.sheen, got.sheen, tol);
+ EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol);
+ EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol);
+ EXPECT_NEAR(exp.aniso, got.aniso, tol);
+ EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol);
+ for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
+ const MTLTexMap &exp_tex = exp.texture_maps[key];
+ const MTLTexMap &got_tex = got.texture_maps[key];
EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str());
EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol);
EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol);
@@ -101,20 +110,20 @@ TEST_F(obj_mtl_parser_test, string_newlines_whitespace)
"map_Ks sometex_s_spaces_after_name.png \t \r\n";
MTLMaterial mat[6];
mat[0].name = "simple";
- mat[0].Ka = {0.1f, 0.2f, 0.3f};
- mat[0].illum = 4;
+ mat[0].ambient_color = {0.1f, 0.2f, 0.3f};
+ mat[0].illum_mode = 4;
mat[1].name = "tab_indentation";
- mat[1].Kd = {0.2f, 0.3f, 0.4f};
+ mat[1].color = {0.2f, 0.3f, 0.4f};
mat[2].name = "space_after_name";
- mat[2].Ks = {0.4f, 0.5f, 0.6f};
+ mat[2].spec_color = {0.4f, 0.5f, 0.6f};
mat[3].name = "space_before_name";
mat[4].name = "indented_values";
- mat[4].Ka = {0.5f, 0.6f, 0.7f};
- mat[4].Kd = {0.6f, 0.7f, 0.8f};
+ mat[4].ambient_color = {0.5f, 0.6f, 0.7f};
+ mat[4].color = {0.6f, 0.7f, 0.8f};
mat[5].name = "crlf_ending";
- mat[5].Ns = 5.0f;
- mat[5].tex_map_of_type(eMTLSyntaxElement::map_Kd).image_path = "sometex_d.png";
- mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ks).image_path = "sometex_s_spaces_after_name.png";
+ mat[5].spec_exponent = 5.0f;
+ mat[5].tex_map_of_type(MTLTexMapType::Color).image_path = "sometex_d.png";
+ mat[5].tex_map_of_type(MTLTexMapType::Specular).image_path = "sometex_s_spaces_after_name.png";
check_string(text, mat, ARRAY_SIZE(mat));
}
@@ -122,8 +131,8 @@ TEST_F(obj_mtl_parser_test, cube)
{
MTLMaterial mat;
mat.name = "red";
- mat.Ka = {0.2f, 0.2f, 0.2f};
- mat.Kd = {1, 0, 0};
+ mat.ambient_color = {0.2f, 0.2f, 0.2f};
+ mat.color = {1, 0, 0};
check("cube.mtl", &mat, 1);
}
@@ -131,28 +140,28 @@ TEST_F(obj_mtl_parser_test, all_objects)
{
MTLMaterial mat[7];
for (auto &m : mat) {
- m.Ka = {1, 1, 1};
- m.Ks = {0.5f, 0.5f, 0.5f};
- m.Ke = {0, 0, 0};
- m.Ns = 250;
- m.Ni = 1;
- m.d = 1;
- m.illum = 2;
+ m.ambient_color = {1, 1, 1};
+ m.spec_color = {0.5f, 0.5f, 0.5f};
+ m.emission_color = {0, 0, 0};
+ m.spec_exponent = 250;
+ m.ior = 1;
+ m.alpha = 1;
+ m.illum_mode = 2;
}
mat[0].name = "Blue";
- mat[0].Kd = {0, 0, 1};
+ mat[0].color = {0, 0, 1};
mat[1].name = "BlueDark";
- mat[1].Kd = {0, 0, 0.5f};
+ mat[1].color = {0, 0, 0.5f};
mat[2].name = "Green";
- mat[2].Kd = {0, 1, 0};
+ mat[2].color = {0, 1, 0};
mat[3].name = "GreenDark";
- mat[3].Kd = {0, 0.5f, 0};
+ mat[3].color = {0, 0.5f, 0};
mat[4].name = "Material";
- mat[4].Kd = {0.8f, 0.8f, 0.8f};
+ mat[4].color = {0.8f, 0.8f, 0.8f};
mat[5].name = "Red";
- mat[5].Kd = {1, 0, 0};
+ mat[5].color = {1, 0, 0};
mat[6].name = "RedDark";
- mat[6].Kd = {0.5f, 0, 0};
+ mat[6].color = {0.5f, 0, 0};
check("all_objects.mtl", mat, ARRAY_SIZE(mat));
}
@@ -160,92 +169,101 @@ TEST_F(obj_mtl_parser_test, materials)
{
MTLMaterial mat[6];
mat[0].name = "no_textures_red";
- mat[0].Ka = {0.3f, 0.3f, 0.3f};
- mat[0].Kd = {0.8f, 0.3f, 0.1f};
- mat[0].Ns = 5.624998f;
+ mat[0].ambient_color = {0.3f, 0.3f, 0.3f};
+ mat[0].color = {0.8f, 0.3f, 0.1f};
+ mat[0].spec_exponent = 5.624998f;
mat[1].name = "four_maps";
- mat[1].Ka = {1, 1, 1};
- mat[1].Kd = {0.8f, 0.8f, 0.8f};
- mat[1].Ks = {0.5f, 0.5f, 0.5f};
- mat[1].Ke = {0, 0, 0};
- mat[1].Ns = 1000;
- mat[1].Ni = 1.45f;
- mat[1].d = 1;
- mat[1].illum = 2;
- mat[1].map_Bump_strength = 1;
+ mat[1].ambient_color = {1, 1, 1};
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].emission_color = {0, 0, 0};
+ mat[1].spec_exponent = 1000;
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ mat[1].normal_strength = 1;
{
- tex_map_XX &kd = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[1].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "texture.png";
- tex_map_XX &ns = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "sometexture_Roughness.png";
- tex_map_XX &refl = mat[1].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[1].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "sometexture_Metallic.png";
- tex_map_XX &bump = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[1].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "sometexture_Normal.png";
}
mat[2].name = "Clay";
- mat[2].Ka = {1, 1, 1};
- mat[2].Kd = {0.8f, 0.682657f, 0.536371f};
- mat[2].Ks = {0.5f, 0.5f, 0.5f};
- mat[2].Ke = {0, 0, 0};
- mat[2].Ns = 440.924042f;
- mat[2].Ni = 1.45f;
- mat[2].d = 1;
- mat[2].illum = 2;
+ mat[2].ambient_color = {1, 1, 1};
+ mat[2].color = {0.8f, 0.682657f, 0.536371f};
+ mat[2].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[2].emission_color = {0, 0, 0};
+ mat[2].spec_exponent = 440.924042f;
+ mat[2].ior = 1.45f;
+ mat[2].alpha = 1;
+ mat[2].illum_mode = 2;
mat[3].name = "Hat";
- mat[3].Ka = {1, 1, 1};
- mat[3].Kd = {0.8f, 0.8f, 0.8f};
- mat[3].Ks = {0.5f, 0.5f, 0.5f};
- mat[3].Ns = 800;
- mat[3].map_Bump_strength = 0.5f;
+ mat[3].ambient_color = {1, 1, 1};
+ mat[3].color = {0.8f, 0.8f, 0.8f};
+ mat[3].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[3].spec_exponent = 800;
+ mat[3].normal_strength = 0.5f;
{
- tex_map_XX &kd = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[3].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "someHatTexture_BaseColor.jpg";
- tex_map_XX &ns = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[3].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "someHatTexture_Roughness.jpg";
- tex_map_XX &refl = mat[3].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[3].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "someHatTexture_Metalness.jpg";
- tex_map_XX &bump = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[3].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "someHatTexture_Normal.jpg";
}
mat[4].name = "Parser_Test";
- mat[4].Ka = {0.1f, 0.2f, 0.3f};
- mat[4].Kd = {0.4f, 0.5f, 0.6f};
- mat[4].Ks = {0.7f, 0.8f, 0.9f};
- mat[4].illum = 6;
- mat[4].Ns = 15.5;
- mat[4].Ni = 1.5;
- mat[4].d = 0.5;
- mat[4].map_Bump_strength = 0.1f;
+ mat[4].ambient_color = {0.1f, 0.2f, 0.3f};
+ mat[4].color = {0.4f, 0.5f, 0.6f};
+ mat[4].spec_color = {0.7f, 0.8f, 0.9f};
+ mat[4].illum_mode = 6;
+ mat[4].spec_exponent = 15.5;
+ mat[4].ior = 1.5;
+ mat[4].alpha = 0.5;
+ mat[4].normal_strength = 0.1f;
+ mat[4].transmit_color = {0.1f, 0.3f, 0.5f};
+ mat[4].normal_strength = 0.1f;
+ mat[4].roughness = 0.2f;
+ mat[4].metallic = 0.3f;
+ mat[4].sheen = 0.4f;
+ mat[4].cc_thickness = 0.5f;
+ mat[4].cc_roughness = 0.6f;
+ mat[4].aniso = 0.7f;
+ mat[4].aniso_rot = 0.8f;
{
- tex_map_XX &kd = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "sometex_d.png";
- tex_map_XX &ns = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[4].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "sometex_ns.psd";
- tex_map_XX &refl = mat[4].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[4].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "clouds.tiff";
refl.scale = {1.5f, 2.5f, 3.5f};
refl.translation = {4.5f, 5.5f, 6.5f};
refl.projection_type = SHD_PROJ_SPHERE;
- tex_map_XX &bump = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[4].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "somebump.tga";
bump.scale = {3, 4, 5};
}
mat[5].name = "Parser_ScaleOffset_Test";
{
- tex_map_XX &kd = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[5].tex_map_of_type(MTLTexMapType::Color);
kd.translation = {2.5f, 0.0f, 0.0f};
kd.image_path = "OffsetOneValue.png";
- tex_map_XX &ks = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ks);
+ MTLTexMap &ks = mat[5].tex_map_of_type(MTLTexMapType::Specular);
ks.scale = {1.5f, 2.5f, 1.0f};
ks.translation = {3.5f, 4.5f, 0.0f};
ks.image_path = "ScaleOffsetBothTwovalues.png";
- tex_map_XX &ns = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[5].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.scale = {0.5f, 1.0f, 1.0f};
ns.image_path = "1.Value.png";
}
@@ -253,4 +271,75 @@ TEST_F(obj_mtl_parser_test, materials)
check("materials.mtl", mat, ARRAY_SIZE(mat));
}
+TEST_F(obj_mtl_parser_test, materials_without_pbr)
+{
+ MTLMaterial mat[2];
+ mat[0].name = "Mat1";
+ mat[0].spec_exponent = 360.0f;
+ mat[0].ambient_color = {0.9f, 0.9f, 0.9f};
+ mat[0].color = {0.8f, 0.276449f, 0.101911f};
+ mat[0].spec_color = {0.25f, 0.25f, 0.25f};
+ mat[0].emission_color = {0, 0, 0};
+ mat[0].ior = 1.45f;
+ mat[0].alpha = 1;
+ mat[0].illum_mode = 3;
+
+ mat[1].name = "Mat2";
+ mat[1].ambient_color = {1, 1, 1};
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ {
+ MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent);
+ ns.image_path = "../blend_geometry/texture_roughness.png";
+ MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
+ ke.image_path = "../blend_geometry/texture_illum.png";
+ }
+
+ check("materials_without_pbr.mtl", mat, ARRAY_SIZE(mat));
+}
+
+TEST_F(obj_mtl_parser_test, materials_pbr)
+{
+ MTLMaterial mat[2];
+ mat[0].name = "Mat1";
+ mat[0].color = {0.8f, 0.276449f, 0.101911f};
+ mat[0].spec_color = {0.25f, 0.25f, 0.25f};
+ mat[0].emission_color = {0, 0, 0};
+ mat[0].ior = 1.45f;
+ mat[0].alpha = 1;
+ mat[0].illum_mode = 3;
+ mat[0].roughness = 0.4f;
+ mat[0].metallic = 0.9f;
+ mat[0].sheen = 0.3f;
+ mat[0].cc_thickness = 0.393182f;
+ mat[0].cc_roughness = 0.05f;
+ mat[0].aniso = 0.2f;
+ mat[0].aniso_rot = 0.0f;
+
+ mat[1].name = "Mat2";
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ mat[1].metallic = 0.0f;
+ mat[1].cc_thickness = 0.3f;
+ mat[1].cc_roughness = 0.4f;
+ mat[1].aniso = 0.8f;
+ mat[1].aniso_rot = 0.7f;
+ {
+ MTLTexMap &pr = mat[1].tex_map_of_type(MTLTexMapType::Roughness);
+ pr.image_path = "../blend_geometry/texture_roughness.png";
+ MTLTexMap &ps = mat[1].tex_map_of_type(MTLTexMapType::Sheen);
+ ps.image_path = "../blend_geometry/texture_checker.png";
+ MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
+ ke.image_path = "../blend_geometry/texture_illum.png";
+ }
+
+ check("materials_pbr.mtl", mat, ARRAY_SIZE(mat));
+}
+
} // namespace blender::io::obj