From e8238859f8ee3ec92cdc4522b8a8a249576d74a7 Mon Sep 17 00:00:00 2001 From: Michael Nilsson Date: Tue, 31 May 2016 17:54:39 -0400 Subject: [PATCH 1/3] refactor cygnetplane and dataplane --- modules/iswa/CMakeLists.txt | 2 + modules/iswa/rendering/cygnetplane.cpp | 40 +-- modules/iswa/rendering/datacygnet.cpp | 192 ++++++++++++- modules/iswa/rendering/datacygnet.h | 45 +++ modules/iswa/rendering/dataplane.cpp | 337 +++++++---------------- modules/iswa/rendering/dataplane.h | 39 ++- modules/iswa/rendering/datasphere.cpp | 2 +- modules/iswa/rendering/datasphere.h | 2 +- modules/iswa/rendering/iswacygnet.cpp | 21 +- modules/iswa/rendering/iswacygnet.h | 21 +- modules/iswa/rendering/kameleonplane.cpp | 2 +- modules/iswa/rendering/kameleonplane.h | 2 +- modules/iswa/rendering/texturecygnet.h | 5 +- modules/iswa/rendering/textureplane.cpp | 2 +- modules/iswa/rendering/textureplane.h | 2 +- modules/iswa/shaders/dataplane_fs.glsl | 2 +- 16 files changed, 403 insertions(+), 313 deletions(-) diff --git a/modules/iswa/CMakeLists.txt b/modules/iswa/CMakeLists.txt index f8688eed9f..daf6deba5c 100644 --- a/modules/iswa/CMakeLists.txt +++ b/modules/iswa/CMakeLists.txt @@ -37,6 +37,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspacecygnet.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/iswagroup.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/texturecygnet.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/datacygnet.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -53,6 +54,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspacecygnet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/iswagroup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/texturecygnet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/datacygnet.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/iswa/rendering/cygnetplane.cpp b/modules/iswa/rendering/cygnetplane.cpp index add4407c54..2f4d3eb934 100644 --- a/modules/iswa/rendering/cygnetplane.cpp +++ b/modules/iswa/rendering/cygnetplane.cpp @@ -1,20 +1,26 @@ -// * 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. * -// ****************************************************************************************/ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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 #include diff --git a/modules/iswa/rendering/datacygnet.cpp b/modules/iswa/rendering/datacygnet.cpp index c943966da1..50007963fc 100644 --- a/modules/iswa/rendering/datacygnet.cpp +++ b/modules/iswa/rendering/datacygnet.cpp @@ -24,6 +24,11 @@ #include +#include +#include +#include +#include + namespace { const std::string _loggerCat = "DataCygnet"; } @@ -32,8 +37,193 @@ namespace openspace{ DataCygnet::DataCygnet(const ghoul::Dictionary& dictionary) :IswaCygnet(dictionary) -{} + ,_dataProcessor(nullptr) + ,_dataOptions("dataOptions", "Data Options") +{ + addProperty(_dataOptions); +} DataCygnet::~DataCygnet(){} +bool DataCygnet::loadTexture(){ + // if The future is done then get the new dataFile + if(_futureObject.valid() && DownloadManager::futureReady(_futureObject)){ + DownloadManager::MemoryFile dataFile = _futureObject.get(); + + if(dataFile.corrupted) + return false; + + _dataBuffer = ""; + _dataBuffer.append(dataFile.buffer, dataFile.size); + delete[] dataFile.buffer; + } + + // if the buffer in the datafile is empty, do not proceed + if(_dataBuffer.empty()) + return false; + + if(!_dataOptions.options().size()){ // load options for value selection + fillOptions(); + _dataProcessor->addValues(_dataBuffer, _dataOptions); + + if(_group) + _group->updateGroup(); + } + + std::vector data = _dataProcessor->readData2(_dataBuffer, _dataOptions); + + if(data.empty()) + return false; + + bool texturesReady = false; + std::vector selectedOptions = _dataOptions.value(); + + for(int option: selectedOptions){ + float* values = data[option]; + if(!values) continue; + + if(!_textures[option]){ + std::unique_ptr texture = std::make_unique( + values, + _dataProcessor->dimensions(), + ghoul::opengl::Texture::Format::Red, + GL_RED, + GL_FLOAT, + ghoul::opengl::Texture::FilterMode::Linear, + ghoul::opengl::Texture::WrappingMode::ClampToEdge + ); + + if(texture){ + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + _textures[option] = std::move(texture); + } + }else{ + _textures[option]->setPixelData(values); + _textures[option]->uploadTexture(); + } + texturesReady = true; + } + + return texturesReady; +} + +bool DataCygnet::updateTexture(){ + if(_futureObject.valid()) + return false; + + std::future future = IswaManager::ref().fetchDataCygnet(_data->id); + + if(future.valid()){ + _futureObject = std::move(future); + return true; + } + + return false; +} + +bool DataCygnet::readyToRender() const{ + return (!_textures.empty()); +} + +/** + * Set both transfer function textures and data textures in same function so that they bind to + * the right texture units. If separate in to two functions a list of ghoul::TextureUnit + * needs to be passed as an argument to both. + */ +void DataCygnet::setTextureUniforms(){ + std::vector selectedOptions = _dataOptions.value(); + int activeTextures = std::min((int)selectedOptions.size(), MAX_TEXTURES); + int activeTransferfunctions = std::min((int)_transferFunctions.size(), MAX_TEXTURES); + + // Set Textures + ghoul::opengl::TextureUnit txUnits[MAX_TEXTURES]; + int j = 0; + for(int option : selectedOptions){ + if(_textures[option]){ + txUnits[j].activate(); + _textures[option]->bind(); + _shader->setUniform( + "textures[" + std::to_string(j) + "]", + txUnits[j] + ); + + j++; + if(j >= MAX_TEXTURES) break; + } + } + + //Set Transfer Functions + if(activeTextures > 0){ + if(selectedOptions.back()>=activeTransferfunctions) + activeTransferfunctions = 1; + } + + ghoul::opengl::TextureUnit tfUnits[MAX_TEXTURES]; + j = 0; + + if((activeTransferfunctions == 1)){ + tfUnits[0].activate(); + _transferFunctions[0]->bind(); + _shader->setUniform( + "transferFunctions[0]", + tfUnits[0] + ); + }else{ + for(int option : selectedOptions){ + + if(_transferFunctions[option]){ + tfUnits[j].activate(); + _transferFunctions[option]->bind(); + _shader->setUniform( + "transferFunctions[" + std::to_string(j) + "]", + tfUnits[j] + ); + + j++; + if(j >= MAX_TEXTURES) break; + } + } + } + + _shader->setUniform("numTransferFunctions", activeTransferfunctions); + _shader->setUniform("numTextures", activeTextures); +} + + +void DataCygnet::readTransferFunctions(std::string tfPath){ + std::string line; + std::ifstream tfFile(absPath(tfPath)); + + std::vector> tfs; + + if(tfFile.is_open()){ + while(getline(tfFile, line)){ + std::shared_ptr tf = std::make_shared(absPath(line)); + if(tf){ + tfs.push_back(tf); + } + } + + tfFile.close(); + } + + if(!tfs.empty()){ + _transferFunctions.clear(); + _transferFunctions = tfs; + } +} + +void DataCygnet::fillOptions(){ + std::vector options = _dataProcessor->readHeader(_dataBuffer); + for(int i=0; i(1,0)); + if(_group) + _group->registerOptions(_dataOptions.options()); +} + + } //namespace openspace \ No newline at end of file diff --git a/modules/iswa/rendering/datacygnet.h b/modules/iswa/rendering/datacygnet.h index beeab0ecf3..52dd6a5936 100644 --- a/modules/iswa/rendering/datacygnet.h +++ b/modules/iswa/rendering/datacygnet.h @@ -26,14 +26,59 @@ #define __DATACYGNET_H__ #include +#include +#include + +namespace { + const int MAX_TEXTURES = 6; +} namespace openspace{ +/** + * This class abstracts away the the loading of data and creation of + * textures for all data cygnets. It specifies the interface that needs to + * be implemented for all concrete subclasses + */ class DataCygnet : public IswaCygnet { public: DataCygnet(const ghoul::Dictionary& dictionary); ~DataCygnet(); protected: + + bool loadTexture() override; + bool updateTexture() override; + bool readyToRender() const override; + + /** + * loads the transferfunctions specified in tfPath into + * _transferFunctions list. + * + * @param tfPath Path to transfer function file + */ + void readTransferFunctions(std::string tfPath); + + /** + * This function binds and sets all textures that should go to the + * shader program, this includes both the data and transferfunctions. + */ + void setTextureUniforms(); + + // Subclass interface + virtual bool createGeometry() = 0; + virtual bool destroyGeometry() = 0; + virtual void renderGeometry() const = 0; + // This function can call parent setTextureUniforms() + virtual void setUniforms() = 0; + + properties::SelectionProperty _dataOptions; + std::shared_ptr _dataProcessor; + +private: + void fillOptions(); + + std::string _dataBuffer; + }; } //namespace openspace diff --git a/modules/iswa/rendering/dataplane.cpp b/modules/iswa/rendering/dataplane.cpp index df8587ae4d..62a2d9a6fa 100644 --- a/modules/iswa/rendering/dataplane.cpp +++ b/modules/iswa/rendering/dataplane.cpp @@ -23,35 +23,20 @@ ****************************************************************************************/ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace { const std::string _loggerCat = "DataPlane"; - const int MAX_TEXTURES = 6; } namespace openspace { DataPlane::DataPlane(const ghoul::Dictionary& dictionary) - :CygnetPlane(dictionary) + :DataCygnet(dictionary) ,_useLog("useLog","Use Logarithm", false) - ,_useHistogram("useHistogram", "Use Histogram", true) - ,_autoFilter("autoFilter", "Auto Filter", true) + ,_useHistogram("useHistogram", "Use Histogram", false) + ,_autoFilter("autoFilter", "Auto Filter", false) ,_normValues("normValues", "Normalize Values", glm::vec2(1.0,1.0), glm::vec2(0), glm::vec2(5.0)) ,_backgroundValues("backgroundValues", "Background Values", glm::vec2(0.0), glm::vec2(0), glm::vec2(1.0)) ,_transferFunctionsFile("transferfunctions", "Transfer Functions", "${SCENE}/iswa/tfs/hot.tf") - ,_dataOptions("dataOptions", "Data Options") - ,_dataProcessor(nullptr) { std::string name; dictionary.getValue("Name", name); @@ -65,7 +50,6 @@ DataPlane::DataPlane(const ghoul::Dictionary& dictionary) addProperty(_normValues); addProperty(_backgroundValues); addProperty(_transferFunctionsFile); - addProperty(_dataOptions); _type = IswaManager::CygnetType::Data; @@ -81,59 +65,7 @@ bool DataPlane::initialize(){ if(_group){ _dataProcessor = _group->dataProcessor(); - - _groupEvent->subscribe(name(), "useLogChanged", [&](const ghoul::Dictionary& dict){ - LDEBUG(name() + " Event useLogChanged"); - _useLog.setValue(dict.value("useLog")); - }); - - _groupEvent->subscribe(name(), "normValuesChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event normValuesChanged"); - std::shared_ptr values; - bool success = dict.getValue("normValues", values); - if(success){ - _normValues.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "useHistogramChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event useHistogramChanged"); - _useHistogram.setValue(dict.value("useHistogram")); - }); - - _groupEvent->subscribe(name(), "dataOptionsChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event dataOptionsChanged"); - std::shared_ptr > values; - bool success = dict.getValue("dataOptions", values); - if(success){ - _dataOptions.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "transferFunctionsChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event transferFunctionsChanged"); - _transferFunctionsFile.setValue(dict.value("transferFunctions")); - }); - - _groupEvent->subscribe(name(), "backgroundValuesChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event backgroundValuesChanged"); - std::shared_ptr values; - bool success = dict.getValue("backgroundValues", values); - if(success){ - _backgroundValues.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "autoFilterChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event autoFilterChanged"); - _autoFilter.setValue(dict.value("autoFilter")); - }); - - _groupEvent->subscribe(name(), "updateGroup", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event updateGroup"); - loadTexture(); - }); - + subscribeToGroup(); }else{ OsEng.gui()._iswa.registerProperty(&_useLog); OsEng.gui()._iswa.registerProperty(&_useHistogram); @@ -149,7 +81,7 @@ bool DataPlane::initialize(){ ); } - setTransferFunctions(_transferFunctionsFile.value()); + readTransferFunctions(_transferFunctionsFile.value()); _normValues.onChange([this](){ _dataProcessor->normValues(_normValues.value()); @@ -173,194 +105,129 @@ bool DataPlane::initialize(){ }); _transferFunctionsFile.onChange([this](){ - setTransferFunctions(_transferFunctionsFile.value()); + readTransferFunctions(_transferFunctionsFile.value()); }); + _autoFilter.onChange([this](){ + if(_autoFilter.value()) + _backgroundValues.setValue(_dataProcessor->filterValues()); + }); + + _autoFilter.setValue(true); + return true; } -bool DataPlane::loadTexture() { - // if The future is done then get the new dataFile - if(_futureObject.valid() && DownloadManager::futureReady(_futureObject)){ - DownloadManager::MemoryFile dataFile = _futureObject.get(); - - if(dataFile.corrupted) - return false; - - _dataBuffer = ""; - _dataBuffer.append(dataFile.buffer, dataFile.size); - delete[] dataFile.buffer; - } - - // if the buffer in the datafile is empty, do not proceed - if(_dataBuffer.empty()) - return false; - - if(!_dataOptions.options().size()){ // load options for value selection - fillOptions(); - _dataProcessor->addValues(_dataBuffer, _dataOptions); - - if(_group) - _group->updateGroup(); - } - - std::vector data = _dataProcessor->readData2(_dataBuffer, _dataOptions); - - if(data.empty()) - return false; - - if(_autoFilter.value()) - _backgroundValues.setValue(_dataProcessor->filterValues()); +bool DataPlane::createGeometry() { + glGenVertexArrays(1, &_quad); // generate array + glGenBuffers(1, &_vertexPositionBuffer); // generate buffer - bool texturesReady = false; - std::vector selectedOptions = _dataOptions.value(); + // ============================ + // GEOMETRY (quad) + // ============================ + // GLfloat x,y, z; + float s = _data->spatialScale.x; + const GLfloat x = s*_data->scale.x/2.0; + const GLfloat y = s*_data->scale.y/2.0; + const GLfloat z = s*_data->scale.z/2.0; + const GLfloat w = _data->spatialScale.w; - for(int option: selectedOptions){ - float* values = data[option]; - if(!values) continue; + const GLfloat vertex_data[] = { // square of two triangles (sigh) + // x y z w s t + -x, -y, -z, w, 0, 1, + x, y, z, w, 1, 0, + -x, ((x>0)?y:-y), z, w, 0, 0, + -x, -y, -z, w, 0, 1, + x, ((x>0)?-y:y), -z, w, 1, 1, + x, y, z, w, 1, 0, + }; - if(!_textures[option]){ - std::unique_ptr texture = std::make_unique( - values, - _dataProcessor->dimensions(), - ghoul::opengl::Texture::Format::Red, - GL_RED, - GL_FLOAT, - ghoul::opengl::Texture::FilterMode::Linear, - ghoul::opengl::Texture::WrappingMode::ClampToEdge - ); - - if(texture){ - texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - _textures[option] = std::move(texture); - } - }else{ - _textures[option]->setPixelData(values); - _textures[option]->uploadTexture(); - } - texturesReady = true; - } - - return texturesReady; + glBindVertexArray(_quad); // bind array + glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); // bind buffer + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast(0)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast(sizeof(GLfloat) * 4)); + return true; } -bool DataPlane::updateTexture(){ - if(_futureObject.valid()) - return false; +bool DataPlane::destroyGeometry(){ + glDeleteVertexArrays(1, &_quad); + _quad = 0; - std::future future = IswaManager::ref().fetchDataCygnet(_data->id); + glDeleteBuffers(1, &_vertexPositionBuffer); + _vertexPositionBuffer = 0; - if(future.valid()){ - _futureObject = std::move(future); - return true; - } - - return false; + return true; } -bool DataPlane::readyToRender() const { - return (!_textures.empty()); +void DataPlane::renderGeometry() const { + glBindVertexArray(_quad); + glDrawArrays(GL_TRIANGLES, 0, 6); } -void DataPlane::setUniformAndTextures(){ - std::vector selectedOptions = _dataOptions.value(); - int activeTextures = std::min((int)selectedOptions.size(), MAX_TEXTURES); - int activeTransferfunctions = std::min((int)_transferFunctions.size(), MAX_TEXTURES); - - ghoul::opengl::TextureUnit txUnits[6]; - int j = 0; - for(int option : selectedOptions){ - if(_textures[option]){ - txUnits[j].activate(); - _textures[option]->bind(); - _shader->setUniform( - "textures[" + std::to_string(j) + "]", - txUnits[j] - ); - - j++; - if(j >= MAX_TEXTURES) break; - } - } - - if(activeTextures > 0){ - if(selectedOptions.back()>=activeTransferfunctions) - activeTransferfunctions = 1; - } - - ghoul::opengl::TextureUnit tfUnits[6]; - j = 0; - - if((activeTransferfunctions == 1)){ - tfUnits[0].activate(); - _transferFunctions[0]->bind(); - _shader->setUniform( - "transferFunctions[0]", - tfUnits[0] - ); - }else{ - for(int option : selectedOptions){ - // std::cout << option << std::endl; - // if(option >= activeTransferfunctions){ - // // LWARNING("No transfer function for this value."); - // break; - // } - - if(_transferFunctions[option]){ - tfUnits[j].activate(); - _transferFunctions[option]->bind(); - _shader->setUniform( - "transferFunctions[" + std::to_string(j) + "]", - tfUnits[j] - ); - - j++; - if(j >= MAX_TEXTURES) break; - } - } - } - - _shader->setUniform("numTextures", activeTextures); - _shader->setUniform("numTransferFunctions", activeTransferfunctions); +void DataPlane::setUniforms(){ + // set both data texture and transfer function texture + setTextureUniforms(); _shader->setUniform("backgroundValues", _backgroundValues.value()); _shader->setUniform("transparency", _alpha.value()); } -void DataPlane::setTransferFunctions(std::string tfPath){ - std::string line; - std::ifstream tfFile(absPath(tfPath)); +void DataPlane::subscribeToGroup(){ + _groupEvent->subscribe(name(), "useLogChanged", [&](const ghoul::Dictionary& dict){ + LDEBUG(name() + " Event useLogChanged"); + _useLog.setValue(dict.value("useLog")); + }); - std::vector> tfs; - - if(tfFile.is_open()){ - while(getline(tfFile, line)){ - std::shared_ptr tf = std::make_shared(absPath(line)); - if(tf){ - tfs.push_back(tf); - } + _groupEvent->subscribe(name(), "normValuesChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event normValuesChanged"); + std::shared_ptr values; + bool success = dict.getValue("normValues", values); + if(success){ + _normValues.setValue(*values); } + }); - tfFile.close(); - } + _groupEvent->subscribe(name(), "useHistogramChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event useHistogramChanged"); + _useHistogram.setValue(dict.value("useHistogram")); + }); - - if(!tfs.empty()){ - _transferFunctions.clear(); - _transferFunctions = tfs; - } + _groupEvent->subscribe(name(), "dataOptionsChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event dataOptionsChanged"); + std::shared_ptr > values; + bool success = dict.getValue("dataOptions", values); + if(success){ + _dataOptions.setValue(*values); + } + }); + + _groupEvent->subscribe(name(), "transferFunctionsChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event transferFunctionsChanged"); + _transferFunctionsFile.setValue(dict.value("transferFunctions")); + }); + + _groupEvent->subscribe(name(), "backgroundValuesChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event backgroundValuesChanged"); + std::shared_ptr values; + bool success = dict.getValue("backgroundValues", values); + if(success){ + _backgroundValues.setValue(*values); + } + }); + + _groupEvent->subscribe(name(), "autoFilterChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event autoFilterChanged"); + _autoFilter.setValue(dict.value("autoFilter")); + }); + + _groupEvent->subscribe(name(), "updateGroup", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event updateGroup"); + loadTexture(); + }); } -void DataPlane::fillOptions(){ - std::vector options = _dataProcessor->readHeader(_dataBuffer); - for(int i=0; i(1,0)); - if(_group) - _group->registerOptions(_dataOptions.options()); -} }// namespace openspace \ No newline at end of file diff --git a/modules/iswa/rendering/dataplane.h b/modules/iswa/rendering/dataplane.h index 6e38bbd832..fc152ee3e6 100644 --- a/modules/iswa/rendering/dataplane.h +++ b/modules/iswa/rendering/dataplane.h @@ -25,49 +25,46 @@ #ifndef __DATAPLANE_H__ #define __DATAPLANE_H__ -#include -#include +#include #include #include -#include namespace openspace{ class IswaGroup; -class DataPlane : public CygnetPlane { +class DataPlane : public DataCygnet { friend class IswaGroup; - public: +public: DataPlane(const ghoul::Dictionary& dictionary); ~DataPlane(); bool initialize() override; - private: - virtual bool loadTexture() override; - virtual bool updateTexture() override; +protected: - virtual bool readyToRender() const override; - virtual void setUniformAndTextures() override; + /** + * Creates a plane geometry + */ + bool createGeometry() override; + bool destroyGeometry() override; + void renderGeometry() const override; + void setUniforms() override; - void setTransferFunctions(std::string tfPath); - void fillOptions(); +private: + + void subscribeToGroup(); - properties::SelectionProperty _dataOptions; properties::StringProperty _transferFunctionsFile; properties::Vec2Property _backgroundValues; - properties::Vec2Property _normValues; - properties::BoolProperty _useLog; properties::BoolProperty _useHistogram; properties::BoolProperty _autoFilter; - std::string _dataBuffer; + GLuint _quad; + GLuint _vertexPositionBuffer; +}; - std::shared_ptr _dataProcessor; - - }; - - } // namespace openspace +} // namespace openspace #endif //__DATAPLANE_H__ \ No newline at end of file diff --git a/modules/iswa/rendering/datasphere.cpp b/modules/iswa/rendering/datasphere.cpp index 454efc6dd8..46582f7188 100644 --- a/modules/iswa/rendering/datasphere.cpp +++ b/modules/iswa/rendering/datasphere.cpp @@ -267,7 +267,7 @@ bool DataSphere::readyToRender() const { } -void DataSphere::setUniformAndTextures(){ +void DataSphere::setUniforms(){ std::vector selectedOptions = _dataOptions.value(); int activeTextures = std::min((int)selectedOptions.size(), MAX_TEXTURES); int activeTransferfunctions = std::min((int)_transferFunctions.size(), MAX_TEXTURES); diff --git a/modules/iswa/rendering/datasphere.h b/modules/iswa/rendering/datasphere.h index 094dd83335..904ffbbdc6 100644 --- a/modules/iswa/rendering/datasphere.h +++ b/modules/iswa/rendering/datasphere.h @@ -46,7 +46,7 @@ private: virtual bool updateTexture() override; virtual bool readyToRender() const override; - virtual void setUniformAndTextures() override; + virtual void setUniforms() override; void setTransferFunctions(std::string tfPath); void fillOptions(); diff --git a/modules/iswa/rendering/iswacygnet.cpp b/modules/iswa/rendering/iswacygnet.cpp index 976d6c81ab..556d2f25a2 100644 --- a/modules/iswa/rendering/iswacygnet.cpp +++ b/modules/iswa/rendering/iswacygnet.cpp @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include @@ -86,26 +84,11 @@ IswaCygnet::IswaCygnet(const ghoul::Dictionary& dictionary) _data->scale = scale; _data->offset = offset; - // std::cout << std::to_string(min) << std::endl; - // std::cout << std::to_string(max) << std::endl; - // std::cout << std::to_string(_data->scale) << std::endl; - // std::cout << std::to_string(_data->offset) << std::endl; - addProperty(_alpha); addProperty(_delete); - // if(dictionary.hasValue("Group")){ dictionary.getValue("Group", _data->groupName); - // } - // _data->groupId = groupId; - // std::cout << _data->id << std::endl; - // std::cout << _data->frame << std::endl; - // std::cout << std::to_string(_data->offset) << std::endl; - // std::cout << std::to_string(_data->scale) << std::endl; - // std::cout << std::to_string(_data->max) << std::endl; - // std::cout << std::to_string(_data->min) << std::endl; - // std::cout << std::to_string(_data->spatialScale) << std::endl; - // OsEng.gui()._iswa.registerProperty(&_enabled); + } IswaCygnet::~IswaCygnet(){} @@ -178,7 +161,7 @@ void IswaCygnet::render(const RenderData& data){ setPscUniforms(*_shader.get(), data.camera, position); - setUniformAndTextures(); + setUniforms(); renderGeometry(); glEnable(GL_CULL_FACE); diff --git a/modules/iswa/rendering/iswacygnet.h b/modules/iswa/rendering/iswacygnet.h index 13afd16813..0999c8d200 100644 --- a/modules/iswa/rendering/iswacygnet.h +++ b/modules/iswa/rendering/iswacygnet.h @@ -30,20 +30,16 @@ #include #include -#include -#include +#include #include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include #include -#include -#include #include #include @@ -89,7 +85,6 @@ protected: void unregisterProperties(); void initializeTime(); void initializeGroup(); - bool destroyShader(); bool createShader(); virtual bool createGeometry() = 0; @@ -99,7 +94,7 @@ protected: virtual bool loadTexture() = 0; virtual bool updateTexture() = 0; virtual bool readyToRender() const = 0; - virtual void setUniformAndTextures() = 0; + virtual void setUniforms() = 0; properties::FloatProperty _alpha; properties::TriggerProperty _delete; @@ -131,6 +126,10 @@ protected: std::string _vsPath; std::string _fsPath; std::string _programName; + +private: + bool destroyShader(); + }; }//namespace openspace diff --git a/modules/iswa/rendering/kameleonplane.cpp b/modules/iswa/rendering/kameleonplane.cpp index 4622689206..1362cf594f 100644 --- a/modules/iswa/rendering/kameleonplane.cpp +++ b/modules/iswa/rendering/kameleonplane.cpp @@ -352,7 +352,7 @@ bool KameleonPlane::readyToRender() const { return (!_textures.empty() && !_transferFunctions.empty()); } -void KameleonPlane::setUniformAndTextures(){ +void KameleonPlane::setUniforms(){ std::vector selectedOptions = _dataOptions.value(); int activeTextures = std::min((int)selectedOptions.size(), MAX_TEXTURES); int activeTransferfunctions = std::min((int)_transferFunctions.size(), MAX_TEXTURES); diff --git a/modules/iswa/rendering/kameleonplane.h b/modules/iswa/rendering/kameleonplane.h index 3a1c7fc545..b1bf47ddd3 100644 --- a/modules/iswa/rendering/kameleonplane.h +++ b/modules/iswa/rendering/kameleonplane.h @@ -47,7 +47,7 @@ virtual bool updateTexture() override; virtual bool readyToRender() const override; - virtual void setUniformAndTextures() override; + virtual void setUniforms() override; /** * Given a path to the json index of seedpoints file, this diff --git a/modules/iswa/rendering/texturecygnet.h b/modules/iswa/rendering/texturecygnet.h index d4837fe2a2..bd0e2ca7c5 100644 --- a/modules/iswa/rendering/texturecygnet.h +++ b/modules/iswa/rendering/texturecygnet.h @@ -30,7 +30,7 @@ namespace openspace{ /** - * This class exist tp abstract away the loading of images + * This class exist to abstract away the loading of images * from iSWA and updating of the textures for child geometries. * The class specifies the minimum interface that child classes * needs to implement. @@ -47,7 +47,8 @@ protected: bool readyToRender() const override; // Interface for concrete subclasses - virtual void setUniformAndTextures() = 0; + //virtual void setUniformAndTextures() = 0; + virtual void setUniforms() = 0; virtual bool createGeometry() = 0; virtual bool destroyGeometry() = 0; virtual void renderGeometry() const = 0; diff --git a/modules/iswa/rendering/textureplane.cpp b/modules/iswa/rendering/textureplane.cpp index f73aaac34d..17323cad40 100644 --- a/modules/iswa/rendering/textureplane.cpp +++ b/modules/iswa/rendering/textureplane.cpp @@ -50,7 +50,7 @@ TexturePlane::TexturePlane(const ghoul::Dictionary& dictionary) TexturePlane::~TexturePlane(){} -void TexturePlane::setUniformAndTextures(){ +void TexturePlane::setUniforms(){ ghoul::opengl::TextureUnit unit; unit.activate(); diff --git a/modules/iswa/rendering/textureplane.h b/modules/iswa/rendering/textureplane.h index 62a2a571d1..aa27d6cd50 100644 --- a/modules/iswa/rendering/textureplane.h +++ b/modules/iswa/rendering/textureplane.h @@ -41,7 +41,7 @@ public: private: bool createGeometry() override; - void setUniformAndTextures() override; + void setUniforms() override; bool destroyGeometry() override; void renderGeometry() const override; diff --git a/modules/iswa/shaders/dataplane_fs.glsl b/modules/iswa/shaders/dataplane_fs.glsl index c17b190d06..c470fd14d3 100644 --- a/modules/iswa/shaders/dataplane_fs.glsl +++ b/modules/iswa/shaders/dataplane_fs.glsl @@ -35,7 +35,6 @@ uniform bool averageValues; uniform vec2 backgroundValues; uniform float transparency; - // uniform float background; in vec2 vs_st; @@ -63,6 +62,7 @@ Fragment getFragment() { vec4 color = texture(transferFunctions[0], vec2(v,0)); if((v<(x+y)) && v>(x-y)) color = mix(transparent, color, clamp(1,0,abs(v-x))); + diffuse = color; }else{ for(int i=0; i Date: Tue, 31 May 2016 18:00:09 -0400 Subject: [PATCH 2/3] add class comments to dataplane --- modules/iswa/rendering/dataplane.h | 5 +++++ modules/iswa/rendering/textureplane.h | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/iswa/rendering/dataplane.h b/modules/iswa/rendering/dataplane.h index fc152ee3e6..210cc40b1a 100644 --- a/modules/iswa/rendering/dataplane.h +++ b/modules/iswa/rendering/dataplane.h @@ -32,6 +32,11 @@ namespace openspace{ class IswaGroup; +/** + * DataPlane is a concrete IswaCygnet with data files as its input source. + * The class handles creation, destruction and rendering of a plane geometry. + * It also specifies what uniforms to use and what GUI properties it needs. + */ class DataPlane : public DataCygnet { friend class IswaGroup; public: diff --git a/modules/iswa/rendering/textureplane.h b/modules/iswa/rendering/textureplane.h index aa27d6cd50..4803ec0176 100644 --- a/modules/iswa/rendering/textureplane.h +++ b/modules/iswa/rendering/textureplane.h @@ -30,9 +30,9 @@ namespace openspace{ /** - * TexturePlane is a "concrete" IswaCygnet. It handles the creation, - * destruction and rendering of a plane geometry. It also specifies - * which shaders to use and the uniforms that it needs. + * TexturePlane is a "concrete" IswaCygnet with texture as its input source. + * It handles the creation, destruction and rendering of a plane geometry. + * It also specifies which shaders to use and the uniforms that it needs. */ class TexturePlane : public TextureCygnet{ public: From f029362372cc5659e03608daff651c587b9d332a Mon Sep 17 00:00:00 2001 From: Michael Nilsson Date: Wed, 1 Jun 2016 13:19:45 -0400 Subject: [PATCH 3/3] refactor datasphere --- modules/iswa/rendering/cygnetsphere.cpp | 44 +-- modules/iswa/rendering/datacygnet.cpp | 37 ++- modules/iswa/rendering/datacygnet.h | 5 +- modules/iswa/rendering/dataplane.cpp | 32 +- modules/iswa/rendering/dataplane.h | 1 - modules/iswa/rendering/datasphere.cpp | 363 ++++++----------------- modules/iswa/rendering/datasphere.h | 39 +-- modules/iswa/rendering/iswacygnet.cpp | 4 + modules/iswa/rendering/iswagroup.cpp | 16 +- modules/iswa/rendering/kameleonplane.cpp | 5 +- modules/iswa/rendering/texturecygnet.cpp | 5 +- modules/iswa/rendering/textureplane.cpp | 7 - modules/iswa/util/iswamanager.cpp | 1 + 13 files changed, 219 insertions(+), 340 deletions(-) diff --git a/modules/iswa/rendering/cygnetsphere.cpp b/modules/iswa/rendering/cygnetsphere.cpp index 7a2df6d29c..05b811862f 100644 --- a/modules/iswa/rendering/cygnetsphere.cpp +++ b/modules/iswa/rendering/cygnetsphere.cpp @@ -1,20 +1,26 @@ -// * 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. * -// ****************************************************************************************/ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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 @@ -46,12 +52,12 @@ bool CygnetSphere::createGeometry(){ int segments = 100; _sphere = std::make_shared(radius, segments); _sphere->initialize(); - return true; + return true; } bool CygnetSphere::destroyGeometry(){ _sphere = nullptr; - return true; + return true; } void CygnetSphere::renderGeometry() const { diff --git a/modules/iswa/rendering/datacygnet.cpp b/modules/iswa/rendering/datacygnet.cpp index 50007963fc..a7070e97a2 100644 --- a/modules/iswa/rendering/datacygnet.cpp +++ b/modules/iswa/rendering/datacygnet.cpp @@ -41,6 +41,8 @@ DataCygnet::DataCygnet(const ghoul::Dictionary& dictionary) ,_dataOptions("dataOptions", "Data Options") { addProperty(_dataOptions); + registerProperties(); + _type = IswaManager::CygnetType::Data; } DataCygnet::~DataCygnet(){} @@ -64,14 +66,29 @@ bool DataCygnet::loadTexture(){ if(!_dataOptions.options().size()){ // load options for value selection fillOptions(); - _dataProcessor->addValues(_dataBuffer, _dataOptions); - if(_group) + //Temporary if statements + if( className == "DataSphere"){ + _dataProcessor->addValuesFromJSON(_dataBuffer, _dataOptions); + } else if(className == "DataPlane"){ + _dataProcessor->addValues(_dataBuffer, _dataOptions); + } + + // if this datacygnet has added new values then reload texture + // for the whole group, including this datacygnet, and return after. + if(_group){ _group->updateGroup(); + return true; + } } - - std::vector data = _dataProcessor->readData2(_dataBuffer, _dataOptions); - + //Temporary if statements + std::vector data; + if( className == "DataSphere"){ + data = _dataProcessor->readJSONData2(_dataBuffer, _dataOptions); + } else if(className == "DataPlane"){ + data = _dataProcessor->readData2(_dataBuffer, _dataOptions); + } + if(data.empty()) return false; @@ -104,7 +121,6 @@ bool DataCygnet::loadTexture(){ } texturesReady = true; } - return texturesReady; } @@ -215,7 +231,14 @@ void DataCygnet::readTransferFunctions(std::string tfPath){ } void DataCygnet::fillOptions(){ - std::vector options = _dataProcessor->readHeader(_dataBuffer); + std::vector options; + //Temporary if statements + if( className == "DataSphere"){ + options = _dataProcessor->readJSONHeader(_dataBuffer); + } else if(className == "DataPlane"){ + options = _dataProcessor->readHeader(_dataBuffer); + } + for(int i=0; i _dataProcessor; + // Temporary variable untill dataprocessor is done + std::string className; + private: void fillOptions(); diff --git a/modules/iswa/rendering/dataplane.cpp b/modules/iswa/rendering/dataplane.cpp index 62a2d9a6fa..dcc47f1b75 100644 --- a/modules/iswa/rendering/dataplane.cpp +++ b/modules/iswa/rendering/dataplane.cpp @@ -32,17 +32,12 @@ namespace openspace { DataPlane::DataPlane(const ghoul::Dictionary& dictionary) :DataCygnet(dictionary) ,_useLog("useLog","Use Logarithm", false) - ,_useHistogram("useHistogram", "Use Histogram", false) + ,_useHistogram("useHistogram", "Auto Contrast", false) ,_autoFilter("autoFilter", "Auto Filter", false) ,_normValues("normValues", "Normalize Values", glm::vec2(1.0,1.0), glm::vec2(0), glm::vec2(5.0)) ,_backgroundValues("backgroundValues", "Background Values", glm::vec2(0.0), glm::vec2(0), glm::vec2(1.0)) ,_transferFunctionsFile("transferfunctions", "Transfer Functions", "${SCENE}/iswa/tfs/hot.tf") { - std::string name; - dictionary.getValue("Name", name); - setName(name); - - registerProperties(); addProperty(_useLog); addProperty(_useHistogram); @@ -51,11 +46,12 @@ DataPlane::DataPlane(const ghoul::Dictionary& dictionary) addProperty(_backgroundValues); addProperty(_transferFunctionsFile); - _type = IswaManager::CygnetType::Data; - _programName = "DataPlaneProgram"; _vsPath = "${MODULE_ISWA}/shaders/dataplane_vs.glsl"; _fsPath = "${MODULE_ISWA}/shaders/dataplane_fs.glsl"; + + // Temporary + className = "DataPlane"; } DataPlane::~DataPlane(){} @@ -79,6 +75,19 @@ bool DataPlane::initialize(){ _useHistogram.value(), _normValues ); + + + //If autofiler is on, background values property should be hidden + _autoFilter.onChange([this](){ + // If autofiler is selected, use _dataProcessor to set backgroundValues + // and unregister backgroundvalues property. + if(_autoFilter.value()){ + _backgroundValues.setValue(_dataProcessor->filterValues()); + // else if autofilter is turned off, register backgroundValues + } else { + OsEng.gui()._iswa.registerProperty(&_backgroundValues, &_autoFilter); + } + }); } readTransferFunctions(_transferFunctionsFile.value()); @@ -96,6 +105,8 @@ bool DataPlane::initialize(){ _useHistogram.onChange([this](){ _dataProcessor->useHistogram(_useHistogram.value()); loadTexture(); + if(_autoFilter.value()) + _backgroundValues.setValue(_dataProcessor->filterValues()); }); _dataOptions.onChange([this](){ @@ -108,11 +119,6 @@ bool DataPlane::initialize(){ readTransferFunctions(_transferFunctionsFile.value()); }); - _autoFilter.onChange([this](){ - if(_autoFilter.value()) - _backgroundValues.setValue(_dataProcessor->filterValues()); - }); - _autoFilter.setValue(true); return true; diff --git a/modules/iswa/rendering/dataplane.h b/modules/iswa/rendering/dataplane.h index 210cc40b1a..2ed9b12f4b 100644 --- a/modules/iswa/rendering/dataplane.h +++ b/modules/iswa/rendering/dataplane.h @@ -30,7 +30,6 @@ #include namespace openspace{ -class IswaGroup; /** * DataPlane is a concrete IswaCygnet with data files as its input source. diff --git a/modules/iswa/rendering/datasphere.cpp b/modules/iswa/rendering/datasphere.cpp index 46582f7188..c093445057 100644 --- a/modules/iswa/rendering/datasphere.cpp +++ b/modules/iswa/rendering/datasphere.cpp @@ -23,35 +23,27 @@ ****************************************************************************************/ #include -#include -#include -#include -#include -#include - +#include namespace { const std::string _loggerCat = "DataSphere"; - const int MAX_TEXTURES = 6; } namespace openspace { DataSphere::DataSphere(const ghoul::Dictionary& dictionary) - :CygnetSphere(dictionary) + :DataCygnet(dictionary) ,_useLog("useLog","Use Logarithm", false) - ,_useHistogram("useHistogram", "Use Histogram", true) - ,_autoFilter("autoFilter", "Auto Filter", true) + ,_useHistogram("useHistogram", "Auto Contrast", false) + ,_autoFilter("autoFilter", "Auto Filter", false) ,_normValues("normValues", "Normalize Values", glm::vec2(1.0,1.0), glm::vec2(0), glm::vec2(5.0)) ,_backgroundValues("backgroundValues", "Background Values", glm::vec2(0.0), glm::vec2(0), glm::vec2(1.0)) ,_transferFunctionsFile("transferfunctions", "Transfer Functions", "${SCENE}/iswa/tfs/hot.tf") - ,_dataOptions("dataOptions", "Data Options") + ,_sphere(nullptr) { - std::string name; - dictionary.getValue("Name", name); - setName(name); - - registerProperties(); + float radius; + dictionary.getValue("Radius", radius); + _radius = radius; addProperty(_useLog); addProperty(_useHistogram); @@ -59,13 +51,13 @@ DataSphere::DataSphere(const ghoul::Dictionary& dictionary) addProperty(_normValues); addProperty(_backgroundValues); addProperty(_transferFunctionsFile); - addProperty(_dataOptions); - - _type = IswaManager::CygnetType::Data; _programName = "DataSphereProgram"; _vsPath = "${MODULE_ISWA}/shaders/datasphere_vs.glsl"; _fsPath = "${MODULE_ISWA}/shaders/datasphere_fs.glsl"; + + // Temporary + className = "DataSphere"; } DataSphere::~DataSphere(){} @@ -75,65 +67,13 @@ bool DataSphere::initialize(){ if(_group){ _dataProcessor = _group->dataProcessor(); - - _groupEvent->subscribe(name(), "useLogChanged", [&](const ghoul::Dictionary& dict){ - LDEBUG(name() + " Event useLogChanged"); - _useLog.setValue(dict.value("useLog")); - }); - - _groupEvent->subscribe(name(), "normValuesChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event normValuesChanged"); - std::shared_ptr values; - bool success = dict.getValue("normValues", values); - if(success){ - _normValues.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "useHistogramChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event useHistogramChanged"); - _useHistogram.setValue(dict.value("useHistogram")); - }); - - _groupEvent->subscribe(name(), "dataOptionsChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event dataOptionsChanged"); - std::shared_ptr > values; - bool success = dict.getValue("dataOptions", values); - if(success){ - _dataOptions.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "transferFunctionsChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event transferFunctionsChanged"); - _transferFunctionsFile.setValue(dict.value("transferFunctions")); - }); - - _groupEvent->subscribe(name(), "backgroundValuesChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event backgroundValuesChanged"); - std::shared_ptr values; - bool success = dict.getValue("backgroundValues", values); - if(success){ - _backgroundValues.setValue(*values); - } - }); - - _groupEvent->subscribe(name(), "autoFilterChanged", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event autoFilterChanged"); - _autoFilter.setValue(dict.value("autoFilter")); - }); - - _groupEvent->subscribe(name(), "updateGroup", [&](ghoul::Dictionary dict){ - LDEBUG(name() + " Event updateGroup"); - loadTexture(); - }); - + subscribeToGroup(); }else{ OsEng.gui()._iswa.registerProperty(&_useLog); OsEng.gui()._iswa.registerProperty(&_useHistogram); OsEng.gui()._iswa.registerProperty(&_autoFilter); - OsEng.gui()._iswa.registerProperty(&_normValues); OsEng.gui()._iswa.registerProperty(&_backgroundValues); + OsEng.gui()._iswa.registerProperty(&_normValues); OsEng.gui()._iswa.registerProperty(&_transferFunctionsFile); OsEng.gui()._iswa.registerProperty(&_dataOptions); _dataProcessor = std::make_shared( @@ -141,9 +81,21 @@ bool DataSphere::initialize(){ _useHistogram.value(), _normValues ); + + //If autofiler is on, background values property should be hidden + _autoFilter.onChange([this](){ + // If autofiler is selected, use _dataProcessor to set backgroundValues + // and unregister backgroundvalues property. + if(_autoFilter.value()){ + _backgroundValues.setValue(_dataProcessor->filterValues()); + // else if autofilter is turned off, register backgroundValues + } else { + OsEng.gui()._iswa.registerProperty(&_backgroundValues, &_autoFilter); + } + }); } - setTransferFunctions(_transferFunctionsFile.value()); + readTransferFunctions(_transferFunctionsFile.value()); _normValues.onChange([this](){ _dataProcessor->normValues(_normValues.value()); @@ -156,8 +108,10 @@ bool DataSphere::initialize(){ }); _useHistogram.onChange([this](){ - _dataProcessor->useHistogram(_useHistogram.value()); + _dataProcessor->useHistogram(_useHistogram.value()); loadTexture(); + if(_autoFilter.value()) + _backgroundValues.setValue(_dataProcessor->filterValues()); }); _dataOptions.onChange([this](){ @@ -167,219 +121,94 @@ bool DataSphere::initialize(){ }); _transferFunctionsFile.onChange([this](){ - setTransferFunctions(_transferFunctionsFile.value()); + readTransferFunctions(_transferFunctionsFile.value()); }); + _useHistogram.setValue(true); + _autoFilter.setValue(true); + return true; } -bool DataSphere::loadTexture(){ - - // if The future is done then get the new dataFile - if(_futureObject.valid() && DownloadManager::futureReady(_futureObject)){ - DownloadManager::MemoryFile dataFile = _futureObject.get(); - - if(dataFile.corrupted) - return false; - - _dataBuffer = ""; - _dataBuffer.append(dataFile.buffer, dataFile.size); - delete[] dataFile.buffer; - } - - // if the buffer in the datafile is empty, do not proceed - if(_dataBuffer.empty()) - return false; - - if(!_dataOptions.options().size()){ // load options for value selection - fillOptions(); - _dataProcessor->addValuesFromJSON(_dataBuffer, _dataOptions); - - if(_group) - _group->updateGroup(); - } - - std::vector data = _dataProcessor->readJSONData2(_dataBuffer, _dataOptions); - - if(data.empty()) - return false; - - if(_autoFilter.value()) - _backgroundValues.setValue(_dataProcessor->filterValues()); - - bool texturesReady = false; - std::vector selectedOptions = _dataOptions.value(); - - for(int option: selectedOptions){ - float* values = data[option]; - if(!values) continue; - - if(!_textures[option]){ - std::unique_ptr texture = std::make_unique( - values, - _dataProcessor->dimensions(), - ghoul::opengl::Texture::Format::Red, - GL_RED, - GL_FLOAT, - ghoul::opengl::Texture::FilterMode::Linear, - ghoul::opengl::Texture::WrappingMode::ClampToEdge - ); - - if(texture){ - texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - _textures[option] = std::move(texture); - } - }else{ - _textures[option]->setPixelData(values); - _textures[option]->uploadTexture(); - } - texturesReady = true; - } - - // _dataBuffer = ""; - return texturesReady; +bool DataSphere::createGeometry(){ + PowerScaledScalar radius = PowerScaledScalar(6.371f*_radius, 6.0); + int segments = 100; + _sphere = std::make_shared(radius, segments); + _sphere->initialize(); + return true; } -bool DataSphere::updateTexture(){ - - if(_futureObject.valid()) - return false; - - std::future future = IswaManager::ref().fetchDataCygnet(_data->id); - - if(future.valid()){ - _futureObject = std::move(future); - return true; - } - - return false; +bool DataSphere::destroyGeometry(){ + _sphere = nullptr; + return true; } - -bool DataSphere::readyToRender() const { - return (!_textures.empty()); - - bool ready = isReady(); - ready &= (!_textures.empty() && _textures[0]); - ready &= (_sphere != nullptr); - return ready; +void DataSphere::renderGeometry() const { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + _sphere->render(); } - void DataSphere::setUniforms(){ - std::vector selectedOptions = _dataOptions.value(); - int activeTextures = std::min((int)selectedOptions.size(), MAX_TEXTURES); - int activeTransferfunctions = std::min((int)_transferFunctions.size(), MAX_TEXTURES); - - ghoul::opengl::TextureUnit txUnits[10]; - int j = 0; - for(int option : selectedOptions){ - if(_textures[option]){ - txUnits[j].activate(); - _textures[option]->bind(); - _shader->setUniform( - "textures[" + std::to_string(j) + "]", - txUnits[j] - ); - - j++; - if(j >= MAX_TEXTURES) break; - - } - } - - if(activeTextures > 0){ - if(selectedOptions.back()>=activeTransferfunctions) - activeTransferfunctions = 1; - } - - ghoul::opengl::TextureUnit tfUnits[10]; - j = 0; - - if((activeTransferfunctions == 1)){ - tfUnits[0].activate(); - _transferFunctions[0]->bind(); - _shader->setUniform( - "transferFunctions[0]", - tfUnits[0] - ); - }else{ - for(int option : selectedOptions){ - // std::cout << option << std::endl; - // if(option >= activeTransferfunctions){ - // // LWARNING("No transfer function for this value."); - // break; - // } - - if(_transferFunctions[option]){ - tfUnits[j].activate(); - _transferFunctions[option]->bind(); - _shader->setUniform( - "transferFunctions[" + std::to_string(j) + "]", - tfUnits[j] - ); - - j++; - if(j >= MAX_TEXTURES) break; - } - } - } - - _shader->setUniform("numTextures", activeTextures); - _shader->setUniform("numTransferFunctions", activeTransferfunctions); + // set both data texture and transfer function texture + setTextureUniforms(); _shader->setUniform("backgroundValues", _backgroundValues.value()); _shader->setUniform("transparency", _alpha.value()); } +void DataSphere::subscribeToGroup(){ + _groupEvent->subscribe(name(), "useLogChanged", [&](const ghoul::Dictionary& dict){ + LDEBUG(name() + " Event useLogChanged"); + _useLog.setValue(dict.value("useLog")); + }); -// bool DataSphere::createShader(){ -// if (_shader == nullptr) { -// // Plane Program -// RenderEngine& renderEngine = OsEng.renderEngine(); -// _shader = renderEngine.buildRenderProgram( -// "DataSphereProgram", -// "${MODULE_ISWA}/shaders/datasphere_vs.glsl", -// "${MODULE_ISWA}/shaders/datasphere_fs.glsl"); -// if (!_shader) -// return false; -// } -// return true; -// } - -void DataSphere::setTransferFunctions(std::string tfPath){ - std::string line; - std::ifstream tfFile(absPath(tfPath)); - - std::vector> tfs; - - if(tfFile.is_open()){ - while(getline(tfFile, line)){ - std::shared_ptr tf = std::make_shared(absPath(line)); - if(tf){ - tfs.push_back(tf); - } + _groupEvent->subscribe(name(), "normValuesChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event normValuesChanged"); + std::shared_ptr values; + bool success = dict.getValue("normValues", values); + if(success){ + _normValues.setValue(*values); } - tfFile.close(); - } + }); + _groupEvent->subscribe(name(), "useHistogramChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event useHistogramChanged"); + _useHistogram.setValue(dict.value("useHistogram")); + }); - if(!tfs.empty()){ - _transferFunctions.clear(); - _transferFunctions = tfs; - } + _groupEvent->subscribe(name(), "dataOptionsChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event dataOptionsChanged"); + std::shared_ptr > values; + bool success = dict.getValue("dataOptions", values); + if(success){ + _dataOptions.setValue(*values); + } + }); + + _groupEvent->subscribe(name(), "transferFunctionsChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event transferFunctionsChanged"); + _transferFunctionsFile.setValue(dict.value("transferFunctions")); + }); + + _groupEvent->subscribe(name(), "backgroundValuesChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event backgroundValuesChanged"); + std::shared_ptr values; + bool success = dict.getValue("backgroundValues", values); + if(success){ + _backgroundValues.setValue(*values); + } + }); + + _groupEvent->subscribe(name(), "autoFilterChanged", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event autoFilterChanged"); + _autoFilter.setValue(dict.value("autoFilter")); + }); + + _groupEvent->subscribe(name(), "updateGroup", [&](ghoul::Dictionary dict){ + LDEBUG(name() + " Event updateGroup"); + loadTexture(); + }); } -void DataSphere::fillOptions(){ - std::vector options = _dataProcessor->readJSONHeader(_dataBuffer); - for(int i=0; i(1,0)); - if(_group) - _group->registerOptions(_dataOptions.options()); - // IswaManager::ref().registerOptionsToGroup(_data->groupName, _dataOptions.options()); -} } //namespace openspace \ No newline at end of file diff --git a/modules/iswa/rendering/datasphere.h b/modules/iswa/rendering/datasphere.h index 904ffbbdc6..3adc404160 100644 --- a/modules/iswa/rendering/datasphere.h +++ b/modules/iswa/rendering/datasphere.h @@ -25,45 +25,48 @@ #ifndef __DATASPHERE_H__ #define __DATASPHERE_H__ -#include +#include #include #include -#include - namespace openspace{ class PowerScaledSphere; -class DataSphere : public CygnetSphere { -friend class IswaGroup; + +/** + * DataSphere is a concrete IswaCygnet with data files as its input source. + * The class handles creation, destruction and rendering of a sphere geometry. + * It also specifies what uniforms to use and what GUI properties it needs. + */ +class DataSphere : public DataCygnet { public: DataSphere(const ghoul::Dictionary& dictionary); ~DataSphere(); bool initialize() override; +protected: + + /** + * Creates a sphere geometry + */ + bool createGeometry() override; + bool destroyGeometry() override; + void renderGeometry() const override; + void setUniforms() override; + private: - virtual bool loadTexture() override; - virtual bool updateTexture() override; - virtual bool readyToRender() const override; - virtual void setUniforms() override; + void subscribeToGroup(); - void setTransferFunctions(std::string tfPath); - void fillOptions(); - - properties::SelectionProperty _dataOptions; properties::StringProperty _transferFunctionsFile; properties::Vec2Property _backgroundValues; - properties::Vec2Property _normValues; - properties::BoolProperty _useLog; properties::BoolProperty _useHistogram; properties::BoolProperty _autoFilter; - - std::string _dataBuffer; - std::shared_ptr _dataProcessor; + std::shared_ptr _sphere; + float _radius; }; diff --git a/modules/iswa/rendering/iswacygnet.cpp b/modules/iswa/rendering/iswacygnet.cpp index 556d2f25a2..8ff6261aea 100644 --- a/modules/iswa/rendering/iswacygnet.cpp +++ b/modules/iswa/rendering/iswacygnet.cpp @@ -43,6 +43,10 @@ IswaCygnet::IswaCygnet(const ghoul::Dictionary& dictionary) ,_group(nullptr) ,_textureDirty(false) { + std::string name; + dictionary.getValue("Name", name); + setName(name); + _data = std::make_shared(); // dict.getValue can only set strings in _data directly diff --git a/modules/iswa/rendering/iswagroup.cpp b/modules/iswa/rendering/iswagroup.cpp index 79d72f6174..8e62953583 100644 --- a/modules/iswa/rendering/iswagroup.cpp +++ b/modules/iswa/rendering/iswagroup.cpp @@ -39,8 +39,8 @@ IswaGroup::IswaGroup(std::string name, IswaManager::CygnetType type) :_enabled("enabled", "Enabled", true) ,_alpha("alpha", "Alpha", 0.9f, 0.0f, 1.0f) ,_useLog("useLog","Use Logarithm", false) - ,_useHistogram("useHistogram", "Use Histogram", false) - ,_autoFilter("autoFilter", "Auto Filter", true) + ,_useHistogram("useHistogram", "Auto Contrast", false) + ,_autoFilter("autoFilter", "Auto Filter", false) ,_normValues("normValues", "Normalize Values", glm::vec2(1.0,1.0), glm::vec2(0), glm::vec2(5.0)) ,_backgroundValues("backgroundValues", "Background Values", glm::vec2(0.0), glm::vec2(0), glm::vec2(1.0)) ,_transferFunctionsFile("transferfunctions", "Transfer Functions", "${SCENE}/iswa/tfs/hot.tf") @@ -74,6 +74,8 @@ IswaGroup::IswaGroup(std::string name, IswaManager::CygnetType type) ); _groupEvent = std::make_shared >(); registerProperties(); + + _autoFilter.setValue(true); } IswaGroup::~IswaGroup(){} @@ -148,6 +150,16 @@ void IswaGroup::registerProperties(){ _autoFilter.onChange([this]{ LDEBUG("Group " + name() + " published autoFilterChanged"); + // If autofiler is selected, use _dataProcessor to set backgroundValues + // and unregister backgroundvalues property. + if(_autoFilter.value()){ + _backgroundValues.setValue(_dataProcessor->filterValues()); + OsEng.gui()._iswa.unregisterProperty(&_backgroundValues); + // else if autofilter is turned off, only register backgroundValues + // if it does not have a group + } else { + OsEng.gui()._iswa.registerProperty(&_backgroundValues, &_autoFilter); + } _groupEvent->publish("autoFilterChanged", ghoul::Dictionary({{"autoFilter", _autoFilter.value()}})); }); diff --git a/modules/iswa/rendering/kameleonplane.cpp b/modules/iswa/rendering/kameleonplane.cpp index 1362cf594f..2c8ec32ef4 100644 --- a/modules/iswa/rendering/kameleonplane.cpp +++ b/modules/iswa/rendering/kameleonplane.cpp @@ -52,7 +52,7 @@ namespace openspace { KameleonPlane::KameleonPlane(const ghoul::Dictionary& dictionary) :CygnetPlane(dictionary) ,_useLog("useLog","Use Logarithm", false) - ,_useHistogram("useHistogram", "Use Histogram", false) + ,_useHistogram("useHistogram", "Auto Contrast", false) ,_autoFilter("autoFilter", "Auto Filter", true) ,_normValues("normValues", "Normalize Values", glm::vec2(1.0,1.0), glm::vec2(0), glm::vec2(5.0)) ,_backgroundValues("backgroundValues", "Background Values", glm::vec2(0.0), glm::vec2(0), glm::vec2(1.0)) @@ -62,9 +62,6 @@ KameleonPlane::KameleonPlane(const ghoul::Dictionary& dictionary) ,_resolution("resolution", "Resolutionx100", 1, 1, 5) ,_slice("slice", "Slice", 0.0, 0.0, 1.0) { - std::string name; - dictionary.getValue("Name", name); - setName(name); registerProperties(); diff --git a/modules/iswa/rendering/texturecygnet.cpp b/modules/iswa/rendering/texturecygnet.cpp index 2212f26f93..d3cfccbf09 100644 --- a/modules/iswa/rendering/texturecygnet.cpp +++ b/modules/iswa/rendering/texturecygnet.cpp @@ -34,7 +34,10 @@ namespace openspace{ TextureCygnet::TextureCygnet(const ghoul::Dictionary& dictionary) :IswaCygnet(dictionary) -{} +{ + registerProperties(); + _type = IswaManager::CygnetType::Texture; +} TextureCygnet::~TextureCygnet(){} diff --git a/modules/iswa/rendering/textureplane.cpp b/modules/iswa/rendering/textureplane.cpp index 17323cad40..777dac2c1a 100644 --- a/modules/iswa/rendering/textureplane.cpp +++ b/modules/iswa/rendering/textureplane.cpp @@ -36,13 +36,6 @@ TexturePlane::TexturePlane(const ghoul::Dictionary& dictionary) ,_quad(0) ,_vertexPositionBuffer(0) { - std::string name; - dictionary.getValue("Name", name); - setName(name); - registerProperties(); - - _type = IswaManager::CygnetType::Texture; - _programName = "PlaneProgram"; _vsPath = "${MODULE_ISWA}/shaders/cygnetplane_vs.glsl"; _fsPath = "${MODULE_ISWA}/shaders/cygnetplane_fs.glsl"; diff --git a/modules/iswa/util/iswamanager.cpp b/modules/iswa/util/iswamanager.cpp index e85ba5c88d..b3a45ce66d 100644 --- a/modules/iswa/util/iswamanager.cpp +++ b/modules/iswa/util/iswamanager.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include