Browse Source

python: expose shader interface APIs.

Custom shaders now possible.
pull/8/head
Vladimír Vondruš 7 years ago
parent
commit
f9851bdb1e
  1. 185
      src/python/magnum/gl.cpp
  2. 75
      src/python/magnum/test/test_gl_gl.py

185
src/python/magnum/gl.cpp

@ -37,6 +37,7 @@
#include <Magnum/GL/Renderer.h>
#include <Magnum/GL/Renderbuffer.h>
#include <Magnum/GL/RenderbufferFormat.h>
#include <Magnum/GL/Shader.h>
#include <Magnum/GL/Version.h>
#include <Magnum/Math/Color.h>
@ -62,6 +63,41 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, magnum::NonDefaultFramebufferHolder<T>)
namespace magnum {
namespace {
struct PublicizedAbstractShaderProgram: GL::AbstractShaderProgram {
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
using GL::AbstractShaderProgram::setRetrievableBinary;
#endif
#ifndef MAGNUM_TARGET_WEBGL
using GL::AbstractShaderProgram::setSeparable;
#endif
using GL::AbstractShaderProgram::attachShader;
using GL::AbstractShaderProgram::bindAttributeLocation;
#ifndef MAGNUM_TARGET_GLES
using GL::AbstractShaderProgram::bindFragmentDataLocation;
using GL::AbstractShaderProgram::bindFragmentDataLocationIndexed;
#endif
#ifndef MAGNUM_TARGET_GLES2
using GL::AbstractShaderProgram::setTransformFeedbackOutputs;
#endif
using GL::AbstractShaderProgram::link;
using GL::AbstractShaderProgram::uniformLocation;
#ifndef MAGNUM_TARGET_GLES2
using GL::AbstractShaderProgram::uniformBlockIndex;
#endif
using GL::AbstractShaderProgram::setUniform;
#ifndef MAGNUM_TARGET_GLES2
using GL::AbstractShaderProgram::setUniformBlockBinding;
#endif
};
template<class T> void setUniform(GL::AbstractShaderProgram& self, Int location, T value) {
static_cast<PublicizedAbstractShaderProgram&>(self).setUniform(location, value);
}
}
void gl(py::module& m) {
/*
Missing APIs:
@ -101,10 +137,153 @@ void gl(py::module& m) {
.def("version", static_cast<std::pair<Int, Int>(*)(GL::Version)>(GL::version), "Major and minor version number from enum value", py::arg("version"))
.def("is_version_es", GL::isVersionES, "Whether given version is OpenGL ES or WebGL");
/* Shader (used by AbstractShaderProgram, so needs to be before) */
{
py::class_<GL::Shader> shader{m, "Shader", "Shader"};
py::enum_<GL::Shader::Type>{shader, "Type", "Shader type"}
.value("VERTEX", GL::Shader::Type::Vertex)
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
.value("TESSELLATION_CONTROL", GL::Shader::Type::TessellationControl)
.value("TESSELLATION_EVALUATION", GL::Shader::Type::TessellationEvaluation)
.value("GEOMETRY", GL::Shader::Type::Geometry)
.value("COMPUTE", GL::Shader::Type::Compute)
#endif
.value("FRAGMENT", GL::Shader::Type::Fragment);
shader
/** @todo limit queries */
/* Constructors */
.def(py::init<GL::Version, GL::Shader::Type>(), "Constructor", py::arg("version"), py::arg("type"))
/* Interface */
.def_property_readonly("id", &GL::Shader::id, "OpenGL shader ID")
.def_property_readonly("type", &GL::Shader::type, "Shader type")
.def_property_readonly("sources", &GL::Shader::sources, "Shader sources")
/* Using lambdas to avoid method chaining leaking to Python */
.def("add_source", [](GL::Shader& self, std::string source) {
self.addSource(std::move(source));
}, "Add shader source")
.def("add_file", [](GL::Shader& self, const std::string& filename) {
self.addFile(filename);
}, "Add shader source file")
.def("compile", static_cast<bool(GL::Shader::*)()>(&GL::Shader::compile), "Compile shader");
}
/* Abstract shader program */
PyNonDestructibleClass<GL::AbstractShaderProgram>{m,
"AbstractShaderProgram", "Base for shader program implementations"};
/** @todo more */
{
/* The original class has protected functions and a pure virtual
destructor to force people to subclass it. */
struct PyAbstractShaderProgram: GL::AbstractShaderProgram {
using GL::AbstractShaderProgram::AbstractShaderProgram;
};
py::class_<GL::AbstractShaderProgram, PyAbstractShaderProgram> abstractShaderProgram{m, "AbstractShaderProgram", "Base for shader program implementations"};
#ifndef MAGNUM_TARGET_GLES2
py::enum_<GL::AbstractShaderProgram::TransformFeedbackBufferMode>{abstractShaderProgram, "TransformFeedbackBufferMode", "Buffer mode for transform feedback"}
.value("INTERLEAVED_ATTRIBUTES", GL::AbstractShaderProgram::TransformFeedbackBufferMode::InterleavedAttributes)
.value("SEPARATE_ATTRIBUTES", GL::AbstractShaderProgram::TransformFeedbackBufferMode::SeparateAttributes);
#endif
abstractShaderProgram
/** @todo limit queries */
/* Constructors */
.def_static("no_create", []() {
return PyAbstractShaderProgram{NoCreate};
}, "Construct without creating the underlying OpenGL object")
.def(py::init(), "Constructor")
/* Public interface */
.def_property_readonly("id", &GL::AbstractShaderProgram::id, "OpenGL program ID")
.def("validate", &GL::AbstractShaderProgram::validate, "Validate program")
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
.def("dispatch_compute", &GL::AbstractShaderProgram::dispatchCompute, "Dispatch compute")
#endif
/* Protected interface */
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
.def_property("retrievable_binary", nullptr, &PublicizedAbstractShaderProgram::setRetrievableBinary, "Allow retrieving program binary")
#endif
#ifndef MAGNUM_TARGET_WEBGL
.def_property("separable", nullptr, &PublicizedAbstractShaderProgram::setSeparable, "Allow the program to be bound to individual pipeline stages")
#endif
.def("attach_shader", &PublicizedAbstractShaderProgram::attachShader, "Attach a shader")
/** @todo list-taking shader attach function */
/* Somehow the overload static_casts don't work and it complains it
can't bind a protected function, have to use lambdas */
.def("bind_attribute_location", [](GL::AbstractShaderProgram& self, UnsignedInt location, const std::string& name) {
static_cast<PublicizedAbstractShaderProgram&>(self).bindAttributeLocation(location, name);
}, "Bind an attribute to given location", py::arg("location"), py::arg("name"))
#ifndef MAGNUM_TARGET_GLES
.def("bind_fragment_data_location_indexed", [](GL::AbstractShaderProgram& self, UnsignedInt location, UnsignedInt index, const std::string& name) {
static_cast<PublicizedAbstractShaderProgram&>(self).bindFragmentDataLocationIndexed(location, index, name);
}, "Bind fragment data to given location and first color input index", py::arg("location"), py::arg("index"), py::arg("name"))
.def("bind_fragment_data_location", [](GL::AbstractShaderProgram& self, UnsignedInt location, const std::string& name) {
static_cast<PublicizedAbstractShaderProgram&>(self).bindFragmentDataLocation(location, name);
}, "Bind fragment data to given location and first color input index", py::arg("location"), py::arg("name"))
#endif
/** @todo setTransformFeedbackOutputs, list-taking link functions */
/* Somehow the overload static_casts don't work and it complains it
can't bind a protected function, have to use lambdas */
.def("link", [](GL::AbstractShaderProgram& self) {
return static_cast<PublicizedAbstractShaderProgram&>(self).link();
}, "Link the shader")
.def("uniform_location", [](GL::AbstractShaderProgram& self, const std::string& name) {
return static_cast<PublicizedAbstractShaderProgram&>(self).uniformLocation(name);
}, "Get uniform location")
#ifndef MAGNUM_TARGET_GLES2
.def("uniform_block_index", [](GL::AbstractShaderProgram& self, const std::string& name) {
return static_cast<PublicizedAbstractShaderProgram&>(self).uniformBlockIndex(name);
}, "Get uniform block index")
#endif
.def("set_uniform", setUniform<Float>, "Set uniform value")
.def("set_uniform", setUniform<Int>, "Set uniform value")
#ifndef MAGNUM_TARGET_GLES2
/** @todo How to distinguish *this*? Python has just an int. */
.def("set_uniform", setUniform<UnsignedInt>, "Set uniform value")
#endif
/** @todo double scalar uniform, how to distinguish? if I add it, it will get a priority over floats */
.def("set_uniform", setUniform<Vector2>, "Set uniform value")
.def("set_uniform", setUniform<Vector3>, "Set uniform value")
.def("set_uniform", setUniform<Vector4>, "Set uniform value")
.def("set_uniform", setUniform<Vector2i>, "Set uniform value")
.def("set_uniform", setUniform<Vector3i>, "Set uniform value")
.def("set_uniform", setUniform<Vector4i>, "Set uniform value")
#ifndef MAGNUM_TARGET_GLES2
.def("set_uniform", setUniform<Vector2ui>, "Set uniform value")
.def("set_uniform", setUniform<Vector3ui>, "Set uniform value")
.def("set_uniform", setUniform<Vector4ui>, "Set uniform value")
#endif
#ifndef MAGNUM_TARGET_GLES
.def("set_uniform", setUniform<Vector2d>, "Set uniform value")
.def("set_uniform", setUniform<Vector3d>, "Set uniform value")
.def("set_uniform", setUniform<Vector4d>, "Set uniform value")
#endif
.def("set_uniform", setUniform<Matrix2x2>, "Set uniform value")
.def("set_uniform", setUniform<Matrix3x3>, "Set uniform value")
.def("set_uniform", setUniform<Matrix4x4>, "Set uniform value")
#ifndef MAGNUM_TARGET_GLES2
.def("set_uniform", setUniform<Matrix2x3>, "Set uniform value")
.def("set_uniform", setUniform<Matrix3x2>, "Set uniform value")
.def("set_uniform", setUniform<Matrix2x4>, "Set uniform value")
.def("set_uniform", setUniform<Matrix4x2>, "Set uniform value")
.def("set_uniform", setUniform<Matrix3x4>, "Set uniform value")
.def("set_uniform", setUniform<Matrix4x3>, "Set uniform value")
#endif
#ifndef MAGNUM_TARGET_GLES
.def("set_uniform", setUniform<Matrix2x3d>, "Set uniform value")
.def("set_uniform", setUniform<Matrix3x2d>, "Set uniform value")
.def("set_uniform", setUniform<Matrix2x4d>, "Set uniform value")
.def("set_uniform", setUniform<Matrix4x2d>, "Set uniform value")
.def("set_uniform", setUniform<Matrix3x4d>, "Set uniform value")
.def("set_uniform", setUniform<Matrix4x3d>, "Set uniform value")
#endif
#ifndef MAGNUM_TARGET_GLES2
.def("set_uniform_block_binding", &PublicizedAbstractShaderProgram::setUniformBlockBinding, "Set uniform block binding")
#endif
;
}
/* (Dynamic) attribute */
py::class_<GL::DynamicAttribute> attribute{m, "Attribute", "Vertex attribute location and type"};

75
src/python/magnum/test/test_gl_gl.py

@ -31,9 +31,69 @@ import unittest
# be run
from . import GLTestCase, setUpModule
import magnum
from magnum import *
from magnum import gl
class AbstractShaderProgram(GLTestCase):
def test(self):
a = gl.AbstractShaderProgram()
if magnum.TARGET_GLES2:
vert = gl.Shader(gl.Version.GLES200, gl.Shader.Type.VERTEX)
elif magnum.TARGET_GLES:
vert = gl.Shader(gl.Version.GLES300, gl.Shader.Type.VERTEX)
else:
vert = gl.Shader(gl.Version.GL300, gl.Shader.Type.VERTEX)
if magnum.TARGET_GLES2:
vert.add_source("""
attribute lowp vec4 position;
uniform lowp mat4 transformationProjectionMatrix;
void main() {
gl_Position = transformationProjectionMatrix*position;
}
""".strip())
else:
vert.add_source("""
in lowp vec4 position;
uniform lowp mat4 transformationProjectionMatrix;
void main() {
gl_Position = transformationProjectionMatrix*position;
}
""".strip())
self.assertTrue(vert.compile())
a.attach_shader(vert)
if magnum.TARGET_GLES2:
frag = gl.Shader(gl.Version.GLES200, gl.Shader.Type.FRAGMENT)
elif magnum.TARGET_GLES:
frag = gl.Shader(gl.Version.GLES300, gl.Shader.Type.FRAGMENT)
else:
frag = gl.Shader(gl.Version.GL300, gl.Shader.Type.FRAGMENT)
if magnum.TARGET_GLES2:
frag.add_source("""
void main() {
gl_FragColor = vec4(0.0);
}
""".strip())
else:
frag.add_source("""
out lowp vec4 color;
void main() {
color = vec4(0.0);
}
""".strip())
self.assertTrue(frag.compile())
a.attach_shader(frag)
a.bind_attribute_location(0, "position")
self.assertTrue(a.link())
self.assertGreaterEqual(a.uniform_location("transformationProjectionMatrix"), 0)
class Buffer(GLTestCase):
def test_init(self):
a = gl.Buffer()
@ -149,3 +209,18 @@ class Renderer(GLTestCase):
gl.Renderer.enable(gl.Renderer.Feature.DEPTH_TEST)
gl.Renderer.disable(gl.Renderer.Feature.FACE_CULLING)
gl.Renderer.set_feature(gl.Renderer.Feature.STENCIL_TEST, True)
class Shader(GLTestCase):
def test(self):
if magnum.TARGET_GLES2:
a = gl.Shader(gl.Version.GLES200, gl.Shader.Type.VERTEX)
elif magnum.TARGET_GLES:
a = gl.Shader(gl.Version.GLES300, gl.Shader.Type.VERTEX)
else:
a = gl.Shader(gl.Version.GL300, gl.Shader.Type.VERTEX)
a.add_source("""
void main() {
gl_Position = vec4(0.0);
}
""")
self.assertTrue(a.compile())

Loading…
Cancel
Save