mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-30 12:21:05 +00:00
Keep OBJ Texture Coordinates That Carry a W Component
The vt parser stopped reading the optional third component when texture coordinates were cut down to u and v, but the check that nothing is left on the line stayed. A legal "vt u v w" line was therefore rejected and silently dropped, shifting every later texture index. The w component is parsed again and discarded. The texture coordinate stride is now a named constant, OBJ_TEXCOORD_LENGTH, used by the parser and the importer, so the relative-index rebase cannot drift from the storage layout again.
This commit is contained in:
@@ -168,11 +168,11 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
|||||||
if (data.textureCoordinates.size() > 0) {
|
if (data.textureCoordinates.size() > 0) {
|
||||||
// A face vertex may omit vt or reference a missing one. Fall back to (0, 0) rather than
|
// A face vertex may omit vt or reference a missing one. Fall back to (0, 0) rather than
|
||||||
// skipping the face, so obj_info.uvs stays aligned with the face indices.
|
// skipping the face, so obj_info.uvs stays aligned with the face indices.
|
||||||
const int uv_count = static_cast<int>(data.textureCoordinates.size() / 2);
|
const int uv_count = static_cast<int>(data.textureCoordinates.size() / OBJ_TEXCOORD_LENGTH);
|
||||||
auto uv_at = [&data, uv_count](int idx) -> Vec2f {
|
auto uv_at = [&data, uv_count](int idx) -> Vec2f {
|
||||||
if (idx < 0 || idx >= uv_count)
|
if (idx < 0 || idx >= uv_count)
|
||||||
return Vec2f::Zero();
|
return Vec2f::Zero();
|
||||||
return Vec2f(data.textureCoordinates[idx * 2], data.textureCoordinates[idx * 2 + 1]);
|
return Vec2f(data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH], data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH + 1]);
|
||||||
};
|
};
|
||||||
std::array<Vec2f, 3> uv_array{uv_at(uvs[0]), uv_at(uvs[1]), uv_at(uvs[2])};
|
std::array<Vec2f, 3> uv_array{uv_at(uvs[0]), uv_at(uvs[1]), uv_at(uvs[2])};
|
||||||
obj_info.uvs.emplace_back(uv_array);
|
obj_info.uvs.emplace_back(uv_array);
|
||||||
|
|||||||
@@ -51,19 +51,18 @@ static bool obj_parseline(const char *line, ObjData &data)
|
|||||||
line = endptr;
|
line = endptr;
|
||||||
EATWS();
|
EATWS();
|
||||||
}
|
}
|
||||||
/*double w = 0;
|
// The optional w is accepted but not stored: only u and v are used.
|
||||||
if (*line != 0) {
|
if (*line != 0) {
|
||||||
w = strtod(line, &endptr);
|
strtod(line, &endptr);
|
||||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||||
return false;
|
return false;
|
||||||
line = endptr;
|
line = endptr;
|
||||||
EATWS();
|
EATWS();
|
||||||
}*/
|
}
|
||||||
if (*line != 0)
|
if (*line != 0)
|
||||||
return false;
|
return false;
|
||||||
data.textureCoordinates.push_back((float)u);
|
data.textureCoordinates.push_back((float)u);
|
||||||
data.textureCoordinates.push_back((float)v);
|
data.textureCoordinates.push_back((float)v);
|
||||||
//data.textureCoordinates.push_back((float)w);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'n':
|
case 'n':
|
||||||
@@ -245,7 +244,7 @@ static bool obj_parseline(const char *line, ObjData &data)
|
|||||||
else
|
else
|
||||||
-- vertex.normalIdx;
|
-- vertex.normalIdx;
|
||||||
if (vertex.textureCoordIdx < 0)
|
if (vertex.textureCoordIdx < 0)
|
||||||
vertex.textureCoordIdx += (int)data.textureCoordinates.size() / 2;
|
vertex.textureCoordIdx += (int)data.textureCoordinates.size() / OBJ_TEXCOORD_LENGTH;
|
||||||
else
|
else
|
||||||
-- vertex.textureCoordIdx;
|
-- vertex.textureCoordIdx;
|
||||||
data.vertices.push_back(vertex);
|
data.vertices.push_back(vertex);
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2)
|
|||||||
}
|
}
|
||||||
#define OBJ_VERTEX_COLOR_ALPHA 6
|
#define OBJ_VERTEX_COLOR_ALPHA 6
|
||||||
#define OBJ_VERTEX_LENGTH 7 // x, y, z, color_x,color_y,color_z,color_w
|
#define OBJ_VERTEX_LENGTH 7 // x, y, z, color_x,color_y,color_z,color_w
|
||||||
|
#define OBJ_TEXCOORD_LENGTH 2 // u, v
|
||||||
#define ONE_FACE_SIZE 4//ONE_FACE format: f 8/4/6 7/3/6 6/2/6 -1/-1/-1
|
#define ONE_FACE_SIZE 4//ONE_FACE format: f 8/4/6 7/3/6 6/2/6 -1/-1/-1
|
||||||
struct ObjData {
|
struct ObjData {
|
||||||
// Version of the data structure for load / store in the private binary format.
|
// Version of the data structure for load / store in the private binary format.
|
||||||
@@ -100,7 +101,7 @@ struct ObjData {
|
|||||||
// x, y, z, color_x,color_y,color_z,color_w
|
// x, y, z, color_x,color_y,color_z,color_w
|
||||||
std::vector<float> coordinates;
|
std::vector<float> coordinates;
|
||||||
bool has_vertex_color{false};
|
bool has_vertex_color{false};
|
||||||
// u, v, w
|
// u, v
|
||||||
std::vector<float> textureCoordinates;
|
std::vector<float> textureCoordinates;
|
||||||
// x, y, z
|
// x, y, z
|
||||||
std::vector<float> normals;
|
std::vector<float> normals;
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ struct LoadedObj
|
|||||||
};
|
};
|
||||||
|
|
||||||
// A tetrahedron with a material and two texture coordinates, (0.25, 0.5) and (0.75, 1).
|
// A tetrahedron with a material and two texture coordinates, (0.25, 0.5) and (0.75, 1).
|
||||||
// Only the first face is varied; the other three reference vt 1.
|
// Only the first face and the vt lines are varied; the other three faces reference vt 1.
|
||||||
LoadedObj load_textured_tetrahedron(const std::string &first_face)
|
LoadedObj load_textured_tetrahedron(const std::string &first_face, const std::string &vts = "vt 0.25 0.5\nvt 0.75 1\n")
|
||||||
{
|
{
|
||||||
ScopedTemporaryFile obj(".obj");
|
ScopedTemporaryFile obj(".obj");
|
||||||
ScopedTemporaryFile mtl(".mtl");
|
ScopedTemporaryFile mtl(".mtl");
|
||||||
@@ -33,7 +33,7 @@ LoadedObj load_textured_tetrahedron(const std::string &first_face)
|
|||||||
boost::nowide::ofstream out(obj.string());
|
boost::nowide::ofstream out(obj.string());
|
||||||
out << "mtllib " << mtl.path().filename().string() << "\n"
|
out << "mtllib " << mtl.path().filename().string() << "\n"
|
||||||
<< "v 0 0 0\nv 10 0 0\nv 0 10 0\nv 0 0 10\n"
|
<< "v 0 0 0\nv 10 0 0\nv 0 10 0\nv 0 0 10\n"
|
||||||
<< "vt 0.25 0.5\nvt 0.75 1\n"
|
<< vts
|
||||||
<< "usemtl a\n"
|
<< "usemtl a\n"
|
||||||
<< first_face << "\n"
|
<< first_face << "\n"
|
||||||
<< "f 1/1 2/1 4/1\nf 1/1 4/1 3/1\nf 2/1 3/1 4/1\n";
|
<< "f 1/1 2/1 4/1\nf 1/1 4/1 3/1\nf 2/1 3/1 4/1\n";
|
||||||
@@ -90,3 +90,33 @@ TEST_CASE("A negative texture index counts back from the last texture coordinate
|
|||||||
CHECK_THAT(uv[1].x(), WithinAbs(0.75, 1e-6));
|
CHECK_THAT(uv[1].x(), WithinAbs(0.75, 1e-6));
|
||||||
CHECK_THAT(uv[1].y(), WithinAbs(1., 1e-6));
|
CHECK_THAT(uv[1].y(), WithinAbs(1., 1e-6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Texture coordinates with a w component are kept", "[OBJ][Regression]")
|
||||||
|
{
|
||||||
|
const LoadedObj loaded = load_textured_tetrahedron("f 1/1 3/2 2/2", "vt 0.25 0.5 0\nvt 0.75 1 0\n");
|
||||||
|
|
||||||
|
REQUIRE(loaded.ok);
|
||||||
|
CHECK(loaded.mesh.facets_count() == 4);
|
||||||
|
REQUIRE(loaded.info.uvs.size() == 4);
|
||||||
|
const std::array<Vec2f, 3> &uv = loaded.info.uvs.front();
|
||||||
|
CHECK_THAT(uv[0].x(), WithinAbs(0.25, 1e-6));
|
||||||
|
CHECK_THAT(uv[0].y(), WithinAbs(0.5, 1e-6));
|
||||||
|
CHECK_THAT(uv[1].x(), WithinAbs(0.75, 1e-6));
|
||||||
|
CHECK_THAT(uv[1].y(), WithinAbs(1., 1e-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A texture coordinate with w does not shift the indices of the ones after it", "[OBJ][Regression]")
|
||||||
|
{
|
||||||
|
// The w on the first vt used to drop that line, so vt 2 resolved to the third coordinate.
|
||||||
|
const LoadedObj loaded = load_textured_tetrahedron("f 1/2 3/3 2/-1", "vt 0.1 0.2 0\nvt 0.25 0.5\nvt 0.75 1\n");
|
||||||
|
|
||||||
|
REQUIRE(loaded.ok);
|
||||||
|
REQUIRE(loaded.info.uvs.size() == 4);
|
||||||
|
const std::array<Vec2f, 3> &uv = loaded.info.uvs.front();
|
||||||
|
CHECK_THAT(uv[0].x(), WithinAbs(0.25, 1e-6));
|
||||||
|
CHECK_THAT(uv[0].y(), WithinAbs(0.5, 1e-6));
|
||||||
|
CHECK_THAT(uv[1].x(), WithinAbs(0.75, 1e-6));
|
||||||
|
CHECK_THAT(uv[1].y(), WithinAbs(1., 1e-6));
|
||||||
|
CHECK_THAT(uv[2].x(), WithinAbs(0.75, 1e-6));
|
||||||
|
CHECK_THAT(uv[2].y(), WithinAbs(1., 1e-6));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user