Blender V5.0
obj_mtl_parser_tests.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <gtest/gtest.h>
6
7#include "BLI_fileops.h"
8
9#include "BKE_appdir.hh"
10
11#include "CLG_log.h"
12
13#include "testing/testing.h"
14
15#include "obj_export_mtl.hh"
17
18namespace blender::io::obj {
19
20class OBJMTLParserTest : public testing::Test {
21 public:
22 static void SetUpTestCase()
23 {
24 CLG_init();
25 }
26 static void TearDownTestCase()
27 {
28 CLG_exit();
29 }
30 void check_string(const char *text, const MTLMaterial *expect, size_t expect_count)
31 {
32 BKE_tempdir_init(nullptr);
33 std::string tmp_dir = BKE_tempdir_base();
34 std::string tmp_file_name = "mtl_test.mtl";
35 std::string tmp_file_path = tmp_dir + SEP_STR + tmp_file_name;
36 FILE *tmp_file = BLI_fopen(tmp_file_path.c_str(), "wb");
37 fputs(text, tmp_file);
38 fclose(tmp_file);
39
40 check_impl(tmp_file_name, tmp_dir, expect, expect_count);
41
42 BLI_delete(tmp_file_path.c_str(), false, false);
43 }
44 void check(const char *file, const MTLMaterial *expect, size_t expect_count)
45 {
46 std::string obj_dir = blender::tests::flags_test_asset_dir() +
47 (SEP_STR "io_tests" SEP_STR "obj" SEP_STR);
48 check_impl(file, obj_dir, expect, expect_count);
49 }
50 void check_impl(StringRefNull mtl_file_path,
51 StringRefNull file_dir,
52 const MTLMaterial *expect,
53 size_t expect_count)
54 {
55 MTLParser parser(mtl_file_path, file_dir + "dummy.obj");
57 parser.parse_and_store(materials);
58
59 for (int i = 0; i < expect_count; ++i) {
60 const MTLMaterial &exp = expect[i];
61 if (!materials.contains(exp.name)) {
62 fprintf(stderr, "Material '%s' was expected in parsed result\n", exp.name.c_str());
63 ADD_FAILURE();
64 continue;
65 }
66 const MTLMaterial &got = *materials.lookup(exp.name);
67 const float tol = 0.0001f;
68 EXPECT_V3_NEAR(exp.ambient_color, got.ambient_color, tol);
69 EXPECT_V3_NEAR(exp.color, got.color, tol);
70 EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol);
71 EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol);
72 EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol);
73 EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol);
74 EXPECT_NEAR(exp.ior, got.ior, tol);
75 EXPECT_NEAR(exp.alpha, got.alpha, tol);
76 EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol);
77 EXPECT_EQ(exp.illum_mode, got.illum_mode);
78 EXPECT_NEAR(exp.roughness, got.roughness, tol);
79 EXPECT_NEAR(exp.metallic, got.metallic, tol);
80 EXPECT_NEAR(exp.sheen, got.sheen, tol);
81 EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol);
82 EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol);
83 EXPECT_NEAR(exp.aniso, got.aniso, tol);
84 EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol);
85 for (int key = 0; key < int(MTLTexMapType::Count); key++) {
86 const MTLTexMap &exp_tex = exp.texture_maps[key];
87 const MTLTexMap &got_tex = got.texture_maps[key];
88 EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str());
89 EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol);
90 EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol);
92 }
93 }
94 EXPECT_EQ(materials.size(), expect_count);
95 }
96};
97
98TEST_F(OBJMTLParserTest, string_newlines_whitespace)
99{
100 const char *text =
101 "# a comment\n"
102 " # indented comment\n"
103 "# comment with CRLF line ending\r\n"
104 "\r\n"
105
106 "newmtl simple\n"
107 "Ka 0.1 0.2 0.3\n"
108 "illum 4\n"
109
110 "newmtl\ttab_indentation\n"
111 "Kd\t \t0.2 0.3\t0.4 \t \n"
112
113 "newmtl space_after_name \t \n"
114 "Ks 0.4 0.5 0.6\n"
115
116 "newmtl space_before_name\n"
117
118 "newmtl indented_values\n"
119 " Ka 0.5 0.6 0.7\n"
120 "\t\t\tKd 0.6 0.7 0.8\n"
121
122 "newmtl crlf_ending\r\n"
123 "Ns 5.0\r\n"
124 "map_Kd sometex_d.png\r\n"
125 "map_Ks sometex_s_spaces_after_name.png \t \r\n";
126 MTLMaterial mat[6];
127 mat[0].name = "simple";
128 mat[0].ambient_color = {0.1f, 0.2f, 0.3f};
129 mat[0].illum_mode = 4;
130 mat[1].name = "tab_indentation";
131 mat[1].color = {0.2f, 0.3f, 0.4f};
132 mat[2].name = "space_after_name";
133 mat[2].spec_color = {0.4f, 0.5f, 0.6f};
134 mat[3].name = "space_before_name";
135 mat[4].name = "indented_values";
136 mat[4].ambient_color = {0.5f, 0.6f, 0.7f};
137 mat[4].color = {0.6f, 0.7f, 0.8f};
138 mat[5].name = "crlf_ending";
139 mat[5].spec_exponent = 5.0f;
140 mat[5].tex_map_of_type(MTLTexMapType::Color).image_path = "sometex_d.png";
141 mat[5].tex_map_of_type(MTLTexMapType::Specular).image_path = "sometex_s_spaces_after_name.png";
142 check_string(text, mat, ARRAY_SIZE(mat));
143}
144
146{
147 MTLMaterial mat[6];
148 mat[0].name = "no_textures_red";
149 mat[0].ambient_color = {0.3f, 0.3f, 0.3f};
150 mat[0].color = {0.8f, 0.3f, 0.1f};
151 mat[0].spec_exponent = 5.624998f;
152
153 mat[1].name = "four_maps";
154 mat[1].ambient_color = {1, 1, 1};
155 mat[1].color = {0.8f, 0.8f, 0.8f};
156 mat[1].spec_color = {0.5f, 0.5f, 0.5f};
157 mat[1].emission_color = {0, 0, 0};
158 mat[1].spec_exponent = 1000;
159 mat[1].ior = 1.45f;
160 mat[1].alpha = 1;
161 mat[1].illum_mode = 2;
162 mat[1].normal_strength = 1;
163 {
165 kd.image_path = "texture.png";
167 ns.image_path = "sometexture_Roughness.png";
169 refl.image_path = "sometexture_Metallic.png";
171 bump.image_path = "sometexture_Normal.png";
172 }
173
174 mat[2].name = "Clay";
175 mat[2].ambient_color = {1, 1, 1};
176 mat[2].color = {0.8f, 0.682657f, 0.536371f};
177 mat[2].spec_color = {0.5f, 0.5f, 0.5f};
178 mat[2].emission_color = {0, 0, 0};
179 mat[2].spec_exponent = 440.924042f;
180 mat[2].ior = 1.45f;
181 mat[2].alpha = 1;
182 mat[2].illum_mode = 2;
183
184 mat[3].name = "Hat";
185 mat[3].ambient_color = {1, 1, 1};
186 mat[3].color = {0.8f, 0.8f, 0.8f};
187 mat[3].spec_color = {0.5f, 0.5f, 0.5f};
188 mat[3].spec_exponent = 800;
189 mat[3].normal_strength = 0.5f;
190 {
192 kd.image_path = "someHatTexture_BaseColor.jpg";
194 ns.image_path = "someHatTexture_Roughness.jpg";
196 refl.image_path = "someHatTexture_Metalness.jpg";
198 bump.image_path = "someHatTexture_Normal.jpg";
199 }
200
201 mat[4].name = "Parser_Test";
202 mat[4].ambient_color = {0.1f, 0.2f, 0.3f};
203 mat[4].color = {0.4f, 0.5f, 0.6f};
204 mat[4].spec_color = {0.7f, 0.8f, 0.9f};
205 mat[4].illum_mode = 6;
206 mat[4].spec_exponent = 15.5;
207 mat[4].ior = 1.5;
208 mat[4].alpha = 0.5;
209 mat[4].normal_strength = 0.1f;
210 mat[4].transmit_color = {0.1f, 0.3f, 0.5f};
211 mat[4].normal_strength = 0.1f;
212 mat[4].roughness = 0.2f;
213 mat[4].metallic = 0.3f;
214 mat[4].sheen = 0.4f;
215 mat[4].cc_thickness = 0.5f;
216 mat[4].cc_roughness = 0.6f;
217 mat[4].aniso = 0.7f;
218 mat[4].aniso_rot = 0.8f;
219 {
221 kd.image_path = "sometex_d.png";
223 ns.image_path = "sometex_ns.psd";
225 refl.image_path = "clouds.tiff";
226 refl.scale = {1.5f, 2.5f, 3.5f};
227 refl.translation = {4.5f, 5.5f, 6.5f};
230 bump.image_path = "somebump.tga";
231 bump.scale = {3, 4, 5};
232 }
233
234 mat[5].name = "Parser_ScaleOffset_Test";
235 {
237 kd.translation = {2.5f, 0.0f, 0.0f};
238 kd.image_path = "OffsetOneValue.png";
240 ks.scale = {1.5f, 2.5f, 1.0f};
241 ks.translation = {3.5f, 4.5f, 0.0f};
242 ks.image_path = "ScaleOffsetBothTwovalues.png";
244 ns.scale = {0.5f, 1.0f, 1.0f};
245 ns.image_path = "1.Value.png";
246 }
247
248 check("materials.mtl", mat, ARRAY_SIZE(mat));
249}
250
252{
253 MTLMaterial mat[2];
254 mat[0].name = "Mat1";
255 mat[0].color = {0.8f, 0.276449f, 0.101911f};
256 mat[0].spec_color = {0.25f, 0.25f, 0.25f};
257 mat[0].emission_color = {0, 0, 0};
258 mat[0].ior = 1.45f;
259 mat[0].alpha = 1;
260 mat[0].illum_mode = 3;
261 mat[0].roughness = 0.4f;
262 mat[0].metallic = 0.9f;
263 mat[0].sheen = 0.06f;
264 mat[0].cc_thickness = 0.393182f;
265 mat[0].cc_roughness = 0.05f;
266 mat[0].aniso = 0.2f;
267 mat[0].aniso_rot = 0.0f;
268
269 mat[1].name = "Mat2";
270 mat[1].color = {0.8f, 0.8f, 0.8f};
271 mat[1].spec_color = {0.5f, 0.5f, 0.5f};
272 mat[1].ior = 1.45f;
273 mat[1].alpha = 1;
274 mat[1].illum_mode = 2;
275 mat[1].metallic = 0.0f;
276 mat[1].cc_thickness = 0.3f;
277 mat[1].cc_roughness = 0.4f;
278 mat[1].aniso = 0.8f;
279 mat[1].aniso_rot = 0.7f;
280 {
282 pr.image_path = "../blend_geometry/texture_roughness.png";
284 ps.image_path = "../blend_geometry/texture_checker.png";
286 ke.image_path = "../blend_geometry/texture_illum.png";
287 }
288
289 check("materials_pbr.mtl", mat, ARRAY_SIZE(mat));
290}
291
292} // namespace blender::io::obj
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1200
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1243
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
#define ARRAY_SIZE(arr)
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
@ SHD_PROJ_SPHERE
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
int64_t size() const
Definition BLI_map.hh:976
bool contains(const Key &key) const
Definition BLI_map.hh:353
void parse_and_store(Map< std::string, std::unique_ptr< MTLMaterial > > &r_materials)
void check_string(const char *text, const MTLMaterial *expect, size_t expect_count)
void check_impl(StringRefNull mtl_file_path, StringRefNull file_dir, const MTLMaterial *expect, size_t expect_count)
void check(const char *file, const MTLMaterial *expect, size_t expect_count)
#define exp
TEST_F(OBJExportTest, filter_objects_curves_as_mesh)
const MTLTexMap & tex_map_of_type(MTLTexMapType key) const
MTLTexMap texture_maps[int(MTLTexMapType::Count)]
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39