mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-09 04:49:43 -05:00
e7767bff11
First working and sorted version
350 lines
12 KiB
C++
350 lines
12 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2015 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <openspace/abuffer/abuffer.h>
|
|
#include <openspace/engine/openspaceengine.h>
|
|
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/filesystem/file.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/opengl/programobject.h>
|
|
#include <ghoul/opengl/texture.h>
|
|
|
|
#include <sgct.h>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
namespace {
|
|
|
|
const std::string generatedSettingsPath = "${SHADERS_GENERATED}/ABufferSettings.hglsl";
|
|
const std::string generatedHeadersPath = "${SHADERS_GENERATED}/ABufferHeaders.hglsl";
|
|
const std::string generatedSamplerCallsPath = "${SHADERS_GENERATED}/ABufferSamplerCalls.hglsl";
|
|
const std::string generatedTransferFunctionVisualizerPath =
|
|
"${SHADERS_GENERATED}/ABufferTransferFunctionVisualizer.hglsl";
|
|
const std::string generatedSamplersPath = "${SHADERS_GENERATED}/ABufferSamplers.hglsl";
|
|
|
|
const std::string _loggerCat = "ABuffer";
|
|
|
|
}
|
|
|
|
namespace openspace {
|
|
|
|
ABuffer::ABuffer()
|
|
: _validShader(false)
|
|
, _resolveShader(nullptr)
|
|
, _volumeStepFactor(0.0f)
|
|
{
|
|
|
|
updateDimensions();
|
|
}
|
|
|
|
ABuffer::~ABuffer() {
|
|
|
|
if(_resolveShader)
|
|
delete _resolveShader;
|
|
|
|
for(auto file: _samplerFiles) {
|
|
delete file;
|
|
}
|
|
}
|
|
|
|
bool ABuffer::initializeABuffer() {
|
|
// ============================
|
|
// SHADERS
|
|
// ============================
|
|
auto shaderCallback = [this](ghoul::opengl::ProgramObject* program) {
|
|
// Error for visibility in log
|
|
_validShader = false;
|
|
};
|
|
|
|
generateShaderSource();
|
|
_resolveShader = ghoul::opengl::ProgramObject::Build(
|
|
"ABufferResolve",
|
|
"${SHADERS}/ABuffer/abufferResolveVertex.glsl",
|
|
"${SHADERS}/ABuffer/abufferResolveFragment.glsl");
|
|
if (!_resolveShader)
|
|
return false;
|
|
_resolveShader->setProgramObjectCallback(shaderCallback);
|
|
|
|
#ifndef __APPLE__
|
|
// ============================
|
|
// GEOMETRY (quad)
|
|
// ============================
|
|
const GLfloat size = 1.0f;
|
|
const GLfloat vertex_data[] = { // square of two triangles (sigh)
|
|
// x y z w s t
|
|
-size, -size, 0.0f, 1.0f,
|
|
size, size, 0.0f, 1.0f,
|
|
-size, size, 0.0f, 1.0f,
|
|
-size, -size, 0.0f, 1.0f,
|
|
size, -size, 0.0f, 1.0f,
|
|
size, size, 0.0f, 1.0f,
|
|
};
|
|
GLuint vertexPositionBuffer;
|
|
glGenVertexArrays(1, &_screenQuad); // generate array
|
|
glBindVertexArray(_screenQuad); // bind array
|
|
glGenBuffers(1, &vertexPositionBuffer); // generate buffer
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertexPositionBuffer); // bind buffer
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
|
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, reinterpret_cast<void*>(0));
|
|
glEnableVertexAttribArray(0);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool ABuffer::reinitialize() {
|
|
|
|
// set the total resolution for all viewports
|
|
updateDimensions();
|
|
return reinitializeInternal();
|
|
}
|
|
|
|
void ABuffer::resolve() {
|
|
#ifndef __APPLE__
|
|
if( ! _validShader) {
|
|
generateShaderSource();
|
|
updateShader();
|
|
_validShader = true;
|
|
}
|
|
|
|
if (!_resolveShader)
|
|
return;
|
|
|
|
_resolveShader->activate();
|
|
int startAt = 0;
|
|
for(int i = 0; i < _volumes.size(); ++i) {
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
_volumes.at(i).second->bind();
|
|
startAt = i + 1;
|
|
}
|
|
for(int i = 0; i < _transferFunctions.size(); ++i) {
|
|
glActiveTexture(GL_TEXTURE0 + startAt + i);
|
|
_transferFunctions.at(i).second->bind();
|
|
}
|
|
|
|
// Decrease stepsize in volumes if right click is pressed
|
|
// TODO: Let the interactionhandler handle this
|
|
//int val = sgct::Engine::getMouseButton(0, SGCT_MOUSE_BUTTON_RIGHT);
|
|
//float volumeStepFactor = (val) ? 0.2f: 1.0f;
|
|
//if(volumeStepFactor != _volumeStepFactor) {
|
|
// _volumeStepFactor = volumeStepFactor;
|
|
// _resolveShader->setUniform("volumeStepFactor", _volumeStepFactor);
|
|
//}
|
|
|
|
glBindVertexArray(_screenQuad);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
_resolveShader->deactivate();
|
|
#endif
|
|
}
|
|
|
|
void ABuffer::addVolume(const std::string& tag,ghoul::opengl::Texture* volume) {
|
|
_volumes.push_back(std::make_pair(tag, volume));
|
|
}
|
|
|
|
void ABuffer::addTransferFunction(const std::string& tag,ghoul::opengl::Texture* transferFunction) {
|
|
_transferFunctions.push_back(std::make_pair(tag, transferFunction));
|
|
}
|
|
|
|
int ABuffer::addSamplerfile(const std::string& filename) {
|
|
if( ! FileSys.fileExists(filename))
|
|
return -1;
|
|
|
|
#ifndef __APPLE__
|
|
auto fileCallback = [this](const ghoul::filesystem::File& file) {
|
|
_validShader = false;
|
|
};
|
|
ghoul::filesystem::File* file = new ghoul::filesystem::File(filename);
|
|
file->setCallback(fileCallback);
|
|
_samplerFiles.push_back(file);
|
|
_samplers.push_back("");
|
|
|
|
// ID is one more than "actual" position since ID=0 is considered geometry
|
|
//return 1 << (_samplers.size()-1);
|
|
return static_cast<int>(_samplers.size());
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
bool ABuffer::updateShader() {
|
|
bool s = _resolveShader->rebuildFromFile();
|
|
if (s) {
|
|
int startAt = 0;
|
|
for (int i = 0; i < _volumes.size(); ++i) {
|
|
_resolveShader->setUniform(_volumes.at(i).first, i);
|
|
startAt = i + 1;
|
|
}
|
|
for (int i = 0; i < _transferFunctions.size(); ++i) {
|
|
_resolveShader->setUniform(_transferFunctions.at(i).first, startAt + i);
|
|
}
|
|
LINFO("Successfully updated ABuffer resolve shader!");
|
|
}
|
|
else {
|
|
LWARNING("Couldn't update ABuffer resolve shader");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void ABuffer::generateShaderSource() {
|
|
|
|
for(int i = 0; i < _samplerFiles.size(); ++i) {
|
|
std::string line, source = "";
|
|
std::ifstream samplerFile(_samplerFiles.at(i)->path());
|
|
if(samplerFile.is_open()) {
|
|
while(std::getline(samplerFile, line)) {
|
|
source += line + "\n";
|
|
}
|
|
}
|
|
samplerFile.close();
|
|
_samplers.at(i) = source;
|
|
}
|
|
|
|
LDEBUG("Generating shader includes");
|
|
openspaceHeaders();
|
|
openspaceSamplerCalls();
|
|
openspaceSamplers();
|
|
openspaceTransferFunction();
|
|
}
|
|
|
|
void ABuffer::openspaceHeaders() {
|
|
|
|
std::ofstream f(absPath(generatedHeadersPath));
|
|
f << "#define MAX_VOLUMES " << std::to_string(_samplers.size()) << "\n"
|
|
<< "#define MAX_TF " << _transferFunctions.size() << "\n";
|
|
for (int i = 0; i < _volumes.size(); ++i) {
|
|
f << "uniform sampler3D " << _volumes.at(i).first << ";\n";
|
|
}
|
|
for (int i = 0; i < _transferFunctions.size(); ++i) {
|
|
f << "uniform sampler1D " << _transferFunctions.at(i).first << ";\n";
|
|
}
|
|
|
|
for (int i = 0; i < _samplers.size(); ++i) {
|
|
auto found = _samplers.at(i).find_first_of('{');
|
|
if (found != std::string::npos) {
|
|
f << _samplers.at(i).substr(0, found) << ";\n";
|
|
}
|
|
}
|
|
|
|
if (_volumes.size() < 1) {
|
|
f.close();
|
|
return;
|
|
}
|
|
|
|
size_t maxLoop = 0;
|
|
f << "const vec3 volume_dim[] = {\n";
|
|
for (int i = 0; i < _volumes.size(); ++i) {
|
|
glm::size3_t size = _volumes.at(i).second->dimensions();
|
|
for (int k = 0; k < 3; ++k)
|
|
maxLoop = glm::max(maxLoop, size[k]);
|
|
f << " vec3(" << std::to_string(size[0]) << ".0," + std::to_string(size[1]) << ".0,"
|
|
<< std::to_string(size[2]) + ".0),\n";
|
|
}
|
|
f << "};\n";
|
|
|
|
f << "#define LOOP_LIMIT " << maxLoop << "\n";
|
|
|
|
f << "float volumeStepSize[] = {\n";
|
|
for (int i = 0; i < _volumes.size(); ++i) {
|
|
glm::size3_t size = _volumes.at(i).second->dimensions();
|
|
f << " stepSize,\n";
|
|
}
|
|
f << "};\n";
|
|
|
|
f << "float volumeStepSizeOriginal[] = {\n";
|
|
for (int i = 0; i < _volumes.size(); ++i) {
|
|
glm::size3_t size = _volumes.at(i).second->dimensions();
|
|
f << " stepSize,\n";
|
|
}
|
|
f << "};\n";
|
|
|
|
f.close();
|
|
}
|
|
|
|
void ABuffer::openspaceSamplerCalls() {
|
|
std::ofstream f(absPath(generatedSamplerCallsPath));
|
|
for (int i = 0; i < _samplers.size(); ++i) {
|
|
auto found1 = _samplers.at(i).find_first_not_of("vec4 ");
|
|
auto found2 = _samplers.at(i).find_first_of("(", found1);
|
|
if (found1 != std::string::npos && found2 != std::string::npos) {
|
|
std::string functionName = _samplers.at(i).substr(found1, found2 - found1);
|
|
f << "#ifndef SKIP_VOLUME_" << i << "\n"
|
|
<< "if((currentVolumeBitmask & (1 << " << i << ")) == " << std::to_string(1 << i) << ") {\n"
|
|
<< " vec4 c = " << functionName << "(final_color,volume_position[" << i << "]);\n"
|
|
<< " blendStep(final_color, c, volumeStepSize[" << i << "]);\n"
|
|
<< " volume_position[" << i << "] += volume_direction[" << i << "]*volumeStepSize[" << i << "];\n"
|
|
<< "}\n"
|
|
<< "#endif\n";
|
|
}
|
|
}
|
|
f.close();
|
|
}
|
|
|
|
void ABuffer::openspaceSamplers() {
|
|
std::ofstream f(absPath(generatedSamplersPath));
|
|
for (const std::string& sampler : _samplers)
|
|
f << sampler << std::endl;
|
|
f.close();
|
|
}
|
|
|
|
void ABuffer::openspaceTransferFunction() {
|
|
std::ofstream f(absPath(generatedTransferFunctionVisualizerPath));
|
|
f << "float showfunc_size = 20.0;\n"
|
|
<< "float SCREEN_HEIGHTf = float(SCREEN_HEIGHT);\n"
|
|
<< "float SCREEN_WIDTHf = float(SCREEN_WIDTH);\n";
|
|
for (int i = 0; i < _transferFunctions.size(); ++i) {
|
|
f << "if( gl_FragCoord.y > SCREEN_HEIGHTf-showfunc_size*" << i + 1
|
|
<< " && gl_FragCoord.y < SCREEN_HEIGHTf-showfunc_size*" << std::to_string(i) << ") {\n"
|
|
<< " float normalizedIntensity = gl_FragCoord.x / (SCREEN_WIDTHf-1) ;\n"
|
|
<< " vec4 tfc = texture(" << _transferFunctions.at(i).first << ", normalizedIntensity);\n"
|
|
<< " final_color = tfc;\n"
|
|
<< " float cmpf = SCREEN_HEIGHTf-showfunc_size*" << i + 1 << " + tfc.a*showfunc_size;\n"
|
|
<< " if(gl_FragCoord.y > cmpf) {\n"
|
|
<< " final_color = vec4(0,0,0,0);\n"
|
|
<< " } else {\n"
|
|
<< " final_color.a = 1.0;\n"
|
|
<< " }\n"
|
|
<< "} else if(ceil(gl_FragCoord.y) == SCREEN_HEIGHTf - showfunc_size*" << i + 1 << ") {\n"
|
|
<< " const float intensity = 0.4;\n"
|
|
<< " final_color = vec4(intensity,intensity,intensity,1.0);\n"
|
|
<< "}\n";
|
|
}
|
|
f.close();
|
|
}
|
|
|
|
void ABuffer::invalidateABuffer() {
|
|
LDEBUG("Shader invalidated");
|
|
_validShader = false;
|
|
}
|
|
|
|
void ABuffer::updateDimensions() {
|
|
_width = sgct::Engine::instance()->getActiveWindowPtr()->getXFramebufferResolution();
|
|
_height = sgct::Engine::instance()->getActiveWindowPtr()->getYFramebufferResolution();
|
|
_totalPixels = _width * _height;
|
|
}
|
|
|
|
|
|
} // openspace
|