Skip to content

Commit

Permalink
Fix parsing [w] component in v line.
Browse files Browse the repository at this point in the history
Improve python binding.
  - Expose more attrib to Python.
  • Loading branch information
syoyo committed Jan 26, 2024
1 parent b2f0457 commit 7b3ba0b
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 78 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ endif()

if (TINYOBJLOADER_WITH_PYTHON)
# pybind11 method:
pybind11_add_module(${PY_TARGET} ${CMAKE_SOURCE_DIR}/python/bindings.cc )
pybind11_add_module(${PY_TARGET} ${CMAKE_SOURCE_DIR}/python/bindings.cc ${CMAKE_SOURCE_DIR}/python/tiny_obj_loader.cc)

add_sanitizers(${PY_TARGET})
set_target_properties(${PY_TARGET} PROPERTIES OUTPUT_NAME "tinyobjloader")
Expand Down
31 changes: 31 additions & 0 deletions models/cube-vertex-w-component.obj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mtllib cube.mtl

v 0.000000 2.000000 2.000000 0.1
v 0.000000 0.000000 2.000000 0.2
v 2.000000 0.000000 2.000000 0.3
v 2.000000 2.000000 2.000000 0.4
v 0.000000 2.000000 0.000000 0.5
v 0.000000 0.000000 0.000000 0.6
v 2.000000 0.000000 0.000000 0.7
v 2.000000 2.000000 0.000000 0.8
# 8 vertices

g front cube
usemtl white
f 1 2 3 4
g back cube
# expects white material
f 8 7 6 5
g right cube
usemtl red
f 4 3 7 8
g top cube
usemtl white
f 5 1 4 8
g left cube
usemtl green
f 5 6 2 1
g bottom cube
usemtl white
f 2 6 7 3
# 6 elements
97 changes: 91 additions & 6 deletions python/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,41 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
py::class_<attrib_t>(tobj_module, "attrib_t")
.def(py::init<>())
.def_readonly("vertices", &attrib_t::vertices)
.def_readonly("vertex_weights", &attrib_t::vertex_weights)
.def_readonly("skin_weights", &attrib_t::skin_weights)
.def_readonly("normals", &attrib_t::normals)
.def_readonly("texcoords", &attrib_t::texcoords)
.def_readonly("colors", &attrib_t::colors)
.def("numpy_vertices", [] (attrib_t &instance) {
auto ret = py::array_t<real_t>(instance.vertices.size());
py::buffer_info buf = ret.request();
memcpy(buf.ptr, instance.vertices.data(), instance.vertices.size() * sizeof(real_t));
return ret;
})
.def_readonly("normals", &attrib_t::normals)
.def_readonly("texcoords", &attrib_t::texcoords)
.def_readonly("colors", &attrib_t::colors)
.def("numpy_vertex_weights", [] (attrib_t &instance) {
auto ret = py::array_t<real_t>(instance.vertex_weights.size());
py::buffer_info buf = ret.request();
memcpy(buf.ptr, instance.vertex_weights.data(), instance.vertex_weights.size() * sizeof(real_t));
return ret;
})
.def("numpy_normals", [] (attrib_t &instance) {
auto ret = py::array_t<real_t>(instance.normals.size());
py::buffer_info buf = ret.request();
memcpy(buf.ptr, instance.normals.data(), instance.normals.size() * sizeof(real_t));
return ret;
})
.def("numpy_texcoords", [] (attrib_t &instance) {
auto ret = py::array_t<real_t>(instance.texcoords.size());
py::buffer_info buf = ret.request();
memcpy(buf.ptr, instance.texcoords.data(), instance.texcoords.size() * sizeof(real_t));
return ret;
})
.def("numpy_colors", [] (attrib_t &instance) {
auto ret = py::array_t<real_t>(instance.colors.size());
py::buffer_info buf = ret.request();
memcpy(buf.ptr, instance.colors.data(), instance.colors.size() * sizeof(real_t));
return ret;
})
;

py::class_<shape_t>(tobj_module, "shape_t")
Expand Down Expand Up @@ -119,7 +145,7 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
.def("GetCustomParameter", &material_t::GetCustomParameter)
;

py::class_<mesh_t>(tobj_module, "mesh_t")
py::class_<mesh_t>(tobj_module, "mesh_t", py::buffer_protocol())
.def(py::init<>())
.def_readonly("num_face_vertices", &mesh_t::num_face_vertices)
.def("numpy_num_face_vertices", [] (mesh_t &instance) {
Expand All @@ -128,6 +154,41 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
memcpy(buf.ptr, instance.num_face_vertices.data(), instance.num_face_vertices.size() * sizeof(unsigned char));
return ret;
})
.def("vertex_indices", [](mesh_t &self) {
// NOTE: we cannot use py::buffer_info and py:buffer as a return type.
// py::memoriview is not suited for vertex indices usecase, since indices data may be used after
// deleting C++ mesh_t object in Python world.
//
// So create a dedicated Python object(std::vector<int>)

std::vector<int> indices;
indices.resize(self.indices.size());
for (size_t i = 0; i < self.indices.size(); i++) {
indices[i] = self.indices[i].vertex_index;
}

return indices;
})
.def("normal_indices", [](mesh_t &self) {

std::vector<int> indices;
indices.resize(self.indices.size());
for (size_t i = 0; i < self.indices.size(); i++) {
indices[i] = self.indices[i].normal_index;
}

return indices;
})
.def("texcoord_indices", [](mesh_t &self) -> py::buffer_info {

std::vector<int> indices;
indices.resize(self.indices.size());
for (size_t i = 0; i < self.indices.size(); i++) {
indices[i] = self.indices[i].texcoord_index;
}

return indices;
})
.def_readonly("indices", &mesh_t::indices)
.def("numpy_indices", [] (mesh_t &instance) {
// Flatten indexes. index_t is composed of 3 ints(vertex_index, normal_index, texcoord_index).
Expand All @@ -154,10 +215,34 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
});

py::class_<lines_t>(tobj_module, "lines_t")
.def(py::init<>());
.def(py::init<>())
.def_readonly("indices", &lines_t::indices)
.def_readonly("num_line_vertices", &lines_t::num_line_vertices)
;

py::class_<points_t>(tobj_module, "points_t")
.def(py::init<>());
.def(py::init<>())
.def_readonly("indices", &points_t::indices)
;

py::class_<joint_and_weight_t>(tobj_module, "joint_and_weight_t")
.def(py::init<>())
.def_readonly("joint_id", &joint_and_weight_t::joint_id, "Joint index(NOTE: Joint info is provided externally, not from .obj")
.def_readonly("weight", &joint_and_weight_t::weight, "Weight value(NOTE: weight is not normalized)")
;

py::class_<skin_weight_t>(tobj_module, "skin_weight_t")
.def(py::init<>())
.def_readonly("vertex_id", &skin_weight_t::vertex_id)
.def_readonly("weightValues", &skin_weight_t::weightValues)
;

py::class_<tag_t>(tobj_module, "tag_t")
.def(py::init<>())
.def_readonly("name", &tag_t::name)
.def_readonly("intValues", &tag_t::intValues)
.def_readonly("floatValues", &tag_t::floatValues)
.def_readonly("stringValues", &tag_t::stringValues)
;
}

33 changes: 32 additions & 1 deletion python/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

filename = "../models/cornell_box.obj"

if len(sys.argv) > 1:
filename = sys.argv[1]


reader = tinyobjloader.ObjReader()

Expand All @@ -35,8 +38,11 @@

attrib = reader.GetAttrib()
print("len(attrib.vertices) = ", len(attrib.vertices))
print("len(attrib.vertex_weights) = ", len(attrib.vertex_weights))
print("len(attrib.normals) = ", len(attrib.normals))
print("len(attrib.texcoords) = ", len(attrib.texcoords))
print("len(attrib.colors) = ", len(attrib.colors))
print("len(attrib.skin_weights) = ", len(attrib.skin_weights))

# vertex data must be `xyzxyzxyz...`
assert len(attrib.vertices) % 3 == 0
Expand All @@ -47,6 +53,9 @@
# texcoords data must be `uvuvuv...`
assert len(attrib.texcoords) % 2 == 0

# colors data must be `rgbrgbrgb...`
assert len(attrib.texcoords) % 3 == 0

# Performance note
# (direct?) array access through member variable is quite slow.
# https://github.com/tinyobjloader/tinyobjloader/issues/275#issuecomment-753465833
Expand All @@ -63,16 +72,34 @@
for i, v in enumerate(attrib.vertices):
print("v[{}] = {}".format(i, v))

# vw is filled with 1.0 if [w] component is not present in `v` line in .obj
for i, w in enumerate(attrib.vertex_weights):
print("vweight[{}] = {}".format(i, w))

for i, v in enumerate(attrib.normals):
print("vn[{}] = {}".format(i, v))

for i, v in enumerate(attrib.texcoords):
print("vt[{}] = {}".format(i, t))
print("vt[{}] = {}".format(i, v))

for i, v in enumerate(attrib.colors):
print("vcol[{}] = {}".format(i, v))

if len(attrib.skin_weights):
print("num skin weights", len(attrib.skin_weights))

for i, skin in enumerate(attrib.skin_weights):
print("skin_weight[{}]".format(i))
print(" vertex_id = ", skin.vertex_id)
print(" len(weights) = ", len(skin.weightValues))
for k, w in enumerate(skin.weightValues):
print(" [{}] joint_id: {}, weight: {}".format(k, w.joint_id, w.weight))

if is_numpy_available:
print("numpy_v = {}".format(attrib.numpy_vertices()))
print("numpy_vn = {}".format(attrib.numpy_normals()))
print("numpy_vt = {}".format(attrib.numpy_texcoords()))
print("numpy_vcol = {}".format(attrib.numpy_colors()))

materials = reader.GetMaterials()
print("Num materials: ", len(materials))
Expand Down Expand Up @@ -101,6 +128,10 @@
print("[{}] vt_idx {}".format(i, idx.texcoord_index))
print("material_ids = {}".format(shape.mesh.material_ids))

# faster access to indices
a = shape.mesh.vertex_indices()
print("vertex_indices", shape.mesh.vertex_indices())

if is_numpy_available:
print("numpy_indices = {}".format(shape.mesh.numpy_indices()))
print("numpy_num_face_vertices = {}".format(shape.mesh.numpy_num_face_vertices()))
Expand Down

0 comments on commit 7b3ba0b

Please sign in to comment.