Blender V4.3
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 "testing/testing.h"
12
13#include "obj_export_mtl.hh"
15
16namespace blender::io::obj {
17
18class OBJMTLParserTest : public testing::Test {
19 public:
20 void check_string(const char *text, const MTLMaterial *expect, size_t expect_count)
21 {
22 BKE_tempdir_init(nullptr);
23 std::string tmp_dir = BKE_tempdir_base();
24 std::string tmp_file_name = "mtl_test.mtl";
25 std::string tmp_file_path = tmp_dir + SEP_STR + tmp_file_name;
26 FILE *tmp_file = BLI_fopen(tmp_file_path.c_str(), "wb");
27 fputs(text, tmp_file);
28 fclose(tmp_file);
29
30 check_impl(tmp_file_name, tmp_dir, expect, expect_count);
31
32 BLI_delete(tmp_file_path.c_str(), false, false);
33 }
34 void check(const char *file, const MTLMaterial *expect, size_t expect_count)
35 {
36 std::string obj_dir = blender::tests::flags_test_asset_dir() +
37 (SEP_STR "io_tests" SEP_STR "obj" SEP_STR);
38 check_impl(file, obj_dir, expect, expect_count);
39 }
40 void check_impl(StringRefNull mtl_file_path,
41 StringRefNull file_dir,
42 const MTLMaterial *expect,
43 size_t expect_count)
44 {
45 MTLParser parser(mtl_file_path, file_dir + "dummy.obj");
47 parser.parse_and_store(materials);
48
49 for (int i = 0; i < expect_count; ++i) {
50 const MTLMaterial &exp = expect[i];
51 if (!materials.contains(exp.name)) {
52 fprintf(stderr, "Material '%s' was expected in parsed result\n", exp.name.c_str());
53 ADD_FAILURE();
54 continue;
55 }
56 const MTLMaterial &got = *materials.lookup(exp.name);
57 const float tol = 0.0001f;
58 EXPECT_V3_NEAR(exp.ambient_color, got.ambient_color, tol);
59 EXPECT_V3_NEAR(exp.color, got.color, tol);
60 EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol);
61 EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol);
62 EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol);
63 EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol);
64 EXPECT_NEAR(exp.ior, got.ior, tol);
65 EXPECT_NEAR(exp.alpha, got.alpha, tol);
66 EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol);
67 EXPECT_EQ(exp.illum_mode, got.illum_mode);
68 EXPECT_NEAR(exp.roughness, got.roughness, tol);
69 EXPECT_NEAR(exp.metallic, got.metallic, tol);
70 EXPECT_NEAR(exp.sheen, got.sheen, tol);
71 EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol);
72 EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol);
73 EXPECT_NEAR(exp.aniso, got.aniso, tol);
74 EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol);
75 for (int key = 0; key < int(MTLTexMapType::Count); key++) {
76 const MTLTexMap &exp_tex = exp.texture_maps[key];
77 const MTLTexMap &got_tex = got.texture_maps[key];
78 EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str());
79 EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol);
80 EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol);
82 }
83 }
84 EXPECT_EQ(materials.size(), expect_count);
85 }
86};
87
88TEST_F(OBJMTLParserTest, string_newlines_whitespace)
89{
90 const char *text =
91 "# a comment\n"
92 " # indented comment\n"
93 "# comment with CRLF line ending\r\n"
94 "\r\n"
95
96 "newmtl simple\n"
97 "Ka 0.1 0.2 0.3\n"
98 "illum 4\n"
99
100 "newmtl\ttab_indentation\n"
101 "Kd\t \t0.2 0.3\t0.4 \t \n"
102
103 "newmtl space_after_name \t \n"
104 "Ks 0.4 0.5 0.6\n"
105
106 "newmtl space_before_name\n"
107
108 "newmtl indented_values\n"
109 " Ka 0.5 0.6 0.7\n"
110 "\t\t\tKd 0.6 0.7 0.8\n"
111
112 "newmtl crlf_ending\r\n"
113 "Ns 5.0\r\n"
114 "map_Kd sometex_d.png\r\n"
115 "map_Ks sometex_s_spaces_after_name.png \t \r\n";
116 MTLMaterial mat[6];
117 mat[0].name = "simple";
118 mat[0].ambient_color = {0.1f, 0.2f, 0.3f};
119 mat[0].illum_mode = 4;
120 mat[1].name = "tab_indentation";
121 mat[1].color = {0.2f, 0.3f, 0.4f};
122 mat[2].name = "space_after_name";
123 mat[2].spec_color = {0.4f, 0.5f, 0.6f};
124 mat[3].name = "space_before_name";
125 mat[4].name = "indented_values";
126 mat[4].ambient_color = {0.5f, 0.6f, 0.7f};
127 mat[4].color = {0.6f, 0.7f, 0.8f};
128 mat[5].name = "crlf_ending";
129 mat[5].spec_exponent = 5.0f;
130 mat[5].tex_map_of_type(MTLTexMapType::Color).image_path = "sometex_d.png";
131 mat[5].tex_map_of_type(MTLTexMapType::Specular).image_path = "sometex_s_spaces_after_name.png";
132 check_string(text, mat, ARRAY_SIZE(mat));
133}
134
136{
137 MTLMaterial mat;
138 mat.name = "red";
139 mat.ambient_color = {0.2f, 0.2f, 0.2f};
140 mat.color = {1, 0, 0};
141 check("cube.mtl", &mat, 1);
142}
143
145{
146 MTLMaterial mat[7];
147 for (auto &m : mat) {
148 m.ambient_color = {1, 1, 1};
149 m.spec_color = {0.5f, 0.5f, 0.5f};
150 m.emission_color = {0, 0, 0};
151 m.spec_exponent = 250;
152 m.ior = 1;
153 m.alpha = 1;
154 m.illum_mode = 2;
155 }
156 mat[0].name = "Blue";
157 mat[0].color = {0, 0, 1};
158 mat[1].name = "BlueDark";
159 mat[1].color = {0, 0, 0.5f};
160 mat[2].name = "Green";
161 mat[2].color = {0, 1, 0};
162 mat[3].name = "GreenDark";
163 mat[3].color = {0, 0.5f, 0};
164 mat[4].name = "Material";
165 mat[4].color = {0.8f, 0.8f, 0.8f};
166 mat[5].name = "Red";
167 mat[5].color = {1, 0, 0};
168 mat[6].name = "RedDark";
169 mat[6].color = {0.5f, 0, 0};
170 check("all_objects.mtl", mat, ARRAY_SIZE(mat));
171}
172
174{
175 MTLMaterial mat[6];
176 mat[0].name = "no_textures_red";
177 mat[0].ambient_color = {0.3f, 0.3f, 0.3f};
178 mat[0].color = {0.8f, 0.3f, 0.1f};
179 mat[0].spec_exponent = 5.624998f;
180
181 mat[1].name = "four_maps";
182 mat[1].ambient_color = {1, 1, 1};
183 mat[1].color = {0.8f, 0.8f, 0.8f};
184 mat[1].spec_color = {0.5f, 0.5f, 0.5f};
185 mat[1].emission_color = {0, 0, 0};
186 mat[1].spec_exponent = 1000;
187 mat[1].ior = 1.45f;
188 mat[1].alpha = 1;
189 mat[1].illum_mode = 2;
190 mat[1].normal_strength = 1;
191 {
193 kd.image_path = "texture.png";
195 ns.image_path = "sometexture_Roughness.png";
197 refl.image_path = "sometexture_Metallic.png";
199 bump.image_path = "sometexture_Normal.png";
200 }
201
202 mat[2].name = "Clay";
203 mat[2].ambient_color = {1, 1, 1};
204 mat[2].color = {0.8f, 0.682657f, 0.536371f};
205 mat[2].spec_color = {0.5f, 0.5f, 0.5f};
206 mat[2].emission_color = {0, 0, 0};
207 mat[2].spec_exponent = 440.924042f;
208 mat[2].ior = 1.45f;
209 mat[2].alpha = 1;
210 mat[2].illum_mode = 2;
211
212 mat[3].name = "Hat";
213 mat[3].ambient_color = {1, 1, 1};
214 mat[3].color = {0.8f, 0.8f, 0.8f};
215 mat[3].spec_color = {0.5f, 0.5f, 0.5f};
216 mat[3].spec_exponent = 800;
217 mat[3].normal_strength = 0.5f;
218 {
220 kd.image_path = "someHatTexture_BaseColor.jpg";
222 ns.image_path = "someHatTexture_Roughness.jpg";
224 refl.image_path = "someHatTexture_Metalness.jpg";
226 bump.image_path = "someHatTexture_Normal.jpg";
227 }
228
229 mat[4].name = "Parser_Test";
230 mat[4].ambient_color = {0.1f, 0.2f, 0.3f};
231 mat[4].color = {0.4f, 0.5f, 0.6f};
232 mat[4].spec_color = {0.7f, 0.8f, 0.9f};
233 mat[4].illum_mode = 6;
234 mat[4].spec_exponent = 15.5;
235 mat[4].ior = 1.5;
236 mat[4].alpha = 0.5;
237 mat[4].normal_strength = 0.1f;
238 mat[4].transmit_color = {0.1f, 0.3f, 0.5f};
239 mat[4].normal_strength = 0.1f;
240 mat[4].roughness = 0.2f;
241 mat[4].metallic = 0.3f;
242 mat[4].sheen = 0.4f;
243 mat[4].cc_thickness = 0.5f;
244 mat[4].cc_roughness = 0.6f;
245 mat[4].aniso = 0.7f;
246 mat[4].aniso_rot = 0.8f;
247 {
249 kd.image_path = "sometex_d.png";
251 ns.image_path = "sometex_ns.psd";
253 refl.image_path = "clouds.tiff";
254 refl.scale = {1.5f, 2.5f, 3.5f};
255 refl.translation = {4.5f, 5.5f, 6.5f};
258 bump.image_path = "somebump.tga";
259 bump.scale = {3, 4, 5};
260 }
261
262 mat[5].name = "Parser_ScaleOffset_Test";
263 {
265 kd.translation = {2.5f, 0.0f, 0.0f};
266 kd.image_path = "OffsetOneValue.png";
268 ks.scale = {1.5f, 2.5f, 1.0f};
269 ks.translation = {3.5f, 4.5f, 0.0f};
270 ks.image_path = "ScaleOffsetBothTwovalues.png";
272 ns.scale = {0.5f, 1.0f, 1.0f};
273 ns.image_path = "1.Value.png";
274 }
275
276 check("materials.mtl", mat, ARRAY_SIZE(mat));
277}
278
279TEST_F(OBJMTLParserTest, materials_without_pbr)
280{
281 MTLMaterial mat[2];
282 mat[0].name = "Mat1";
283 mat[0].spec_exponent = 360.0f;
284 mat[0].ambient_color = {0.9f, 0.9f, 0.9f};
285 mat[0].color = {0.8f, 0.276449f, 0.101911f};
286 mat[0].spec_color = {0.25f, 0.25f, 0.25f};
287 mat[0].emission_color = {0, 0, 0};
288 mat[0].ior = 1.45f;
289 mat[0].alpha = 1;
290 mat[0].illum_mode = 3;
291
292 mat[1].name = "Mat2";
293 mat[1].ambient_color = {1, 1, 1};
294 mat[1].color = {0.8f, 0.8f, 0.8f};
295 mat[1].spec_color = {0.5f, 0.5f, 0.5f};
296 mat[1].ior = 1.45f;
297 mat[1].alpha = 1;
298 mat[1].illum_mode = 2;
299 {
301 ns.image_path = "../blend_geometry/texture_roughness.png";
303 ke.image_path = "../blend_geometry/texture_illum.png";
304 }
305
306 check("materials_without_pbr.mtl", mat, ARRAY_SIZE(mat));
307}
308
310{
311 MTLMaterial mat[2];
312 mat[0].name = "Mat1";
313 mat[0].color = {0.8f, 0.276449f, 0.101911f};
314 mat[0].spec_color = {0.25f, 0.25f, 0.25f};
315 mat[0].emission_color = {0, 0, 0};
316 mat[0].ior = 1.45f;
317 mat[0].alpha = 1;
318 mat[0].illum_mode = 3;
319 mat[0].roughness = 0.4f;
320 mat[0].metallic = 0.9f;
321 mat[0].sheen = 0.06f;
322 mat[0].cc_thickness = 0.393182f;
323 mat[0].cc_roughness = 0.05f;
324 mat[0].aniso = 0.2f;
325 mat[0].aniso_rot = 0.0f;
326
327 mat[1].name = "Mat2";
328 mat[1].color = {0.8f, 0.8f, 0.8f};
329 mat[1].spec_color = {0.5f, 0.5f, 0.5f};
330 mat[1].ior = 1.45f;
331 mat[1].alpha = 1;
332 mat[1].illum_mode = 2;
333 mat[1].metallic = 0.0f;
334 mat[1].cc_thickness = 0.3f;
335 mat[1].cc_roughness = 0.4f;
336 mat[1].aniso = 0.8f;
337 mat[1].aniso_rot = 0.7f;
338 {
340 pr.image_path = "../blend_geometry/texture_roughness.png";
342 ps.image_path = "../blend_geometry/texture_checker.png";
344 ke.image_path = "../blend_geometry/texture_illum.png";
345 }
346
347 check("materials_pbr.mtl", mat, ARRAY_SIZE(mat));
348}
349
350} // namespace blender::io::obj
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1190
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1211
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)
@ SHD_PROJ_SPHERE
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)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
ccl_device_inline float3 exp(float3 v)
TEST_F(OBJExportTest, filter_objects_curves_as_mesh)
const MTLTexMap & tex_map_of_type(MTLTexMapType key) const
MTLTexMap texture_maps[int(MTLTexMapType::Count)]
#define SEP_STR
Definition unit.cc:39