Skip to content

Commit 7b3ba0b

Browse files
committed
Fix parsing [w] component in v line.
Improve python binding. - Expose more attrib to Python.
1 parent b2f0457 commit 7b3ba0b

5 files changed

Lines changed: 266 additions & 78 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ endif()
125125

126126
if (TINYOBJLOADER_WITH_PYTHON)
127127
# pybind11 method:
128-
pybind11_add_module(${PY_TARGET} ${CMAKE_SOURCE_DIR}/python/bindings.cc )
128+
pybind11_add_module(${PY_TARGET} ${CMAKE_SOURCE_DIR}/python/bindings.cc ${CMAKE_SOURCE_DIR}/python/tiny_obj_loader.cc)
129129

130130
add_sanitizers(${PY_TARGET})
131131
set_target_properties(${PY_TARGET} PROPERTIES OUTPUT_NAME "tinyobjloader")

models/cube-vertex-w-component.obj

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
mtllib cube.mtl
2+
3+
v 0.000000 2.000000 2.000000 0.1
4+
v 0.000000 0.000000 2.000000 0.2
5+
v 2.000000 0.000000 2.000000 0.3
6+
v 2.000000 2.000000 2.000000 0.4
7+
v 0.000000 2.000000 0.000000 0.5
8+
v 0.000000 0.000000 0.000000 0.6
9+
v 2.000000 0.000000 0.000000 0.7
10+
v 2.000000 2.000000 0.000000 0.8
11+
# 8 vertices
12+
13+
g front cube
14+
usemtl white
15+
f 1 2 3 4
16+
g back cube
17+
# expects white material
18+
f 8 7 6 5
19+
g right cube
20+
usemtl red
21+
f 4 3 7 8
22+
g top cube
23+
usemtl white
24+
f 5 1 4 8
25+
g left cube
26+
usemtl green
27+
f 5 6 2 1
28+
g bottom cube
29+
usemtl white
30+
f 2 6 7 3
31+
# 6 elements

python/bindings.cc

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,41 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
3838
py::class_<attrib_t>(tobj_module, "attrib_t")
3939
.def(py::init<>())
4040
.def_readonly("vertices", &attrib_t::vertices)
41+
.def_readonly("vertex_weights", &attrib_t::vertex_weights)
42+
.def_readonly("skin_weights", &attrib_t::skin_weights)
43+
.def_readonly("normals", &attrib_t::normals)
44+
.def_readonly("texcoords", &attrib_t::texcoords)
45+
.def_readonly("colors", &attrib_t::colors)
4146
.def("numpy_vertices", [] (attrib_t &instance) {
4247
auto ret = py::array_t<real_t>(instance.vertices.size());
4348
py::buffer_info buf = ret.request();
4449
memcpy(buf.ptr, instance.vertices.data(), instance.vertices.size() * sizeof(real_t));
4550
return ret;
4651
})
47-
.def_readonly("normals", &attrib_t::normals)
48-
.def_readonly("texcoords", &attrib_t::texcoords)
49-
.def_readonly("colors", &attrib_t::colors)
52+
.def("numpy_vertex_weights", [] (attrib_t &instance) {
53+
auto ret = py::array_t<real_t>(instance.vertex_weights.size());
54+
py::buffer_info buf = ret.request();
55+
memcpy(buf.ptr, instance.vertex_weights.data(), instance.vertex_weights.size() * sizeof(real_t));
56+
return ret;
57+
})
58+
.def("numpy_normals", [] (attrib_t &instance) {
59+
auto ret = py::array_t<real_t>(instance.normals.size());
60+
py::buffer_info buf = ret.request();
61+
memcpy(buf.ptr, instance.normals.data(), instance.normals.size() * sizeof(real_t));
62+
return ret;
63+
})
64+
.def("numpy_texcoords", [] (attrib_t &instance) {
65+
auto ret = py::array_t<real_t>(instance.texcoords.size());
66+
py::buffer_info buf = ret.request();
67+
memcpy(buf.ptr, instance.texcoords.data(), instance.texcoords.size() * sizeof(real_t));
68+
return ret;
69+
})
70+
.def("numpy_colors", [] (attrib_t &instance) {
71+
auto ret = py::array_t<real_t>(instance.colors.size());
72+
py::buffer_info buf = ret.request();
73+
memcpy(buf.ptr, instance.colors.data(), instance.colors.size() * sizeof(real_t));
74+
return ret;
75+
})
5076
;
5177

5278
py::class_<shape_t>(tobj_module, "shape_t")
@@ -119,7 +145,7 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
119145
.def("GetCustomParameter", &material_t::GetCustomParameter)
120146
;
121147

122-
py::class_<mesh_t>(tobj_module, "mesh_t")
148+
py::class_<mesh_t>(tobj_module, "mesh_t", py::buffer_protocol())
123149
.def(py::init<>())
124150
.def_readonly("num_face_vertices", &mesh_t::num_face_vertices)
125151
.def("numpy_num_face_vertices", [] (mesh_t &instance) {
@@ -128,6 +154,41 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
128154
memcpy(buf.ptr, instance.num_face_vertices.data(), instance.num_face_vertices.size() * sizeof(unsigned char));
129155
return ret;
130156
})
157+
.def("vertex_indices", [](mesh_t &self) {
158+
// NOTE: we cannot use py::buffer_info and py:buffer as a return type.
159+
// py::memoriview is not suited for vertex indices usecase, since indices data may be used after
160+
// deleting C++ mesh_t object in Python world.
161+
//
162+
// So create a dedicated Python object(std::vector<int>)
163+
164+
std::vector<int> indices;
165+
indices.resize(self.indices.size());
166+
for (size_t i = 0; i < self.indices.size(); i++) {
167+
indices[i] = self.indices[i].vertex_index;
168+
}
169+
170+
return indices;
171+
})
172+
.def("normal_indices", [](mesh_t &self) {
173+
174+
std::vector<int> indices;
175+
indices.resize(self.indices.size());
176+
for (size_t i = 0; i < self.indices.size(); i++) {
177+
indices[i] = self.indices[i].normal_index;
178+
}
179+
180+
return indices;
181+
})
182+
.def("texcoord_indices", [](mesh_t &self) -> py::buffer_info {
183+
184+
std::vector<int> indices;
185+
indices.resize(self.indices.size());
186+
for (size_t i = 0; i < self.indices.size(); i++) {
187+
indices[i] = self.indices[i].texcoord_index;
188+
}
189+
190+
return indices;
191+
})
131192
.def_readonly("indices", &mesh_t::indices)
132193
.def("numpy_indices", [] (mesh_t &instance) {
133194
// Flatten indexes. index_t is composed of 3 ints(vertex_index, normal_index, texcoord_index).
@@ -154,10 +215,34 @@ PYBIND11_MODULE(tinyobjloader, tobj_module)
154215
});
155216

156217
py::class_<lines_t>(tobj_module, "lines_t")
157-
.def(py::init<>());
218+
.def(py::init<>())
219+
.def_readonly("indices", &lines_t::indices)
220+
.def_readonly("num_line_vertices", &lines_t::num_line_vertices)
221+
;
158222

159223
py::class_<points_t>(tobj_module, "points_t")
160-
.def(py::init<>());
224+
.def(py::init<>())
225+
.def_readonly("indices", &points_t::indices)
226+
;
161227

228+
py::class_<joint_and_weight_t>(tobj_module, "joint_and_weight_t")
229+
.def(py::init<>())
230+
.def_readonly("joint_id", &joint_and_weight_t::joint_id, "Joint index(NOTE: Joint info is provided externally, not from .obj")
231+
.def_readonly("weight", &joint_and_weight_t::weight, "Weight value(NOTE: weight is not normalized)")
232+
;
233+
234+
py::class_<skin_weight_t>(tobj_module, "skin_weight_t")
235+
.def(py::init<>())
236+
.def_readonly("vertex_id", &skin_weight_t::vertex_id)
237+
.def_readonly("weightValues", &skin_weight_t::weightValues)
238+
;
239+
240+
py::class_<tag_t>(tobj_module, "tag_t")
241+
.def(py::init<>())
242+
.def_readonly("name", &tag_t::name)
243+
.def_readonly("intValues", &tag_t::intValues)
244+
.def_readonly("floatValues", &tag_t::floatValues)
245+
.def_readonly("stringValues", &tag_t::stringValues)
246+
;
162247
}
163248

python/sample.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

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

16+
if len(sys.argv) > 1:
17+
filename = sys.argv[1]
18+
1619

1720
reader = tinyobjloader.ObjReader()
1821

@@ -35,8 +38,11 @@
3538

3639
attrib = reader.GetAttrib()
3740
print("len(attrib.vertices) = ", len(attrib.vertices))
41+
print("len(attrib.vertex_weights) = ", len(attrib.vertex_weights))
3842
print("len(attrib.normals) = ", len(attrib.normals))
3943
print("len(attrib.texcoords) = ", len(attrib.texcoords))
44+
print("len(attrib.colors) = ", len(attrib.colors))
45+
print("len(attrib.skin_weights) = ", len(attrib.skin_weights))
4046

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

56+
# colors data must be `rgbrgbrgb...`
57+
assert len(attrib.texcoords) % 3 == 0
58+
5059
# Performance note
5160
# (direct?) array access through member variable is quite slow.
5261
# https://github.com/tinyobjloader/tinyobjloader/issues/275#issuecomment-753465833
@@ -63,16 +72,34 @@
6372
for i, v in enumerate(attrib.vertices):
6473
print("v[{}] = {}".format(i, v))
6574

75+
# vw is filled with 1.0 if [w] component is not present in `v` line in .obj
76+
for i, w in enumerate(attrib.vertex_weights):
77+
print("vweight[{}] = {}".format(i, w))
78+
6679
for i, v in enumerate(attrib.normals):
6780
print("vn[{}] = {}".format(i, v))
6881

6982
for i, v in enumerate(attrib.texcoords):
70-
print("vt[{}] = {}".format(i, t))
83+
print("vt[{}] = {}".format(i, v))
84+
85+
for i, v in enumerate(attrib.colors):
86+
print("vcol[{}] = {}".format(i, v))
87+
88+
if len(attrib.skin_weights):
89+
print("num skin weights", len(attrib.skin_weights))
90+
91+
for i, skin in enumerate(attrib.skin_weights):
92+
print("skin_weight[{}]".format(i))
93+
print(" vertex_id = ", skin.vertex_id)
94+
print(" len(weights) = ", len(skin.weightValues))
95+
for k, w in enumerate(skin.weightValues):
96+
print(" [{}] joint_id: {}, weight: {}".format(k, w.joint_id, w.weight))
7197

7298
if is_numpy_available:
7399
print("numpy_v = {}".format(attrib.numpy_vertices()))
74100
print("numpy_vn = {}".format(attrib.numpy_normals()))
75101
print("numpy_vt = {}".format(attrib.numpy_texcoords()))
102+
print("numpy_vcol = {}".format(attrib.numpy_colors()))
76103

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

131+
# faster access to indices
132+
a = shape.mesh.vertex_indices()
133+
print("vertex_indices", shape.mesh.vertex_indices())
134+
104135
if is_numpy_available:
105136
print("numpy_indices = {}".format(shape.mesh.numpy_indices()))
106137
print("numpy_num_face_vertices = {}".format(shape.mesh.numpy_num_face_vertices()))

0 commit comments

Comments
 (0)