Skip to content

Commit

Permalink
First working GLVisualize example
Browse files Browse the repository at this point in the history
  • Loading branch information
barche committed Sep 29, 2016
1 parent 1db61dc commit 74efa7a
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 36 deletions.
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
julia 0.4
BinDeps
CxxWrap 0.1.4
CxxWrap 0.1.6+
2 changes: 2 additions & 0 deletions deps/src/qmlwrap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ if(WIN32)
endif(WIN32)

add_library(qmlwrap SHARED
glvisualize_viewport.hpp
glvisualize_viewport.cpp
julia_api.hpp
julia_api.cpp
julia_display.hpp
Expand Down
69 changes: 69 additions & 0 deletions deps/src/qmlwrap/glvisualize_viewport.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <functions.hpp>

#include "glvisualize_viewport.hpp"
#include "julia_api.hpp"

#include <QOpenGLContext>
#include <QQuickWindow>

namespace qmlwrap
{

GLVisualizeViewport::GLVisualizeViewport(QQuickItem *parent) : OpenGLViewport(parent)
{
QObject::connect(this, &QQuickItem::windowChanged, [this] (QQuickWindow* w)
{
if(w == nullptr && m_state != nullptr)
{
cxx_wrap::julia_call(cxx_wrap::julia_function("on_window_close", "QML"), m_state);
}

if(w == nullptr)
{
return;
}

connect(w, &QQuickWindow::openglContextCreated, [this] (QOpenGLContext* context)
{
connect(context, &QOpenGLContext::aboutToBeDestroyed, [] ()
{
jl_function_t* on_context_destroy = cxx_wrap::julia_function("on_context_destroy", "QML");
cxx_wrap::julia_call(on_context_destroy);
});
});
});
}

GLVisualizeViewport::~GLVisualizeViewport()
{
if(m_state != nullptr)
{
cxx_wrap::unprotect_from_gc(m_state);
}
}

void GLVisualizeViewport::componentComplete()
{
OpenGLViewport::componentComplete();
jl_function_t* sigs_ctor = cxx_wrap::julia_function("initialize_signals", "QML");
assert(sigs_ctor != nullptr);
m_state = cxx_wrap::julia_call(sigs_ctor);
cxx_wrap::protect_from_gc(m_state);
assert(m_state != nullptr);

auto win_size_changed = [this] () { cxx_wrap::julia_call(cxx_wrap::julia_function("on_window_size_change", "QML"), m_state, width(), height()); };
QObject::connect(this, &QQuickItem::widthChanged, win_size_changed);
QObject::connect(this, &QQuickItem::heightChanged, win_size_changed);
}

void GLVisualizeViewport::setup_buffer(GLuint handle, int width, int height)
{
cxx_wrap::julia_call(cxx_wrap::julia_function("on_framebuffer_setup", "QML"), m_state, handle, static_cast<int64_t>(width), static_cast<int64_t>(height));
}

void GLVisualizeViewport::post_render()
{
cxx_wrap::julia_call(cxx_wrap::julia_function("render_glvisualize_scene", "QML"), m_state);
}

} // namespace qmlwrap
29 changes: 29 additions & 0 deletions deps/src/qmlwrap/glvisualize_viewport.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef QML_glvisualize_viewport_H
#define QML_glvisualize_viewport_H

#include <cxx_wrap.hpp>

#include "opengl_viewport.hpp"

namespace qmlwrap
{

/// Multimedia display for Julia
class GLVisualizeViewport : public OpenGLViewport
{
Q_OBJECT
public:
GLVisualizeViewport(QQuickItem* parent = 0);
virtual ~GLVisualizeViewport();
virtual void componentComplete();

private:
// Julia type holding the signals and other state needed for GLVisualize. Manipulated from within Julia callbacks
jl_value_t* m_state = nullptr;
virtual void setup_buffer(GLuint handle, int width, int height);
virtual void post_render();
};

} // namespace qmlwrap

#endif
2 changes: 1 addition & 1 deletion deps/src/qmlwrap/julia_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ QVariant JuliaAPI::call(const QString& fname, const QVariantList& args)
// Process arguments
for(int i = 0; i != nb_args; ++i)
{
julia_args[i] = cxx_wrap::convert_to_julia<QVariant>(args.at(i));
julia_args[i] = cxx_wrap::convert_to_julia(args.at(i));
if(julia_args[i] == nullptr)
{
qWarning() << "Julia argument type for function " << fname << " is unsupported:" << args[i].typeName();
Expand Down
35 changes: 19 additions & 16 deletions deps/src/qmlwrap/opengl_viewport.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
#include <QSGNode>
#include <QSGSimpleTextureNode>

Expand All @@ -17,7 +18,14 @@ class OpenGLViewport::JuliaRenderer : public QQuickFramebufferObject::Renderer

void render()
{
if(m_need_setup)
{
m_vp->setup_buffer(m_handle, m_width, m_height);
m_need_setup = false;
}
m_vp->render();
m_vp->post_render();
m_vp->window()->resetOpenGLState();
}

void synchronize(QQuickFramebufferObject *item)
Expand All @@ -28,19 +36,29 @@ class OpenGLViewport::JuliaRenderer : public QQuickFramebufferObject::Renderer

QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
{
m_need_setup = true;
m_width = size.width();
m_height = size.height();
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
format.setSamples(4);
return new QOpenGLFramebufferObject(size, format);
auto result = new QOpenGLFramebufferObject(size, format);
m_handle = result->handle();
return result;
}
private:
OpenGLViewport* m_vp;
bool m_need_setup = true;
int m_width = 0;
int m_height = 0;
GLuint m_handle = 0;
};

OpenGLViewport::OpenGLViewport(QQuickItem *parent) : QQuickFramebufferObject(parent)
{
QObject::connect(this, &OpenGLViewport::renderFunctionChanged, this, &OpenGLViewport::update);
QObject::connect(this, &OpenGLViewport::renderArgumentsChanged, this, &OpenGLViewport::update);
setMirrorVertically(true);
}

void OpenGLViewport::render()
Expand All @@ -53,19 +71,4 @@ QQuickFramebufferObject::Renderer* OpenGLViewport::createRenderer() const
return new JuliaRenderer();
}

QSGNode* OpenGLViewport::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData)
{
// This is needed to prevent the image from being upside-down
if(!node)
{
node = QQuickFramebufferObject::updatePaintNode(node, nodeData);
QSGSimpleTextureNode *n = static_cast<QSGSimpleTextureNode*>(node);
if(n)
n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
return node;
}
return QQuickFramebufferObject::updatePaintNode(node, nodeData);
}


} // namespace qmlwrap
14 changes: 11 additions & 3 deletions deps/src/qmlwrap/opengl_viewport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cxx_wrap.hpp>

#include <QObject>
#include <QOpenGLFramebufferObject>
#include <QQuickFramebufferObject>

namespace qmlwrap
Expand Down Expand Up @@ -46,10 +47,17 @@ class OpenGLViewport : public QQuickFramebufferObject
void renderFunctionChanged(QString);
void renderArgumentsChanged(QVariantList);

protected:
QSGNode* updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData);

private:
/// Hook to do extra setup the first time an FBO is used. The FBO is called in render, i.e. when the FBO is bound
virtual void setup_buffer(GLuint handle, int width, int height)
{
}

/// Called after the user-defined rendering function
virtual void post_render()
{
}

QString m_render_function;
QVariantList m_render_arguments;
Q_INVOKABLE void render();
Expand Down
4 changes: 3 additions & 1 deletion deps/src/qmlwrap/wrap_qml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "julia_object.hpp"
#include "julia_signals.hpp"
#include "opengl_viewport.hpp"
#include "glvisualize_viewport.hpp"
#include "type_conversion.hpp"

namespace qmlwrap
Expand Down Expand Up @@ -259,6 +260,7 @@ JULIA_CPP_MODULE_BEGIN(registry)
qmlRegisterType<qmlwrap::JuliaSignals>("org.julialang", 1, 0, "JuliaSignals");
qmlRegisterType<qmlwrap::JuliaDisplay>("org.julialang", 1, 0, "JuliaDisplay");
qmlRegisterType<qmlwrap::OpenGLViewport>("org.julialang", 1, 0, "OpenGLViewport");
qmlRegisterType<qmlwrap::GLVisualizeViewport>("org.julialang", 1, 0, "GLVisualizeViewport");

qml_module.add_abstract<QObject>("QObject");

Expand Down Expand Up @@ -320,7 +322,7 @@ JULIA_CPP_MODULE_BEGIN(registry)

qml_module.add_type<qmlwrap::JuliaObject>("JuliaObject", julia_type<QObject>())
.method("set", &qmlwrap::JuliaObject::set) // Not exported, use @qmlset
.method("value", &qmlwrap::JuliaObject::value); // Not exported, use @qmlget
.method("julia_object_value", &qmlwrap::JuliaObject::value); // Not exported, use @qmlget

// Emit signals helper
qml_module.method("emit", [](const char* signal_name, cxx_wrap::ArrayRef<jl_value_t*> args)
Expand Down
5 changes: 5 additions & 0 deletions example/drag.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using QML

@qmlfunction println
@qmlapp joinpath(dirname(@__FILE__), "qml", "drag.qml")
exec()
54 changes: 42 additions & 12 deletions example/glvisualize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,54 @@ ENV["QSG_RENDER_LOOP"] = "basic"

using QML
using GLVisualize, GeometryTypes, GLAbstraction, Colors
using Reactive

function render(t)
#timesignal = bounce(linspace(0.0, 1.0, 360))
const N = 2048
function spiral(i, start_radius, offset)
Point2f0(sin(i), cos(i)) * (start_radius + ((i/2pi)*offset))
# lines2d example
const N = 2048
function spiral(i, start_radius, offset)
Point2f0(sin(i), cos(i)) * (start_radius + ((i/2pi)*offset))
end
# 2D particles
curve_data(i, N) = Point2f0[spiral(i+x/20f0, 1, (i/20)+1) for x=1:N]

timesignal = Signal(0.)

t = const_lift(x-> (1f0-x)*100f0, timesignal)
color = map(RGBA{Float32}, colormap("Blues", N))

# The qml_time parameter is a Float64 from the QML slider
function render(qml_time)
global robj
if(!isdefined(:robj))
robj = visualize(const_lift(curve_data, t, N), :lines, color=color)
end

# 2D particles
curve_data(i, N) = Point2f0[spiral(i+x/20f0, 1, (i/20)+1) for x=1:N]
push!(timesignal, qml_time)

# t = const_lift(x-> (1f0-x)*100f0, timesignal)
color = map(RGBA{Float32}, colormap("Blues", N))
#view(visualize(const_lift(curve_data, t, N), :lines, color=color))
visualize(const_lift(curve_data, t, N), :lines, color=color)
view(robj)

#renderloop(window)
return
end

# Cat example shows up blank
# mesh = loadasset("cat.obj")
# timesignal = Signal(0.)
# rotation_angle = const_lift(*, timesignal, 2f0*pi)
# start_rotation = Signal(rotationmatrix_x(deg2rad(90f0)))
# rotation = map(rotationmatrix_y, rotation_angle)
# final_rotation = map(*, start_rotation, rotation)
#
# # Render function that takes a parameter t from a QML slider
# function render(t)
# global robj
# if(!isdefined(:robj))
# robj = visualize(mesh, model=final_rotation)
# end
#
# push!(timesignal, t)
#
# view(robj)
# end

@qmlapp joinpath(dirname(@__FILE__), "qml", "glvisualize.qml")
exec()
67 changes: 67 additions & 0 deletions example/qml/drag.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import org.julialang 1.0

ApplicationWindow {
title: "My Application"
width: 800
height: 600
visible: true

Rectangle {
id: container
width: 800; height: 600

SystemPalette { id: pal; colorGroup: SystemPalette.Active }

Canvas {
id: paths
anchors.fill: parent
contextType: "2d"

Path {
id: myPath
startX: 0; startY: 100

PathCurve { x: 75; y: 75 }
PathCurve { x: 200; y: 150 }
PathCurve { x: 325; y: 25 }
PathCurve { x: rect.x; y: rect.y }
}

onPaint: {
context.fillStyle = pal.window;
context.fillRect(0, 0, width, height);
context.strokeStyle = Qt.rgba(.4,.6,.8);
context.path = myPath;
context.stroke();
}
}

Rectangle {
id: rect
width: 50; height: 50
color: "red"

onXChanged: paths.requestPaint()

MouseArea {
anchors.fill: parent
drag.target: rect
}

Rectangle {
id: conn
y: 10
width: 10; height: 5
color: "yellow"

MouseArea {
anchors.fill: parent
onClicked: console.log(rect.x, ", ", rect.y)
}
}
}
}
}
Loading

0 comments on commit 74efa7a

Please sign in to comment.