ObjParser and small libvecn refactor

This commit is contained in:
Astazium 2026-08-05 23:06:16 +03:00
parent 7536ac2736
commit 859935f3b5
2 changed files with 90 additions and 93 deletions

View file

@ -10,53 +10,69 @@ class ObjParser : BasicParser<char> {
std::vector<glm::vec2> uvs {{0, 0}}; std::vector<glm::vec2> uvs {{0, 0}};
std::vector<glm::vec3> normals {{0, 1, 0}}; std::vector<glm::vec3> normals {{0, 1, 0}};
// TODO: refactor Vertex parseFaceVertex() {
void parseFace(Mesh& mesh) { auto parseIndex = [&]() -> uint {
std::vector<Vertex> vertices; if (!hasNext() || !is_digit(peekInLine())) {
while (hasNext()) { return 0;
auto c = peekInLine(); }
if (c == '\n') { return parseSimpleInt(10);
break; };
} else if (hasNext()) {
uint indices[3] {};
uint i = 0;
do {
char next = peekInLine();
if (is_digit(next)) {
indices[i] = parseSimpleInt(10);
if (hasNext() && peekInLine() == '/') {
pos++;
}
} else if (next == '/') {
pos++;
} else {
break;
}
} while (hasNext() && peekInLine() != '\n' && ++i < 3);
vertices.push_back(Vertex { uint coord = parseIndex();
coords[indices[0]], uvs[indices[1]], normals[indices[2]]}); uint uv = 0;
uint normal = 0;
if (hasNext() && peekInLine() == '/') {
pos++;
uv = parseIndex();
if (hasNext() && peekInLine() == '/') {
pos++;
normal = parseIndex();
} }
} }
if (hasNext() && peekInLine() != '\n') {
skipLine(); return Vertex {
} coords[coord],
uvs[uv],
normals[normal]
};
}
void triangulate(const std::vector<Vertex>& vertices, std::vector<Vertex>& output) {
if (vertices.size() >= 3) { if (vertices.size() >= 3) {
for (size_t j = 0; j < vertices.size() - 2; j++) { for (size_t j = 0; j < vertices.size() - 2; j++) {
mesh.vertices.push_back(vertices[0]); output.push_back(vertices[0]);
for (size_t i = 1; i < 3; i++) { for (size_t i = 1; i < 3; i++) {
mesh.vertices.push_back(vertices[i + j]); output.push_back(vertices[i + j]);
} }
} }
} }
} }
public:
ObjParser(const std::string_view file, const std::string_view src) void parseFace(Mesh& mesh) {
: BasicParser(file, src) { std::vector<Vertex> vertices;
while (hasNext() && peekInLine() != '\n') {
vertices.push_back(parseFaceVertex());
}
triangulate(vertices, mesh.vertices);
} }
std::unique_ptr<Model> parse() { glm::vec3 parseVec3() {
// first iteration - collecting vertex data float x = parseNumber().asNumber();
float y = parseNumber().asNumber();
float z = parseNumber().asNumber();
return glm::vec3 {x, y, z};
}
glm::vec2 parseVec2() {
float x = parseNumber().asNumber();
float y = parseNumber().asNumber();
return glm::vec2 {x, y};
}
void collectVertexData() {
while (hasNext()) { while (hasNext()) {
if (peek() == '#') { if (peek() == '#') {
skipLine(); skipLine();
@ -64,27 +80,18 @@ public:
} }
auto cmd = parseName(); auto cmd = parseName();
if (cmd == "v") { if (cmd == "v") {
float x = parseNumber().asNumber(); coords.emplace_back(parseVec3());
float y = parseNumber().asNumber();
float z = parseNumber().asNumber();
coords.emplace_back(x, y, z);
} else if (cmd == "vt") { } else if (cmd == "vt") {
float u = parseNumber().asNumber(); uvs.emplace_back(parseVec2());
float v = parseNumber().asNumber();
uvs.emplace_back(u, v);
} else if (cmd == "vn") { } else if (cmd == "vn") {
float x = parseNumber().asNumber(); normals.emplace_back(parseVec3());
float y = parseNumber().asNumber();
float z = parseNumber().asNumber();
normals.emplace_back(x, y, z);
} else { } else {
skipLine(); skipLine();
} }
} }
// second iteration - building meshes }
reset();
auto model = std::make_unique<Model>(); void buildMeshes(Model& model) {
std::string texture; std::string texture;
while (hasNext()) { while (hasNext()) {
if (peek() != '#' && parseName() == "usemtl") { if (peek() != '#' && parseName() == "usemtl") {
@ -95,7 +102,7 @@ public:
skipLine(); skipLine();
} }
do { do {
Mesh* mesh = &model->addMesh(texture); Mesh* mesh = &model.addMesh(texture);
while (hasNext()) { while (hasNext()) {
if (peek() == '#') { if (peek() == '#') {
skipLine(); skipLine();
@ -105,7 +112,7 @@ public:
if (cmd == "usemtl") { if (cmd == "usemtl") {
skipWhitespace(); skipWhitespace();
texture = readUntilEOL(); texture = readUntilEOL();
mesh = &model->addMesh(texture); mesh = &model.addMesh(texture);
break; break;
} else if (cmd == "f") { } else if (cmd == "f") {
parseFace(*mesh); parseFace(*mesh);
@ -113,6 +120,19 @@ public:
skipLine(); skipLine();
} }
} while (hasNext()); } while (hasNext());
}
public:
ObjParser(const std::string_view file, const std::string_view src)
: BasicParser(file, src) {
}
std::unique_ptr<Model> parse() {
// first iteration - collecting vertex data
collectVertexData();
// second iteration - building meshes
reset();
auto model = std::make_unique<Model>();
buildMeshes(*model);
model->clean(); model->clean();
return model; return model;
} }

View file

@ -211,59 +211,36 @@ static int l_tostring(lua::State* L) {
return lua::pushstring(L, ss.str()); return lua::pushstring(L, ss.str());
} }
#define VECLIB_BASE(dimension) \
{"add", lua::wrap<l_binop<dimension, std::plus>>}, \
{"sub", lua::wrap<l_binop<dimension, std::minus>>}, \
{"mul", lua::wrap<l_binop<dimension, std::multiplies>>}, \
{"div", lua::wrap<l_binop<dimension, std::divides>>}, \
{"distance", lua::wrap<l_distance<dimension>>}, \
{"normalize", lua::wrap<l_unaryop<dimension, glm::normalize>>}, \
{"length", lua::wrap<l_scalar_op<dimension, glm::length>>}, \
{"tostring", lua::wrap<l_tostring<dimension>>}, \
{"abs", lua::wrap<l_unaryop<dimension, glm::abs>>}, \
{"round", lua::wrap<l_unaryop<dimension, glm::round>>}, \
{"inverse", lua::wrap<l_inverse<dimension>>}, \
{"pow", lua::wrap<l_pow<dimension>>}, \
{"dot", lua::wrap<l_dot<dimension>>}, \
{"mix", lua::wrap<l_mix<dimension>>},
const luaL_Reg vec2lib[] = { const luaL_Reg vec2lib[] = {
{"add", lua::wrap<l_binop<2, std::plus>>}, VECLIB_BASE(2)
{"sub", lua::wrap<l_binop<2, std::minus>>},
{"mul", lua::wrap<l_binop<2, std::multiplies>>},
{"div", lua::wrap<l_binop<2, std::divides>>},
{"distance", lua::wrap<l_distance<2>>},
{"normalize", lua::wrap<l_unaryop<2, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<2, glm::length>>},
{"tostring", lua::wrap<l_tostring<2>>},
{"abs", lua::wrap<l_unaryop<2, glm::abs>>},
{"round", lua::wrap<l_unaryop<2, glm::round>>},
{"inverse", lua::wrap<l_inverse<2>>},
{"pow", lua::wrap<l_pow<2>>},
{"dot", lua::wrap<l_dot<2>>},
{"angle", lua::wrap<l_vec2_angle>}, {"angle", lua::wrap<l_vec2_angle>},
{"mix", lua::wrap<l_mix<2>>},
{"rotate", lua::wrap<l_vec2_rotate>}, {"rotate", lua::wrap<l_vec2_rotate>},
{nullptr, nullptr} {nullptr, nullptr}
}; };
const luaL_Reg vec3lib[] = { const luaL_Reg vec3lib[] = {
{"add", lua::wrap<l_binop<3, std::plus>>}, VECLIB_BASE(3)
{"sub", lua::wrap<l_binop<3, std::minus>>},
{"mul", lua::wrap<l_binop<3, std::multiplies>>},
{"div", lua::wrap<l_binop<3, std::divides>>},
{"distance", lua::wrap<l_distance<3>>},
{"normalize", lua::wrap<l_unaryop<3, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<3, glm::length>>},
{"tostring", lua::wrap<l_tostring<3>>},
{"abs", lua::wrap<l_unaryop<3, glm::abs>>},
{"round", lua::wrap<l_unaryop<3, glm::round>>},
{"inverse", lua::wrap<l_inverse<3>>},
{"pow", lua::wrap<l_pow<3>>},
{"dot", lua::wrap<l_dot<3>>},
{"spherical_rand", lua::wrap<l_spherical_rand>}, {"spherical_rand", lua::wrap<l_spherical_rand>},
{"mix", lua::wrap<l_mix<3>>},
{nullptr, nullptr} {nullptr, nullptr}
}; };
const luaL_Reg vec4lib[] = { const luaL_Reg vec4lib[] = {
{"add", lua::wrap<l_binop<4, std::plus>>}, VECLIB_BASE(4)
{"sub", lua::wrap<l_binop<4, std::minus>>},
{"mul", lua::wrap<l_binop<4, std::multiplies>>},
{"div", lua::wrap<l_binop<4, std::divides>>},
{"distance", lua::wrap<l_distance<4>>},
{"normalize", lua::wrap<l_unaryop<4, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<4, glm::length>>},
{"tostring", lua::wrap<l_tostring<4>>},
{"abs", lua::wrap<l_unaryop<4, glm::abs>>},
{"round", lua::wrap<l_unaryop<4, glm::round>>},
{"inverse", lua::wrap<l_inverse<4>>},
{"pow", lua::wrap<l_pow<4>>},
{"dot", lua::wrap<l_dot<4>>},
{"mix", lua::wrap<l_mix<4>>},
{nullptr, nullptr} {nullptr, nullptr}
}; };