1#ifdef NANOGUI_PYTHON
2
3#include "python.h"
4#include <pybind11/numpy.h>
5
6static 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
46static 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
95void 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