/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2019 * * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { /*constexpr const char* GlslRayCastPath = "${MODULES}/toyvolume/shaders/rayCast.glsl"; constexpr const char* GlslBoundsVsPath = "${MODULES}/toyvolume/shaders/boundsVs.glsl"; constexpr const char* GlslBoundsFsPath = "${MODULES}/toyvolume/shaders/boundsFs.glsl";*/ constexpr const char* GlslRaycastPath = "${MODULES}/galaxy/shaders/galaxyraycast.glsl"; constexpr const char* GlslBoundsVsPath = "${MODULES}/galaxy/shaders/raycasterbounds.vs"; constexpr const char* GlslBoundsFsPath = "${MODULES}/galaxy/shaders/raycasterbounds.fs"; constexpr const char* _loggerCat = "Renderable Galaxy"; constexpr openspace::properties::Property::PropertyInfo StepSizeInfo = { "StepSize", "Step Size", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo AbsorptionMultiplyInfo = { "AbsorptionMultiply", "Absorption Multiplier", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo EmissionMultiplyInfo = { "EmissionMultiply", "Emission Multiplier", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo PointStepSizeInfo = { "PointStepSize", "Point Step Size", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo TranslationInfo = { "Translation", "Translation", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo RotationInfo = { "Rotation", "Euler rotation", "" // @TODO Missing documentation }; constexpr openspace::properties::Property::PropertyInfo EnabledPointsRatioInfo = { "NEnabledPointsRatio", "Enabled points", "" // @TODO Missing documentation }; } // namespace namespace openspace { RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _stepSize(StepSizeInfo, 0.01f, 0.0005f, 0.05f, 0.001f) , _absorptionMultiply(AbsorptionMultiplyInfo, 40.f, 0.0f, 100.0f) , _emissionMultiply(EmissionMultiplyInfo, 400.f, 0.0f, 1000.0f) //, _pointStepSize(PointStepSizeInfo, 0.01f, 0.01f, 0.1f) //, _enabledPointsRatio(EnabledPointsRatioInfo, 0.2f, 0.f, 1.f) , _translation(TranslationInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(1.f)) , _rotation(RotationInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(6.28f)) { dictionary.getValue("StepSize", _stepSize); dictionary.getValue("AbsorptionMultiply", _absorptionMultiply); dictionary.getValue("EmissionMultiply", _emissionMultiply); dictionary.getValue("Translation", _translation); dictionary.getValue("Rotation", _rotation); if (dictionary.hasKeyAndValue(StepSizeInfo.identifier)) { _stepSize = static_cast(dictionary.value(StepSizeInfo.identifier)); } if (dictionary.hasKeyAndValue(AbsorptionMultiplyInfo.identifier)) { _absorptionMultiply = static_cast(dictionary.value(AbsorptionMultiplyInfo.identifier)); } if (dictionary.hasKeyAndValue(EmissionMultiplyInfo.identifier)) { _emissionMultiply = static_cast(dictionary.value(EmissionMultiplyInfo.identifier)); } if (dictionary.hasKeyAndValue(TranslationInfo.identifier)) { _translation = dictionary.value(TranslationInfo.identifier); } if (dictionary.hasKeyAndValue(RotationInfo.identifier)) { _rotation = dictionary.value(RotationInfo.identifier); } if (!dictionary.hasKeyAndValue("Volume")) { LERROR("No volume dictionary specified."); } ghoul::Dictionary volumeDictionary = dictionary.value("Volume"); std::string volumeFilename; if (volumeDictionary.getValue("Filename", volumeFilename)) { _volumeFilename = absPath(volumeFilename); } else { LERROR("No volume filename specified."); } glm::vec3 volumeDimensions; if (volumeDictionary.getValue("Dimensions", volumeDimensions)) { _volumeDimensions = static_cast(volumeDimensions); } else { LERROR("No volume dimensions specified."); } glm::vec3 volumeSize; if (volumeDictionary.getValue("Size", volumeSize)) { _volumeSize = volumeSize; } else { LERROR("No volume dimensions specified."); } if (!dictionary.hasKeyAndValue("Points")) { LERROR("No points dictionary specified."); } ghoul::Dictionary pointsDictionary = dictionary.value("Points"); std::string pointsFilename; if (pointsDictionary.getValue("Filename", pointsFilename)) { _pointsFilename = absPath(pointsFilename); } else { LERROR("No points filename specified."); } //pointsDictionary.getValue("Scaling", _pointScaling); } void RenderableGalaxy::initializeGL() { // Aspect is currently hardcoded to cubic voxels. _aspect = static_cast(_volumeDimensions); _aspect /= std::max(std::max(_aspect.x, _aspect.y), _aspect.z); volume::RawVolumeReader> reader( _volumeFilename, _volumeDimensions ); _volume = reader.read(); _texture = std::make_unique( _volumeDimensions, ghoul::opengl::Texture::Format::RGBA, GL_RGBA, GL_UNSIGNED_BYTE, ghoul::opengl::Texture::FilterMode::Linear, ghoul::opengl::Texture::WrappingMode::Clamp); _texture->setPixelData(reinterpret_cast( _volume->data()), ghoul::opengl::Texture::TakeOwnership::No ); _texture->setDimensions(_volume->dimensions()); _texture->uploadTexture(); _raycaster = std::make_unique(*_texture); _raycaster->initialize(); global::raycasterManager.attachRaycaster(*_raycaster); auto onChange = [&](bool enabled) { if (enabled) { global::raycasterManager.attachRaycaster(*_raycaster); } else { global::raycasterManager.detachRaycaster(*_raycaster); } }; onEnabledChange(onChange); addProperty(_stepSize); addProperty(_absorptionMultiply); addProperty(_emissionMultiply); //addProperty(_pointStepSize); //addProperty(_enabledPointsRatio); addProperty(_translation); addProperty(_rotation); // initialize points. if (!_pointsFilename.empty()) { std::ifstream pointFile(_pointsFilename, std::ios::in); std::vector pointPositions; std::vector pointColors; int64_t nPoints; // Read header for OFF (Object File Format) std::string line; std::getline(pointFile, line); // Read point count std::getline(pointFile, line); std::istringstream iss(line); iss >> nPoints; // Prepare point reading _nPoints = static_cast(nPoints); float maxdist = 0; // Read points float x, y, z, r, g, b, a; for (size_t i = 0; i < _nPoints; ++i) { std::getline(pointFile, line); std::istringstream iss(line); iss >> x >> y >> z >> r >> g >> b >> a; maxdist = std::max(maxdist, glm::length(glm::vec3(x, y, z))); pointPositions.emplace_back(x, y, z); pointColors.emplace_back(r, g, b); } pointFile.close(); std::cout << maxdist << std::endl; glGenVertexArrays(1, &_pointsVao); glGenBuffers(1, &_positionVbo); glGenBuffers(1, &_colorVbo); glBindVertexArray(_pointsVao); glBindBuffer(GL_ARRAY_BUFFER, _positionVbo); glBufferData(GL_ARRAY_BUFFER, pointPositions.size() * sizeof(glm::vec3), pointPositions.data(), GL_STATIC_DRAW ); glBindBuffer(GL_ARRAY_BUFFER, _colorVbo); glBufferData(GL_ARRAY_BUFFER, pointColors.size() * sizeof(glm::vec3), pointColors.data(), GL_STATIC_DRAW ); _pointsProgram = global::renderEngine.buildRenderProgram( "Galaxy points", absPath("${MODULE_GALAXY}/shaders/points.vs"), absPath("${MODULE_GALAXY}/shaders/points.fs") ); _pointsProgram->setIgnoreUniformLocationError( ghoul::opengl::ProgramObject::IgnoreError::Yes ); GLint positionAttrib = _pointsProgram->attributeLocation("inPosition"); GLint colorAttrib = _pointsProgram->attributeLocation("inColor"); glBindBuffer(GL_ARRAY_BUFFER, _positionVbo); glEnableVertexAttribArray(positionAttrib); glVertexAttribPointer(positionAttrib, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, _colorVbo); glEnableVertexAttribArray(colorAttrib); glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } } void RenderableGalaxy::deinitializeGL() { if (_raycaster) { global::raycasterManager.detachRaycaster(*_raycaster); _raycaster = nullptr; } } bool RenderableGalaxy::isReady() const { return true; } void RenderableGalaxy::update(const UpdateData& data) { if (_raycaster) { //glm::mat4 transform = glm::translate(, static_cast(_translation)); const glm::vec3 eulerRotation = static_cast(_rotation); glm::mat4 transform = glm::rotate( glm::mat4(1.0), eulerRotation.x, glm::vec3(1, 0, 0) ); transform = glm::rotate(transform, eulerRotation.y, glm::vec3(0, 1, 0)); transform = glm::rotate(transform, eulerRotation.z, glm::vec3(0, 0, 1)); glm::mat4 volumeTransform = glm::scale(transform, _volumeSize); _pointTransform = glm::scale(transform, _pointScaling); const glm::vec4 translation = glm::vec4(_translation.value()*_volumeSize, 0.0); // Todo: handle floating point overflow, to actually support translation. volumeTransform[3] += translation; _pointTransform[3] += translation; _raycaster->setStepSize(_stepSize); _raycaster->setAspect(_aspect); _raycaster->setModelTransform(volumeTransform); _raycaster->setAbsorptionMultiplier(_absorptionMultiply); _raycaster->setEmissionMultiplier(_emissionMultiply); _raycaster->setTime(data.time.j2000Seconds()); } } void RenderableGalaxy::render(const RenderData& data, RendererTasks& tasks) { RaycasterTask task { _raycaster.get(), data }; const glm::vec3 position = data.camera.positionVec3(); const float length = safeLength(position); const glm::vec3 galaxySize = _volumeSize; const float maxDim = std::max(std::max(galaxySize.x, galaxySize.y), galaxySize.z); const float lowerRampStart = maxDim * 0.02f; const float lowerRampEnd = maxDim * 0.5f; const float upperRampStart = maxDim * 2.f; const float upperRampEnd = maxDim * 10.f; float opacityCoefficient = 1.f; if (length < lowerRampStart) { opacityCoefficient = 0.f; // camera really close } else if (length < lowerRampEnd) { opacityCoefficient = (length - lowerRampStart) / (lowerRampEnd - lowerRampStart); } else if (length < upperRampStart) { opacityCoefficient = 1.f; // sweet spot (max) } else if (length < upperRampEnd) { opacityCoefficient = 1.f - (length - upperRampStart) / (upperRampEnd - upperRampStart); //fade out } else { opacityCoefficient = 0; } _opacityCoefficient = opacityCoefficient; ghoul_assert( _opacityCoefficient >= 0.f && _opacityCoefficient <= 1.f, "Opacity coefficient was not between 0 and 1" ); if (opacityCoefficient > 0) { _raycaster->setOpacityCoefficient(_opacityCoefficient); tasks.raycasterTasks.push_back(task); } } float RenderableGalaxy::safeLength(const glm::vec3& vector) const { const float maxComponent = std::max( std::max(std::abs(vector.x), std::abs(vector.y)), std::abs(vector.z) ); return glm::length(vector / maxComponent) * maxComponent; } /*void RenderableGalaxy::postRender(const RenderData& data) { _raycaster->setStepSize(_pointStepSize); _pointsProgram->activate(); setPscUniforms(*_pointsProgram.get(), data.camera, data.position); OsEng.ref().renderEngine().preRaycast(*_pointsProgram); glm::mat4 modelMatrix = _pointTransform; glm::mat4 viewMatrix = data.camera.viewMatrix(); glm::mat4 projectionMatrix = data.camera.projectionMatrix(); _pointsProgram->setUniform("model", modelMatrix); _pointsProgram->setUniform("view", viewMatrix); _pointsProgram->setUniform("projection", projectionMatrix); float emittanceFactor = _opacityCoefficient * static_cast(_volumeSize).x; _pointsProgram->setUniform("emittanceFactor", emittanceFactor); glBindVertexArray(_pointsVao); glDisable(GL_DEPTH_TEST); glDepthMask(false); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glDrawArrays(GL_POINTS, 0, _nPoints * _enabledPointsRatio); glBindVertexArray(0); glDepthMask(true); glEnable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); OsEng.ref().renderEngine().postRaycast(*_pointsProgram); }*/ } // namespace openspace