Reimplement drawing system with vertex buffers.
Now with 10000% more performance.
This commit is contained in:
93
src/FlatLayerVisualisation.cpp
Normal file
93
src/FlatLayerVisualisation.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include <glog/logging.h>
|
||||||
|
#include <GL/gl.h>
|
||||||
|
|
||||||
|
#include "FlatLayerVisualisation.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace fmri;
|
||||||
|
|
||||||
|
static inline void computeColor(float intensity, float limit, float* destination)
|
||||||
|
{
|
||||||
|
const float saturation = min(-log(abs(intensity) / limit) / 10.0f, 1.0f);
|
||||||
|
if (intensity > 0) {
|
||||||
|
destination[0] = saturation;
|
||||||
|
destination[1] = saturation;
|
||||||
|
destination[2] = 1;
|
||||||
|
} else {
|
||||||
|
destination[0] = 1;
|
||||||
|
destination[1] = saturation;
|
||||||
|
destination[2] = saturation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatLayerVisualisation::FlatLayerVisualisation(const fmri::LayerData &layer) :
|
||||||
|
faceCount(layer.numEntries() * 4),
|
||||||
|
vertexBuffer(faceCount * 3),
|
||||||
|
colorBuffer(faceCount * 3),
|
||||||
|
indexBuffer(faceCount * 3)
|
||||||
|
{
|
||||||
|
auto& shape = layer.shape();
|
||||||
|
CHECK_EQ(shape.size(), 2) << "layer should be flat!" << endl;
|
||||||
|
CHECK_EQ(shape[0], 1) << "Only single images supported." << endl;
|
||||||
|
|
||||||
|
const int limit = static_cast<const int>(layer.numEntries());
|
||||||
|
auto data = layer.data();
|
||||||
|
const auto [minElem, maxElem] = minmax_element(data, data + limit);
|
||||||
|
|
||||||
|
auto scalingMax = max(abs(*minElem), abs(*maxElem));
|
||||||
|
|
||||||
|
int v = 0;
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < limit; ++i) {
|
||||||
|
const float zOffset = -2 * i;
|
||||||
|
const int vertexBase = i * 4;
|
||||||
|
// Create the 4 vertices for the pyramid
|
||||||
|
vertexBuffer[j++] = -0.5f;
|
||||||
|
vertexBuffer[j++] = 0;
|
||||||
|
vertexBuffer[j++] = 0.5f + zOffset;
|
||||||
|
vertexBuffer[j++] = 0;
|
||||||
|
vertexBuffer[j++] = 0;
|
||||||
|
vertexBuffer[j++] = -0.5f + zOffset;
|
||||||
|
vertexBuffer[j++] = 0;
|
||||||
|
vertexBuffer[j++] = 1;
|
||||||
|
vertexBuffer[j++] = 0 + zOffset;
|
||||||
|
vertexBuffer[j++] = 0.5;
|
||||||
|
vertexBuffer[j++] = 0;
|
||||||
|
vertexBuffer[j++] = 0.5 + zOffset;
|
||||||
|
|
||||||
|
// Define the colors for the vertices
|
||||||
|
for (int c = 0; c < 4; ++c) {
|
||||||
|
computeColor(data[i], scalingMax, &colorBuffer[12 * i + 3 * c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the index set for the faces
|
||||||
|
// Simply connect all vertices in ascending order and it works.
|
||||||
|
indexBuffer[v++] = vertexBase;
|
||||||
|
indexBuffer[v++] = vertexBase + 1;
|
||||||
|
indexBuffer[v++] = vertexBase + 2;
|
||||||
|
indexBuffer[v++] = vertexBase;
|
||||||
|
indexBuffer[v++] = vertexBase + 1;
|
||||||
|
indexBuffer[v++] = vertexBase + 3;
|
||||||
|
indexBuffer[v++] = vertexBase;
|
||||||
|
indexBuffer[v++] = vertexBase + 2;
|
||||||
|
indexBuffer[v++] = vertexBase + 3;
|
||||||
|
indexBuffer[v++] = vertexBase + 1;
|
||||||
|
indexBuffer[v++] = vertexBase + 2;
|
||||||
|
indexBuffer[v++] = vertexBase + 3;
|
||||||
|
}
|
||||||
|
assert(v == faceCount * 3);
|
||||||
|
assert(j == faceCount * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatLayerVisualisation::render()
|
||||||
|
{
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
|
|
||||||
|
glVertexPointer(3, GL_FLOAT, 0, vertexBuffer.data());
|
||||||
|
glColorPointer(3, GL_FLOAT, 0, colorBuffer.data());
|
||||||
|
glDrawElements(GL_TRIANGLES, faceCount, GL_UNSIGNED_INT, indexBuffer.data());
|
||||||
|
|
||||||
|
glDisable(GL_VERTEX_ARRAY);
|
||||||
|
glDisable(GL_COLOR_ARRAY);
|
||||||
|
}
|
||||||
23
src/FlatLayerVisualisation.hpp
Normal file
23
src/FlatLayerVisualisation.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "LayerData.hpp"
|
||||||
|
#include "LayerVisualisation.hpp"
|
||||||
|
|
||||||
|
namespace fmri
|
||||||
|
{
|
||||||
|
class FlatLayerVisualisation : public LayerVisualisation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FlatLayerVisualisation(const LayerData& layer);
|
||||||
|
|
||||||
|
void render() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t faceCount;
|
||||||
|
std::vector<float> vertexBuffer;
|
||||||
|
std::vector<float> colorBuffer;
|
||||||
|
std::vector<int> indexBuffer;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
src/LayerVisualisation.hpp
Normal file
12
src/LayerVisualisation.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace fmri
|
||||||
|
{
|
||||||
|
class LayerVisualisation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~LayerVisualisation() = default;
|
||||||
|
|
||||||
|
virtual void render() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
37
src/MultiImageVisualisation.cpp
Normal file
37
src/MultiImageVisualisation.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include <glog/logging.h>
|
||||||
|
#include "MultiImageVisualisation.hpp"
|
||||||
|
#include "glutils.hpp"
|
||||||
|
|
||||||
|
using namespace fmri;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
MultiImageVisualisation::MultiImageVisualisation(const fmri::LayerData &layer)
|
||||||
|
{
|
||||||
|
auto dimensions = layer.shape();
|
||||||
|
CHECK_EQ(4, dimensions.size()) << "Should be image-like layer";
|
||||||
|
|
||||||
|
const auto images = dimensions[0],
|
||||||
|
channels = dimensions[1],
|
||||||
|
width = dimensions[2],
|
||||||
|
height = dimensions[3];
|
||||||
|
|
||||||
|
auto dataPtr = layer.data();
|
||||||
|
for (auto i = 0; i < images; ++i) {
|
||||||
|
for (auto j = 0; j < channels; ++j) {
|
||||||
|
textureReferences[make_pair(i, j)] = loadTexture(dataPtr, width, height);
|
||||||
|
dataPtr += width * height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiImageVisualisation::~MultiImageVisualisation()
|
||||||
|
{
|
||||||
|
for (auto entry : textureReferences) {
|
||||||
|
glDeleteTextures(0, &entry.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiImageVisualisation::render()
|
||||||
|
{
|
||||||
|
// TODO: do something.
|
||||||
|
}
|
||||||
21
src/MultiImageVisualisation.hpp
Normal file
21
src/MultiImageVisualisation.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#include <map>
|
||||||
|
#include "LayerVisualisation.hpp"
|
||||||
|
#include "LayerData.hpp"
|
||||||
|
|
||||||
|
namespace fmri
|
||||||
|
{
|
||||||
|
class MultiImageVisualisation : public LayerVisualisation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MultiImageVisualisation(const LayerData&);
|
||||||
|
~MultiImageVisualisation() override;
|
||||||
|
|
||||||
|
void render() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::pair<int, int>, GLuint> textureReferences;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ static void handleMouseMove(int x, int y)
|
|||||||
|
|
||||||
camera.angle[0] = (x - width) / width * 180;
|
camera.angle[0] = (x - width) / width * 180;
|
||||||
camera.angle[1] = (y - height) / height * 90;
|
camera.angle[1] = (y - height) / height * 90;
|
||||||
|
|
||||||
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void move(unsigned char key)
|
static void move(unsigned char key)
|
||||||
@@ -63,6 +65,8 @@ static void handleKeys(unsigned char key, int, int)
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Camera::infoLine()
|
std::string Camera::infoLine()
|
||||||
|
|||||||
@@ -103,8 +103,11 @@ GLuint fmri::compileShader(GLenum type, char const *source)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ::fmri::setColorFromIntensity(float i)
|
void fmri::renderText(std::string_view text)
|
||||||
{
|
{
|
||||||
// TODO: something more expressive.
|
constexpr auto font = GLUT_BITMAP_HELVETICA_10;
|
||||||
glColor3f(i, i, i);
|
glRasterPos2i(0, 0);
|
||||||
|
for (char c : text) {
|
||||||
|
glutBitmapCharacter(font, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "LayerData.hpp"
|
#include "LayerData.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include <GL/glut.h>
|
#include <GL/glut.h>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace fmri {
|
namespace fmri {
|
||||||
/**
|
/**
|
||||||
@@ -35,9 +36,9 @@ namespace fmri {
|
|||||||
void changeWindowSize(int w, int h);
|
void changeWindowSize(int w, int h);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current drawing color based on some intensity value.
|
* Draw a bitmap string at the current location.
|
||||||
*
|
*
|
||||||
* @param i The intensity.
|
* @param text The text to draw.
|
||||||
*/
|
*/
|
||||||
void setColorFromIntensity(float i);
|
void renderText(std::string_view text);
|
||||||
}
|
}
|
||||||
|
|||||||
129
src/main.cpp
129
src/main.cpp
@@ -10,6 +10,9 @@
|
|||||||
#include "Simulator.hpp"
|
#include "Simulator.hpp"
|
||||||
#include "glutils.hpp"
|
#include "glutils.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
|
#include "LayerVisualisation.hpp"
|
||||||
|
#include "FlatLayerVisualisation.hpp"
|
||||||
|
#include "MultiImageVisualisation.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
@@ -21,6 +24,7 @@ struct
|
|||||||
float angle = 0;
|
float angle = 0;
|
||||||
vector<LayerData>* currentData = nullptr;
|
vector<LayerData>* currentData = nullptr;
|
||||||
map<tuple<string, int, int>, GLuint> textureMap;
|
map<tuple<string, int, int>, GLuint> textureMap;
|
||||||
|
vector<unique_ptr<LayerVisualisation>> layerVisualisations;
|
||||||
} rendererData;
|
} rendererData;
|
||||||
|
|
||||||
static vector<vector<LayerData>> getSimulationData(const Options &options)
|
static vector<vector<LayerData>> getSimulationData(const Options &options)
|
||||||
@@ -44,7 +48,7 @@ static vector<vector<LayerData>> getSimulationData(const Options &options)
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void renderLayer(const LayerData& data);
|
static void renderLayerName(const LayerData &data);
|
||||||
|
|
||||||
static void render()
|
static void render()
|
||||||
{
|
{
|
||||||
@@ -55,11 +59,16 @@ static void render()
|
|||||||
|
|
||||||
camera.configureRenderingContext();
|
camera.configureRenderingContext();
|
||||||
|
|
||||||
|
const auto& dataSet = *rendererData.currentData;
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(5 * rendererData.currentData->size(), 0, 0);
|
glTranslatef(5 * dataSet.size(), 0, 0);
|
||||||
for (auto& layer : *rendererData.currentData) {
|
|
||||||
|
for (unsigned int i = 0; i < dataSet.size(); ++i) {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
renderLayer(layer);
|
renderLayerName(dataSet[i]);
|
||||||
|
rendererData.layerVisualisations[i]->render();
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
glTranslatef(-10, 0, 0);
|
glTranslatef(-10, 0, 0);
|
||||||
}
|
}
|
||||||
@@ -68,110 +77,36 @@ static void render()
|
|||||||
glutSwapBuffers();
|
glutSwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawOneParticle()
|
static void renderLayerName(const LayerData &data)
|
||||||
{
|
{
|
||||||
// Code taken from CG workshop 1. Should probably replace with something nicer.
|
|
||||||
glBegin(GL_TRIANGLE_STRIP);
|
|
||||||
// triangle 1
|
|
||||||
glVertex3f(-0.5, 0.0, 0.5); // A
|
|
||||||
glVertex3f(0.0, 0.0, -0.5); // B
|
|
||||||
glVertex3f(0.0, 1.0, 0.0); // top
|
|
||||||
// triangle 2
|
|
||||||
glVertex3f(0.5, 0.0, 0.5); // C
|
|
||||||
// triangle 3
|
|
||||||
glVertex3f(-0.5, 0.0, 0.5); // A again
|
|
||||||
// triangle 4 (bottom)
|
|
||||||
glVertex3f(0.0, 0.0, -0.5); // B again
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderFlatLayer(const LayerData& data)
|
|
||||||
{
|
|
||||||
const auto [minElem,maxElem] = minmax_element(data.data(), data.data() + data.numEntries());
|
|
||||||
const float intensityLimit = max(abs(*minElem), abs(*maxElem));
|
|
||||||
auto& shape = data.shape();
|
|
||||||
CHECK_EQ(shape[0], 1) << "Should have only one instance per layer." << endl;
|
|
||||||
// Draw one triangle for every point in the layer
|
|
||||||
// Color depends on current value.
|
|
||||||
vector<float> intensities(data.data(), data.data() + data.numEntries());
|
|
||||||
transform(intensities.begin(), intensities.end(), intensities.begin(), [=](auto x) { return clamp(x, -intensityLimit, intensityLimit);});
|
|
||||||
|
|
||||||
glPushMatrix();
|
|
||||||
for (auto i : intensities) {
|
|
||||||
auto intensity = min(-log(abs(i) / intensityLimit) / 10.0f, 1.0f);
|
|
||||||
if (i > 0) {
|
|
||||||
glColor3f(intensity, intensity, 1);
|
|
||||||
} else {
|
|
||||||
glColor3f(1, intensity, intensity);
|
|
||||||
}
|
|
||||||
drawOneParticle();
|
|
||||||
glTranslatef(0, 0, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
glPopMatrix();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderText(string_view text)
|
|
||||||
{
|
|
||||||
constexpr auto font = GLUT_BITMAP_HELVETICA_10;
|
|
||||||
glRasterPos2i(0, 0);
|
|
||||||
for (char c : text) {
|
|
||||||
glutBitmapCharacter(font, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renderLayer(const LayerData& data)
|
|
||||||
{
|
|
||||||
auto& shape = data.shape();
|
|
||||||
// Draw the name of the layer for reference.
|
// Draw the name of the layer for reference.
|
||||||
glColor3f(0.5, 0.5, 0.5);
|
glColor3f(0.5, 0.5, 0.5);
|
||||||
renderText(data.name());
|
renderText(data.name());
|
||||||
|
|
||||||
glTranslatef(0, 0, -10);
|
glTranslatef(0, 0, -10);
|
||||||
switch (shape.size()) {
|
|
||||||
case 4:
|
|
||||||
// TODO: implement this.
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
renderFlatLayer(data);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
cerr << "What is this even: " << data << endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reloadTextures(unsigned dataIndex)
|
static void updateVisualisers(unsigned int dataset)
|
||||||
{
|
{
|
||||||
// First, release any existing textures
|
rendererData.layerVisualisations.clear();
|
||||||
for (auto &entry : rendererData.textureMap) {
|
|
||||||
glDeleteTextures(0, &entry.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
rendererData.textureMap.clear();
|
|
||||||
rendererData.currentData = &rendererData.data[dataIndex];
|
|
||||||
|
|
||||||
|
rendererData.currentData = &rendererData.data[dataset];
|
||||||
for (auto &layer : *rendererData.currentData) {
|
for (auto &layer : *rendererData.currentData) {
|
||||||
auto dimensions = layer.shape();
|
LayerVisualisation* visualisation = nullptr;
|
||||||
if (dimensions.size() != 4) {
|
switch (layer.shape().size()) {
|
||||||
continue;
|
case 2:
|
||||||
|
visualisation = new FlatLayerVisualisation(layer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
visualisation = new MultiImageVisualisation(layer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto images = dimensions[0],
|
rendererData.layerVisualisations.emplace_back(visualisation);
|
||||||
channels = dimensions[1],
|
|
||||||
width = dimensions[2],
|
|
||||||
height = dimensions[3];
|
|
||||||
|
|
||||||
auto dataPtr = layer.data();
|
|
||||||
for (auto i = 0; i < images; ++i) {
|
|
||||||
for (auto j = 0; j < channels; ++j) {
|
|
||||||
rendererData.textureMap[make_tuple(layer.name(), i, j)] = loadTexture(dataPtr, width, height);
|
|
||||||
dataPtr += width * height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +126,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Register callbacks
|
// Register callbacks
|
||||||
glutDisplayFunc(render);
|
glutDisplayFunc(render);
|
||||||
glutIdleFunc(render);
|
glutIdleFunc(glutPostRedisplay);
|
||||||
glutReshapeFunc(changeWindowSize);
|
glutReshapeFunc(changeWindowSize);
|
||||||
|
|
||||||
Camera::instance().registerControls();
|
Camera::instance().registerControls();
|
||||||
@@ -202,7 +137,7 @@ int main(int argc, char *argv[])
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadTextures(0);
|
updateVisualisers(0);
|
||||||
|
|
||||||
// Enable depth test to fix objects behind you
|
// Enable depth test to fix objects behind you
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|||||||
Reference in New Issue
Block a user