316 lines
12 KiB
C++
316 lines
12 KiB
C++
#include <string>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "parse.hpp"
|
|
#include "scan.hpp"
|
|
|
|
namespace {
|
|
#define ASSERT_COORDINATE_3D(container, line, x, y, z) \
|
|
{ \
|
|
const auto &coord = (container).at(line); \
|
|
EXPECT_FLOAT_EQ(coord[0], (x)); \
|
|
EXPECT_FLOAT_EQ(coord[1], (y)); \
|
|
EXPECT_FLOAT_EQ(coord[2], (z)); \
|
|
}
|
|
|
|
#define ASSERT_COORDINATE_2D(container, line, x, y) \
|
|
{ \
|
|
const auto &coord = (container).at(line); \
|
|
EXPECT_FLOAT_EQ(coord[0], (x)); \
|
|
EXPECT_FLOAT_EQ(coord[1], (y)); \
|
|
}
|
|
|
|
TEST(ParseFaceData, BuildsTrianglesAndIndexSets) {
|
|
wavefront::scan_result scan_result;
|
|
scan_result.total_lines = 26;
|
|
scan_result.line_data = {
|
|
{17u, "f 1/1/1 2/2/2 3/3/2"},
|
|
{19u, "f 4/3/2 5/2/1 6/1/2"},
|
|
{21u, "f 7/2/2 8/3/2 9/1/1"},
|
|
{23u, "f 8/1/1 5/2/2 3/3/2"},
|
|
{25u, "f 2/1/1 6/2/2 4/3/2"}
|
|
};
|
|
scan_result.category_map = {
|
|
{"f", {17u, 19u, 21u, 23u, 25u}}
|
|
};
|
|
|
|
const auto face_data = wavefront::parse_face_data(scan_result);
|
|
|
|
EXPECT_EQ(face_data.triangle_list.size(), 5u);
|
|
|
|
EXPECT_EQ(face_data.index_position_set.size(), 9u);
|
|
EXPECT_TRUE(face_data.index_position_set.contains(1));
|
|
EXPECT_TRUE(face_data.index_position_set.contains(9));
|
|
|
|
EXPECT_EQ(face_data.index_normal_set.size(), 2u);
|
|
EXPECT_TRUE(face_data.index_normal_set.contains(1));
|
|
EXPECT_TRUE(face_data.index_normal_set.contains(2));
|
|
|
|
EXPECT_EQ(face_data.index_texcoord_set.size(), 3u);
|
|
EXPECT_TRUE(face_data.index_texcoord_set.contains(1));
|
|
EXPECT_TRUE(face_data.index_texcoord_set.contains(3));
|
|
|
|
const auto &first_triangle = face_data.triangle_list.at(0);
|
|
EXPECT_EQ(first_triangle[0].face_line_number, 17u);
|
|
EXPECT_EQ(first_triangle[0].position_index, 1);
|
|
EXPECT_EQ(first_triangle[0].texcoord_index, 1);
|
|
EXPECT_EQ(first_triangle[0].normal_index, 1);
|
|
}
|
|
|
|
TEST(ParseCoordinateData, MapsCoordinatesWithoutParsingFaces) {
|
|
wavefront::scan_result scan_result;
|
|
scan_result.total_lines = 26;
|
|
scan_result.line_data = {
|
|
{3u, "v 0.1 0.2 0.3"},
|
|
{4u, "v 0.2 0.3 0.4"},
|
|
{5u, "v 0.3 0.4 0.5"},
|
|
{6u, "v 1.1 1.2 1.3"},
|
|
{7u, "v 1.2 1.3 1.4"},
|
|
{8u, "v 1.3 1.4 1.5"},
|
|
{9u, "v 2.1 2.2 2.3"},
|
|
{10u, "v 2.2 2.3 2.4"},
|
|
{11u, "v 2.3 2.4 2.5"},
|
|
{12u, "vn 0.15 0.25 0.35"},
|
|
{13u, "vn 0.25 0.35 0.45"},
|
|
{14u, "vn 0.35 0.45 0.55"},
|
|
{15u, "vt 0.9 0.8"},
|
|
{16u, "vt 0.8 0.7"},
|
|
{17u, "f 1/1/1 2/2/2 3/3/2"},
|
|
{19u, "f 4/3/2 5/2/1 6/1/2"},
|
|
{21u, "f 7/2/2 8/3/2 9/1/1"},
|
|
{23u, "f 8/1/1 5/2/2 3/3/2"},
|
|
{25u, "f 2/1/1 6/2/2 4/3/2"}
|
|
};
|
|
scan_result.category_map = {
|
|
{"v", {0u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u}},
|
|
{"vn", {0u, 12u, 13u, 14u}},
|
|
{"vt", {0u, 15u, 16u}},
|
|
{"f", {17u, 19u, 21u, 23u, 25u}}
|
|
};
|
|
|
|
wavefront::wavefront_face_data_result_t face_data;
|
|
face_data.index_position_set = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
|
face_data.index_normal_set = {1, 2, 3};
|
|
face_data.index_texcoord_set = {1, 2};
|
|
face_data.triangle_list = {
|
|
{{{17u, 1, 1, 1}, {17u, 2, 2, 2}, {17u, 3, 3, 2}}},
|
|
{{{19u, 4, 3, 2}, {19u, 5, 2, 1}, {19u, 6, 1, 2}}},
|
|
{{{21u, 7, 2, 2}, {21u, 8, 3, 2}, {21u, 9, 1, 1}}},
|
|
{{{23u, 8, 1, 1}, {23u, 5, 2, 2}, {23u, 3, 3, 2}}},
|
|
{{{25u, 2, 1, 1}, {25u, 6, 2, 2}, {25u, 4, 3, 2}}}
|
|
};
|
|
|
|
const auto coordinate_data = wavefront::parse_coordinate_data<float>(scan_result, face_data);
|
|
|
|
ASSERT_EQ(coordinate_data.position_coordinates.size(), 9u);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 3u, 0.1f, 0.2f, 0.3f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 4u, 0.2f, 0.3f, 0.4f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 5u, 0.3f, 0.4f, 0.5f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 6u, 1.1f, 1.2f, 1.3f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 7u, 1.2f, 1.3f, 1.4f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 8u, 1.3f, 1.4f, 1.5f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 9u, 2.1f, 2.2f, 2.3f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 10u, 2.2f, 2.3f, 2.4f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.position_coordinates, 11u, 2.3f, 2.4f, 2.5f);
|
|
|
|
ASSERT_EQ(coordinate_data.normal_coordinates.size(), 3u);
|
|
ASSERT_COORDINATE_3D(coordinate_data.normal_coordinates, 12u, 0.15f, 0.25f, 0.35f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.normal_coordinates, 13u, 0.25f, 0.35f, 0.45f);
|
|
ASSERT_COORDINATE_3D(coordinate_data.normal_coordinates, 14u, 0.35f, 0.45f, 0.55f);
|
|
|
|
ASSERT_EQ(coordinate_data.texture_coordinates.index(), 2u);
|
|
ASSERT_EQ(std::get<2>(coordinate_data.texture_coordinates).size(), 2u);
|
|
ASSERT_COORDINATE_2D(std::get<2>(coordinate_data.texture_coordinates), 15u, 0.9f, 0.8f);
|
|
ASSERT_COORDINATE_2D(std::get<2>(coordinate_data.texture_coordinates), 16u, 0.8f, 0.7f);
|
|
}
|
|
|
|
struct InvalidNumberCase {
|
|
std::string line_type;
|
|
std::size_t invalid_index;
|
|
std::string expected_message;
|
|
};
|
|
|
|
class ParseCoordinateDataInvalidNumberTest
|
|
: public ::testing::TestWithParam<InvalidNumberCase> {
|
|
};
|
|
|
|
static std::string build_line_with_invalid_number(
|
|
const std::string &line_type,
|
|
std::size_t invalid_index
|
|
) {
|
|
std::vector<std::string> values;
|
|
if (line_type == "vt") {
|
|
values = {"0.9", "0.8", "0.7"};
|
|
} else {
|
|
values = {"0.1", "0.2", "0.3"};
|
|
}
|
|
if (invalid_index < values.size()) {
|
|
values[invalid_index] = "x";
|
|
}
|
|
std::string line = line_type;
|
|
for (const auto &value : values) {
|
|
line += " ";
|
|
line += value;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
TEST_P(ParseCoordinateDataInvalidNumberTest, ThrowsOnInvalidNumber) {
|
|
const auto ¶m = GetParam();
|
|
|
|
wavefront::scan_result scan_result;
|
|
scan_result.total_lines = 3;
|
|
scan_result.line_data = {
|
|
{1u, "v 0.1 0.2 0.3"},
|
|
{3u, build_line_with_invalid_number(param.line_type, param.invalid_index)}
|
|
};
|
|
|
|
if (param.line_type == "v") {
|
|
scan_result.category_map = {
|
|
{"v", {0u, 1u, 3u}}
|
|
};
|
|
} else {
|
|
scan_result.category_map = {
|
|
{"v", {0u, 1u}},
|
|
{param.line_type, {0u, 3u}}
|
|
};
|
|
}
|
|
|
|
wavefront::wavefront_face_data_result_t face_data;
|
|
if (param.line_type == "v") {
|
|
face_data.index_position_set = {2};
|
|
} else if (param.line_type == "vn") {
|
|
face_data.index_normal_set = {1};
|
|
} else {
|
|
face_data.index_texcoord_set = {1};
|
|
}
|
|
|
|
try {
|
|
(void)wavefront::parse_coordinate_data<float>(scan_result, face_data);
|
|
FAIL() << "Expected parse_error";
|
|
} catch (const wavefront::parse_error &ex) {
|
|
EXPECT_EQ(std::string(ex.what()), param.expected_message);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
ParseCoordinateData,
|
|
ParseCoordinateDataInvalidNumberTest,
|
|
::testing::Values(
|
|
InvalidNumberCase{"v", 0u, "[3]: Unable to parse \"v\" floating point value"},
|
|
InvalidNumberCase{"v", 1u, "[3]: Unable to parse \"v\" floating point value"},
|
|
InvalidNumberCase{"v", 2u, "[3]: Unable to parse \"v\" floating point value"},
|
|
InvalidNumberCase{"vn", 0u, "[3]: Unable to parse \"vn\" floating point value"},
|
|
InvalidNumberCase{"vn", 1u, "[3]: Unable to parse \"vn\" floating point value"},
|
|
InvalidNumberCase{"vn", 2u, "[3]: Unable to parse \"vn\" floating point value"},
|
|
InvalidNumberCase{"vt", 0u, "[3]: Unable to parse \"vt\" floating point value"},
|
|
InvalidNumberCase{"vt", 1u, "[3]: Unable to parse \"vt\" floating point value"},
|
|
InvalidNumberCase{"vt", 2u, "[3]: Unable to parse \"vt\" floating point value"}
|
|
)
|
|
);
|
|
|
|
TEST(ParseCoordinateData, ThrowsWhenMissingVertexData) {
|
|
wavefront::scan_result scan_result;
|
|
scan_result.total_lines = 3;
|
|
scan_result.line_data = {
|
|
{3u, "vn 0.1 0.2 0.3"}
|
|
};
|
|
scan_result.category_map = {
|
|
{"vn", {0u, 3u}}
|
|
};
|
|
|
|
wavefront::wavefront_face_data_result_t face_data;
|
|
face_data.index_position_set = {1};
|
|
|
|
try {
|
|
(void)wavefront::parse_coordinate_data<float>(scan_result, face_data);
|
|
FAIL() << "Expected parse_error";
|
|
} catch (const wavefront::parse_error &ex) {
|
|
EXPECT_EQ(std::string(ex.what()), "Missing required \"v\" data");
|
|
}
|
|
}
|
|
|
|
struct UnderflowCase {
|
|
std::string line_type;
|
|
std::size_t number_count;
|
|
std::string expected_message;
|
|
};
|
|
|
|
class ParseCoordinateDataUnderflowTest
|
|
: public ::testing::TestWithParam<UnderflowCase> {
|
|
};
|
|
|
|
static std::string build_line_with_count(
|
|
const std::string &line_type,
|
|
std::size_t number_count
|
|
) {
|
|
std::vector<std::string> values;
|
|
if (line_type == "vt") {
|
|
values = {"0.9", "0.8", "0.7"};
|
|
} else {
|
|
values = {"0.1", "0.2", "0.3"};
|
|
}
|
|
std::string line = line_type;
|
|
for (std::size_t i = 0; i < number_count && i < values.size(); ++i) {
|
|
line += " ";
|
|
line += values[i];
|
|
}
|
|
return line;
|
|
}
|
|
|
|
TEST_P(ParseCoordinateDataUnderflowTest, ThrowsOnInsufficientNumbers) {
|
|
const auto ¶m = GetParam();
|
|
|
|
wavefront::scan_result scan_result;
|
|
scan_result.total_lines = 3;
|
|
scan_result.line_data = {
|
|
{1u, "v 0.1 0.2 0.3"},
|
|
{3u, build_line_with_count(param.line_type, param.number_count)}
|
|
};
|
|
|
|
if (param.line_type == "v") {
|
|
scan_result.category_map = {
|
|
{"v", {0u, 1u, 3u}}
|
|
};
|
|
} else {
|
|
scan_result.category_map = {
|
|
{"v", {0u, 1u}},
|
|
{param.line_type, {0u, 3u}}
|
|
};
|
|
}
|
|
|
|
wavefront::wavefront_face_data_result_t face_data;
|
|
if (param.line_type == "v") {
|
|
face_data.index_position_set = {2};
|
|
} else if (param.line_type == "vn") {
|
|
face_data.index_normal_set = {1};
|
|
} else {
|
|
face_data.index_texcoord_set = {1};
|
|
}
|
|
|
|
try {
|
|
(void)wavefront::parse_coordinate_data<float>(scan_result, face_data);
|
|
FAIL() << "Expected parse_error";
|
|
} catch (const wavefront::parse_error &ex) {
|
|
EXPECT_EQ(std::string(ex.what()), param.expected_message);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
ParseCoordinateData,
|
|
ParseCoordinateDataUnderflowTest,
|
|
::testing::Values(
|
|
UnderflowCase{"v", 0u, "[3]: Line \"v\" must contain at least 3 numbers"},
|
|
UnderflowCase{"v", 1u, "[3]: Line \"v\" must contain at least 3 numbers"},
|
|
UnderflowCase{"v", 2u, "[3]: Line \"v\" must contain at least 3 numbers"},
|
|
UnderflowCase{"vn", 0u, "[3]: Line \"vn\" must contain exactly 3 numbers"},
|
|
UnderflowCase{"vn", 1u, "[3]: Line \"vn\" must contain exactly 3 numbers"},
|
|
UnderflowCase{"vn", 2u, "[3]: Line \"vn\" must contain exactly 3 numbers"},
|
|
UnderflowCase{"vt", 0u, "[3]: Unable to parse \"vt\" line: expected minimum one number"}
|
|
)
|
|
);
|
|
}
|