diff --git a/examples/viewports.py b/examples/viewports.py new file mode 100644 index 00000000..0223e934 --- /dev/null +++ b/examples/viewports.py @@ -0,0 +1,66 @@ +import zengl + +from window import Window + +window = Window(1280, 720) +ctx = zengl.instance(zengl.context()) + +window.set_caption('Hello World | Vendor: %s | Renderer: %s | Version: %s' % ctx.info) + +image = ctx.image(window.size, 'rgba8unorm', samples=4) + +triangle = ctx.renderer( + vertex_shader=''' + #version 330 + + out vec3 v_color; + + vec2 positions[3] = vec2[]( + vec2(0.0, 0.8), + vec2(-0.6, -0.8), + vec2(0.6, -0.8) + ); + + vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) + ); + + void main() { + gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); + v_color = colors[gl_VertexID]; + } + ''', + fragment_shader=''' + #version 330 + + in vec3 v_color; + + layout (location = 0) out vec4 out_color; + + void main() { + out_color = vec4(v_color, 1.0); + } + ''', + framebuffer=[image], + topology='triangles', + vertex_count=3, +) + + +@window.render +def render(): + image.clear(1.0, 1.0, 1.0, 1.0) + triangle.viewport = (0, 0, 640, 360) + triangle.render() + triangle.viewport = (0, 360, 640, 360) + triangle.render() + triangle.viewport = (640, 0, 640, 360) + triangle.render() + triangle.viewport = (640, 360, 640, 360) + triangle.render() + image.blit() + + +window.run() diff --git a/zengl.cpp b/zengl.cpp index 6427eaaa..d234d6a4 100644 --- a/zengl.cpp +++ b/zengl.cpp @@ -56,11 +56,10 @@ struct Instance { DescriptorSetBuffers * current_buffers; DescriptorSetImages * current_images; GlobalSettings * current_global_settings; + Viewport viewport; int current_framebuffer; int current_program; int current_vertex_array; - int viewport_width; - int viewport_height; int default_texture_unit; GLMethods gl; }; @@ -98,8 +97,7 @@ struct Renderer { int vertex_count; int instance_count; int index_type; - int framebuffer_width; - int framebuffer_height; + Viewport viewport; DescriptorSetBuffers * descriptor_set_buffers; DescriptorSetImages * descriptor_set_images; GlobalSettings * global_settings; @@ -477,8 +475,7 @@ Instance * meth_instance(PyObject * self, PyObject * vargs, PyObject * kwargs) { res->current_framebuffer = 0; res->current_program = 0; res->current_vertex_array = 0; - res->viewport_width = -1; - res->viewport_height = -1; + res->viewport = {}; res->default_texture_unit = default_texture_unit; res->gl = gl; return res; @@ -748,6 +745,7 @@ Renderer * Instance_meth_renderer(Instance * self, PyObject * vargs, PyObject * "stencil", "blending", "polygon_offset", + "viewport", NULL, }; @@ -772,11 +770,12 @@ Renderer * Instance_meth_renderer(Instance * self, PyObject * vargs, PyObject * PyObject * stencil = Py_False; PyObject * blending = Py_False; PyObject * polygon_offset = Py_False; + PyObject * viewport = Py_None; int args_ok = PyArg_ParseTupleAndKeywords( vargs, kwargs, - "|$OOOsOOOOiipOOOOOOOOOO", + "|$OOOsOOOOiipOOOOOOOOOOO", keywords, &vertex_shader, &fragment_shader, @@ -798,7 +797,8 @@ Renderer * Instance_meth_renderer(Instance * self, PyObject * vargs, PyObject * &depth, &stencil, &blending, - &polygon_offset + &polygon_offset, + &viewport ); if (!args_ok) { @@ -934,7 +934,15 @@ Renderer * Instance_meth_renderer(Instance * self, PyObject * vargs, PyObject * DescriptorSetImages * descriptor_set_images = build_descriptor_set_images(self, sampler_bindings); GlobalSettings * global_settings = build_global_settings(self, settings); - Image * first_image = (Image *)PySequence_GetItem(framebuffer_images, 0); + Viewport viewport_value = to_viewport(viewport); + if (!viewport_value.viewport) { + if (PyErr_Occurred()) { + return NULL; + } + Image * first_image = (Image *)PySequence_GetItem(framebuffer_images, 0); + viewport_value.width = (short)first_image->width; + viewport_value.height = (short)first_image->height; + } Renderer * res = PyObject_New(Renderer, self->module_state->Renderer_type); res->instance = self; @@ -945,8 +953,7 @@ Renderer * Instance_meth_renderer(Instance * self, PyObject * vargs, PyObject * res->vertex_count = vertex_count; res->instance_count = instance_count; res->index_type = index_type; - res->framebuffer_width = first_image->width; - res->framebuffer_height = first_image->height; + res->viewport = viewport_value; res->descriptor_set_buffers = descriptor_set_buffers; res->descriptor_set_images = descriptor_set_images; res->global_settings = global_settings; @@ -1267,8 +1274,8 @@ PyObject * Image_meth_blit(Image * self, PyObject * vargs, PyObject * kwargs) { PyObject * Renderer_meth_render(Renderer * self) { const GLMethods & gl = self->instance->gl; - if (self->framebuffer_width != self->instance->viewport_width || self->framebuffer_height != self->instance->viewport_height) { - gl.Viewport(0, 0, self->framebuffer_width, self->framebuffer_height); + if (self->viewport.viewport != self->instance->viewport.viewport) { + gl.Viewport(self->viewport.x, self->viewport.y, self->viewport.width, self->viewport.height); } bind_global_settings(self->instance, self->global_settings); bind_framebuffer(self->instance, self->framebuffer); @@ -1284,6 +1291,19 @@ PyObject * Renderer_meth_render(Renderer * self) { Py_RETURN_NONE; } +PyObject * Renderer_get_viewport(Renderer * self) { + return Py_BuildValue("iiii", self->viewport.x, self->viewport.y, self->viewport.width, self->viewport.height); +} + +int Renderer_set_viewport(Renderer * self, PyObject * viewport) { + Viewport viewport_value = to_viewport(viewport); + if (!viewport_value.viewport) { + return -1; + } + self->viewport = viewport_value; + return 0; +} + struct vec3 { double x, y, z; }; @@ -1556,6 +1576,11 @@ PyMethodDef Renderer_methods[] = { {}, }; +PyGetSetDef Renderer_getset[] = { + {"viewport", (getter)Renderer_get_viewport, (setter)Renderer_set_viewport, NULL, NULL}, + {}, +}; + PyMemberDef Renderer_members[] = { {"vertex_count", T_OBJECT_EX, offsetof(Renderer, vertex_count), 0, NULL}, {"instance_count", T_OBJECT_EX, offsetof(Renderer, instance_count), 0, NULL}, @@ -1585,6 +1610,7 @@ PyType_Slot Image_slots[] = { PyType_Slot Renderer_slots[] = { {Py_tp_methods, Renderer_methods}, + {Py_tp_getset, Renderer_getset}, {Py_tp_members, Renderer_members}, {Py_tp_dealloc, default_dealloc}, {}, diff --git a/zengl.hpp b/zengl.hpp index 241de25d..2ef113e1 100644 --- a/zengl.hpp +++ b/zengl.hpp @@ -372,6 +372,16 @@ struct StencilSettings { int reference; }; +union Viewport { + unsigned long long viewport; + struct { + short x; + short y; + short width; + short height; + }; +}; + VertexFormat get_vertex_format(const char * format) { if (!strcmp(format, "uint8x2")) return {GL_UNSIGNED_BYTE, 2, false, true}; if (!strcmp(format, "uint8x4")) return {GL_UNSIGNED_BYTE, 4, false, true}; @@ -476,6 +486,30 @@ PyObject * to_str(const unsigned char * ptr) { return PyUnicode_FromString((char *)ptr); } +Viewport to_viewport(PyObject * obj) { + Viewport res = {}; + if (obj == Py_None) { + return res; + } + PyObject * seq = PySequence_Fast(obj, "viewport is not iterable"); + if (!seq) { + return res; + } + int size = (int)PySequence_Size(seq); + if (size != 2 && size != 4) { + return res; + } + PyObject ** items = PySequence_Fast_ITEMS(seq); + res.x = (short)PyLong_AsLong(items[0]); + res.y = (short)PyLong_AsLong(items[1]); + res.width = (short)PyLong_AsLong(items[2]); + res.height = (short)PyLong_AsLong(items[3]); + if (PyErr_Occurred()) { + return res; + } + return res; +} + void * load_method(PyObject * context, const char * method) { PyObject * res = PyObject_CallMethod(context, "load", "s", method); if (!res) {