Cleanup: obj: simplify import/export syntax handling code
I want to add support for PBR materials extension to OBJ, but the way current I/O code syntax handling was done made it quite cumbersome to extend the number of MTL textures/parameters. Simplify all that by removing FormatHandler template on "syntax" that gets routed through keyword enums, and instead just have simple `write_obj_*` and `write_mtl_*` functions. Simplify MTLMaterial to not contain a map of textures (that is always fully filled with all possible textures), instead now there's a simple array. Rename `tex_map_XX` to `MTLTexMap`. All this does not affect behavior or performance, but it does result in 170 fewer lines of code, and saves a couple kilobytes of executable size.
This commit is contained in:
parent
caa352bc45
commit
68f234b8ab
|
@ -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,8 +172,8 @@ 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_);
|
||||
}
|
||||
|
||||
|
@ -184,18 +182,17 @@ static void spaces_to_underscores(std::string &r_name)
|
|||
std::replace(r_name.begin(), r_name.end(), ' ', '_');
|
||||
}
|
||||
|
||||
void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh,
|
||||
const OBJMesh &obj_mesh_data) const
|
||||
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) {
|
||||
std::string mesh_name = obj_mesh_data.get_object_mesh_name();
|
||||
spaces_to_underscores(mesh_name);
|
||||
fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + 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
|
||||
|
@ -213,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;
|
||||
|
@ -231,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;
|
||||
|
@ -248,7 +243,7 @@ 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
|
||||
{
|
||||
|
@ -265,41 +260,40 @@ void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
|
|||
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]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -334,7 +328,7 @@ static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams ¶ms,
|
|||
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)
|
||||
|
@ -346,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. */
|
||||
|
@ -362,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,9 +369,8 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,7 +380,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
|
|||
const int16_t mat = obj_mesh_data.ith_poly_matnr(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);
|
||||
|
@ -397,9 +390,9 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
|
|||
if (export_params_.export_material_groups) {
|
||||
std::string object_name = obj_mesh_data.get_object_name();
|
||||
spaces_to_underscores(object_name);
|
||||
fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name);
|
||||
fh.write_obj_group(object_name + "_" + mat_name);
|
||||
}
|
||||
buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name);
|
||||
buf.write_obj_usemtl(mat_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +407,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
|
||||
{
|
||||
|
@ -426,13 +419,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++) {
|
||||
|
@ -440,15 +432,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.
|
||||
|
@ -457,13 +448,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
|
||||
|
@ -474,7 +465,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) {
|
||||
|
@ -485,11 +476,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,6 +487,18 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
|
|||
/** \name .MTL writers.
|
||||
* \{ */
|
||||
|
||||
static const char *tex_map_type_to_string[] = {
|
||||
"map_Kd",
|
||||
"map_Ks",
|
||||
"map_Ns",
|
||||
"map_d",
|
||||
"map_refl",
|
||||
"map_Ke",
|
||||
"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.
|
||||
|
@ -537,9 +539,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
|
||||
|
@ -552,67 +554,52 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl)
|
|||
/* 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. */
|
||||
if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ns).is_valid()) {
|
||||
fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl.Ns);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Ns).is_valid()) {
|
||||
fmt_handler_.write_mtl_float("Ns", mtl.Ns);
|
||||
}
|
||||
fmt_handler_.write<eMTLSyntaxElement::Ka>(mtl.Ka.x, mtl.Ka.y, mtl.Ka.z);
|
||||
if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Kd).is_valid()) {
|
||||
fmt_handler_.write<eMTLSyntaxElement::Kd>(mtl.Kd.x, mtl.Kd.y, mtl.Kd.z);
|
||||
fmt_handler_.write_mtl_float3("Ka", mtl.Ka.x, mtl.Ka.y, mtl.Ka.z);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Kd).is_valid()) {
|
||||
fmt_handler_.write_mtl_float3("Kd", mtl.Kd.x, mtl.Kd.y, mtl.Kd.z);
|
||||
}
|
||||
if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ks).is_valid()) {
|
||||
fmt_handler_.write<eMTLSyntaxElement::Ks>(mtl.Ks.x, mtl.Ks.y, mtl.Ks.z);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Ks).is_valid()) {
|
||||
fmt_handler_.write_mtl_float3("Ks", mtl.Ks.x, mtl.Ks.y, mtl.Ks.z);
|
||||
}
|
||||
if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) {
|
||||
fmt_handler_.write<eMTLSyntaxElement::Ke>(mtl.Ke.x, mtl.Ke.y, mtl.Ke.z);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Ke).is_valid()) {
|
||||
fmt_handler_.write_mtl_float3("Ke", mtl.Ke.x, mtl.Ke.y, mtl.Ke.z);
|
||||
}
|
||||
fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl.Ni);
|
||||
if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_d).is_valid()) {
|
||||
fmt_handler_.write<eMTLSyntaxElement::d>(mtl.d);
|
||||
fmt_handler_.write_mtl_float("Ni", mtl.Ni);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::d).is_valid()) {
|
||||
fmt_handler_.write_mtl_float("d", mtl.d);
|
||||
}
|
||||
fmt_handler_.write<eMTLSyntaxElement::illum>(mtl.illum);
|
||||
fmt_handler_.write_mtl_illum(mtl.illum);
|
||||
}
|
||||
|
||||
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>> ©_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>> ©_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) {
|
||||
if (texture_key == MTLTexMapType::bump && mtl_material.map_Bump_strength > 0.0001f) {
|
||||
options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
|
||||
}
|
||||
|
||||
std::string path = path_reference(
|
||||
texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set);
|
||||
texture_map.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set);
|
||||
/* Always emit forward slashes for cross-platform compatibility. */
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
|
||||
#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
|
||||
if (texture_map.key == eMTLSyntaxElement) { \
|
||||
fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \
|
||||
return; \
|
||||
}
|
||||
|
||||
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);
|
||||
#undef SYNTAX_DISPATCH
|
||||
|
||||
BLI_assert(!"This map type was not written to the file.");
|
||||
fmt_handler_.write_mtl_map(tex_map_type_to_string[(int)texture_key], options, path);
|
||||
}
|
||||
|
||||
void MTLWriter::write_materials(const char *blen_filepath,
|
||||
|
@ -633,14 +620,16 @@ 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);
|
||||
fmt_handler_.write_string("");
|
||||
fmt_handler_.write_mtl_newmtl(mtlmat.name);
|
||||
write_bsdf_properties(mtlmat);
|
||||
for (const auto &tex : mtlmat.texture_maps.items()) {
|
||||
if (!tex.value.is_valid()) {
|
||||
for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
|
||||
const MTLTexMap &tex = mtlmat.texture_maps[key];
|
||||
if (!tex.is_valid()) {
|
||||
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);
|
||||
|
|
|
@ -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_;
|
||||
|
@ -208,7 +208,8 @@ class MTLWriter : NonMovable, NonCopyable {
|
|||
* 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,
|
||||
|
|
|
@ -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)
|
||||
void write_obj_vertex(float x, float y, float z)
|
||||
{
|
||||
/* 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)...);
|
||||
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_));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,18 @@
|
|||
|
||||
namespace blender::io::obj {
|
||||
|
||||
const char *tex_map_type_to_socket_id[] = {
|
||||
"Base Color",
|
||||
"Specular",
|
||||
"Roughness",
|
||||
"Alpha",
|
||||
"Metallic",
|
||||
"Emission",
|
||||
"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.
|
||||
*/
|
||||
|
@ -73,7 +85,7 @@ static void copy_property_from_node(const eNodeSocketDatatype property_type,
|
|||
*/
|
||||
static void linked_sockets_to_dest_id(const bNode *dest_node,
|
||||
const nodes::NodeTreeRef &node_tree,
|
||||
StringRefNull dest_socket_id,
|
||||
const char *dest_socket_id,
|
||||
Vector<const nodes::OutputSocketRef *> &r_linked_sockets)
|
||||
{
|
||||
r_linked_sockets.clear();
|
||||
|
@ -84,7 +96,7 @@ static void linked_sockets_to_dest_id(const bNode *dest_node,
|
|||
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())) {
|
||||
if (STREQ(curr_socket->bsocket()->identifier, dest_socket_id)) {
|
||||
dest_socket = curr_socket;
|
||||
break;
|
||||
}
|
||||
|
@ -269,12 +281,12 @@ static void store_image_textures(const nodes::NodeRef *bsdf_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()) {
|
||||
for (int key = 0; key < (int)MTLTexMapType::Count; ++key) {
|
||||
MTLTexMap &value = r_mtl_mat.texture_maps[key];
|
||||
Vector<const nodes::OutputSocketRef *> linked_sockets;
|
||||
const bNode *normal_map_node{nullptr};
|
||||
|
||||
if (texture_map.key == eMTLSyntaxElement::map_Bump) {
|
||||
if (key == (int)MTLTexMapType::bump) {
|
||||
/* Find sockets linked to destination "Normal" socket in P-BSDF node. */
|
||||
linked_sockets_to_dest_id(bnode, *node_tree, "Normal", linked_sockets);
|
||||
/* Among the linked sockets, find Normal Map shader node. */
|
||||
|
@ -285,7 +297,7 @@ 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::Ke) {
|
||||
float emission_strength = 0.0f;
|
||||
copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
|
||||
if (emission_strength == 0.0f) {
|
||||
|
@ -293,8 +305,7 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
|
|||
}
|
||||
}
|
||||
/* 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);
|
||||
linked_sockets_to_dest_id(bnode, *node_tree, tex_map_type_to_socket_id[key], linked_sockets);
|
||||
}
|
||||
|
||||
/* Among the linked sockets, find Image Texture shader node. */
|
||||
|
@ -317,10 +328,10 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
|
|||
}
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,36 +6,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vec_types.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 {
|
||||
|
||||
/**
|
||||
* 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 { Kd = 0, Ks, Ns, d, refl, Ke, bump, 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;
|
||||
float3 translation{0.0f};
|
||||
float3 scale{1.0f};
|
||||
/* Only Flat and Sphere projections are supported. */
|
||||
|
@ -48,26 +35,13 @@ 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];
|
||||
}
|
||||
|
||||
const tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key) const
|
||||
MTLTexMap &tex_map_of_type(MTLTexMapType key)
|
||||
{
|
||||
BLI_assert(texture_maps.contains(key));
|
||||
return texture_maps.lookup(key);
|
||||
}
|
||||
tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key)
|
||||
{
|
||||
BLI_assert(texture_maps.contains(key));
|
||||
return texture_maps.lookup(key);
|
||||
return texture_maps[(int)key];
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
@ -81,7 +55,7 @@ struct MTLMaterial {
|
|||
float Ni{-1.0f};
|
||||
float d{-1.0f};
|
||||
int illum{-1};
|
||||
Map<const eMTLSyntaxElement, tex_map_XX> texture_maps;
|
||||
MTLTexMap texture_maps[(int)MTLTexMapType::Count];
|
||||
/** Only used for Normal Map node: "map_Bump". */
|
||||
float map_Bump_strength{-1.0f};
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -592,36 +592,31 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
|
|||
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::Kd;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Ks")) {
|
||||
return eMTLSyntaxElement::map_Ks;
|
||||
return MTLTexMapType::Ks;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Ns")) {
|
||||
return eMTLSyntaxElement::map_Ns;
|
||||
return MTLTexMapType::Ns;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_d")) {
|
||||
return eMTLSyntaxElement::map_d;
|
||||
return MTLTexMapType::d;
|
||||
}
|
||||
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::refl;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Ke")) {
|
||||
return eMTLSyntaxElement::map_Ke;
|
||||
return MTLTexMapType::Ke;
|
||||
}
|
||||
if (parse_keyword(p, end, "bump")) {
|
||||
return eMTLSyntaxElement::map_Bump;
|
||||
if (parse_keyword(p, end, "bump") || parse_keyword(p, end, "map_Bump") ||
|
||||
parse_keyword(p, end, "map_bump")) {
|
||||
return MTLTexMapType::bump;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) {
|
||||
return eMTLSyntaxElement::map_Bump;
|
||||
}
|
||||
return eMTLSyntaxElement::string;
|
||||
return MTLTexMapType::Count;
|
||||
}
|
||||
|
||||
static const std::pair<StringRef, int> unsupported_texture_options[] = {
|
||||
|
@ -639,7 +634,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")) {
|
||||
|
@ -693,13 +688,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. */
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include "NOD_shader.h"
|
||||
|
||||
/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
|
||||
#include "obj_export_io.hh"
|
||||
#include "obj_import_mtl.hh"
|
||||
#include "obj_import_string_utils.hh"
|
||||
|
||||
|
@ -29,12 +27,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: {
|
||||
|
@ -95,7 +93,7 @@ static Image *create_placeholder_image(Main *bmain, const std::string &path)
|
|||
return image;
|
||||
}
|
||||
|
||||
static Image *load_texture_image(Main *bmain, const tex_map_XX &tex_map, bool relative_paths)
|
||||
static Image *load_texture_image(Main *bmain, const MTLTexMap &tex_map, bool relative_paths)
|
||||
{
|
||||
Image *image = nullptr;
|
||||
|
||||
|
@ -206,15 +204,15 @@ std::pair<float, float> ShaderNodetreeWrap::set_node_locations(const int pos_x)
|
|||
}
|
||||
|
||||
void ShaderNodetreeWrap::link_sockets(bNode *from_node,
|
||||
StringRef from_node_id,
|
||||
const char *from_node_id,
|
||||
bNode *to_node,
|
||||
StringRef to_node_id,
|
||||
const char *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);
|
||||
}
|
||||
|
@ -338,7 +336,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
|
|||
if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) {
|
||||
set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_);
|
||||
}
|
||||
if (mtl_mat_.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) {
|
||||
if (mtl_mat_.tex_map_of_type(MTLTexMapType::Ke).is_valid()) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_);
|
||||
}
|
||||
set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_);
|
||||
|
@ -359,38 +357,36 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
|
|||
|
||||
void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat, bool relative_paths)
|
||||
{
|
||||
for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map :
|
||||
mtl_mat_.texture_maps.items()) {
|
||||
if (!texture_map.value.is_valid()) {
|
||||
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);
|
||||
BLI_assert(image_texture);
|
||||
Image *image = load_texture_image(bmain, texture_map.value, relative_paths);
|
||||
Image *image = load_texture_image(bmain, value, relative_paths);
|
||||
if (image == nullptr) {
|
||||
continue;
|
||||
}
|
||||
image_texture->id = &image->id;
|
||||
static_cast<NodeTexImage *>(image_texture->storage)->projection =
|
||||
texture_map.value.projection_type;
|
||||
static_cast<NodeTexImage *>(image_texture->storage)->projection = value.projection_type;
|
||||
|
||||
/* Add normal map node if needed. */
|
||||
bNode *normal_map = nullptr;
|
||||
if (texture_map.key == eMTLSyntaxElement::map_Bump) {
|
||||
if (key == (int)MTLTexMapType::bump) {
|
||||
normal_map = add_node_to_tree(SH_NODE_NORMAL_MAP);
|
||||
const float bump = std::max(0.0f, mtl_mat_.map_Bump_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)) {
|
||||
if (value.translation != float3(0, 0, 0) || 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);
|
||||
set_property_of_socket(SOCK_VECTOR, "Location", {value.translation, 3}, mapping);
|
||||
set_property_of_socket(SOCK_VECTOR, "Scale", {value.scale, 3}, mapping);
|
||||
|
||||
link_sockets(texture_coordinate, "UV", mapping, "Vector", 0);
|
||||
link_sockets(mapping, "Vector", image_texture, "Vector", 1);
|
||||
|
@ -400,12 +396,12 @@ void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat, bool rel
|
|||
link_sockets(image_texture, "Color", normal_map, "Color", 2);
|
||||
link_sockets(normal_map, "Normal", bsdf_, "Normal", 3);
|
||||
}
|
||||
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::d) {
|
||||
link_sockets(image_texture, "Alpha", bsdf_, tex_map_type_to_socket_id[key], 2);
|
||||
mat->blend_method = MA_BM_BLEND;
|
||||
}
|
||||
else {
|
||||
link_sockets(image_texture, "Color", bsdf_, texture_map.value.dest_socket_id, 2);
|
||||
link_sockets(image_texture, "Color", bsdf_, tex_map_type_to_socket_id[key], 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
@ -75,9 +74,9 @@ class ShaderNodetreeWrap {
|
|||
* \param from_node_pos_x: 0 to 4 value as per nodetree arrangement.
|
||||
*/
|
||||
void link_sockets(bNode *from_node,
|
||||
StringRef from_node_id,
|
||||
const char *from_node_id,
|
||||
bNode *to_node,
|
||||
StringRef to_node_id,
|
||||
const char *to_node_id,
|
||||
const int from_node_pos_x);
|
||||
/**
|
||||
* Set values of sockets in p-BSDF node of the nodetree.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -58,9 +58,9 @@ class obj_mtl_parser_test : public testing::Test {
|
|||
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);
|
||||
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);
|
||||
|
@ -113,8 +113,8 @@ TEST_F(obj_mtl_parser_test, string_newlines_whitespace)
|
|||
mat[4].Kd = {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].tex_map_of_type(MTLTexMapType::Kd).image_path = "sometex_d.png";
|
||||
mat[5].tex_map_of_type(MTLTexMapType::Ks).image_path = "sometex_s_spaces_after_name.png";
|
||||
check_string(text, mat, ARRAY_SIZE(mat));
|
||||
}
|
||||
|
||||
|
@ -175,13 +175,13 @@ TEST_F(obj_mtl_parser_test, materials)
|
|||
mat[1].illum = 2;
|
||||
mat[1].map_Bump_strength = 1;
|
||||
{
|
||||
tex_map_XX &kd = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Kd);
|
||||
MTLTexMap &kd = mat[1].tex_map_of_type(MTLTexMapType::Kd);
|
||||
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::Ns);
|
||||
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::refl);
|
||||
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::bump);
|
||||
bump.image_path = "sometexture_Normal.png";
|
||||
}
|
||||
|
||||
|
@ -202,13 +202,13 @@ TEST_F(obj_mtl_parser_test, materials)
|
|||
mat[3].Ns = 800;
|
||||
mat[3].map_Bump_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::Kd);
|
||||
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::Ns);
|
||||
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::refl);
|
||||
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::bump);
|
||||
bump.image_path = "someHatTexture_Normal.jpg";
|
||||
}
|
||||
|
||||
|
@ -222,30 +222,30 @@ TEST_F(obj_mtl_parser_test, materials)
|
|||
mat[4].d = 0.5;
|
||||
mat[4].map_Bump_strength = 0.1f;
|
||||
{
|
||||
tex_map_XX &kd = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Kd);
|
||||
MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Kd);
|
||||
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::Ns);
|
||||
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::refl);
|
||||
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::bump);
|
||||
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::Kd);
|
||||
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::Ks);
|
||||
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::Ns);
|
||||
ns.scale = {0.5f, 1.0f, 1.0f};
|
||||
ns.image_path = "1.Value.png";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue