#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr std::string_view _loggerCat = "BlackHoleModule"; constexpr std::string_view ProgramName = "BlackHoleProgram"; constexpr std::array QuadVtx = { -1.f, -1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, -1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f }; } namespace openspace { RenderableBlackHole::RenderableBlackHole(const ghoul::Dictionary& dictionary) : Renderable(dictionary, { .automaticallyUpdateRenderBin = false }) { } RenderableBlackHole::~RenderableBlackHole() {} void RenderableBlackHole::initialize() { global::navigationHandler->camera()->setRotation(glm::dquat(0,0,0,0)); _schwarzschildWarpTable = std::vector(_rayCount * 2, 0.f); } void RenderableBlackHole::initializeGL() { const glm::vec2 screenSize = glm::vec2(global::renderEngine->renderingResolution()); ZoneScoped; setupQuad(); setupShaders(); loadEnvironmentTexture(); _viewport.uploadViewGrid(screenSize); } void RenderableBlackHole::deinitializeGL() { _warpTableTex = nullptr; _environmentTexture = nullptr; _viewport.viewGrid = nullptr; glDeleteBuffers(1, &_quadVbo); glDeleteVertexArrays(1, &_quadVao); std::string program = std::string(ProgramName); BlackHoleModule::ProgramObjectManager.release( program, [](ghoul::opengl::ProgramObject* p) { global::renderEngine->removeRenderProgram(p); } ); _program = nullptr; } bool RenderableBlackHole::isReady() const { return _program != nullptr; } void RenderableBlackHole::update(const UpdateData&) { glm::vec3 cameraPosition = global::navigationHandler->camera()->positionVec3(); glm::vec3 anchorNodePosition = global::navigationHandler->anchorNode()->position(); float distanceToAnchor = (float)glm::distance(cameraPosition, anchorNodePosition) / distanceconstants::LightYear; if (abs(_rCamera - distanceToAnchor) > 0.01) { _rCamera = distanceToAnchor; _rEnvmap = 2 * _rCamera; schwarzchild(_rsBlackHole, _rEnvmap, _rayCount, _stepsCount, 1.0f / _rCamera, _stepLength, _schwarzschildWarpTable.data()); } bindSSBOData(_program, "ssbo_warp_table", _ssboDataBinding, _ssboData); } void RenderableBlackHole::render(const RenderData&, RendererTasks&) { _program->activate(); bindFramebuffer(); ghoul::opengl::TextureUnit enviromentUnit; if (!bindTexture(_uniformCache.environmentTexture, enviromentUnit, _environmentTexture)) { LWARNING("UniformCache is missing 'environmentTexture'"); } ghoul::opengl::TextureUnit viewGridUnit; if (!bindTexture(_uniformCache.viewGrid, viewGridUnit, _viewport.viewGrid)) { LWARNING("UniformCache is missing 'viewGrid'"); } SendSchwarzchildTableToShader(); interaction::OrbitalNavigator::CameraRotationDecomposition camRot = global::navigationHandler->orbitalNavigator().decomposeCameraRotationSurface( CameraPose{global::navigationHandler->camera()->positionVec3(), global::navigationHandler->camera()->rotationQuaternion()}, *global::navigationHandler->anchorNode() ); _program->setUniform( _uniformCache.cameraRotationMatrix, glm::fmat4(glm::mat4_cast(camRot.localRotation)) ); glm::fmat4 worldRotationMatrix = glm::mat4_cast(camRot.globalRotation); _program->setUniform( _uniformCache.worldRotationMatrix, worldRotationMatrix ); drawQuad(); _program->deactivate(); glBindTexture(GL_TEXTURE_2D, 0); } void RenderableBlackHole::SendSchwarzchildTableToShader() { // Update SSBO Index array with accumulated stars in all chunks. glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssboData); const size_t indexBufferSize = _schwarzschildWarpTable.size() * sizeof(float); glBufferData( GL_SHADER_STORAGE_BUFFER, indexBufferSize, _schwarzschildWarpTable.data(), GL_STREAM_DRAW ); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); } void RenderableBlackHole::setupShaders() { const std::string vertexShaderPath = "${MODULE_BLACKHOLE}/shaders/blackhole_vs.glsl"; const std::string fragmentShaderPath = "${MODULE_BLACKHOLE}/shaders/blackhole_fs.glsl"; // Initialize shaders std::string program = std::string(ProgramName); _program = BlackHoleModule::ProgramObjectManager.request( program, [fragmentShaderPath, vertexShaderPath, program]() -> std::unique_ptr { const std::filesystem::path vs = absPath(vertexShaderPath); const std::filesystem::path fs = absPath(fragmentShaderPath); return global::renderEngine->buildRenderProgram(program, vs, fs); } ); ghoul::opengl::updateUniformLocations(*_program, _uniformCache); } void RenderableBlackHole::setupQuad() { glGenVertexArrays(1, &_quadVao); glBindVertexArray(_quadVao); glGenBuffers(1, &_quadVbo); glBindBuffer(GL_ARRAY_BUFFER, _quadVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(QuadVtx), QuadVtx.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), reinterpret_cast(2 * sizeof(GLfloat))); } void RenderableBlackHole::loadEnvironmentTexture() { //const std::string texturePath = "${MODULE_BLACKHOLE}/rendering/uv.png"; const std::string texturePath = "${BASE}/sync/http/milkyway_textures/2/DarkUniverse_mellinger_8k.jpg"; //const std::string texturePath = "${MODULE_BLACKHOLE}/rendering/skybox.jpg"; _environmentTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(texturePath), 2); if (_environmentTexture) { _environmentTexture->uploadTexture(); } else { LWARNING(std::format("Failed to load environment texture from path '{}'", absPath(texturePath).string())); } } void RenderableBlackHole::bindFramebuffer() { const GLint defaultFBO = ghoul::opengl::FramebufferObject::getActiveObject(); glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); } bool RenderableBlackHole::bindTexture(GLint chacheRegistry, ghoul::opengl::TextureUnit& textureUnit, std::unique_ptr& texture) { if (!texture) return false; textureUnit.activate(); texture->bind(); _program->setUniform(chacheRegistry, textureUnit); return true; } void RenderableBlackHole::drawQuad() { glBindVertexArray(_quadVao); glDrawArrays(GL_TRIANGLES, 0, 6); } void RenderableBlackHole::bindSSBOData( ghoul::opengl::ProgramObject* program, const std::string& ssboName, std::unique_ptr>& ssboBinding, GLuint& ssboID ) { if (ssboID == 0) { glGenBuffers(1, &ssboID); LDEBUG(std::format( "Generating Data Shader Storage Buffer Object id '{}'", ssboID )); } glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboID); ssboBinding = std::make_unique >(); glBindBufferBase( GL_SHADER_STORAGE_BUFFER, ssboBinding->bindingNumber(), ssboID ); program->setSsboBinding(ssboName, ssboBinding->bindingNumber()); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); } documentation::Documentation RenderableBlackHole::Documentation() { return documentation::Documentation(); } } // namespace openspace