mirror of
https://github.com/thp/pyotherside.git
synced 2025-01-28 23:52:55 +08:00
Unified renderer interface for PyFBO and PyGLArea.
This commit is contained in:
parent
092370631a
commit
77ecb76492
@ -544,16 +544,20 @@ following properties:
|
||||
Initialize OpenGL resources required for rendering.
|
||||
This method is optional.
|
||||
|
||||
.. function:: IRenderer.render(x, y, width, height)
|
||||
.. function:: IRenderer.reshape(x, y, width, height)
|
||||
|
||||
Render within the given area.
|
||||
It is the renderer's responsibility to confine itself to that area.
|
||||
Also, it must unbind any used resources to leave the context in a clean
|
||||
state.
|
||||
Called when the geometry has changed.
|
||||
|
||||
``(x, y)`` is the position of the bottom left corner of the area, in
|
||||
window coordinates, e.g. (0, 0) is the bottom left corner of the window.
|
||||
|
||||
.. function:: IRenderer.render()
|
||||
|
||||
Render to the OpenGL context.
|
||||
|
||||
It is the renderer's responsibility to unbind any used resources to leave
|
||||
the context in a clean state.
|
||||
|
||||
.. function:: IRenderer.cleanup()
|
||||
|
||||
Free any resources allocated by :func:`IRenderer.init`.
|
||||
@ -905,7 +909,7 @@ The example below shows how to do raw OpenGL rendering in PyOpenGL using a
|
||||
PyGLArea. It has been adapted from the tutorial in the Qt documentation at
|
||||
http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html.
|
||||
|
||||
**pyglarea.py**
|
||||
**renderer.py**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -957,10 +961,10 @@ http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html.
|
||||
self.vertices_attr = glGetAttribLocation(self.program, b'vertices')
|
||||
self.t_attr = glGetUniformLocation(self.program, b't')
|
||||
|
||||
def render(self, x, y, width, height):
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
def reshape(self, x, y, width, height):
|
||||
glViewport(x, y, width, height)
|
||||
|
||||
def render(self):
|
||||
glUseProgram(self.program)
|
||||
try:
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
@ -1041,8 +1045,8 @@ http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html.
|
||||
|
||||
Component.onCompleted: {
|
||||
addImportPath(Qt.resolvedUrl('.'));
|
||||
importModule('pyglarea', function () {
|
||||
call('pyglarea.Renderer', [], function (renderer) {
|
||||
importModule('renderer', function () {
|
||||
call('renderer', [], function (renderer) {
|
||||
glArea.renderer = renderer;
|
||||
});
|
||||
});
|
||||
|
@ -1,74 +0,0 @@
|
||||
import numpy
|
||||
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL.shaders import compileShader, compileProgram
|
||||
|
||||
|
||||
VERTEX_SHADER = """#version 130
|
||||
attribute highp vec4 vertices;
|
||||
varying highp vec2 coords;
|
||||
|
||||
void main() {
|
||||
gl_Position = vertices;
|
||||
coords = vertices.xy;
|
||||
}
|
||||
"""
|
||||
|
||||
FRAGMENT_SHADER = """#version 130
|
||||
uniform lowp float t;
|
||||
varying highp vec2 coords;
|
||||
void main() {
|
||||
lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));
|
||||
i = smoothstep(t - 0.8, t + 0.8, i);
|
||||
i = floor(i * 20.) / 20.;
|
||||
gl_FragColor = vec4(coords * .5 + .5, i, i);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
|
||||
def __init__(self):
|
||||
self.t = 0.0
|
||||
self.values = numpy.array([
|
||||
-1.0, -1.0,
|
||||
1.0, -1.0,
|
||||
-1.0, 1.0,
|
||||
1.0, 1.0
|
||||
], dtype=numpy.float32)
|
||||
|
||||
def set_t(self, t):
|
||||
self.t = t
|
||||
|
||||
def init(self):
|
||||
self.vertexbuffer = glGenBuffers(1)
|
||||
vertex_shader = compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
|
||||
fragment_shader = compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
|
||||
self.program = compileProgram(vertex_shader, fragment_shader)
|
||||
self.vertices_attr = glGetAttribLocation(self.program, b'vertices')
|
||||
self.t_attr = glGetUniformLocation(self.program, b't')
|
||||
|
||||
def render(self):
|
||||
glUseProgram(self.program)
|
||||
try:
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glClearColor(0, 0, 0, 1)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.vertexbuffer)
|
||||
glEnableVertexAttribArray(self.vertices_attr)
|
||||
glBufferData(GL_ARRAY_BUFFER, self.values, GL_STATIC_DRAW)
|
||||
glVertexAttribPointer(self.vertices_attr, 2, GL_FLOAT, GL_FALSE, 0, None)
|
||||
glUniform1f(self.t_attr, self.t)
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
|
||||
finally:
|
||||
glDisableVertexAttribArray(self.vertices_attr)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
glUseProgram(0)
|
||||
|
||||
def cleanup(self):
|
||||
glDeleteProgram(self.program)
|
||||
glDeleteBuffers(1, [self.vertexbuffer])
|
@ -147,8 +147,8 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
addImportPath(Qt.resolvedUrl('.'));
|
||||
importModule('pyfbo', function () {
|
||||
call('pyfbo.Renderer', [], function (renderer) {
|
||||
importModule('renderer', function () {
|
||||
call('renderer.Renderer', [], function (renderer) {
|
||||
fbo.renderer = renderer;
|
||||
});
|
||||
});
|
||||
|
@ -50,8 +50,8 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
addImportPath(Qt.resolvedUrl('.'));
|
||||
importModule('pyglarea', function () {
|
||||
call('pyglarea.Renderer', [], function (renderer) {
|
||||
importModule('renderer', function () {
|
||||
call('renderer.Renderer', [], function (renderer) {
|
||||
glArea.renderer = renderer;
|
||||
});
|
||||
});
|
||||
|
@ -46,10 +46,10 @@ class Renderer(object):
|
||||
self.vertices_attr = glGetAttribLocation(self.program, b'vertices')
|
||||
self.t_attr = glGetUniformLocation(self.program, b't')
|
||||
|
||||
def render(self, x, y, width, height):
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
def reshape(self, x, y, width, height):
|
||||
glViewport(x, y, width, height)
|
||||
|
||||
def render(self):
|
||||
glUseProgram(self.program)
|
||||
try:
|
||||
glDisable(GL_DEPTH_TEST)
|
@ -19,6 +19,7 @@
|
||||
#include "pyfbo.h"
|
||||
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
#include <QSize>
|
||||
|
||||
|
||||
class PyFboRenderer : public QQuickFramebufferObject::Renderer
|
||||
@ -26,6 +27,7 @@ class PyFboRenderer : public QQuickFramebufferObject::Renderer
|
||||
public:
|
||||
PyFboRenderer()
|
||||
: m_renderer(0)
|
||||
, m_size(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -39,8 +41,7 @@ public:
|
||||
|
||||
void render()
|
||||
{
|
||||
if (!m_renderer)
|
||||
return;
|
||||
if (m_renderer)
|
||||
m_renderer->render();
|
||||
}
|
||||
|
||||
@ -48,24 +49,33 @@ public:
|
||||
{
|
||||
PyFbo *pyFbo = static_cast<PyFbo *>(item);
|
||||
|
||||
if (pyFbo->renderer() == m_rendererRef)
|
||||
return;
|
||||
|
||||
if (pyFbo->renderer() != m_rendererRef) {
|
||||
// The renderer has changed.
|
||||
if (m_renderer) {
|
||||
m_renderer->cleanup();
|
||||
delete m_renderer;
|
||||
m_renderer = 0;
|
||||
}
|
||||
|
||||
m_rendererRef = pyFbo->renderer();
|
||||
if (!m_rendererRef.isNull()) {
|
||||
m_renderer = new PyGLRenderer(m_rendererRef, false);
|
||||
m_renderer = new PyGLRenderer(m_rendererRef);
|
||||
m_renderer->init();
|
||||
m_sizeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_renderer && m_sizeChanged) {
|
||||
// The size has changed.
|
||||
m_renderer->reshape(QRect(QPoint(0, 0), m_size));
|
||||
m_sizeChanged = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
|
||||
{
|
||||
m_size = size;
|
||||
m_sizeChanged = true;
|
||||
QOpenGLFramebufferObjectFormat format;
|
||||
// TODO: get the FBO format from the PyGLRenderer.
|
||||
return new QOpenGLFramebufferObject(size, format);
|
||||
@ -73,12 +83,10 @@ public:
|
||||
private:
|
||||
QVariant m_rendererRef;
|
||||
PyGLRenderer *m_renderer;
|
||||
QSize m_size;
|
||||
bool m_sizeChanged;
|
||||
};
|
||||
|
||||
PyFbo::PyFbo()
|
||||
{
|
||||
}
|
||||
|
||||
void PyFbo::setRenderer(QVariant rendererRef)
|
||||
{
|
||||
if (rendererRef == m_rendererRef)
|
||||
|
@ -30,7 +30,6 @@ class PyFbo : public QQuickFramebufferObject
|
||||
Q_PROPERTY(QVariant renderer READ renderer WRITE setRenderer)
|
||||
|
||||
public:
|
||||
PyFbo();
|
||||
Renderer *createRenderer() const;
|
||||
|
||||
QVariant renderer() const { return m_rendererRef; };
|
||||
|
@ -115,10 +115,10 @@ void PyGLArea::render()
|
||||
if (!m_renderer)
|
||||
return;
|
||||
QPointF pos = mapToScene(QPointF(.0, .0));
|
||||
m_renderer->setRect(
|
||||
m_renderer->reshape(
|
||||
QRect(
|
||||
(long)pos.x(), (long)(window()->height() - this->height() - pos.y()),
|
||||
(long)this->width(), (long)this->height()
|
||||
this->width(), this->height()
|
||||
)
|
||||
);
|
||||
m_renderer->render();
|
||||
|
@ -25,180 +25,129 @@
|
||||
#include <QMetaType>
|
||||
|
||||
|
||||
PyGLRenderer::PyGLRenderer(QVariant pyRenderer, bool useRect)
|
||||
PyGLRenderer::PyGLRenderer(QVariant pyRenderer)
|
||||
: m_pyRendererObject(0)
|
||||
, m_initMethod(0)
|
||||
, m_reshapeMethod(0)
|
||||
, m_renderMethod(0)
|
||||
, m_cleanupMethod(0)
|
||||
, m_initialized(false)
|
||||
{
|
||||
m_pyRenderer = pyRenderer;
|
||||
m_useRect = useRect;
|
||||
|
||||
ENSURE_GIL_STATE;
|
||||
|
||||
if (pyRenderer.userType() != qMetaTypeId<PyObjectRef>()) {
|
||||
qWarning() << "Renderer must be of type PyObjectRef (got "
|
||||
<< pyRenderer << ").";
|
||||
return;
|
||||
}
|
||||
|
||||
m_pyRendererObject = pyRenderer.value<PyObjectRef>().newRef();
|
||||
|
||||
if (PyObject_HasAttrString(m_pyRendererObject, "render")) {
|
||||
m_renderMethod = PyObject_GetAttrString(m_pyRendererObject, "render");
|
||||
if (!m_renderMethod) {
|
||||
qWarning() << "Error getting render method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Renderer has no render method.";
|
||||
}
|
||||
|
||||
if (PyObject_HasAttrString(m_pyRendererObject, "init")) {
|
||||
m_initMethod = PyObject_GetAttrString(m_pyRendererObject, "init");
|
||||
if (!m_initMethod) {
|
||||
qWarning() << "Error getting init method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyObject_HasAttrString(m_pyRendererObject, "reshape")) {
|
||||
m_reshapeMethod = PyObject_GetAttrString(m_pyRendererObject, "reshape");
|
||||
if (!m_reshapeMethod) {
|
||||
qWarning() << "Error getting reshape method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyObject_HasAttrString(m_pyRendererObject, "cleanup")) {
|
||||
m_cleanupMethod = PyObject_GetAttrString(m_pyRendererObject, "cleanup");
|
||||
if (!m_cleanupMethod) {
|
||||
qWarning() << "Error getting cleanup method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PyGLRenderer::~PyGLRenderer()
|
||||
{
|
||||
if (m_pyRendererObject) {
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
ENSURE_GIL_STATE;
|
||||
Py_CLEAR(m_initMethod);
|
||||
Py_CLEAR(m_reshapeMethod);
|
||||
Py_CLEAR(m_renderMethod);
|
||||
Py_CLEAR(m_cleanupMethod);
|
||||
Py_CLEAR(m_pyRendererObject);
|
||||
if (m_renderMethod)
|
||||
Py_DECREF(m_renderMethod);
|
||||
priv->leave();
|
||||
m_pyRendererObject = 0;
|
||||
m_renderMethod = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PyGLRenderer::setRect(QRect rect) {
|
||||
m_rect = rect;
|
||||
}
|
||||
|
||||
void PyGLRenderer::init() {
|
||||
if (m_initialized)
|
||||
if (m_initialized || !m_initMethod)
|
||||
return;
|
||||
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
|
||||
PyObject *pyRendererObject = getPyRendererObject();
|
||||
if (!pyRendererObject || pyRendererObject == Py_None) {
|
||||
priv->leave();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PyObject_HasAttrString(m_pyRendererObject, "init")) {
|
||||
// Optional init() method not found, consider the renderer initialized.
|
||||
priv->leave();
|
||||
m_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
PyObject *initMethod = PyObject_GetAttrString(m_pyRendererObject, "init");
|
||||
if (!initMethod) {
|
||||
qWarning() << "Failed to get init method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
priv->leave();
|
||||
return;
|
||||
}
|
||||
ENSURE_GIL_STATE;
|
||||
|
||||
PyObject *args = PyTuple_New(0);
|
||||
PyObject *o = PyObject_Call(initMethod, args, NULL);
|
||||
PyObject *o = PyObject_Call(m_initMethod, args, NULL);
|
||||
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(initMethod);
|
||||
priv->leave();
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
void PyGLRenderer::render()
|
||||
void PyGLRenderer::reshape(QRect geometry)
|
||||
{
|
||||
if (!m_initialized)
|
||||
if (!m_initialized || !m_reshapeMethod)
|
||||
return;
|
||||
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
|
||||
PyObject *renderMethod = getRenderMethod();
|
||||
if (!renderMethod) {
|
||||
qWarning() << "Failed to get render method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
priv->leave();
|
||||
return;
|
||||
}
|
||||
|
||||
PyObject *o = NULL;
|
||||
if (m_useRect) {
|
||||
// Call the paintGL callback with arguments x, y, width, height.
|
||||
// Call the reshape callback with arguments x, y, width, height.
|
||||
// These are the boundaries in which the callback should render,
|
||||
// though it may choose to ignore them and simply paint anywhere over
|
||||
// (or below) the QML scene.
|
||||
// (x, y) is the bottom left corner.
|
||||
// (x + width, y + height) is the top right corner.
|
||||
PyObject *x = PyLong_FromLong(m_rect.x());
|
||||
PyObject *y = PyLong_FromLong(m_rect.y());
|
||||
PyObject *width = PyLong_FromLong(m_rect.width());
|
||||
PyObject *height = PyLong_FromLong(m_rect.height());
|
||||
PyObject *args = PyTuple_Pack(4, x, y, width, height);
|
||||
o = PyObject_Call(renderMethod, args, NULL);
|
||||
Py_DECREF(x);
|
||||
Py_DECREF(y);
|
||||
Py_DECREF(width);
|
||||
Py_DECREF(height);
|
||||
PyObject *args = Py_BuildValue(
|
||||
"llll", geometry.x(), geometry.y(), geometry.width(), geometry.height()
|
||||
);
|
||||
PyObject *o = PyObject_Call(m_reshapeMethod, args, NULL);
|
||||
Py_DECREF(args);
|
||||
} else {
|
||||
PyObject *args = PyTuple_New(0);
|
||||
o = PyObject_Call(renderMethod, args, NULL);
|
||||
Py_DECREF(args);
|
||||
}
|
||||
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||
priv->leave();
|
||||
}
|
||||
|
||||
void PyGLRenderer::cleanup()
|
||||
void PyGLRenderer::render()
|
||||
{
|
||||
if (!m_initialized)
|
||||
if (!m_initialized || !m_renderMethod)
|
||||
return;
|
||||
|
||||
ENSURE_GIL_STATE;
|
||||
|
||||
PyObject *pyRendererObject = getPyRendererObject();
|
||||
if (!pyRendererObject || pyRendererObject == Py_None ||
|
||||
!PyObject_HasAttrString(m_pyRendererObject, "cleanup")) {
|
||||
priv->leave();
|
||||
return;
|
||||
PyObject *args = PyTuple_New(0);
|
||||
PyObject *o = PyObject_Call(m_renderMethod, args, NULL);
|
||||
Py_DECREF(args);
|
||||
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||
}
|
||||
|
||||
PyObject *cleanupMethod = PyObject_GetAttrString(m_pyRendererObject, "cleanup");
|
||||
if (!cleanupMethod) {
|
||||
qWarning() << "Failed to get cleanup method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
priv->leave();
|
||||
void PyGLRenderer::cleanup()
|
||||
{
|
||||
if (!m_initialized || !m_cleanupMethod)
|
||||
return;
|
||||
}
|
||||
|
||||
ENSURE_GIL_STATE;
|
||||
|
||||
PyObject *args = PyTuple_New(0);
|
||||
PyObject *o = PyObject_Call(cleanupMethod, args, NULL);
|
||||
PyObject *o = PyObject_Call(m_cleanupMethod, args, NULL);
|
||||
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||
m_initialized = false;
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(cleanupMethod);
|
||||
priv->leave();
|
||||
}
|
||||
|
||||
PyObject *PyGLRenderer::getPyRendererObject() {
|
||||
if (m_pyRendererObject) {
|
||||
return m_pyRendererObject;
|
||||
}
|
||||
if (m_pyRenderer.userType() != qMetaTypeId<PyObjectRef>()) {
|
||||
qWarning() << "Renderer must be of type PyObjectRef (got "
|
||||
<< m_pyRenderer << ").";
|
||||
return NULL;
|
||||
}
|
||||
m_pyRendererObject = m_pyRenderer.value<PyObjectRef>().newRef();
|
||||
return m_pyRendererObject;
|
||||
}
|
||||
|
||||
PyObject *PyGLRenderer::getRenderMethod() {
|
||||
if (m_renderMethod) {
|
||||
return m_renderMethod;
|
||||
}
|
||||
|
||||
PyObject *pyRendererObject = getPyRendererObject();
|
||||
if (!pyRendererObject || pyRendererObject == Py_None) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!PyObject_HasAttrString(m_pyRendererObject, "render")) {
|
||||
qWarning() << "Renderer has no render method.";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *m_renderMethod = PyObject_GetAttrString(m_pyRendererObject, "render");
|
||||
if (!m_renderMethod) {
|
||||
qWarning() << "Failed to get render method of renderer.";
|
||||
PyErr_PrintEx(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_renderMethod;
|
||||
}
|
||||
|
@ -29,25 +29,21 @@
|
||||
class PyGLRenderer {
|
||||
|
||||
public:
|
||||
PyGLRenderer(QVariant pyRenderer, bool useRect=true);
|
||||
PyGLRenderer(QVariant pyRenderer);
|
||||
~PyGLRenderer();
|
||||
|
||||
void init();
|
||||
void reshape(QRect geometry);
|
||||
void render();
|
||||
void cleanup();
|
||||
|
||||
void setRect(QRect rect);
|
||||
|
||||
private:
|
||||
QRect m_rect;
|
||||
QVariant m_pyRenderer;
|
||||
PyObject *m_pyRendererObject;
|
||||
PyObject *m_initMethod;
|
||||
PyObject *m_reshapeMethod;
|
||||
PyObject *m_renderMethod;
|
||||
PyObject *m_cleanupMethod;
|
||||
bool m_initialized;
|
||||
bool m_useRect;
|
||||
|
||||
PyObject *getPyRendererObject();
|
||||
PyObject *getRenderMethod();
|
||||
};
|
||||
|
||||
#endif /* PYOTHERSIDE_PYGLRENDERER_H */
|
||||
|
@ -32,7 +32,7 @@ HEADERS += qpython_imageprovider.h
|
||||
SOURCES += pyglarea.cpp pyglrenderer.cpp
|
||||
HEADERS += pyglarea.h pyglrenderer.h
|
||||
|
||||
# PyGLFbo
|
||||
# PyFBO
|
||||
SOURCES += pyfbo.cpp
|
||||
HEADERS += pyfbo.h
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user