Skip to content

Instantly share code, notes, and snippets.

@sansumbrella
Last active July 14, 2016 22:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sansumbrella/c747bb75ac51c8d5d9448f13c379d6a3 to your computer and use it in GitHub Desktop.
Save sansumbrella/c747bb75ac51c8d5d9448f13c379d6a3 to your computer and use it in GitHub Desktop.
Tiny Cinder C++ Scene Graph
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder/Utilities.h"
#include "cinder/Log.h"
#include "cinder/Json.h"
using namespace ci;
using namespace ci::app;
using namespace std;
class GraphRenderer {
public:
virtual ~GraphRenderer() = default;
virtual void setOrigin(const ci::vec2 &pos) = 0;
virtual void translateOrigin(const ci::vec2 &pos) = 0;
virtual void addRectangle(const ci::Rectf &rect) = 0;
virtual void addText(const std::string &text) = 0;
};
class ImmediateRenderer : public GraphRenderer {
public:
void setOrigin(const ci::vec2 &pos) override;
void translateOrigin(const ci::vec2 &pos) override;
void addRectangle(const ci::Rectf &rect) override;
void addText(const std::string &text) override;
private:
ci::gl::TextureFontRef _font = gl::TextureFont::create(Font("Helvetica", 12.0f));
ci::vec2 _origin;
};
void ImmediateRenderer::translateOrigin(const ci::vec2 &pos) {
_origin += pos;
}
void ImmediateRenderer::setOrigin(const ci::vec2 &pos) {
_origin = pos;
}
void ImmediateRenderer::addRectangle(const ci::Rectf &rect) {
gl::ScopedModelMatrix mat;
gl::translate(_origin);
gl::drawSolidRect(rect);
}
void ImmediateRenderer::addText(const std::string &text) {
gl::ScopedModelMatrix mat;
gl::translate(_origin);
_font->drawString(text, vec2(0));
}
#pragma mark - Node
using NodeRef = std::shared_ptr<class Node>;
class Node {
public:
Node() = default;
virtual ~Node() = default;
explicit Node(const ci::vec2 &position);
void draw(GraphRenderer &renderer);
virtual void drawCustom(GraphRenderer &renderer) {}
void appendChild(const NodeRef &child);
void clearChildren() { _children.clear(); }
ci::vec2& position() { return _position; }
private:
std::vector<NodeRef> _children;
ci::vec2 _position;
};
Node::Node(const ci::vec2 &position)
: _position(position)
{}
void Node::appendChild(const NodeRef &child)
{
_children.push_back(child);
}
void Node::draw(GraphRenderer &renderer)
{
renderer.translateOrigin(_position);
drawCustom(renderer);
for (auto &c : _children) {
c->draw(renderer);
}
renderer.translateOrigin(-_position);
}
#pragma mark - TextNode
class TextNode : public Node {
public:
using Node::Node; // inherit constructors
TextNode(const std::string &text, const ci::vec2 &position)
: Node(position),
_text(text)
{}
void drawCustom(GraphRenderer &renderer) override;
private:
std::string _text;
};
void TextNode::drawCustom(GraphRenderer &renderer) {
renderer.addText(_text);
}
#pragma mark - RectangleNode
class RectangleNode : public Node {
public:
RectangleNode(const ci::vec2 &position, const ci::vec2 &size)
: Node(position),
_rectangle(0, -size.y / 2.0f, size.x, size.y / 2.0f)
{}
void drawCustom(GraphRenderer &renderer) override;
private:
ci::Rectf _rectangle;
};
void RectangleNode::drawCustom(GraphRenderer &renderer) {
renderer.addRectangle(_rectangle);
}
#pragma mark - FlexiNode
class FlexiNode : public Node {
public:
explicit FlexiNode(const std::function<void ()> &draw_fn)
: _draw_fn(draw_fn)
{}
void drawCustom(GraphRenderer &renderer) override;
private:
std::function<void ()> _draw_fn;
};
void FlexiNode::drawCustom(GraphRenderer &renderer) {
if (_draw_fn) {
_draw_fn();
}
}
#pragma mark - App
class CinderNoodlingApp : public App {
public:
~CinderNoodlingApp();
void setup() override;
void mouseDown( MouseEvent event ) override;
void update() override;
void draw() override;
void graphData(const std::string &data);
private:
std::future<string> _future;
Node _root;
ImmediateRenderer _renderer;
};
CinderNoodlingApp::~CinderNoodlingApp()
{
}
void CinderNoodlingApp::setup()
{
auto load_fixer_data = [] {
return loadString(loadUrl("https://api.fixer.io/latest"));
};
Timer timer(true);
// load_fixer_data();
_future = std::async(std::launch::async, load_fixer_data);
timer.stop();
cout << timer.getSeconds() * 1000 << "ms" << endl;
for (auto i = 0; i < 10; i += 1) {
auto t = i / (10 - 1.0f);
auto y = mix(0.0f, 200.0f, t);
_root.appendChild(make_shared<Node>(vec2(50.0f, y)));
}
_root.position() = vec2(getWindowSize()) * vec2(0.1f);
}
void CinderNoodlingApp::mouseDown( MouseEvent event )
{
}
void CinderNoodlingApp::update()
{
if (_future.valid()) {
auto status = _future.wait_for(chrono::nanoseconds(0));
if (status == future_status::ready) {
graphData(_future.get());
}
}
}
void CinderNoodlingApp::graphData(const std::string &data)
{
_root.clearChildren();
JsonTree json(data);
auto rates = json.getChild("rates");
auto pos = vec2(50.0f, 0.0f);
auto lowest = std::numeric_limits<float>::max();
auto highest = std::numeric_limits<float>::lowest();
for (auto &rate : rates) {
auto value = rate.getValue<float>();
lowest = min(value, lowest);
highest = max(value, highest);
}
for (auto &rate : rates) {
auto r = logf(rate.getValue<float>());
auto width = lmap(r, logf(lowest), logf(highest), 10.0f, 100.0f);
auto size = vec2(width, 12.0f);
auto child = make_shared<Node>(pos);
auto text = make_shared<TextNode>(rate.getKey(), vec2(0, 0));
auto bar = make_shared<RectangleNode>(vec2(30, 0), size);
child->appendChild(text);
child->appendChild(bar);
_root.appendChild(child);
pos.y += 20.0f;
}
auto node = make_shared<FlexiNode>([] {
gl::ScopedDepth depth(true);
gl::drawColorCube(vec3(0), vec3(50.0f));
});
_root.appendChild(node);
}
void CinderNoodlingApp::draw()
{
gl::clear( Color( 0, 0, 0 ) );
gl::setMatricesWindowPersp(getWindowSize());
Timer timer(true);
_root.draw(_renderer);
timer.stop();
console() << "Draw time: " << timer.getSeconds() * 1000 << "ms" << endl;
}
CINDER_APP( CinderNoodlingApp, RendererGl )
@sansumbrella
Copy link
Author

Rudimentary scene graph (parent->child flow only) with separate renderer (so nodes don't need to know about openGL/asset management).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment