| 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 | |