Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 64d5bf7

Browse files
committed
pybind11 for vulkan model loading demo
1 parent 64d1e3f commit 64d5bf7

File tree

2 files changed

+151
-204
lines changed

2 files changed

+151
-204
lines changed

python/main.cpp

Lines changed: 51 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1,206 +1,60 @@
1-
// python2/3 module for tinyobjloader
2-
//
3-
// usage:
4-
// import tinyobjloader as tol
5-
// model = tol.LoadObj(name)
6-
// print(model["shapes"])
7-
// print(model["materials"]
8-
// note:
9-
// `shape.mesh.index_t` is represented as flattened array: (vertex_index, normal_index, texcoord_index) * num_faces
10-
11-
#include <Python.h>
12-
#include <vector>
13-
#include "../tiny_obj_loader.h"
14-
15-
typedef std::vector<double> vectd;
16-
typedef std::vector<int> vecti;
17-
18-
PyObject* pyTupleFromfloat3(float array[3]) {
19-
int i;
20-
PyObject* tuple = PyTuple_New(3);
21-
22-
for (i = 0; i <= 2; i++) {
23-
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
24-
}
25-
26-
return tuple;
1+
#include <pybind11\pybind11.h>
2+
#include <pybind11\stl.h>
3+
4+
#define TINYOBJLOADER_IMPLEMENTATION
5+
#include <tiny_obj_loader.h>
6+
7+
8+
namespace py = pybind11;
9+
10+
11+
py::tuple loadModel(std::string modelfile) {
12+
tinyobj::attrib_t attrib;
13+
std::vector<tinyobj::shape_t> shapes;
14+
std::vector<tinyobj::material_t> materials;
15+
std::string err;
16+
17+
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, modelfile.c_str())) {
18+
std::exception(err);
19+
}
20+
21+
py::dict uniqueVertices;
22+
py::list vertices;
23+
py::list indices;
24+
25+
for (const auto& shape : shapes) {
26+
for (const auto& index : shape.mesh.indices) {
27+
py::tuple vertex = py::make_tuple(
28+
// vertex position
29+
attrib.vertices[3 * index.vertex_index + 0],
30+
attrib.vertices[3 * index.vertex_index + 1],
31+
attrib.vertices[3 * index.vertex_index + 2],
32+
// color
33+
1.0, 1.0, 1.0,
34+
// texcoord
35+
attrib.texcoords[2 * index.texcoord_index + 0],
36+
1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
37+
);
38+
39+
if (!uniqueVertices.contains(vertex)) {
40+
uniqueVertices[vertex] = static_cast<uint32_t>(vertices.size());
41+
vertices.append(vertex);
42+
}
43+
44+
indices.append(uniqueVertices[vertex]);
45+
}
46+
}
47+
48+
return py::make_tuple(vertices, indices);
2749
}
2850

29-
extern "C" {
30-
31-
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
32-
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
33-
34-
char const* current_name;
35-
char const* filename;
36-
vectd vect;
37-
std::vector<tinyobj::index_t> indices;
38-
std::vector<unsigned char> face_verts;
39-
40-
tinyobj::attrib_t attrib;
41-
std::vector<tinyobj::shape_t> shapes;
42-
std::vector<tinyobj::material_t> materials;
43-
44-
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
45-
46-
std::string err;
47-
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
48-
49-
pyshapes = PyDict_New();
50-
pymaterials = PyDict_New();
51-
pymaterial_indices = PyList_New(0);
52-
rtndict = PyDict_New();
53-
54-
attribobj = PyDict_New();
55-
56-
for (int i = 0; i <= 2; i++) {
57-
current = PyList_New(0);
58-
59-
switch (i) {
60-
case 0:
61-
current_name = "vertices";
62-
vect = vectd(attrib.vertices.begin(), attrib.vertices.end());
63-
break;
64-
case 1:
65-
current_name = "normals";
66-
vect = vectd(attrib.normals.begin(), attrib.normals.end());
67-
break;
68-
case 2:
69-
current_name = "texcoords";
70-
vect = vectd(attrib.texcoords.begin(), attrib.texcoords.end());
71-
break;
72-
}
73-
74-
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
75-
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
76-
}
77-
78-
PyDict_SetItemString(attribobj, current_name, current);
79-
}
80-
81-
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
82-
shape != shapes.end(); shape++) {
83-
meshobj = PyDict_New();
84-
tinyobj::mesh_t cm = (*shape).mesh;
85-
86-
{
87-
current = PyList_New(0);
88-
89-
for (size_t i = 0; i < cm.indices.size(); i++) {
90-
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
91-
// ...
92-
PyList_Insert(current, 3 * i + 0,
93-
PyLong_FromLong(cm.indices[i].vertex_index));
94-
PyList_Insert(current, 3 * i + 1,
95-
PyLong_FromLong(cm.indices[i].normal_index));
96-
PyList_Insert(current, 3 * i + 2,
97-
PyLong_FromLong(cm.indices[i].texcoord_index));
98-
}
99-
100-
PyDict_SetItemString(meshobj, "indices", current);
101-
}
102-
103-
{
104-
current = PyList_New(0);
105-
106-
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
107-
// Widen data type to long.
108-
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i]));
109-
}
110-
111-
PyDict_SetItemString(meshobj, "num_face_vertices", current);
112-
}
113-
114-
{
115-
current = PyList_New(0);
116-
117-
for (size_t i = 0; i < cm.material_ids.size(); i++) {
118-
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
119-
}
120-
121-
PyDict_SetItemString(meshobj, "material_ids", current);
122-
}
123-
124-
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
125-
}
126-
127-
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
128-
mat != materials.end(); mat++) {
129-
PyObject* matobj = PyDict_New();
130-
PyObject* unknown_parameter = PyDict_New();
131-
132-
for (std::map<std::string, std::string>::iterator p =
133-
mat->unknown_parameter.begin();
134-
p != mat->unknown_parameter.end(); ++p) {
135-
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
136-
PyUnicode_FromString(p->second.c_str()));
137-
}
138-
139-
PyDict_SetItemString(matobj, "shininess",
140-
PyFloat_FromDouble(mat->shininess));
141-
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior));
142-
PyDict_SetItemString(matobj, "dissolve",
143-
PyFloat_FromDouble(mat->dissolve));
144-
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum));
145-
PyDict_SetItemString(matobj, "ambient_texname",
146-
PyUnicode_FromString(mat->ambient_texname.c_str()));
147-
PyDict_SetItemString(matobj, "diffuse_texname",
148-
PyUnicode_FromString(mat->diffuse_texname.c_str()));
149-
PyDict_SetItemString(matobj, "specular_texname",
150-
PyUnicode_FromString(mat->specular_texname.c_str()));
151-
PyDict_SetItemString(
152-
matobj, "specular_highlight_texname",
153-
PyUnicode_FromString(mat->specular_highlight_texname.c_str()));
154-
PyDict_SetItemString(matobj, "bump_texname",
155-
PyUnicode_FromString(mat->bump_texname.c_str()));
156-
PyDict_SetItemString(
157-
matobj, "displacement_texname",
158-
PyUnicode_FromString(mat->displacement_texname.c_str()));
159-
PyDict_SetItemString(matobj, "alpha_texname",
160-
PyUnicode_FromString(mat->alpha_texname.c_str()));
161-
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
162-
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
163-
PyDict_SetItemString(matobj, "specular",
164-
pyTupleFromfloat3(mat->specular));
165-
PyDict_SetItemString(matobj, "transmittance",
166-
pyTupleFromfloat3(mat->transmittance));
167-
PyDict_SetItemString(matobj, "emission",
168-
pyTupleFromfloat3(mat->emission));
169-
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
170-
171-
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
172-
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str()));
173-
}
174-
175-
PyDict_SetItemString(rtndict, "shapes", pyshapes);
176-
PyDict_SetItemString(rtndict, "materials", pymaterials);
177-
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
178-
PyDict_SetItemString(rtndict, "attribs", attribobj);
179-
180-
return rtndict;
181-
}
182-
183-
static PyMethodDef mMethods[] = {
18451

185-
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
18652

187-
};
18853

189-
#if PY_MAJOR_VERSION >= 3
54+
PYBIND11_MODULE(tol, m) {
55+
m.doc() = "tinyobjloader for python(for vulkan tutorial model loading demo)";
19056

191-
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
192-
NULL, -1, mMethods};
193-
194-
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
195-
return PyModule_Create(&moduledef);
57+
m.def("loadModel", &loadModel, "load an obj model.");
19658
}
19759

198-
#else
19960

200-
PyMODINIT_FUNC inittinyobjloader(void) {
201-
Py_InitModule3("tinyobjloader", mMethods, NULL);
202-
}
203-
204-
#endif // PY_MAJOR_VERSION >= 3
205-
206-
}

python/setup.py

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,106 @@
1-
from distutils.core import setup, Extension
1+
from setuptools import setup, Extension
2+
from setuptools.command.build_ext import build_ext
3+
import sys
4+
import setuptools
5+
import os
26

7+
__version__ = '0.0.1'
38

4-
m = Extension('tinyobjloader',
5-
sources = ['main.cpp', '../tiny_obj_loader.cc'])
9+
__currentpath = os.path.abspath(os.path.dirname(__file__))
10+
tinyobjloaderDir = os.path.join(__currentpath, '..')
611

12+
class get_pybind_include(object):
13+
"""Helper class to determine the pybind11 include path
714
8-
setup (name = 'tinyobjloader',
9-
version = '0.1',
10-
description = 'Python module for tinyobjloader',
11-
ext_modules = [m])
15+
The purpose of this class is to postpone importing pybind11
16+
until it is actually installed, so that the ``get_include()``
17+
method can be invoked. """
1218

19+
def __init__(self, user=False):
20+
self.user = user
1321

22+
def __str__(self):
23+
import pybind11
24+
return pybind11.get_include(self.user)
25+
26+
27+
ext_modules = [
28+
Extension(
29+
'tol',
30+
['main.cpp'],
31+
include_dirs=[
32+
# Path to pybind11 headers
33+
get_pybind_include(),
34+
get_pybind_include(user=True),
35+
#"E:\\coding\\pythonProjects\\tinyobjloader"
36+
tinyobjloaderDir
37+
],
38+
language='c++'
39+
),
40+
]
41+
42+
43+
# As of Python 3.6, CCompiler has a `has_flag` method.
44+
# cf http://bugs.python.org/issue26689
45+
def has_flag(compiler, flagname):
46+
"""Return a boolean indicating whether a flag name is supported on
47+
the specified compiler.
48+
"""
49+
import tempfile
50+
with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:
51+
f.write('int main (int argc, char **argv) { return 0; }')
52+
try:
53+
compiler.compile([f.name], extra_postargs=[flagname])
54+
except setuptools.distutils.errors.CompileError:
55+
return False
56+
return True
57+
58+
59+
def cpp_flag(compiler):
60+
"""Return the -std=c++[11/14] compiler flag.
61+
62+
The c++14 is prefered over c++11 (when it is available).
63+
"""
64+
if has_flag(compiler, '-std=c++14'):
65+
return '-std=c++14'
66+
elif has_flag(compiler, '-std=c++11'):
67+
return '-std=c++11'
68+
else:
69+
raise RuntimeError('Unsupported compiler -- at least C++11 support '
70+
'is needed!')
71+
72+
73+
class BuildExt(build_ext):
74+
"""A custom build extension for adding compiler-specific options."""
75+
c_opts = {
76+
'msvc': ['/EHsc'],
77+
'unix': [],
78+
}
79+
80+
if sys.platform == 'darwin':
81+
c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7']
82+
83+
def build_extensions(self):
84+
ct = self.compiler.compiler_type
85+
opts = self.c_opts.get(ct, [])
86+
if ct == 'unix':
87+
opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version())
88+
opts.append(cpp_flag(self.compiler))
89+
if has_flag(self.compiler, '-fvisibility=hidden'):
90+
opts.append('-fvisibility=hidden')
91+
elif ct == 'msvc':
92+
opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version())
93+
for ext in self.extensions:
94+
ext.extra_compile_args = opts
95+
build_ext.build_extensions(self)
96+
97+
setup(
98+
name='tol',
99+
version=__version__,
100+
description='Python module for tinyobjloader using pybind11',
101+
long_description='',
102+
ext_modules=ext_modules,
103+
install_requires=['pybind11>=2.2'],
104+
cmdclass={'build_ext': BuildExt},
105+
zip_safe=False,
106+
)

0 commit comments

Comments
 (0)