From 1c872c99698a3c6eb4976b652a52da2772c9c252 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 29 Mar 2018 12:34:39 +0200 Subject: [PATCH] Move all rendering logic to a single RenderingState. --- src/fmri/RenderingState.cpp | 158 ++++++++++++++++++++++++++++++------ src/fmri/RenderingState.hpp | 30 ++++++- src/fmri/glutils.hpp | 3 + src/fmri/main.cpp | 135 +----------------------------- 4 files changed, 162 insertions(+), 164 deletions(-) diff --git a/src/fmri/RenderingState.cpp b/src/fmri/RenderingState.cpp index a95bc30..902c71b 100644 --- a/src/fmri/RenderingState.cpp +++ b/src/fmri/RenderingState.cpp @@ -3,32 +3,13 @@ #include #include #include "RenderingState.hpp" -#include "utils.hpp" +#include "visualisations.hpp" +#include "Range.hpp" +#include "glutils.hpp" using namespace fmri; -static RenderingState& state = RenderingState::instance(); - -/** - * Static utility function to bind to GLUT. - * - * @param x - * @param y - */ -static void handleMouseMove(int x, int y) -{ - state.handleMouseAt(x, y); -} - -/** - * Static utility function to bind to GLUT. - * - * @param key - */ -static void handleKeys(unsigned char key, int, int) -{ - state.handleKey(key); -} +static RenderingState &state = RenderingState::instance(); static float getFPS() { @@ -100,7 +81,7 @@ void RenderingState::handleKey(unsigned char x) glutPostRedisplay(); } -std::string RenderingState::infoLine() +std::string RenderingState::infoLine()const { std::stringstream buffer; buffer << "Pos(x,y,z) = (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")\n"; @@ -119,7 +100,7 @@ void RenderingState::reset() angle[1] = 0; } -void RenderingState::configureRenderingContext() +void RenderingState::configureRenderingContext()const { glLoadIdentity(); glRotatef(angle[1], 1, 0, 0); @@ -136,8 +117,24 @@ RenderingState &RenderingState::instance() void RenderingState::registerControls() { reset(); - glutPassiveMotionFunc(handleMouseMove); - glutKeyboardFunc(handleKeys); + glutPassiveMotionFunc([](int x, int y) { + RenderingState::instance().handleMouseAt(x, y); + }); + glutKeyboardFunc([](auto key, auto, auto) { + RenderingState::instance().handleKey(key); + }); + glutDisplayFunc([]() { + float time = getAnimationStep(std::chrono::seconds(5)); + RenderingState::instance().render(time); + }); + glutIdleFunc([]() { + checkGLErrors(); + throttleIdleFunc(); + glutPostRedisplay(); + }); + glutSpecialFunc([](int key, int, int) { + RenderingState::instance().handleSpecialKey(key); + }); } void RenderingState::handleMouseAt(int x, int y) @@ -150,3 +147,110 @@ void RenderingState::handleMouseAt(int x, int y) glutPostRedisplay(); } + +void RenderingState::loadSimulationData(const std::map &info, vector> &&data) +{ + layerInfo = std::move(info); + layerData = std::move(data); + currentData = layerData.begin(); + + updateVisualisers(); +} + +void RenderingState::updateVisualisers() +{ + layerVisualisations.clear(); + interactionAnimations.clear(); + LayerData *prevState = nullptr; + LayerVisualisation *prevVisualisation = nullptr; + + for (LayerData &layer : *currentData) { + LayerVisualisation *visualisation = getVisualisationForLayer(layer, layerInfo.at(layer.name())); + if (prevState && prevVisualisation && visualisation) { + auto interaction = getActivityAnimation(*prevState, layer, layerInfo.at(layer.name()), + prevVisualisation->nodePositions(), visualisation->nodePositions()); + interactionAnimations.emplace_back(interaction); + } + + layerVisualisations.emplace_back(visualisation); + + prevVisualisation = visualisation; + prevState = &layer; + } + + glutPostRedisplay(); +} + +void RenderingState::render(float time) const +{ + // Clear Color and Depth Buffers + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + configureRenderingContext(); + + + glPushMatrix(); + glTranslatef(5 * currentData->size(), 0, 0); + + for (auto i : Range(currentData->size())) { + glPushMatrix(); + renderLayerName(currentData->at(i).name()); + layerVisualisations[i]->render(); + if (i < interactionAnimations.size() && interactionAnimations[i]) { + interactionAnimations[i]->draw(time); + } + + glPopMatrix(); + glTranslatef(LAYER_X_OFFSET, 0, 0); + } + + glPopMatrix(); + + renderDebugInfo(); + + glutSwapBuffers(); +} + +void RenderingState::renderDebugInfo() const +{ + glLoadIdentity(); + setOrthographicProjection(); + glColor3f(1, 1, 0); + renderText(infoLine(), 2, 10); + restorePerspectiveProjection(); +} + +void RenderingState::renderLayerName(const std::string &name) const +{ + glColor3f(0.5, 0.5, 0.5); + auto layerName = name; + layerName += ": "; + layerName += LayerInfo::nameByType(layerInfo.at(name).type()); + renderText(layerName); + + glTranslatef(0, 0, -10); +} + +void RenderingState::handleSpecialKey(int key) +{ + switch (key) { + case GLUT_KEY_LEFT: + if (currentData == layerData.begin()) { + currentData = layerData.end(); + } + --currentData; + updateVisualisers(); + break; + + case GLUT_KEY_RIGHT: + ++currentData; + if (currentData == layerData.end()) { + currentData = layerData.begin(); + } + updateVisualisers(); + break; + + default: + LOG(INFO) << "Received keystroke " << key; + } +} diff --git a/src/fmri/RenderingState.hpp b/src/fmri/RenderingState.hpp index 7212cad..61f53a6 100644 --- a/src/fmri/RenderingState.hpp +++ b/src/fmri/RenderingState.hpp @@ -1,6 +1,10 @@ #pragma once #include +#include "LayerInfo.hpp" +#include "LayerData.hpp" +#include "LayerVisualisation.hpp" +#include "Animation.hpp" namespace fmri { @@ -15,7 +19,6 @@ namespace fmri * Reset the rendering state */ void reset(); - void configureRenderingContext(); void registerControls(); /** * GLUT mouse handler function @@ -28,16 +31,35 @@ namespace fmri * @param x */ void handleKey(unsigned char x); - std::string infoLine(); + /** + * GLUT special keyboard handler. + * @param key + */ + void handleSpecialKey(int key); + void render(float time) const; + + void loadSimulationData(const std::map &info, std::vector> &&data); static RenderingState& instance(); private: - float pos[3]; - float angle[2]; + std::array pos; + std::array angle; + std::map layerInfo; + std::vector> layerData; + std::vector>::iterator currentData; + std::vector> layerVisualisations; + std::vector> interactionAnimations; RenderingState() noexcept = default; + void configureRenderingContext() const; + void move(unsigned char key); + void updateVisualisers(); + + std::string infoLine()const; + void renderDebugInfo() const; + void renderLayerName(const std::string& name) const; }; } diff --git a/src/fmri/glutils.hpp b/src/fmri/glutils.hpp index 11224e1..fcab4e8 100644 --- a/src/fmri/glutils.hpp +++ b/src/fmri/glutils.hpp @@ -32,6 +32,9 @@ namespace fmri { /** * Draw a bitmap string at the current location. * + * This function wraps glutBitmapCharacter, because its nicer version + * (glutBitmapString) is an extension that is possibly unavailable. + * * @param text The text to draw. */ void renderText(std::string_view text, int x = 0, int y = 0); diff --git a/src/fmri/main.cpp b/src/fmri/main.cpp index ca9db44..8e76afd 100644 --- a/src/fmri/main.cpp +++ b/src/fmri/main.cpp @@ -16,31 +16,19 @@ using namespace std; using namespace fmri; -struct -{ - optional> labels; - map layerInfo; - vector> data; - vector>::iterator currentData; - vector> layerVisualisations; - vector> animations; - float animationStep = 0; -} rendererData; - static void loadSimulationData(const Options &options) { - vector> &results = rendererData.data; - results.clear(); + vector> results; auto dumper = options.imageDumper(); Simulator simulator(options.model(), options.weights(), options.means()); - rendererData.layerInfo = simulator.layerInfo(); for (const auto &image : options.inputs()) { results.emplace_back(simulator.simulate(image)); } CHECK_GT(results.size(), 0) << "Should have some results" << endl; + RenderingState::instance().loadSimulationData(simulator.layerInfo(), std::move(results)); if (dumper) { for (auto &layer : *results.begin()) { @@ -60,118 +48,6 @@ static void loadSimulationData(const Options &options) } } -static void renderLayerName(const LayerData &data); - -static void renderDebugInfo() -{ - glLoadIdentity(); - setOrthographicProjection(); - glColor3f(1, 1, 0); - renderText(RenderingState::instance().infoLine(), 2, 10); - restorePerspectiveProjection(); -} - -static void render() -{ - // Clear Color and Depth Buffers - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - auto& camera = RenderingState::instance(); - - camera.configureRenderingContext(); - - const auto& dataSet = *rendererData.currentData; - - glPushMatrix(); - glTranslatef(5 * dataSet.size(), 0, 0); - - for (auto i : Range(dataSet.size())) { - glPushMatrix(); - renderLayerName(dataSet[i]); - rendererData.layerVisualisations[i]->render(); - if (i < rendererData.animations.size() && rendererData.animations[i]) { - rendererData.animations[i]->draw(rendererData.animationStep); - } - - glPopMatrix(); - glTranslatef(LAYER_X_OFFSET, 0, 0); - } - - - glPopMatrix(); - - renderDebugInfo(); - - glutSwapBuffers(); -} - -static void renderLayerName(const LayerData &data) -{ - // Draw the name of the layer for reference. - glColor3f(0.5, 0.5, 0.5); - auto layerName = data.name(); - layerName += ": "; - layerName += LayerInfo::nameByType(rendererData.layerInfo.at(data.name()).type()); - renderText(layerName); - - glTranslatef(0, 0, -10); -} - -static void updateVisualisers() -{ - rendererData.layerVisualisations.clear(); - rendererData.animations.clear(); - LayerData* prevState = nullptr; - LayerVisualisation* prevVisualisation = nullptr; - - for (LayerData &layer : *rendererData.currentData) { - LayerVisualisation* visualisation = getVisualisationForLayer(layer, rendererData.layerInfo.at(layer.name())); - if (prevState && prevVisualisation && visualisation) { - auto interaction = getActivityAnimation(*prevState, layer, rendererData.layerInfo.at(layer.name()), prevVisualisation->nodePositions(), visualisation->nodePositions()); - rendererData.animations.emplace_back(interaction); - } - - rendererData.layerVisualisations.emplace_back(visualisation); - - prevVisualisation = visualisation; - prevState = &layer; - } - - glutPostRedisplay(); -} - -static void specialKeyFunc(int key, int, int) -{ - switch (key) { - case GLUT_KEY_LEFT: - if (rendererData.currentData == rendererData.data.begin()) { - rendererData.currentData = rendererData.data.end(); - } - --rendererData.currentData; - updateVisualisers(); - break; - - case GLUT_KEY_RIGHT: - ++rendererData.currentData; - if (rendererData.currentData == rendererData.data.end()) { - rendererData.currentData = rendererData.data.begin(); - } - updateVisualisers(); - break; - - default: - LOG(INFO) << "Received keystroke " << key; - } -} - -static void idleFunc() -{ - checkGLErrors(); - glutPostRedisplay(); - throttleIdleFunc(); - rendererData.animationStep = (1 - cos(M_PI * getAnimationStep(std::chrono::seconds(5)))) / 2; -} - int main(int argc, char *argv[]) { google::InitGoogleLogging(argv[0]); @@ -183,20 +59,13 @@ int main(int argc, char *argv[]) // Prepare data for simulations Options options = Options::parse(argc, argv); - rendererData.labels = options.labels(); loadSimulationData(options); // Register callbacks - glutDisplayFunc(render); - glutIdleFunc(idleFunc); glutReshapeFunc(changeWindowSize); - glutSpecialFunc(specialKeyFunc); RenderingState::instance().registerControls(); - rendererData.currentData = rendererData.data.begin(); - updateVisualisers(); - // Enable depth test to fix objects behind you glEnable(GL_DEPTH_TEST);