From f4b5d915d9554f5b3c1c3e8e895c9f20a2ce2ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 2 Sep 2022 20:45:45 +0200 Subject: [PATCH] python: bind also the vector shaders. Last missing piece for the Python text example. --- doc/python/conf.py | 24 ++++- doc/python/magnum.shaders.rst | 30 ++++++ doc/python/pages/changelog.rst | 3 + src/python/magnum/shaders.cpp | 114 ++++++++++++++++++++++ src/python/magnum/test/test_shaders_gl.py | 52 ++++++++++ 5 files changed, 221 insertions(+), 2 deletions(-) diff --git a/doc/python/conf.py b/doc/python/conf.py index d421679..44140fe 100644 --- a/doc/python/conf.py +++ b/doc/python/conf.py @@ -72,6 +72,14 @@ magnum.__annotations__ = { magnum.gl.__annotations__ = { 'default_framebuffer': magnum.gl.DefaultFramebuffer } +magnum.shaders.DistanceFieldVectorGL2D.__annotations__ = { + 'POSITION': magnum.gl.Attribute, + 'TEXTURE_COORDINATES': magnum.gl.Attribute +} +magnum.shaders.DistanceFieldVectorGL3D.__annotations__ = { + 'POSITION': magnum.gl.Attribute, + 'TEXTURE_COORDINATES': magnum.gl.Attribute +} magnum.shaders.FlatGL2D.__annotations__ = { 'POSITION': magnum.gl.Attribute, 'TEXTURE_COORDINATES': magnum.gl.Attribute, @@ -110,15 +118,27 @@ magnum.shaders.PhongGL.__annotations__ = { 'TRANSFORMATION_MATRIX': magnum.gl.Attribute, 'TEXTURE_OFFSET': magnum.gl.Attribute, } +magnum.shaders.VectorGL2D.__annotations__ = { + 'POSITION': magnum.gl.Attribute, + 'TEXTURE_COORDINATES': magnum.gl.Attribute +} +magnum.shaders.VectorGL3D.__annotations__ = { + 'POSITION': magnum.gl.Attribute, + 'TEXTURE_COORDINATES': magnum.gl.Attribute +} # An extremely hacky way to remove noise for shader docs. It doesn't hide # those, but at least puts them way down in the page, removing all docs. # TODO needs a better solution directly in m.css -for shader in [magnum.shaders.FlatGL2D, +for shader in [magnum.shaders.DistanceFieldVectorGL2D, + magnum.shaders.DistanceFieldVectorGL3D, + magnum.shaders.FlatGL2D, magnum.shaders.FlatGL3D, magnum.shaders.VertexColorGL2D, magnum.shaders.VertexColorGL3D, - magnum.shaders.PhongGL]: + magnum.shaders.PhongGL, + magnum.shaders.VectorGL2D, + magnum.shaders.VectorGL3D]: shader.attach_shader = DoNotPrintValue() shader.bind_attribute_location = DoNotPrintValue() shader.bind_fragment_data_location = DoNotPrintValue() diff --git a/doc/python/magnum.shaders.rst b/doc/python/magnum.shaders.rst index 06ee20a..ed593d2 100644 --- a/doc/python/magnum.shaders.rst +++ b/doc/python/magnum.shaders.rst @@ -23,6 +23,21 @@ DEALINGS IN THE SOFTWARE. .. +.. py:class:: magnum.shaders.DistanceFieldVectorGL2D + :data POSITION: Vertex position + :data TEXTURE_COORDINATES: 2D texture coordinates + +.. py:class:: magnum.shaders.DistanceFieldVectorGL3D + :data POSITION: Vertex position + :data TEXTURE_COORDINATES: 2D texture coordinates + +.. py:property:: magnum.shaders.DistanceFieldVectorGL2D.texture_matrix + :raise AttributeError: If the shader was not created with + :ref:`Flags.TEXTURE_TRANSFORMATION` +.. py:property:: magnum.shaders.DistanceFieldVectorGL3D.texture_matrix + :raise AttributeError: If the shader was not created with + :ref:`Flags.TEXTURE_TRANSFORMATION` + .. py:class:: magnum.shaders.FlatGL2D :data POSITION: Vertex position :data TEXTURE_COORDINATES: 2D texture coordinates @@ -115,3 +130,18 @@ :raise AttributeError: If the shader was not created with any of :ref:`Flags.AMBIENT_TEXTURE`, :ref:`Flags.DIFFUSE_TEXTURE`, :ref:`Flags.SPECULAR_TEXTURE` or :ref:`Flags.NORMAL_TEXTURE` + +.. py:class:: magnum.shaders.VectorGL2D + :data POSITION: Vertex position + :data TEXTURE_COORDINATES: 2D texture coordinates + +.. py:class:: magnum.shaders.VectorGL3D + :data POSITION: Vertex position + :data TEXTURE_COORDINATES: 2D texture coordinates + +.. py:property:: magnum.shaders.VectorGL2D.texture_matrix + :raise AttributeError: If the shader was not created with + :ref:`Flags.TEXTURE_TRANSFORMATION` +.. py:property:: magnum.shaders.VectorGL3D.texture_matrix + :raise AttributeError: If the shader was not created with + :ref:`Flags.TEXTURE_TRANSFORMATION` diff --git a/doc/python/pages/changelog.rst b/doc/python/pages/changelog.rst index 0c71bcb..6685a39 100644 --- a/doc/python/pages/changelog.rst +++ b/doc/python/pages/changelog.rst @@ -60,6 +60,9 @@ Changelog lighting features in :ref:`shaders.PhongGL` - Exposed new instancing and texture transformation features in :ref:`shaders.FlatGL2D` and :ref:`shaders.FlatGL3D` +- Exposed :ref:`shaders.DistanceFieldVectorGL2D`, + :ref:`shaders.DistanceFieldVectorGL3D`, :ref:`shaders.VectorGL2D` and + :ref:`shaders.VectorGL3D` shaders - Renamed all helper ``Python.h`` headers to ``PythonBindings.h`` to avoid issues with shitty IDE indexers such as Eclipse, confusing these with Python's ```` diff --git a/src/python/magnum/shaders.cpp b/src/python/magnum/shaders.cpp index b492f2f..d0d737b 100644 --- a/src/python/magnum/shaders.cpp +++ b/src/python/magnum/shaders.cpp @@ -31,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #include "Corrade/PythonBindings.h" @@ -44,6 +46,41 @@ namespace magnum { namespace { +template void distanceFieldVector(PyNonDestructibleClass, GL::AbstractShaderProgram>& c) { + /* Attributes */ + c.attr("POSITION") = GL::DynamicAttribute{typename Shaders::DistanceFieldVectorGL::Position{}}; + c.attr("TEXTURE_COORDINATES") = GL::DynamicAttribute{typename Shaders::DistanceFieldVectorGL::TextureCoordinates{}}; + + /* Methods */ + c + .def(py::init::Flag>(), "Constructor", + py::arg("flags") = typename Shaders::DistanceFieldVectorGL::Flag{}) + + /* Using lambdas to avoid method chaining getting into signatures */ + .def_property_readonly("flags", [](Shaders::DistanceFieldVectorGL& self) { + return typename Shaders::DistanceFieldVectorGL::Flag(UnsignedByte(self.flags())); + }, "Flags") + .def_property("transformation_projection_matrix", nullptr, &Shaders::DistanceFieldVectorGL::setTransformationProjectionMatrix, + "Transformation and projection matrix") + .def_property("texture_matrix", nullptr, [](Shaders::DistanceFieldVectorGL& self, const Matrix3& matrix) { + if(!(self.flags() & Shaders::DistanceFieldVectorGL::Flag::TextureTransformation)) { + PyErr_SetString(PyExc_AttributeError, "the shader was not created with texture transformation enabled"); + throw py::error_already_set{}; + } + + self.setTextureMatrix(matrix); + }, "Texture matrix") + .def_property("color", nullptr, &Shaders::DistanceFieldVectorGL::setColor, "Color") + .def_property("outline_color", nullptr, &Shaders::DistanceFieldVectorGL::setOutlineColor, "Outline color") + .def_property("outline_range", nullptr, [](Shaders::DistanceFieldVectorGL& self, const std::pair& startEnd) { + self.setOutlineRange(startEnd.first, startEnd.second); + }, "Outline range start and end") + .def_property("smoothness", nullptr, &Shaders::DistanceFieldVectorGL::setSmoothness, "Smoothness") + .def("bind_vector_texture", [](Shaders::DistanceFieldVectorGL& self, GL::Texture2D& texture) { + self.bindVectorTexture(texture); + }, "Bind a vector texture"); +} + template void flat(PyNonDestructibleClass, GL::AbstractShaderProgram>& c) { /* Attributes */ c.attr("POSITION") = GL::DynamicAttribute{typename Shaders::FlatGL::Position{}}; @@ -91,6 +128,37 @@ template void flat(PyNonDestructibleClass void vector(PyNonDestructibleClass, GL::AbstractShaderProgram>& c) { + /* Attributes */ + c.attr("POSITION") = GL::DynamicAttribute{typename Shaders::VectorGL::Position{}}; + c.attr("TEXTURE_COORDINATES") = GL::DynamicAttribute{typename Shaders::VectorGL::TextureCoordinates{}}; + + /* Methods */ + c + .def(py::init::Flag>(), "Constructor", + py::arg("flags") = typename Shaders::VectorGL::Flag{}) + + /* Using lambdas to avoid method chaining getting into signatures */ + .def_property_readonly("flags", [](Shaders::VectorGL& self) { + return typename Shaders::VectorGL::Flag(UnsignedByte(self.flags())); + }, "Flags") + .def_property("transformation_projection_matrix", nullptr, &Shaders::VectorGL::setTransformationProjectionMatrix, + "Transformation and projection matrix") + .def_property("texture_matrix", nullptr, [](Shaders::VectorGL& self, const Matrix3& matrix) { + if(!(self.flags() & Shaders::VectorGL::Flag::TextureTransformation)) { + PyErr_SetString(PyExc_AttributeError, "the shader was not created with texture transformation enabled"); + throw py::error_already_set{}; + } + + self.setTextureMatrix(matrix); + }, "Texture matrix") + .def_property("color", nullptr, &Shaders::VectorGL::setColor, "Fill color") + .def_property("background_color", nullptr, &Shaders::VectorGL::setBackgroundColor, "Background color") + .def("bind_vector_texture", [](Shaders::VectorGL& self, GL::Texture2D& texture) { + self.bindVectorTexture(texture); + }, "Bind a vector texture"); +} + template void vertexColor(PyNonDestructibleClass, GL::AbstractShaderProgram>& c) { /* Attributes */ c.attr("POSITION") = GL::DynamicAttribute{typename Shaders::VertexColorGL::Position{}}; @@ -118,6 +186,29 @@ void shaders(py::module_& m) { py::module_::import("magnum.gl"); #endif + /* 2D/3D distance field vector shader */ + { + PyNonDestructibleClass distanceFieldVectorGL2D{m, + "DistanceFieldVectorGL2D", "2D distance field vector OpenGL shader"}; + PyNonDestructibleClass distanceFieldVectorGL3D{m, + "DistanceFieldVectorGL3D", "3D distance field vector OpenGL shader"}; + + /* The flags are currently the same type for both 2D and 3D and pybind + doesn't want to have a single type registered twice, so doing it + this way instead */ + py::enum_ flags{distanceFieldVectorGL2D, "Flags", "Flags"}; + flags + .value("TEXTURE_TRANSFORMATION", Shaders::DistanceFieldVectorGL2D::Flag::TextureTransformation) + .value("NONE", Shaders::DistanceFieldVectorGL2D::Flag{}) + ; + distanceFieldVectorGL3D.attr("Flags") = flags; + + distanceFieldVector(distanceFieldVectorGL2D); + distanceFieldVector(distanceFieldVectorGL3D); + + corrade::enumOperators(flags); + } + /* 2D/3D flat shader */ { PyNonDestructibleClass flatGL2D{m, @@ -315,6 +406,29 @@ void shaders(py::module_& m) { }, "Bind textures", py::arg("ambient") = nullptr, py::arg("diffuse") = nullptr, py::arg("specular") = nullptr, py::arg("normal") = nullptr) ; } + + /* 2D/3D vector shader */ + { + PyNonDestructibleClass vectorGL2D{m, + "VectorGL2D", "2D vector OpenGL shader"}; + PyNonDestructibleClass vectorGL3D{m, + "VectorGL3D", "3D vector OpenGL shader"}; + + /* The flags are currently the same type for both 2D and 3D and pybind + doesn't want to have a single type registered twice, so doing it + this way instead */ + py::enum_ flags{vectorGL2D, "Flags", "Flags"}; + flags + .value("TEXTURE_TRANSFORMATION", Shaders::VectorGL2D::Flag::TextureTransformation) + .value("NONE", Shaders::VectorGL2D::Flag{}) + ; + vectorGL3D.attr("Flags") = flags; + + vector(vectorGL2D); + vector(vectorGL3D); + + corrade::enumOperators(flags); + } } } diff --git a/src/python/magnum/test/test_shaders_gl.py b/src/python/magnum/test/test_shaders_gl.py index 5196678..a67271f 100644 --- a/src/python/magnum/test/test_shaders_gl.py +++ b/src/python/magnum/test/test_shaders_gl.py @@ -32,6 +32,33 @@ from . import GLTestCase, setUpModule from magnum import * from magnum import gl, shaders +class DistanceFieldGL(GLTestCase): + def test_init(self): + a = shaders.DistanceFieldVectorGL3D() + self.assertEqual(a.flags, shaders.DistanceFieldVectorGL3D.Flags.NONE) + + b = shaders.DistanceFieldVectorGL3D(shaders.DistanceFieldVectorGL3D.Flags.TEXTURE_TRANSFORMATION) + self.assertEqual(b.flags, shaders.DistanceFieldVectorGL3D.Flags.TEXTURE_TRANSFORMATION) + + def test_uniforms_bindings(self): + a = shaders.DistanceFieldVectorGL3D(shaders.DistanceFieldVectorGL3D.Flags.TEXTURE_TRANSFORMATION) + a.color = (0.5, 1.0, 0.9) + a.outline_color = (1.0, 0.5, 0.9, 0.3) + a.outline_range = (0.5, 0.8) + a.smoothness = 0.1 + a.transformation_projection_matrix = Matrix4.translation(Vector3.x_axis()) + a.texture_matrix = Matrix3() + + texture = gl.Texture2D() + texture.set_storage(1, gl.TextureFormat.R8, Vector2i(8)) + a.bind_vector_texture(texture) + + def test_uniforms_bindings_errors(self): + a = shaders.DistanceFieldVectorGL2D() + + with self.assertRaisesRegex(AttributeError, "the shader was not created with texture transformation enabled"): + a.texture_matrix = Matrix3() + class FlatGL(GLTestCase): def test_init(self): a = shaders.FlatGL3D() @@ -136,3 +163,28 @@ class PhongGL(GLTestCase): a.bind_normal_texture(texture) with self.assertRaisesRegex(AttributeError, "the shader was not created with any textures enabled"): a.bind_textures(diffuse=texture) + +class DistanceFieldGL(GLTestCase): + def test_init(self): + a = shaders.VectorGL3D() + self.assertEqual(a.flags, shaders.VectorGL3D.Flags.NONE) + + b = shaders.VectorGL3D(shaders.VectorGL3D.Flags.TEXTURE_TRANSFORMATION) + self.assertEqual(b.flags, shaders.VectorGL3D.Flags.TEXTURE_TRANSFORMATION) + + def test_uniforms_bindings(self): + a = shaders.VectorGL3D(shaders.VectorGL3D.Flags.TEXTURE_TRANSFORMATION) + a.color = (0.5, 1.0, 0.9) + a.background_color = (1.0, 0.5, 0.9, 0.3) + a.transformation_projection_matrix = Matrix4.translation(Vector3.x_axis()) + a.texture_matrix = Matrix3() + + texture = gl.Texture2D() + texture.set_storage(1, gl.TextureFormat.R8, Vector2i(8)) + a.bind_vector_texture(texture) + + def test_uniforms_bindings_errors(self): + a = shaders.VectorGL2D() + + with self.assertRaisesRegex(AttributeError, "the shader was not created with texture transformation enabled"): + a.texture_matrix = Matrix3()