1 | #ifdef NANOGUI_PYTHON |
2 | |
3 | #include "python.h" |
4 | #include <pybind11/numpy.h> |
5 | |
6 | static void uploadAttribPy(GLShader &sh, const std::string &name, py::array M, int version) { |
7 | if (M.ndim() != 2) |
8 | throw py::type_error("uploadAttrib(): expects 2D array" ); |
9 | |
10 | M = py::array::ensure(M, py::array::f_style); |
11 | |
12 | py::dtype dtype = M.dtype(); |
13 | GLuint glType; |
14 | bool integral = true; |
15 | |
16 | if (dtype.kind() == 'i') { |
17 | switch (dtype.itemsize()) { |
18 | case 1: glType = GL_BYTE; break; |
19 | case 2: glType = GL_SHORT; break; |
20 | case 4: glType = GL_INT; break; |
21 | default: throw py::type_error("uploadAttrib(): Invalid integer type!" ); |
22 | } |
23 | } else if (dtype.kind() == 'u') { |
24 | switch (dtype.itemsize()) { |
25 | case 1: glType = GL_UNSIGNED_BYTE; break; |
26 | case 2: glType = GL_UNSIGNED_SHORT; break; |
27 | case 4: glType = GL_UNSIGNED_INT; break; |
28 | default: throw py::type_error("uploadAttrib(): Invalid unsigned integer type!" ); |
29 | } |
30 | } else if (dtype.kind() == 'f') { |
31 | switch (dtype.itemsize()) { |
32 | case 2: glType = GL_HALF_FLOAT; break; |
33 | case 4: glType = GL_FLOAT; break; |
34 | case 8: glType = GL_DOUBLE; break; |
35 | default: throw py::type_error("uploadAttrib(): Invalid floating point type!" ); |
36 | } |
37 | integral = false; |
38 | } else { |
39 | throw py::type_error("uploadAttrib(): Invalid type!" ); |
40 | } |
41 | |
42 | sh.uploadAttrib(name, M.shape(0) * M.shape(1), (int) M.shape(0), |
43 | (uint32_t)M.itemsize(), glType, integral, M.data(), version); |
44 | } |
45 | |
46 | static void setUniformPy(GLShader &sh, const std::string &name, py::object arg, bool warn = true) { |
47 | GLuint id = sh.uniform(name, warn); |
48 | py::array value_ = py::array::ensure(arg); |
49 | auto dtype = value_.dtype(); |
50 | if (dtype.kind() == 'f') { |
51 | auto value = py::array_t<float, py::array::forcecast | py::array::c_style>(value_); |
52 | if (value.ndim() == 0 || (value.ndim() == 1 && value.shape(0) == 1)) |
53 | glUniform1fv(id, 1, value.data()); |
54 | else if (value.ndim() == 1 && value.shape(0) == 2) |
55 | glUniform2fv(id, 1, value.data()); |
56 | else if (value.ndim() == 1 && value.shape(0) == 3) |
57 | glUniform3fv(id, 1, value.data()); |
58 | else if (value.ndim() == 1 && value.shape(0) == 4) |
59 | glUniform4fv(id, 1, value.data()); |
60 | else if (value.ndim() == 2 && value.shape(0) == 3 && value.shape(1) == 3) |
61 | glUniformMatrix3fv(id, 1, GL_TRUE, value.data()); |
62 | else if (value.ndim() == 2 && value.shape(0) == 4 && value.shape(1) == 4) |
63 | glUniformMatrix4fv(id, 1, GL_TRUE, value.data()); |
64 | else |
65 | throw py::type_error("setUniform(): invalid dimension/size!" ); |
66 | } else if (dtype.kind() == 'i') { |
67 | auto value = py::array_t<int, py::array::forcecast>(value_); |
68 | |
69 | if (value.ndim() == 0 || (value.ndim() == 1 && value.shape(0) == 1)) |
70 | glUniform1iv(id, 1, value.data()); |
71 | else if (value.ndim() == 1 && value.shape(0) == 2) |
72 | glUniform2iv(id, 1, value.data()); |
73 | else if (value.ndim() == 1 && value.shape(0) == 3) |
74 | glUniform3iv(id, 1, value.data()); |
75 | else if (value.ndim() == 1 && value.shape(0) == 4) |
76 | glUniform4iv(id, 1, value.data()); |
77 | else |
78 | throw py::type_error("setUniform(): invalid dimension/size!" ); |
79 | } else if (dtype.kind() == 'u') { |
80 | auto value = py::array_t<unsigned int, py::array::forcecast>(value_); |
81 | |
82 | if (value.ndim() == 0 || (value.ndim() == 1 && value.shape(0) == 1)) |
83 | glUniform1uiv(id, 1, value.data()); |
84 | else if (value.ndim() == 1 && value.shape(0) == 2) |
85 | glUniform2uiv(id, 1, value.data()); |
86 | else if (value.ndim() == 1 && value.shape(0) == 3) |
87 | glUniform3uiv(id, 1, value.data()); |
88 | else if (value.ndim() == 1 && value.shape(0) == 4) |
89 | glUniform4uiv(id, 1, value.data()); |
90 | else |
91 | throw py::type_error("setUniform(): invalid dimension/size!" ); |
92 | } |
93 | } |
94 | |
95 | void register_glutil(py::module &m) { |
96 | py::class_<GLShader>(m, "GLShader" , D(GLShader)) |
97 | .def(py::init<>()) |
98 | .def("init" , &GLShader::init, py::arg("name" ), |
99 | py::arg("vertex_str" ), py::arg("fragment_str" ), |
100 | py::arg("geometry_str" ) = "" , D(GLShader, init)) |
101 | .def("initFromFiles" , &GLShader::initFromFiles, py::arg("name" ), |
102 | py::arg("vertex_fname" ), py::arg("fragment_fname" ), |
103 | py::arg("geometry_fname" ) = "" , D(GLShader, initFromFiles)) |
104 | .def("name" , &GLShader::name, D(GLShader, name)) |
105 | .def("define" , &GLShader::define, py::arg("key" ), py::arg("value" ), |
106 | D(GLShader, define)) |
107 | .def("bind" , &GLShader::bind, D(GLShader, bind)) |
108 | .def("free" , &GLShader::free, D(GLShader, free)) |
109 | .def("attrib" , &GLShader::attrib, py::arg("name" ), |
110 | py::arg("warn" ) = true, D(GLShader, attrib)) |
111 | .def("uniform" , &GLShader::uniform, py::arg("name" ), |
112 | py::arg("warn" ) = true, D(GLShader, uniform)) |
113 | .def("uploadAttrib" , &uploadAttribPy, py::arg("name" ), |
114 | py::arg("M" ), py::arg("version" ) = -1) |
115 | .def("uploadIndices" , [](GLShader &sh, py::array M, int version) { |
116 | uploadAttribPy(sh, "indices" , M, version); |
117 | }, py::arg("M" ), py::arg("version" ) = -1) |
118 | .def("invalidateAttribs" , &GLShader::invalidateAttribs, |
119 | D(GLShader, invalidateAttribs)) |
120 | .def("freeAttrib" , &GLShader::freeAttrib, |
121 | D(GLShader, freeAttrib)) |
122 | .def("hasAttrib" , &GLShader::hasAttrib, |
123 | D(GLShader, hasAttrib)) |
124 | .def("attribVersion" , &GLShader::attribVersion, |
125 | D(GLShader, attribVersion)) |
126 | .def("resetAttribVersion" , &GLShader::resetAttribVersion, |
127 | D(GLShader, resetAttribVersion)) |
128 | .def("shareAttrib" , &GLShader::shareAttrib, |
129 | D(GLShader, shareAttrib), py::arg("otherShader" ), |
130 | py::arg("name" ), py::arg("as" ) = "" ) |
131 | .def("drawArray" , &GLShader::drawArray, |
132 | D(GLShader, drawArray), py::arg("type" ), |
133 | py::arg("offset" ), py::arg("count" )) |
134 | .def("drawIndexed" , &GLShader::drawIndexed, |
135 | D(GLShader, drawIndexed), py::arg("type" ), |
136 | py::arg("offset" ), py::arg("count" )) |
137 | .def("setUniform" , &setUniformPy, py::arg("name" ), |
138 | py::arg("value" ), py::arg("warn" ) = true) |
139 | .def("attribBuffer" , &GLShader::attribBuffer, D(GLShader, attribBuffer)); |
140 | |
141 | py::class_<GLShader::Buffer>(m, "Buffer" , D(GLShader, Buffer)) |
142 | .def(py::init<>()) |
143 | .def_readonly("id" , &GLShader::Buffer::id, D(GLShader, Buffer, id)) |
144 | .def_readonly("glType" , &GLShader::Buffer::glType, D(GLShader, Buffer, glType)) |
145 | .def_readonly("dim" , &GLShader::Buffer::dim, D(GLShader, Buffer, dim)) |
146 | .def_readonly("compSize" , &GLShader::Buffer::compSize, D(GLShader, Buffer, compSize)) |
147 | .def_readonly("size" , &GLShader::Buffer::size, D(GLShader, Buffer, size)) |
148 | .def_readonly("version" , &GLShader::Buffer::version, D(GLShader, Buffer, version)); |
149 | |
150 | py::class_<Arcball>(m, "Arcball" , D(Arcball)) |
151 | .def(py::init<float>(), py::arg("speedFactor" ) = 2.f, D(Arcball, Arcball)) |
152 | .def(py::init<const Quaternionf &>(), D(Arcball, Arcball, 2)) |
153 | .def("state" , (Quaternionf& (Arcball::*)()) &Arcball::state, D(Arcball, state)) |
154 | .def("setState" , &Arcball::setState, D(Arcball, setState)) |
155 | .def("size" , &Arcball::size, D(Arcball, size)) |
156 | .def("setSize" , &Arcball::setSize, D(Arcball, setSize)) |
157 | .def("speedFactor" , &Arcball::speedFactor, D(Arcball, speedFactor)) |
158 | .def("setSpeedFactor" , &Arcball::setSpeedFactor, D(Arcball, setSpeedFactor)) |
159 | .def("active" , &Arcball::active, D(Arcball, active)) |
160 | .def("button" , &Arcball::button, py::arg("pos" ), py::arg("pressed" ), D(Arcball, button)) |
161 | .def("motion" , &Arcball::motion, py::arg("pos" ), D(Arcball, motion)) |
162 | .def("matrix" , &Arcball::matrix, D(Arcball, matrix)) |
163 | .def("activeState" , &Arcball::activeState, D(Arcball, activeState)) |
164 | .def("interrupt" , &Arcball::interrupt, D(Arcball, interrupt)); |
165 | |
166 | m.def("project" , &project, py::arg("obj" ), py::arg("model" ), |
167 | py::arg("proj" ), py::arg("viewportSize" ), D(project)); |
168 | |
169 | m.def("unproject" , &unproject, py::arg("win" ), py::arg("model" ), |
170 | py::arg("proj" ), py::arg("viewportSize" ), D(unproject)); |
171 | |
172 | m.def("lookAt" , &lookAt, py::arg("origin" ), py::arg("target" ), |
173 | py::arg("up" ), D(lookAt)); |
174 | |
175 | m.def("ortho" , &ortho, py::arg("left" ), py::arg("right" ), py::arg("bottom" ), |
176 | py::arg("top" ), py::arg("zNear" ), py::arg("zFar" ), D(ortho)); |
177 | |
178 | m.def("frustum" , &frustum, py::arg("left" ), py::arg("right" ), py::arg("bottom" ), |
179 | py::arg("top" ), py::arg("nearVal" ), py::arg("farVal" ), D(frustum)); |
180 | |
181 | m.def("scale" , &scale, py::arg("v" ), D(scale)); |
182 | |
183 | m.def("translate" , &translate, py::arg("v" ), D(translate)); |
184 | |
185 | /* Very basic OpenGL coverage */ |
186 | |
187 | #define C(name) gl.attr(#name) = py::int_(GL_##name); |
188 | py::module gl = m.def_submodule("gl" , "OpenGL bindings" ); |
189 | |
190 | gl.def("Enable" , [](GLenum cap) { glEnable(cap); }, py::arg("cap" )); |
191 | gl.def("Disable" , [](GLenum cap) { glDisable(cap); }, py::arg("cap" )); |
192 | gl.def("BlendFunc" , [](GLenum sfactor, |
193 | GLenum dfactor) { glBlendFunc(sfactor, dfactor); }, |
194 | py::arg("sfactor" ), py::arg("dfactor" )); |
195 | gl.def("Scissor" , [](GLint x, GLint y, GLsizei w, GLsizei h) { glScissor(x, y, w, h); }); |
196 | gl.def("Cull" , [](GLenum mode) { glCullFace(mode); }); |
197 | gl.def("PointSize" , [](GLfloat size) { glPointSize(size); }); |
198 | gl.def("LineWidth" , [](GLfloat size) { glLineWidth(size); }); |
199 | |
200 | /* Primitive types */ |
201 | C(POINTS); C(LINE_STRIP); C(LINE_LOOP); C(LINES); C(LINE_STRIP_ADJACENCY); |
202 | C(LINES_ADJACENCY); C(TRIANGLE_STRIP); C(TRIANGLE_FAN); C(TRIANGLES); |
203 | C(TRIANGLE_STRIP_ADJACENCY); C(TRIANGLES_ADJACENCY); |
204 | |
205 | /* Depth testing */ |
206 | C(DEPTH_TEST); C(NEVER); C(LESS); C(EQUAL); C(LEQUAL); C(GREATER); |
207 | C(NOTEQUAL); C(GEQUAL); C(ALWAYS); |
208 | |
209 | /* Blend functions */ |
210 | C(BLEND); C(ZERO); C(ONE); C(SRC_COLOR); C(DST_COLOR); |
211 | C(ONE_MINUS_DST_COLOR); C(SRC_ALPHA); C(ONE_MINUS_SRC_ALPHA); |
212 | C(DST_ALPHA); C(ONE_MINUS_DST_ALPHA); C(CONSTANT_COLOR); |
213 | C(ONE_MINUS_CONSTANT_COLOR); C(CONSTANT_ALPHA); |
214 | C(ONE_MINUS_CONSTANT_ALPHA); |
215 | |
216 | /* Culling functions */ |
217 | C(FRONT); C(BACK); C(FRONT_AND_BACK); |
218 | |
219 | /* Remaining glEnable/glDisable enums */ |
220 | C(SCISSOR_TEST); C(STENCIL_TEST); C(PROGRAM_POINT_SIZE); |
221 | C(LINE_SMOOTH); C(POLYGON_SMOOTH); C(CULL_FACE); |
222 | } |
223 | |
224 | #endif |
225 | |