Changed content in the module to content from the gaiamodule

This commit is contained in:
aniisaaden
2020-06-16 13:31:13 +02:00
parent d9a7740fdb
commit 3f731f70a2
39 changed files with 7725 additions and 1859 deletions
+29 -20
View File
@@ -25,38 +25,47 @@
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/galaxymodule.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/galaxyraycaster.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablegalaxy.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/milkywayconversiontask.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/milkywaypointsconversiontask.h
${CMAKE_CURRENT_SOURCE_DIR}/gaiamodule.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablegaiastars.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/octreemanager.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/octreeculler.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readfilejob.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readfitstask.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readspecktask.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/constructoctreetask.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/gaiaoptions.h
)
source_group("Header Files" FILES ${HEADER_FILES})
set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/galaxymodule.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/galaxyraycaster.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablegalaxy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/milkywayconversiontask.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/milkywaypointsconversiontask.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gaiamodule.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablegaiastars.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/octreemanager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/octreeculler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readfilejob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readfitstask.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/readspecktask.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/constructoctreetask.cpp
)
source_group("Source Files" FILES ${SOURCE_FILES})
set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/shaders/galaxyraycast.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/billboard_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/billboard_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/billboard_ge.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/points_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/points_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/raycasterbounds_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/raycasterbounds_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_vbo_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_ssbo_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_billboard_nofbo_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_billboard_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_billboard_ge.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_point_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_point_ge.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_tonemapping_vs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_tonemapping_point_fs.glsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders/gaia_tonemapping_billboard_fs.glsl
)
source_group("Shader Files" FILES ${SHADER_FILES})
create_new_module(
"Galaxy"
galaxy
"Gaia"
gaia
STATIC
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
)
+4 -4
View File
@@ -1,6 +1,6 @@
set(DEFAULT_MODULE ON)
set (OPENSPACE_DEPENDENCIES
volume
space
)
set(OPENSPACE_DEPENDENCIES
fitsfilereader
globebrowsing
)
@@ -22,22 +22,33 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#version __CONTEXT__
#ifndef __OPENSPACE_MODULE_GAIA___GAIAOPTIONS___H__
#define __OPENSPACE_MODULE_GAIA___GAIAOPTIONS___H__
layout(location = 0) in vec4 vertPosition;
namespace openspace::gaia {
out vec3 modelPosition;
out vec4 viewPosition;
enum RenderOption {
Static = 0,
Color = 1,
Motion = 2
};
uniform mat4 projectionTransform;
uniform mat4 modelViewTransform;
enum FileReaderOption {
Fits = 0,
Speck = 1,
BinaryRaw = 2,
BinaryOctree = 3,
StreamOctree = 4
};
enum ShaderOption {
Point_SSBO = 0,
Point_VBO = 1,
Billboard_SSBO = 2,
Billboard_VBO = 3,
Billboard_SSBO_noFBO = 4
};
void main() {
modelPosition = vertPosition.xyz;
viewPosition = modelViewTransform*vertPosition;
} // namespace openspace::gaiamission
// project the position to view space
gl_Position = projectionTransform * viewPosition;
gl_Position.z = 0.0;
}
#endif // __OPENSPACE_MODULE_GAIA___GAIAOPTIONS___H__
@@ -1,185 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/galaxy/rendering/galaxyraycaster.h>
#include <openspace/rendering/renderable.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/opengl/texture.h>
namespace {
constexpr const char* GlslRaycastPath =
"${MODULES}/galaxy/shaders/galaxyraycast.glsl";
constexpr const char* GlslBoundsVsPath =
"${MODULES}/galaxy/shaders/raycasterbounds_vs.glsl";
constexpr const char* GlslBoundsFsPath =
"${MODULES}/galaxy/shaders/raycasterbounds_fs.glsl";
} // namespace
namespace openspace {
GalaxyRaycaster::GalaxyRaycaster(ghoul::opengl::Texture& texture)
: _boundingBox(glm::vec3(1.0))
, _texture(texture)
, _textureUnit(nullptr)
{}
void GalaxyRaycaster::initialize() {
_boundingBox.initialize();
}
void GalaxyRaycaster::renderEntryPoints(const RenderData& data,
ghoul::opengl::ProgramObject& program)
{
program.setUniform("modelViewTransform", glm::mat4(modelViewTransform(data)));
program.setUniform("projectionTransform", data.camera.projectionMatrix());
// Cull back face
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Render bounding geometry
_boundingBox.render();
}
void GalaxyRaycaster::renderExitPoints(const RenderData& data,
ghoul::opengl::ProgramObject& program)
{
// Uniforms
program.setUniform("modelViewTransform", glm::mat4(modelViewTransform(data)));
program.setUniform("projectionTransform", data.camera.projectionMatrix());
// Cull front face
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
// Render bounding geometry
_boundingBox.render();
// Restore defaults
glCullFace(GL_BACK);
}
glm::dmat4 GalaxyRaycaster::modelViewTransform(const RenderData& data) {
glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) *
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) *
glm::dmat4(_modelTransform);
return data.camera.combinedViewMatrix() * modelTransform;
}
void GalaxyRaycaster::preRaycast(const RaycastData& data,
ghoul::opengl::ProgramObject& program)
{
const std::string colorUniformName = "color" + std::to_string(data.id);
const std::string stepSizeUniformName = "maxStepSize" + std::to_string(data.id);
const std::string galaxyTextureUniformName = "galaxyTexture" +
std::to_string(data.id);
const std::string volumeAspectUniformName = "aspect" + std::to_string(data.id);
const std::string opacityCoefficientUniformName = "opacityCoefficient" +
std::to_string(data.id);
const std::string absorptionMultiplyUniformName = "absorptionMultiply" +
std::to_string(data.id);
const std::string emissionMultiplyUniformName = "emissionMultiply" +
std::to_string(data.id);
program.setUniform(volumeAspectUniformName, _aspect);
program.setUniform(stepSizeUniformName, _stepSize);
program.setUniform(opacityCoefficientUniformName, _opacityCoefficient);
program.setUniform(absorptionMultiplyUniformName, _absorptionMultiply);
program.setUniform(emissionMultiplyUniformName, _emissionMultiply);
_textureUnit = std::make_unique<ghoul::opengl::TextureUnit>();
_textureUnit->activate();
_texture.bind();
program.setUniform(galaxyTextureUniformName, *_textureUnit);
}
void GalaxyRaycaster::postRaycast(const RaycastData&, ghoul::opengl::ProgramObject&) {
_textureUnit = nullptr; // release texture unit.
}
bool GalaxyRaycaster::isCameraInside(const RenderData& data, glm::vec3& localPosition) {
glm::vec4 modelPos = glm::inverse(modelViewTransform(data)) *
glm::vec4(0.f, 0.f, 0.f, 1.f);
localPosition = (glm::vec3(modelPos) + glm::vec3(0.5));
return (localPosition.x > 0 && localPosition.x < 1 &&
localPosition.y > 0 && localPosition.y < 1 &&
localPosition.z > 0 && localPosition.z < 1);
}
std::string GalaxyRaycaster::boundsVertexShaderPath() const {
return GlslBoundsVsPath;
}
std::string GalaxyRaycaster::boundsFragmentShaderPath() const {
return GlslBoundsFsPath;
}
std::string GalaxyRaycaster::raycasterPath() const {
return GlslRaycastPath;
}
std::string GalaxyRaycaster::helperPath() const {
return ""; // no helper file
}
void GalaxyRaycaster::setAspect(const glm::vec3& aspect) {
_aspect = aspect / std::max(std::max(aspect.x, aspect.y), aspect.z);
}
void GalaxyRaycaster::setModelTransform(glm::mat4 transform) {
_modelTransform = transform;
}
void GalaxyRaycaster::setOpacityCoefficient(float opacityCoefficient) {
_opacityCoefficient = opacityCoefficient;
}
void GalaxyRaycaster::setAbsorptionMultiplier(float absorptionMultiply) {
_absorptionMultiply = absorptionMultiply;
}
void GalaxyRaycaster::setEmissionMultiplier(float emissionMultiply) {
_emissionMultiply = emissionMultiply;
}
void GalaxyRaycaster::setTime(double time) {
_time = time;
}
void GalaxyRaycaster::setStepSize(float stepSize) {
_stepSize = stepSize;
}
} // namespace openspace
@@ -1,95 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GALAXY___GALAXYRAYCASTER___H__
#define __OPENSPACE_MODULE_GALAXY___GALAXYRAYCASTER___H__
#include <openspace/rendering/volumeraycaster.h>
#include <openspace/util/boxgeometry.h>
#include <ghoul/glm.h>
#include <string>
#include <memory>
namespace ghoul::opengl {
class Texture;
class TextureUnit;
class ProgramObject;
} // namespace ghoul::opengl
namespace openspace {
struct RenderData;
struct RaycastData;
class GalaxyRaycaster : public VolumeRaycaster {
public:
GalaxyRaycaster(ghoul::opengl::Texture& texture);
virtual ~GalaxyRaycaster() = default;
void initialize();
void renderEntryPoints(const RenderData& data,
ghoul::opengl::ProgramObject& program) override;
void renderExitPoints(const RenderData& data,
ghoul::opengl::ProgramObject& program) override;
void preRaycast(const RaycastData& data,
ghoul::opengl::ProgramObject& program) override;
void postRaycast(const RaycastData& data,
ghoul::opengl::ProgramObject& program) override;
bool isCameraInside(const RenderData& data,
glm::vec3& localPosition) override;
std::string boundsVertexShaderPath() const override;
std::string boundsFragmentShaderPath() const override;
std::string raycasterPath() const override;
std::string helperPath() const override;
void setAspect(const glm::vec3& aspect);
void setModelTransform(glm::mat4 transform);
void setTime(double time);
void setStepSize(float stepSize);
void setOpacityCoefficient(float opacityCoefficient);
void setAbsorptionMultiplier(float absorptionMultiply);
void setEmissionMultiplier(float emissionMultiply);
private:
glm::dmat4 modelViewTransform(const RenderData& data);
BoxGeometry _boundingBox;
float _stepSize = 0.f;
glm::mat4 _modelTransform = glm::mat4(1.f);
glm::vec3 _aspect = glm::vec3(0.f);
double _time = 0.0;
float _opacityCoefficient = 0.f;
float _absorptionMultiply = 0.f;
float _emissionMultiply = 0.f;
ghoul::opengl::Texture& _texture;
std::unique_ptr<ghoul::opengl::TextureUnit> _textureUnit;
}; // GalaxyRaycaster
} // namespace openspace
#endif // __OPENSPACE_MODULE_GALAXY___GALAXYRAYCASTER___H__
@@ -22,76 +22,61 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/galaxy/tasks/milkywaypointsconversiontask.h>
#include <modules/gaia/rendering/octreeculler.h>
#include <openspace/documentation/documentation.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <ghoul/glm.h>
#include <ghoul/logging/logmanager.h>
namespace openspace {
/*MilkywayPointsConversionTask::MilkywayPointsConversionTask(
const std::string& inFilename,
const std::string& outFilename)
: _inFilename(inFilename)
, _outFilename(outFilename) {}*/
MilkywayPointsConversionTask::MilkywayPointsConversionTask(const ghoul::Dictionary&) {}
std::string MilkywayPointsConversionTask::description() {
return std::string();
}
void MilkywayPointsConversionTask::perform(const Task::ProgressCallback& progressCallback)
{
std::ifstream in(_inFilename, std::ios::in);
std::ofstream out(_outFilename, std::ios::out | std::ios::binary);
std::string format;
int64_t nPoints;
in >> format >> nPoints;
size_t nFloats = nPoints * 7;
std::vector<float> pointData(nFloats);
float x;
float y;
float z;
float r;
float g;
float b;
float a;
for (int64_t i = 0; i < nPoints; ++i) {
in >> x >> y >> z >> r >> g >> b >> a;
if (in.good()) {
pointData[i * 7 + 0] = x;
pointData[i * 7 + 1] = y;
pointData[i * 7 + 2] = z;
pointData[i * 7 + 3] = r;
pointData[i * 7 + 4] = g;
pointData[i * 7 + 5] = b;
pointData[i * 7 + 6] = a;
progressCallback(static_cast<float>(i + 1) / nPoints);
}
else {
std::cout << "Failed to convert point data.";
return;
}
namespace {
bool intersects(const globebrowsing::AABB3& bb, const globebrowsing::AABB3& o) {
return (bb.min.x <= o.max.x) && (o.min.x <= bb.max.x)
&& (bb.min.y <= o.max.y) && (o.min.y <= bb.max.y)
&& (bb.min.z <= o.max.z) && (o.min.z <= bb.max.z);
}
out.write(reinterpret_cast<char*>(&nPoints), sizeof(int64_t));
out.write(reinterpret_cast<char*>(pointData.data()), nFloats * sizeof(float));
void expand(globebrowsing::AABB3& bb, const glm::vec3& p) {
bb.min = glm::min(bb.min, p);
bb.max = glm::max(bb.max, p);
}
} // namespace
in.close();
out.close();
OctreeCuller::OctreeCuller(globebrowsing::AABB3 viewFrustum)
: _viewFrustum(std::move(viewFrustum))
{}
bool OctreeCuller::isVisible(const std::vector<glm::dvec4>& corners,
const glm::dmat4& mvp)
{
createNodeBounds(corners, mvp);
return intersects(_viewFrustum, _nodeBounds);
}
documentation::Documentation MilkywayPointsConversionTask::documentation() {
return documentation::Documentation();
glm::vec2 OctreeCuller::getNodeSizeInPixels(const std::vector<glm::dvec4>& corners,
const glm::dmat4& mvp,
const glm::vec2& screenSize)
{
createNodeBounds(corners, mvp);
// Screen space is mapped to [-1, 1] so divide by 2 and multiply with screen size.
glm::vec3 size = (_nodeBounds.max - _nodeBounds.min) / 2.f;
size = glm::abs(size);
return glm::vec2(size.x * screenSize.x, size.y * screenSize.y);
}
void OctreeCuller::createNodeBounds(const std::vector<glm::dvec4>& corners,
const glm::dmat4& mvp)
{
// Create a bounding box in clipping space from node boundaries.
_nodeBounds = globebrowsing::AABB3();
for (size_t i = 0; i < 8; ++i) {
glm::dvec4 cornerClippingSpace = mvp * corners[i];
glm::dvec4 ndc = (1.f / glm::abs(cornerClippingSpace.w)) * cornerClippingSpace;
expand(_nodeBounds, glm::dvec3(ndc));
}
}
} // namespace openspace
@@ -22,38 +22,55 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GALAXY___MILKYWAYPOINTSCONVERSIONTASK_H__
#define __OPENSPACE_MODULE_GALAXY___MILKYWAYPOINTSCONVERSIONTASK_H__
#ifndef __OPENSPACE_MODULE_GAIA___OCTREECULLER___H__
#define __OPENSPACE_MODULE_GAIA___OCTREECULLER___H__
#include <openspace/util/task.h>
#include <modules/globebrowsing/src/basictypes.h>
#include <vector>
#include <string>
// TODO: Move /geometry/* to libOpenSpace so as not to depend on globebrowsing.
namespace openspace {
namespace documentation { struct Documentation; }
/**
* Converts ascii based point data
* int64_t n
* (float x, float y, float z, float r, float g, float b) * n
* to a binary (floating point) representation with the same layout.
* Culls all octree nodes that are completely outside the view frustum.
*
* The frustum culling uses a 2D axis aligned bounding box for the OctreeNode in
* screen space.
*/
class MilkywayPointsConversionTask : public Task {
class OctreeCuller {
public:
MilkywayPointsConversionTask(const ghoul::Dictionary& dictionary);
virtual ~MilkywayPointsConversionTask() = default;
std::string description() override;
void perform(const Task::ProgressCallback& progressCallback) override;
/**
* \param viewFrustum is the view space in normalized device coordinates space.
* Hence it is an axis aligned bounding box and not a real frustum.
*/
OctreeCuller(globebrowsing::AABB3 viewFrustum);
static documentation::Documentation documentation();
~OctreeCuller() = default;
/**
* \return true if any part of the node is visible in the current view.
*/
bool isVisible(const std::vector<glm::dvec4>& corners, const glm::dmat4& mvp);
/**
* \return the size [in pixels] of the node in clipping space.
*/
glm::vec2 getNodeSizeInPixels(const std::vector<glm::dvec4>& corners,
const glm::dmat4& mvp, const glm::vec2& screenSize);
private:
std::string _inFilename;
std::string _outFilename;
/**
* Creates an axis-aligned bounding box containing all \p corners in clipping space.
*/
void createNodeBounds(const std::vector<glm::dvec4>& corners, const glm::dmat4& mvp);
const globebrowsing::AABB3 _viewFrustum;
globebrowsing::AABB3 _nodeBounds;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GALAXY___MILKYWAYPOINTSCONVERSIONTASK_H__
#endif // __OPENSPACE_MODULE_GAIA___OCTREECULLER___H__
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,387 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GAIA___OCTREEMANAGER___H__
#define __OPENSPACE_MODULE_GAIA___OCTREEMANAGER___H__
#include <modules/gaia/rendering/gaiaoptions.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <map>
#include <mutex>
#include <queue>
#include <stack>
#include <vector>
namespace openspace {
class OctreeCuller;
class OctreeManager {
public:
struct OctreeNode {
std::shared_ptr<OctreeNode> Children[8];
std::vector<float> posData;
std::vector<float> colData;
std::vector<float> velData;
std::vector<std::pair<float, size_t>> magOrder;
float originX;
float originY;
float originZ;
float halfDimension;
size_t numStars;
bool isLeaf;
bool isLoaded;
bool hasLoadedDescendant;
std::mutex loadingLock;
int bufferIndex;
unsigned long long octreePositionIndex;
};
OctreeManager() = default;
~OctreeManager() = default;
/**
* Initializes a one layer Octree with root and 8 children that covers all stars.
*
* \param maxDist together with \param maxstarsPerNode (if defined) determines the
* depth of the tree as well as how many nodes will be created.
*/
void initOctree(long long cpuRamBudget = 0, int maxDist = 0, int maxStarsPerNode = 0);
/**
* Initializes a stack of size \param maxNodes that keeps track of all free spot in
* buffer stream. Can be used to trigger a rebuild of buffer(s).
*
* \param useVBO defines if VBO or SSBO is used as buffer(s)
* \param datasetFitInMemory defines if streaming of nodes during runtime is used
*/
void initBufferIndexStack(long long maxNodes, bool useVBO, bool datasetFitInMemory);
/**
* Inserts star values in correct position in Octree. Makes use of a recursive
* traversal strategy. Internally calls <code>insertInNode()</code>
*/
void insert(const std::vector<float>& starValues);
/**
* Slices LOD data so only the MAX_STARS_PER_NODE brightest stars are stored in inner
* nodes. If \p branchIndex is defined then only that branch will be sliced.
* Calls <code>sliceNodeLodCache()</code> internally.
*/
void sliceLodData(size_t branchIndex = 8);
/**
* Prints the whole tree structure, including number of stars per node, number of
* nodes, tree depth and if node is a leaf.
* Calls <code>printStarsPerNode(node, prefix)</code> internally.
*/
void printStarsPerNode() const;
/**
* Used while streaming nodes from files. Checks if any nodes need to be loaded or
* unloaded. If entire dataset fits in RAM then the whole dataset will be loaded
* asynchronously. Otherwise only nodes close to the camera will be fetched.
* When RAM stars to fill up least-recently used nodes will start to unload.
* Calls <code>findAndFetchNeighborNode()</code> and
* <code>removeNodesFromRam()</code> internally.
*/
void fetchSurroundingNodes(const glm::dvec3& cameraPos, size_t chunkSizeInBytes,
const glm::ivec2& additionalNodes);
/**
* Builds render data structure by traversing the Octree and checking for intersection
* with view frustum. Every vector in map contains data for one node.
* The corresponding integer key is the index where chunk should be inserted into
* streaming buffer. Calls <code>checkNodeIntersection()</code> for every branch.
* \pdeltaStars keeps track of how many stars that were added/removed this render
* call.
*/
std::map<int, std::vector<float>> traverseData(const glm::dmat4& mvp,
const glm::vec2& screenSize, int& deltaStars, gaia::RenderOption option,
float lodPixelThreshold);
/**
* Builds full render data structure by traversing all leaves in the Octree.
*/
std::vector<float> getAllData(gaia::RenderOption option);
/**
* Removes all data from Octree, or only from a specific branch if specified.
* \param branchIndex defined which branch to clear if defined.
*/
void clearAllData(int branchIndex = -1);
/**
* Write entire Octree structure to a binary file. \param writeData defines if data
* should be included or if only structure should be written to the file.
* Calls <code>writeNodeToFile()</code> which recursively writes all nodes.
*/
void writeToFile(std::ofstream& outFileStream, bool writeData);
/**
* Read a constructed Octree from a file. \returns the total number of (distinct)
* stars read.
*
* \param readData defines if full data or only structure should be read.
* Calls <code>readNodeFromFile()</code> which recursively reads all nodes.
*/
int readFromFile(std::ifstream& inFileStream, bool readData,
const std::string& folderPath = std::string());
/**
* Write specified part of Octree to multiple files, including all data.
* \param branchIndex defines which branch to write.
* Clears specified branch after writing is done.
* Calls <code>writeNodeToMultipleFiles()</code> for the specified branch.
*/
void writeToMultipleFiles(const std::string& outFolderPath, size_t branchIndex);
/**
* Getters.
*/
size_t numLeafNodes() const;
size_t numInnerNodes() const;
size_t totalNodes() const;
size_t totalDepth() const;
size_t maxDist() const;
size_t maxStarsPerNode() const;
size_t biggestChunkIndexInUse() const;
size_t numFreeSpotsInBuffer() const;
bool isRebuildOngoing() const;
/**
* \returns current CPU RAM budget in bytes.
*/
long long cpuRamBudget() const;
private:
const size_t POS_SIZE = 3;
const size_t COL_SIZE = 2;
const size_t VEL_SIZE = 3;
// MAX_DIST [kPc] - Determines the depth of Octree together with MAX_STARS_PER_NODE.
// A smaller distance is better (i.e. a smaller total depth) and a smaller MAX_STARS
// is also better (i.e. finer borders and fewer nodes/less data needs to be uploaded
// to the GPU), but MAX_STARS still needs to be big enough to be able to swallow all
// stars that falls outside of top border nodes, otherwise it causes a stack overflow
// when building Octree. However, fewer total nodes (i.e. bigger Stars/Node) reduces
// traversing time which is preferable, especially with big datasets
// DR1_TGAS [2M] - A MAX_DIST of 5 kPc works fine with down to 1 kSPN.
// DR1_full [1.2B] - A MAX_DIST of 10 kPc works fine with most SPN.
// DR2_rv [7.2M] - A MAX_DIST of 15 kPc works fine with down to 10 kSPN.
// DR2_subset [42.9M] - A MAX_DIST of 100 kPc works fine with 20 kSPN.
// DR2_full [1.7B] - A MAX_DIST of 250 kPc works fine with 150 kSPN.
size_t MAX_DIST = 2; // [kPc]
size_t MAX_STARS_PER_NODE = 2000;
const int DEFAULT_INDEX = -1;
const std::string BINARY_SUFFIX = ".bin";
/**
* \returns the correct index of child node. Maps [1,1,1] to 0 and [-1,-1,-1] to 7.
*/
size_t getChildIndex(float posX, float posY, float posZ, float origX = 0.f,
float origY = 0.f, float origZ = 0.f);
/**
* Private help function for <code>insert()</code>. Inserts star into node if leaf and
* numStars < MAX_STARS_PER_NODE. If a leaf goes above the threshold it is subdivided
* into 8 new nodes.
* If node is an inner node, then star is stores in LOD cache if it is among the
* brightest stars in all children.
*/
bool insertInNode(OctreeNode& node, const std::vector<float>& starValues,
int depth = 1);
/**
* Slices LOD cache data in node to the MAX_STARS_PER_NODE brightest stars. This needs
* to be called after the last star has been inserted into Octree but before it is
* saved to file(s). Slices all descendants recursively.
*/
void sliceNodeLodCache(OctreeNode& node);
/**
* Private help function for <code>insertInNode()</code>. Stores star data in node and
* keeps track of the brightest stars all children.
*/
void storeStarData(OctreeNode& node, const std::vector<float>& starValues);
/**
* Private help function for <code>printStarsPerNode()</code>. \returns an accumulated
* string containing all descendant nodes.
*/
std::string printStarsPerNode(const OctreeNode& node,
const std::string& prefix) const;
/**
* Private help function for <code>traverseData()</code>. Recursively checks which
* nodes intersect with the view frustum (interpreted as an AABB) and decides if data
* should be optimized away or not. Keeps track of which nodes that are visible and
* loaded (if streaming). \param deltaStars keeps track of how many stars that were
* added/removed this render call.
*/
std::map<int, std::vector<float>> checkNodeIntersection(OctreeNode& node,
const glm::dmat4& mvp, const glm::vec2& screenSize, int& deltaStars,
gaia::RenderOption option);
/**
* Checks if specified node existed in cache, and removes it if that's the case.
* If node is an inner node then all children will be checked recursively as well as
* long as \param recursive is not set to false. \param deltaStars keeps track of how
* many stars that were removed.
*/
std::map<int, std::vector<float>> removeNodeFromCache(OctreeNode& node,
int& deltaStars, bool recursive = true);
/**
* Get data in node and its descendants regardless if they are visible or not.
*/
std::vector<float> getNodeData(const OctreeNode& node, gaia::RenderOption option);
/**
* Clear data from node and its descendants and shrink vectors to deallocate memory.
*/
void clearNodeData(OctreeNode& node);
/**
* Contruct default children nodes for specified node.
*/
void createNodeChildren(OctreeNode& node);
/**
* Checks if node should be inserted into stream or not. \returns true if it should,
* (i.e. it doesn't already exists, there is room for it in the buffer and node data
* is loaded if streaming). \returns false otherwise.
*/
bool updateBufferIndex(OctreeNode& node);
/**
* Node should be inserted into stream. This function \returns the data to be
* inserted. If VBOs are used then the chunks will be appended by zeros, otherwise
* only the star data corresponding to RenderOption \param option will be inserted.
*
* \param deltaStars keeps track of how many stars that were added.
*/
std::vector<float> constructInsertData(const OctreeNode& node,
gaia::RenderOption option, int& deltaStars);
/**
* Write a node to outFileStream. \param writeData defines if data should be included
* or if only structure should be written.
*/
void writeNodeToFile(std::ofstream& outFileStream, const OctreeNode& node,
bool writeData);
/**
* Read a node from file and its potential children. \param readData defines if full
* data or only structure should be read.
* \returns accumulated sum of all read stars in node and its descendants.
*/
int readNodeFromFile(std::ifstream& inFileStream, OctreeNode& node, bool readData);
/**
* Write node data to a file. \param outFilePrefix specifies the accumulated path
* and name of the file. If \param threadWrites is set to true then one new thread
* will be created for each child to write its descendents.
*/
void writeNodeToMultipleFiles(const std::string& outFilePrefix, OctreeNode& node,
bool threadWrites);
/**
* Finds the neighboring node on the same level (or a higher level if there is no
* corresponding level) in the specified direction. Also fetches data from found node
* if it's not already loaded. \param additionalLevelsToFetch determines if any
* descendants of the found node should be fetched as well (if they exists).
*/
void findAndFetchNeighborNode(unsigned long long firstParentId, int x, int y, int z,
int additionalLevelsToFetch);
/**
* Fetches data from all children of \param parentNode, as long as it's not already
* fetched, it exists and it can fit in RAM.
* \param additionalLevelsToFetch determines how many levels of descendants to fetch.
* If it is set to 0 no additional level will be fetched.
* If it is set to a negative value then all descendants will be fetched recursively.
* Calls <code>fetchNodeDataFromFile()</code> for every child that passes the tests.
*/
void fetchChildrenNodes(OctreeNode& parentNode, int additionalLevelsToFetch);
/**
* Fetches data for specified node from file.
* OBS! Only call if node file exists (i.e. node has any data, node->numStars > 0)
* and is not already loaded.
*/
void fetchNodeDataFromFile(OctreeNode& node);
/**
* Loops though all nodes in \param nodesToRemove and clears them from RAM.
* Also checks if any ancestor should change the <code>hasLoadedDescendant</code> flag
* by calling <code>propagateUnloadedNodes()</code> with all ancestors.
*/
void removeNodesFromRam(const std::vector<unsigned long long>& nodesToRemove);
/**
* Removes data in specified node from main memory and updates RAM budget and flags
* accordingly.
*/
void removeNode(OctreeNode& node);
/**
* Loops through \param ancestorNodes backwards and checks if parent node has any
* loaded descendants left. If not, then flag <code>hasLoadedDescendant</code> will be
* set to false for that parent node and next parent in line will be checked.
*/
void propagateUnloadedNodes(std::vector<std::shared_ptr<OctreeNode>> ancestorNodes);
std::shared_ptr<OctreeNode> _root;
std::unique_ptr<OctreeCuller> _culler;
std::stack<int> _freeSpotsInBuffer;
std::set<int> _removedKeysInPrevCall;
std::queue<unsigned long long> _leastRecentlyFetchedNodes;
std::mutex _leastRecentlyFetchedNodesMutex;
size_t _totalDepth = 0;
size_t _numLeafNodes = 0;
size_t _numInnerNodes = 0;
size_t _biggestChunkIndexInUse = 0;
size_t _valuesPerStar = 0;
float _minTotalPixelsLod = 0.f;
size_t _maxStackSize = 0;
bool _rebuildBuffer = false;
bool _useVBO = false;
bool _streamOctree = false;
bool _datasetFitInMemory = false;
long long _cpuRamBudget = 0;
long long _maxCpuRamBudget = 0;
unsigned long long _parentNodeOfCamera = 8;
std::string _streamFolderPath;
size_t _traversedBranchesInRenderCall = 0;
}; // class OctreeManager
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___OCTREEMANAGER___H__
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,216 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GAIA___RENDERABLEGAIASTARS___H__
#define __OPENSPACE_MODULE_GAIA___RENDERABLEGAIASTARS___H__
#include <openspace/rendering/renderable.h>
#include <modules/gaia/rendering/octreemanager.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec2property.h>
#include <openspace/properties/vector/ivec2property.h>
#include <ghoul/opengl/bufferbinding.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/uniformcache.h>
namespace ghoul::filesystem { class File; }
namespace ghoul::opengl {
class ProgramObject;
class Texture;
} // namespace ghoul::opengl
namespace openspace {
namespace documentation { struct Documentation; }
class RenderableGaiaStars : public Renderable {
public:
explicit RenderableGaiaStars(const ghoul::Dictionary& dictionary);
virtual ~RenderableGaiaStars() = default;
void initializeGL() override;
void deinitializeGL() override;
bool isReady() const override;
void render(const RenderData& data, RendererTasks& rendererTask) override;
void update(const UpdateData& data) override;
static documentation::Documentation Documentation();
private:
/**
* Reads data file in format defined by FileReaderOption.
*
* \return true if data was successfully read.
*/
bool readDataFile();
/**
* Reads a FITS file by using FitsFileReader.readFitsFile() and constructs an octree.
*
* \return the number of stars read.
*/
int readFitsFile(const std::string& filePath);
/**
* Read a SPECK file by using FitsFileReader.readSpeckFile() and constructs an octree.
*
* \return the number of stars read.
*/
int readSpeckFile(const std::string& filePath);
/**
* Reads a preprocessed binary file and constructs an octree.
*
* \return the number of stars read.
*/
int readBinaryRawFile(const std::string& filePath);
/**
* Reads a pre-constructed octree, with all data, from a binary file.
*
* \return the number of stars read.
*/
int readBinaryOctreeFile(const std::string& filePath);
/**
* Reads the structure of a pre-constructed octree from a binary file, without any
* data.
*
* \return the number of stars read.
*/
int readBinaryOctreeStructureFile(const std::string& folderPath);
/**
* Checks for any OpenGL errors and reports these to the log if _reportGlErrors is
* set to true.
*/
void checkGlErrors(const std::string& identifier) const;
properties::StringProperty _filePath;
std::unique_ptr<ghoul::filesystem::File> _dataFile;
bool _dataIsDirty = true;
bool _buffersAreDirty = true;
bool _shadersAreDirty = false;
properties::StringProperty _pointSpreadFunctionTexturePath;
std::unique_ptr<ghoul::opengl::Texture> _pointSpreadFunctionTexture;
std::unique_ptr<ghoul::filesystem::File> _pointSpreadFunctionFile;
bool _pointSpreadFunctionTextureIsDirty = true;
properties::StringProperty _colorTexturePath;
std::unique_ptr<ghoul::opengl::Texture> _colorTexture;
std::unique_ptr<ghoul::filesystem::File> _colorTextureFile;
bool _colorTextureIsDirty = true;
properties::FloatProperty _luminosityMultiplier;
properties::FloatProperty _magnitudeBoost;
properties::FloatProperty _cutOffThreshold;
properties::FloatProperty _sharpness;
properties::FloatProperty _billboardSize;
properties::FloatProperty _closeUpBoostDist;
properties::IntProperty _tmPointFilterSize;
properties::FloatProperty _tmPointSigma;
properties::IVec2Property _additionalNodes;
properties::FloatProperty _tmPointPixelWeightThreshold;
properties::FloatProperty _lodPixelThreshold;
properties::Vec2Property _posXThreshold;
properties::Vec2Property _posYThreshold;
properties::Vec2Property _posZThreshold;
properties::Vec2Property _gMagThreshold;
properties::Vec2Property _bpRpThreshold;
properties::Vec2Property _distThreshold;
properties::IntProperty _firstRow;
properties::IntProperty _lastRow;
properties::StringListProperty _columnNamesList;
std::vector<std::string> _columnNames;
properties::OptionProperty _fileReaderOption;
properties::OptionProperty _renderOption;
properties::OptionProperty _shaderOption;
properties::IntProperty _nRenderedStars;
// LongLongProperty doesn't show up in menu, use FloatProperty instead.
properties::FloatProperty _cpuRamBudgetProperty;
properties::FloatProperty _gpuStreamBudgetProperty;
properties::FloatProperty _maxGpuMemoryPercent;
properties::FloatProperty _maxCpuMemoryPercent;
properties::BoolProperty _reportGlErrors;
std::unique_ptr<ghoul::opengl::ProgramObject> _program;
UniformCache(model, view, cameraPos, cameraLookUp, viewScaling, projection,
renderOption, luminosityMultiplier, magnitudeBoost, cutOffThreshold,
sharpness, billboardSize, closeUpBoostDist, screenSize, psfTexture,
time, colorTexture, nChunksToRender, valuesPerStar, maxStarsPerNode)
_uniformCache;
UniformCache(posXThreshold, posYThreshold, posZThreshold, gMagThreshold,
bpRpThreshold, distThreshold) _uniformFilterCache;
std::unique_ptr<ghoul::opengl::ProgramObject> _programTM;
UniformCache(renderedTexture, screenSize, filterSize, sigma, pixelWeightThreshold,
projection) _uniformCacheTM;
std::unique_ptr<ghoul::opengl::Texture> _fboTexture;
OctreeManager _octreeManager;
std::unique_ptr<ghoul::opengl::BufferBinding<
ghoul::opengl::bufferbinding::Buffer::ShaderStorage>> _ssboIdxBinding;
std::unique_ptr<ghoul::opengl::BufferBinding<
ghoul::opengl::bufferbinding::Buffer::ShaderStorage>> _ssboDataBinding;
std::vector<int> _accumulatedIndices;
size_t _nRenderValuesPerStar = 0;
int _nStarsToRender = 0;
bool _firstDrawCalls = true;
glm::dquat _previousCameraRotation = glm::dquat(1.0, 0.0, 0.0, 0.0);
bool _useVBO = false;
long long _cpuRamBudgetInBytes = 0;
long long _totalDatasetSizeInBytes = 0;
long long _gpuMemoryBudgetInBytes = 0;
long long _maxStreamingBudgetInBytes = 0;
size_t _chunkSize = 0;
GLuint _vao = 0;
GLuint _vaoEmpty = 0;
GLuint _vboPos = 0;
GLuint _vboCol = 0;
GLuint _vboVel = 0;
GLuint _ssboIdx = 0;
GLuint _ssboData = 0;
GLuint _vaoQuad = 0;
GLuint _vboQuad = 0;
GLuint _fbo = 0;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___RENDERABLEGAIASTARS___H__
@@ -1,892 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/galaxy/rendering/renderablegalaxy.h>
#include <modules/galaxy/rendering/galaxyraycaster.h>
#include <modules/volume/rawvolume.h>
#include <modules/volume/rawvolumereader.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/raycastermanager.h>
#include <openspace/rendering/renderable.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/boxgeometry.h>
#include <openspace/util/distanceconstants.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/fmt.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <glm/gtc/matrix_transform.hpp>
#include <fstream>
namespace {
constexpr int8_t CurrentCacheVersion = 1;
constexpr const char* GlslRaycastPath =
"${MODULE_GALAXY}/shaders/galaxyraycast.glsl";
constexpr const char* GlslBoundsVsPath =
"${MODULE_GALAXY}/shaders/raycasterbounds_vs.glsl";
constexpr const char* GlslBoundsFsPath =
"${MODULE_GALAXY}/shaders/raycasterbounds_fs.glsl";
constexpr const char* _loggerCat = "Renderable Galaxy";
constexpr const std::array<const char*, 4> UniformNamesPoints = {
"modelMatrix", "cameraViewProjectionMatrix", "eyePosition",
"opacityCoefficient"
};
constexpr const std::array<const char*, 5> UniformNamesBillboards = {
"modelMatrix", "cameraViewProjectionMatrix",
"cameraUp", "eyePosition", "psfTexture"
};
constexpr openspace::properties::Property::PropertyInfo VolumeRenderingEnabledInfo = {
"VolumeRenderingEnabled",
"Volume Rendering",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StarRenderingEnabledInfo = {
"StarRenderingEnabled",
"Star Rendering",
"" // @TODO Missing documentation
};
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 TranslationInfo = {
"Translation",
"Translation",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo RotationInfo = {
"Rotation",
"Euler rotation",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StarRenderingMethodInfo = {
"StarRenderingMethod",
"Star Rendering Method",
"This value determines which rendering method is used for visualization of the "
"stars."
};
constexpr openspace::properties::Property::PropertyInfo EnabledPointsRatioInfo = {
"EnabledPointsRatio",
"Enabled points",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo DownscaleVolumeRenderingInfo =
{
"Downscale",
"Downscale Factor Volume Rendering",
"This value set the downscaling factor"
" when rendering the current volume."
};
constexpr openspace::properties::Property::PropertyInfo NumberOfRayCastingStepsInfo =
{
"Steps",
"Number of RayCasting Steps",
"This value set the number of integration steps during the raycasting procedure."
};
void saveCachedFile(const std::string& file, const std::vector<glm::vec3>& positions,
const std::vector<glm::vec3>& colors, int64_t nPoints,
float pointsRatio)
{
std::ofstream fileStream(file, std::ofstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return;
}
fileStream.write(
reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t)
);
fileStream.write(reinterpret_cast<const char*>(&nPoints), sizeof(int64_t));
fileStream.write(reinterpret_cast<const char*>(&pointsRatio), sizeof(float));
uint64_t nPositions = static_cast<uint64_t>(positions.size());
fileStream.write(reinterpret_cast<const char*>(&nPositions), sizeof(uint64_t));
fileStream.write(
reinterpret_cast<const char*>(positions.data()),
positions.size() * sizeof(glm::vec3)
);
uint64_t nColors = static_cast<uint64_t>(colors.size());
fileStream.write(reinterpret_cast<const char*>(&nColors), sizeof(uint64_t));
fileStream.write(
reinterpret_cast<const char*>(colors.data()),
colors.size() * sizeof(glm::vec3)
);
}
} // namespace
namespace openspace {
RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _volumeRenderingEnabled(VolumeRenderingEnabledInfo, true)
, _starRenderingEnabled(StarRenderingEnabledInfo, true)
, _stepSize(StepSizeInfo, 0.01f, 0.0005f, 0.05f, 0.001f)
, _absorptionMultiply(AbsorptionMultiplyInfo, 40.f, 0.0f, 200.0f)
, _emissionMultiply(EmissionMultiplyInfo, 200.f, 0.0f, 1000.0f)
, _starRenderingMethod(
StarRenderingMethodInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _enabledPointsRatio(EnabledPointsRatioInfo, 0.5f, 0.01f, 1.0f)
, _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(glm::two_pi<float>())
)
, _downScaleVolumeRendering(DownscaleVolumeRenderingInfo, 1.f, 0.1f, 1.f)
, _numberOfRayCastingSteps(NumberOfRayCastingStepsInfo, 1000.f, 1.f, 1000.f)
{
dictionary.getValue("VolumeRenderingEnabled", _volumeRenderingEnabled);
dictionary.getValue("StarRenderingEnabled", _starRenderingEnabled);
dictionary.getValue("StepSize", _stepSize);
dictionary.getValue("AbsorptionMultiply", _absorptionMultiply);
dictionary.getValue("EmissionMultiply", _emissionMultiply);
dictionary.getValue("StarRenderingMethod", _starRenderingMethod);
dictionary.getValue("EnabledPointsRatio", _enabledPointsRatio);
dictionary.getValue("Translation", _translation);
dictionary.getValue("Rotation", _rotation);
if (dictionary.hasKeyAndValue<bool>(VolumeRenderingEnabledInfo.identifier)) {
_volumeRenderingEnabled = dictionary.value<bool>(
VolumeRenderingEnabledInfo.identifier
);
}
if (dictionary.hasKeyAndValue<bool>(StarRenderingEnabledInfo.identifier)) {
_starRenderingEnabled = static_cast<bool>(StarRenderingEnabledInfo.identifier);
}
if (dictionary.hasKeyAndValue<double>(StepSizeInfo.identifier)) {
_stepSize = static_cast<float>(dictionary.value<double>(StepSizeInfo.identifier));
}
if (dictionary.hasKeyAndValue<double>(AbsorptionMultiplyInfo.identifier)) {
_absorptionMultiply = static_cast<float>(
dictionary.value<double>(AbsorptionMultiplyInfo.identifier)
);
}
if (dictionary.hasKeyAndValue<double>(EmissionMultiplyInfo.identifier)) {
_emissionMultiply = static_cast<float>(
dictionary.value<double>(EmissionMultiplyInfo.identifier)
);
}
_starRenderingMethod.addOptions({
{ 0, "Points" },
{ 1, "Billboards" }
});
if (dictionary.hasKey(StarRenderingMethodInfo.identifier)) {
const std::string starRenderingMethod = dictionary.value<std::string>(
StarRenderingMethodInfo.identifier
);
if (starRenderingMethod == "Points") {
_starRenderingMethod = 0;
}
else if (starRenderingMethod == "Billboards") {
_starRenderingMethod = 1;
}
}
if (dictionary.hasKeyAndValue<glm::vec3>(TranslationInfo.identifier)) {
_translation = dictionary.value<glm::vec3>(TranslationInfo.identifier);
}
if (dictionary.hasKeyAndValue<glm::vec3>(RotationInfo.identifier)) {
_rotation = dictionary.value<glm::vec3>(RotationInfo.identifier);
}
if (!dictionary.hasKeyAndValue<ghoul::Dictionary>("Volume")) {
LERROR("No volume dictionary specified.");
}
ghoul::Dictionary volumeDictionary = dictionary.value<ghoul::Dictionary>("Volume");
std::string volumeFilename;
if (volumeDictionary.getValue("Filename", volumeFilename)) {
_volumeFilename = absPath(volumeFilename);
}
else {
LERROR("No volume filename specified.");
}
glm::vec3 volumeDimensions = glm::vec3(0.f);
if (volumeDictionary.getValue("Dimensions", volumeDimensions)) {
_volumeDimensions = static_cast<glm::ivec3>(volumeDimensions);
}
else {
LERROR("No volume dimensions specified.");
}
glm::vec3 volumeSize = glm::vec3(0.f);
if (volumeDictionary.getValue("Size", volumeSize)) {
_volumeSize = volumeSize;
}
else {
LERROR("No volume dimensions specified.");
}
if (volumeDictionary.hasKey(NumberOfRayCastingStepsInfo.identifier)) {
_numberOfRayCastingSteps = static_cast<float>(
volumeDictionary.value<double>(NumberOfRayCastingStepsInfo.identifier)
);
}
else {
LINFO("Number of raycasting steps not specified. Using default value.");
}
_downScaleVolumeRendering.setVisibility(properties::Property::Visibility::Developer);
if (volumeDictionary.hasKey(DownscaleVolumeRenderingInfo.identifier)) {
_downScaleVolumeRendering =
volumeDictionary.value<float>(DownscaleVolumeRenderingInfo.identifier);
}
if (!dictionary.hasKeyAndValue<ghoul::Dictionary>("Points")) {
LERROR("No points dictionary specified.");
}
ghoul::Dictionary pointsDictionary = dictionary.value<ghoul::Dictionary>("Points");
std::string pointsFilename;
if (pointsDictionary.getValue("Filename", pointsFilename)) {
_pointsFilename = absPath(pointsFilename);
}
else {
LERROR("No points filename specified.");
}
if (pointsDictionary.hasKeyAndValue<double>(EnabledPointsRatioInfo.identifier)) {
_enabledPointsRatio = static_cast<float>(
pointsDictionary.value<double>(EnabledPointsRatioInfo.identifier)
);
}
std::string pointSpreadFunctionTexturePath;
if (pointsDictionary.getValue("Texture", pointSpreadFunctionTexturePath)) {
_pointSpreadFunctionTexturePath = absPath(pointSpreadFunctionTexturePath);
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
_pointSpreadFunctionTexturePath
);
}
else {
LERROR("No points filename specified.");
}
}
void RenderableGalaxy::initializeGL() {
// Aspect is currently hardcoded to cubic voxels.
_aspect = static_cast<glm::vec3>(_volumeDimensions);
_aspect /= std::max(std::max(_aspect.x, _aspect.y), _aspect.z);
// The volume
volume::RawVolumeReader<glm::tvec4<GLubyte>> reader(
_volumeFilename,
_volumeDimensions
);
_volume = reader.read();
_texture = std::make_unique<ghoul::opengl::Texture>(
_volumeDimensions,
ghoul::opengl::Texture::Format::RGBA,
GL_RGBA,
GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::FilterMode::Linear,
ghoul::opengl::Texture::WrappingMode::ClampToEdge
);
_texture->setPixelData(
reinterpret_cast<char*>(_volume->data()),
ghoul::opengl::Texture::TakeOwnership::No
);
_texture->setDimensions(_volume->dimensions());
_texture->uploadTexture();
_raycaster = std::make_unique<GalaxyRaycaster>(*_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(_volumeRenderingEnabled);
addProperty(_starRenderingEnabled);
addProperty(_stepSize);
addProperty(_absorptionMultiply);
addProperty(_emissionMultiply);
addProperty(_starRenderingMethod);
addProperty(_enabledPointsRatio);
addProperty(_translation);
addProperty(_rotation);
addProperty(_downScaleVolumeRendering);
addProperty(_numberOfRayCastingSteps);
// initialize points.
if (_pointsFilename.empty()) {
return;
}
_pointsProgram = global::renderEngine.buildRenderProgram(
"Galaxy points",
absPath("${MODULE_GALAXY}/shaders/points_vs.glsl"),
absPath("${MODULE_GALAXY}/shaders/points_fs.glsl")
);
_billboardsProgram = global::renderEngine.buildRenderProgram(
"Galaxy billboard",
absPath("${MODULE_GALAXY}/shaders/billboard_vs.glsl"),
absPath("${MODULE_GALAXY}/shaders/billboard_fs.glsl"),
absPath("${MODULE_GALAXY}/shaders/billboard_ge.glsl")
);
if (!_pointSpreadFunctionTexturePath.empty()) {
_pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture(
absPath(_pointSpreadFunctionTexturePath)
);
if (_pointSpreadFunctionTexture) {
LDEBUG(fmt::format(
"Loaded texture from '{}'",
absPath(_pointSpreadFunctionTexturePath)
));
_pointSpreadFunctionTexture->uploadTexture();
}
_pointSpreadFunctionTexture->setFilter(
ghoul::opengl::Texture::FilterMode::AnisotropicMipMap
);
_pointSpreadFunctionFile = std::make_unique<ghoul::filesystem::File>(
_pointSpreadFunctionTexturePath
);
}
ghoul::opengl::updateUniformLocations(
*_pointsProgram,
_uniformCachePoints,
UniformNamesPoints
);
ghoul::opengl::updateUniformLocations(
*_billboardsProgram,
_uniformCacheBillboards,
UniformNamesBillboards
);
_pointsProgram->setIgnoreUniformLocationError(
ghoul::opengl::ProgramObject::IgnoreError::Yes
);
GLint positionAttrib = _pointsProgram->attributeLocation("in_position");
GLint colorAttrib = _pointsProgram->attributeLocation("in_color");
std::vector<glm::vec3> pointPositions;
std::vector<glm::vec3> pointColors;
std::string cachedPointsFile = FileSys.cacheManager()->cachedFilename(
_pointsFilename,
ghoul::filesystem::CacheManager::Persistent::Yes
);
const bool hasCachedFile = FileSys.fileExists(cachedPointsFile);
if (hasCachedFile) {
LINFO(fmt::format("Cached file '{}' used for galaxy point file '{}'",
cachedPointsFile, _pointsFilename
));
Result res = loadCachedFile(cachedPointsFile);
if (res.success) {
pointPositions = std::move(res.positions);
pointColors = std::move(res.color);
}
else {
FileSys.cacheManager()->removeCacheFile(_pointsFilename);
Result resPoint = loadPointFile(_pointsFilename);
pointPositions = std::move(resPoint.positions);
pointColors = std::move(resPoint.color);
saveCachedFile(
cachedPointsFile,
pointPositions,
pointColors,
_nPoints,
_enabledPointsRatio
);
}
}
else {
Result res = loadPointFile(_pointsFilename);
ghoul_assert(res.success, "Point file loading failed");
pointPositions = std::move(res.positions);
pointColors = std::move(res.color);
saveCachedFile(
cachedPointsFile,
pointPositions,
pointColors,
_nPoints,
_enabledPointsRatio
);
}
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
);
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;
}
glDeleteVertexArrays(1, &_pointsVao);
glDeleteBuffers(1, &_positionVbo);
glDeleteBuffers(1, &_colorVbo);
}
bool RenderableGalaxy::isReady() const {
return true;
}
void RenderableGalaxy::update(const UpdateData& data) {
if (!_raycaster) {
return;
}
//glm::mat4 transform = glm::translate(, static_cast<glm::vec3>(_translation));
const glm::vec3 eulerRotation = static_cast<glm::vec3>(_rotation);
glm::mat4 transform = glm::rotate(
glm::mat4(1.f),
eulerRotation.x,
glm::vec3(1.f, 0.f, 0.f)
);
transform = glm::rotate(transform, eulerRotation.y, glm::vec3(0.f, 1.f, 0.f));
transform = glm::rotate(transform, eulerRotation.z, glm::vec3(0.f, 0.f, 1.f));
glm::mat4 volumeTransform = glm::scale(transform, _volumeSize);
_pointTransform = transform;
//_pointTransform = glm::scale(transform, _pointScaling);
const glm::vec4 translation = glm::vec4(_translation.value()*_volumeSize, 0.f);
// Todo: handle floating point overflow, to actually support translation.
volumeTransform[3] += translation;
_pointTransform[3] += translation;
_raycaster->setDownscaleRender(_downScaleVolumeRendering);
_raycaster->setMaxSteps(static_cast<int>(_numberOfRayCastingSteps));
_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) {
// Render the volume
if (_raycaster && _volumeRenderingEnabled) {
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.01f;
const float lowerRampEnd = maxDim * 0.1f;
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);
}
}
// Render the stars
if (_starRenderingEnabled && _opacityCoefficient > 0.f) {
if (_starRenderingMethod == 1) {
renderBillboards(data);
}
else {
renderPoints(data);
}
}
}
void RenderableGalaxy::renderPoints(const RenderData& data) {
if (!_pointsProgram) {
return;
}
// Saving current OpenGL state
GLenum blendEquationRGB;
GLenum blendEquationAlpha;
GLenum blendDestAlpha;
GLenum blendDestRGB;
GLenum blendSrcAlpha;
GLenum blendSrcRGB;
GLboolean depthMask;
glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRGB);
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRGB);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRGB);
glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
_pointsProgram->activate();
glm::dmat4 rotMatrix = glm::rotate(
glm::dmat4(1.0),
glm::pi<double>(),
glm::dvec3(1.0, 0.0, 0.0)) *
glm::rotate(glm::dmat4(1.0), 3.1248, glm::dvec3(0.0, 1.0, 0.0)) *
glm::rotate(glm::dmat4(1.0), 4.45741, glm::dvec3(0.0, 0.0, 1.0)
);
glm::dmat4 modelMatrix =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) * rotMatrix *
glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)));
glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix());
glm::dmat4 cameraViewProjectionMatrix = projectionMatrix *
data.camera.combinedViewMatrix();
_pointsProgram->setUniform(_uniformCachePoints.modelMatrix, modelMatrix);
_pointsProgram->setUniform(
_uniformCachePoints.cameraViewProjectionMatrix,
cameraViewProjectionMatrix
);
glm::dvec3 eyePosition = glm::dvec3(
glm::inverse(data.camera.combinedViewMatrix()) *
glm::dvec4(0.0, 0.0, 0.0, 1.0)
);
_pointsProgram->setUniform(_uniformCachePoints.eyePosition, eyePosition);
_pointsProgram->setUniform(
_uniformCachePoints.opacityCoefficient,
_opacityCoefficient
);
glBindVertexArray(_pointsVao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_nPoints * _enabledPointsRatio));
glBindVertexArray(0);
_pointsProgram->deactivate();
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Restores OpenGL blending state
glBlendEquationSeparate(blendEquationRGB, blendEquationAlpha);
glBlendFuncSeparate(blendSrcRGB, blendDestRGB, blendSrcAlpha, blendDestAlpha);
glDepthMask(depthMask);
}
void RenderableGalaxy::renderBillboards(const RenderData& data) {
if (!_billboardsProgram) {
return;
}
// Saving current OpenGL state
GLenum blendEquationRGB;
GLenum blendEquationAlpha;
GLenum blendDestAlpha;
GLenum blendDestRGB;
GLenum blendSrcAlpha;
GLenum blendSrcRGB;
GLboolean depthMask;
glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRGB);
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRGB);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRGB);
glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
_billboardsProgram->activate();
glm::dmat4 rotMatrix = glm::rotate(
glm::dmat4(1.0),
glm::pi<double>(),
glm::dvec3(1.0, 0.0, 0.0)) *
glm::rotate(glm::dmat4(1.0), 3.1248, glm::dvec3(0.0, 1.0, 0.0)) *
glm::rotate(glm::dmat4(1.0), 4.45741, glm::dvec3(0.0, 0.0, 1.0)
);
glm::dmat4 modelMatrix =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
glm::dmat4(data.modelTransform.rotation) * rotMatrix *
glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)));
glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix());
glm::dmat4 cameraViewProjectionMatrix = projectionMatrix *
data.camera.combinedViewMatrix();
_billboardsProgram->setUniform(_uniformCacheBillboards.modelMatrix, modelMatrix);
_billboardsProgram->setUniform(
_uniformCacheBillboards.cameraViewProjectionMatrix,
cameraViewProjectionMatrix
);
glm::dvec3 eyePosition = glm::dvec3(
glm::inverse(data.camera.combinedViewMatrix()) *
glm::dvec4(0.0, 0.0, 0.0, 1.0)
);
_billboardsProgram->setUniform(_uniformCacheBillboards.eyePosition, eyePosition);
glm::dvec3 cameraUp = data.camera.lookUpVectorWorldSpace();
_billboardsProgram->setUniform(_uniformCacheBillboards.cameraUp, cameraUp);
ghoul::opengl::TextureUnit psfUnit;
psfUnit.activate();
_pointSpreadFunctionTexture->bind();
_billboardsProgram->setUniform(_uniformCacheBillboards.psfTexture, psfUnit);
glBindVertexArray(_pointsVao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_nPoints * _enabledPointsRatio));
glBindVertexArray(0);
_billboardsProgram->deactivate();
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Restores OpenGL blending state
glBlendEquationSeparate(blendEquationRGB, blendEquationAlpha);
glBlendFuncSeparate(blendSrcRGB, blendDestRGB, blendSrcAlpha, blendDestAlpha);
glDepthMask(depthMask);
}
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;
}
RenderableGalaxy::Result RenderableGalaxy::loadPointFile(const std::string&) {
std::vector<glm::vec3> pointPositions;
std::vector<glm::vec3> pointColors;
int64_t nPoints;
std::ifstream pointFile(_pointsFilename, std::ios::in);
// 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<size_t>(nPoints);
// Read points
float x, y, z, r, g, b, a;
for (size_t i = 0;
i < static_cast<size_t>(_nPoints * _enabledPointsRatio.maxValue()) + 1;
++i)
{
std::getline(pointFile, line);
std::istringstream issp(line);
issp >> x >> y >> z >> r >> g >> b >> a;
// Convert kiloparsec to meters
glm::vec3 position = glm::vec3(x, y, z);
position *= (distanceconstants::Parsec * 100);
pointPositions.emplace_back(position);
pointColors.emplace_back(r, g, b);
}
Result res;
res.success = true;
res.positions = std::move(pointPositions);
res.color = std::move(pointColors);
return res;
}
RenderableGalaxy::Result RenderableGalaxy::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (!fileStream.good()) {
LERROR(fmt::format("Error opening file '{}' for loading cache file", file));
return { false, {}, {} };
}
int8_t cacheVersion;
fileStream.read(reinterpret_cast<char*>(&cacheVersion), sizeof(int8_t));
if (cacheVersion != CurrentCacheVersion) {
LINFO(fmt::format("Removing cache file '{}' as the version changed"));
return { false, {}, {} };
}
int64_t nPoints;
fileStream.read(reinterpret_cast<char*>(&nPoints), sizeof(int64_t));
_nPoints = static_cast<size_t>(nPoints);
float enabledPointsRatio;
fileStream.read(reinterpret_cast<char*>(&enabledPointsRatio), sizeof(float));
_enabledPointsRatio = enabledPointsRatio;
uint64_t nPositions;
fileStream.read(reinterpret_cast<char*>(&nPositions), sizeof(uint64_t));
std::vector<glm::vec3> positions;
positions.resize(nPositions);
fileStream.read(
reinterpret_cast<char*>(positions.data()),
nPositions * sizeof(glm::vec3)
);
uint64_t nColors;
fileStream.read(reinterpret_cast<char*>(&nColors), sizeof(uint64_t));
std::vector<glm::vec3> colors;
colors.resize(nColors);
fileStream.read(
reinterpret_cast<char*>(colors.data()),
nColors * sizeof(glm::vec3)
);
Result result;
result.success = true;
result.positions = std::move(positions);
result.color = std::move(colors);
return result;
}
} // namespace openspace
@@ -1,117 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GALAXY___RENDERABLEGALAXY___H__
#define __OPENSPACE_MODULE_GALAXY___RENDERABLEGALAXY___H__
#include <openspace/rendering/renderable.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/properties/optionproperty.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/uniformcache.h>
namespace ghoul::opengl { class ProgramObject; }
namespace openspace {
namespace volume { template <typename T> class RawVolume; }
class GalaxyRaycaster;
struct RenderData;
class RenderableGalaxy : public Renderable {
public:
explicit RenderableGalaxy(const ghoul::Dictionary& dictionary);
virtual ~RenderableGalaxy() = default;
void initializeGL() override;
void deinitializeGL() override;
bool isReady() const override;
void render(const RenderData& data, RendererTasks& tasks) override;
void update(const UpdateData& data) override;
private:
void renderPoints(const RenderData& data);
void renderBillboards(const RenderData& data);
float safeLength(const glm::vec3& vector) const;
struct Result {
bool success;
std::vector<glm::vec3> positions;
std::vector<glm::vec3> color;
};
Result loadPointFile(const std::string& file);
Result loadCachedFile(const std::string& file);
glm::vec3 _volumeSize = glm::vec3(0.f);
glm::vec3 _pointScaling = glm::vec3(0.f);
properties::BoolProperty _volumeRenderingEnabled;
properties::BoolProperty _starRenderingEnabled;
properties::FloatProperty _stepSize;
properties::FloatProperty _absorptionMultiply;
properties::FloatProperty _emissionMultiply;
properties::OptionProperty _starRenderingMethod;
properties::FloatProperty _enabledPointsRatio;
properties::Vec3Property _translation;
properties::Vec3Property _rotation;
properties::FloatProperty _downScaleVolumeRendering;
properties::FloatProperty _numberOfRayCastingSteps;
std::unique_ptr<ghoul::opengl::Texture> _pointSpreadFunctionTexture;
std::unique_ptr<ghoul::filesystem::File> _pointSpreadFunctionFile;
std::string _volumeFilename;
glm::ivec3 _volumeDimensions = glm::ivec3(0);
std::string _pointsFilename;
std::string _pointSpreadFunctionTexturePath;
std::unique_ptr<GalaxyRaycaster> _raycaster;
std::unique_ptr<volume::RawVolume<glm::tvec4<GLubyte>>> _volume;
std::unique_ptr<ghoul::opengl::Texture> _texture;
glm::mat4 _pointTransform = glm::mat4(1.f);
glm::vec3 _aspect = glm::vec3(0.f);
float _opacityCoefficient = 0.f;
std::unique_ptr<ghoul::opengl::ProgramObject> _pointsProgram;
std::unique_ptr<ghoul::opengl::ProgramObject> _billboardsProgram;
UniformCache(
modelMatrix, cameraViewProjectionMatrix, eyePosition,
opacityCoefficient
) _uniformCachePoints;
UniformCache(
modelMatrix, cameraViewProjectionMatrix,
cameraUp, eyePosition, psfTexture
) _uniformCacheBillboards;
std::vector<float> _pointsData;
size_t _nPoints = 0;
GLuint _pointsVao = 0;
GLuint _positionVbo = 0;
GLuint _colorVbo = 0;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GALAXY___RENDERABLEGALAXY___H__
@@ -0,0 +1,102 @@
openspace.gaia.documentation = {
{
Name = "addClippingBox",
Arguments = "string, vec3, vec3",
Documentation = "Creates a clipping box for the Gaia renderable in the first argument"
},
{
Name = "removeClippingBox",
Arguments = "",
Documentation = ""
},
{
Name = "addClippingSphere",
Arguments = "string, float",
Documentation = "Creates a clipping sphere for the Gaia renderable in the first argument"
},
{
Name = "removeClippingBox",
Arguments = "",
Documentation = ""
}
}
openspace.gaia.addClippingBox = function (name, size, position)
local grid_identifier = "Filtering_Box"
local kilo_parsec_in_meter = 30856775814913700000
if openspace.hasSceneGraphNode(grid_identifier) then
openspace.removeSceneGraphNode(grid_identifier)
end
local grid = {
Identifier = grid_identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { position[1] * kilo_parsec_in_meter, position[2] * kilo_parsec_in_meter, position[3] * kilo_parsec_in_meter }
}
},
Renderable = {
Type = "RenderableBoxGrid",
GridColor = { 0.6, 0.5, 0.7, 1.0 },
LineWidth = 2.0,
Size = { size[1] * kilo_parsec_in_meter, size[2] * kilo_parsec_in_meter, size[3] * kilo_parsec_in_meter}
},
GUI = {
Name = "Filtering Grid",
Path = "/Other/Grids"
}
}
openspace.addSceneGraphNode(grid)
openspace.setPropertyValue('Scene.' .. name .. '.renderable.FilterPosX', { (position[1] - size[1] / 2) * kilo_parsec_in_meter, (position[1] + size[1] / 2) * kilo_parsec_in_meter })
openspace.setPropertyValue('Scene.' .. name .. '.renderable.FilterPosY', { (position[2] - size[2] / 2) * kilo_parsec_in_meter, (position[2] + size[2] / 2) * kilo_parsec_in_meter })
openspace.setPropertyValue('Scene.' .. name .. '.renderable.FilterPosZ', { (position[3] - size[3] / 2) * kilo_parsec_in_meter, (position[3] + size[3] / 2) * kilo_parsec_in_meter })
end
openspace.gaia.removeClippingBox = function()
local grid_identifier = "Filtering_Box"
if openspace.hasSceneGraphNode(grid_identifier) then
openspace.removeSceneGraphNode(grid_identifier)
end
end
openspace.gaia.addClippingSphere = function (name, radius)
local grid_identifier = "Filtering_Sphere"
local kilo_parsec_in_meter = 30856775814913700000
if openspace.hasSceneGraphNode(grid_identifier) then
openspace.removeSceneGraphNode(grid_identifier)
end
local grid = {
Identifier = grid_identifier,
Renderable = {
Type = "RenderableSphericalGrid",
GridColor = { 0.6, 0.5, 0.7, 1.0 },
LineWidth = 1.0,
Radius = radius * kilo_parsec_in_meter
},
GUI = {
Name = "Filtering Sphere",
Path = "/Other/Grids"
}
}
openspace.addSceneGraphNode(grid)
openspace.setPropertyValue('Scene.' .. name .. '.renderable.FilterDist', radius * kilo_parsec_in_meter)
end
openspace.gaia.removeClippingSphere = function()
local grid_identifier = "Filtering_Sphere"
if openspace.hasSceneGraphNode(grid_identifier) then
openspace.removeSceneGraphNode(grid_identifier)
end
end
@@ -1,101 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
#include "PowerScaling/powerScalingMath.hglsl"
uniform dvec3 eyePosition;
uniform dvec3 cameraUp;
uniform dmat4 cameraViewProjectionMatrix;
uniform dmat4 modelMatrix;
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
in vec4 vs_gPosition[];
in vec3 vs_color[];
out vec4 vs_position;
out vec2 psfCoords;
flat out vec3 ge_color;
flat out float ge_screenSpaceDepth;
const double PARSEC = 3.08567756E16;
void main() {
vs_position = gl_in[0].gl_Position; // in object space
ge_color = vs_color[0];
double scaleMultiply = 8.0;
dvec4 dpos = dvec4(vs_position);
dpos.xyz *= scaleMultiply;
dpos = modelMatrix * dpos;
dpos /= PARSEC;
//It lies about 8 kpc from the center on what is known as the Orion Arm of the Milky Way
dpos.x += 8000;
scaleMultiply *= 4.0;
dvec3 scaledRight = dvec3(0.0);
dvec3 scaledUp = dvec3(0.0);
vec4 bottomLeftVertex, bottomRightVertex, topLeftVertex, topRightVertex;
dvec3 normal = normalize(eyePosition - dpos.xyz);
dvec3 newRight = normalize(cross(cameraUp, normal));
dvec3 newUp = cross(normal, newRight);
scaledRight = scaleMultiply * newRight;
scaledUp = scaleMultiply * newUp;
bottomLeftVertex = z_normalization(vec4(cameraViewProjectionMatrix *
dvec4(dpos.xyz - scaledRight - scaledUp, dpos.w)));
//dvec4 dposCamera = cameraViewProjectionMatrix * dpos;
//ge_screenSpaceDepth = float(length(eyePosition - dposCamera.xyz)/PARSEC);
ge_screenSpaceDepth = bottomLeftVertex.w;
topRightVertex = z_normalization(vec4(cameraViewProjectionMatrix *
dvec4(dpos.xyz + scaledUp + scaledRight, dpos.w)));
bottomRightVertex = z_normalization(vec4(cameraViewProjectionMatrix *
dvec4(dpos.xyz + scaledRight - scaledUp, dpos.w)));
topLeftVertex = z_normalization(vec4(cameraViewProjectionMatrix *
dvec4(dpos.xyz + scaledUp - scaledRight, dpos.w)));
// Build primitive
gl_Position = topLeftVertex;
psfCoords = vec2(-1.0, 1.0);
EmitVertex();
gl_Position = bottomLeftVertex;
psfCoords = vec2(-1.0, -1.0);
EmitVertex();
gl_Position = topRightVertex;
psfCoords = vec2(1.0, 1.0);
EmitVertex();
gl_Position = bottomRightVertex;
psfCoords = vec2(1.0, -1.0);
EmitVertex();
EndPrimitive();
}
@@ -0,0 +1,114 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
layout (location = 0) out vec4 outColor;
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float ONE_PARSEC = 3.08567758e16; // 1 Parsec
const float FLT_MAX = 3.402823466e38; // Max float constant in GLSL
const float LUM_LOWER_CAP = 0.01;
in vec2 ge_brightness;
in vec4 ge_gPosition;
in vec2 texCoord;
in float ge_starDistFromSun;
in float ge_cameraDistFromSun;
in float ge_observedDist;
uniform sampler2D psfTexture;
uniform sampler1D colorTexture;
uniform float luminosityMultiplier;
uniform float sharpness;
uniform int renderOption;
vec3 color2rgb(float color) {
// BV is [-0.4, 2.0]
float st = (color + 0.4) / (2.0 + 0.4);
// Bp-Rp[-2.0, 6.5], Bp-G[-2.1, 5.0], G-Rp[-1.0, 3.0]
//float st = (color + 1.0) / (5.0 + 1.0);
return texture(colorTexture, st).rgb;
}
void main() {
// Assume all stars has equal luminosity as the Sun when no magnitude is loaded.
float luminosity = 0.05;
vec3 color = vec3(luminosity);
float ratioMultiplier = 0.05;
vec4 textureColor = texture(psfTexture, texCoord);
textureColor.a = pow(textureColor.a, sharpness);
if (textureColor.a < 0.001) {
discard;
}
// Calculate the color and luminosity if we have the magnitude and B-V color.
if ( renderOption != RENDEROPTION_STATIC ) {
color = color2rgb(ge_brightness.y);
ratioMultiplier = 0.5;
// Absolute magnitude is brightness a star would have at 10 pc away.
float absoluteMagnitude = ge_brightness.x;
// From formula: MagSun - MagStar = 2.5*log(LumStar / LumSun), it gives that:
// LumStar = 10^(1.89 - 0.4*Magstar) , if LumSun = 1 and MagSun = 4.72
luminosity = pow(10.0, 1.89 - 0.4 * absoluteMagnitude);
// If luminosity is really really small then set it to a static low number.
if (luminosity < LUM_LOWER_CAP) {
luminosity = LUM_LOWER_CAP;
}
}
// Luminosity decrease by {squared} distance [measured in Pc].
float observedDistance = ge_observedDist / ONE_PARSEC;
luminosity /= pow(observedDistance, 2.0);
// Multiply our color with the luminosity as well as a user-controlled property.
color *= luminosity * pow(luminosityMultiplier, 3.0);
// Decrease contributing brightness for stars in central cluster.
if ( ge_cameraDistFromSun > ge_starDistFromSun ) {
float ratio = ge_starDistFromSun / ge_cameraDistFromSun;
//color *= ratio * ratioMultiplier;
}
// Use truncating tonemapping here so we don't overexposure individual stars.
//color = 1.0 - 1.0 * exp(-5.0 * color.rgb);
float maxVal = max(max(color.r, color.g), color.b);
if (maxVal > 1.0) {
color /= maxVal;
}
outColor = vec4(color, textureColor.a);
}
@@ -0,0 +1,132 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float EPS = 1e-5;
layout(points) in;
in vec2 vs_brightness[];
in vec4 vs_gPosition[];
in float vs_starDistFromSun[];
in float vs_cameraDistFromSun[];
layout(triangle_strip, max_vertices = 4) out;
out vec2 ge_brightness;
out vec4 ge_gPosition;
out vec2 texCoord;
out float ge_starDistFromSun;
out float ge_cameraDistFromSun;
out float ge_observedDist;
uniform dmat4 view;
uniform dmat4 projection;
uniform dvec3 cameraPos;
uniform dvec3 cameraLookUp;
uniform float viewScaling;
uniform float cutOffThreshold;
uniform float closeUpBoostDist;
uniform float billboardSize;
uniform int renderOption;
uniform float magnitudeBoost;
const vec2 corners[4] = vec2[4](
vec2(0.0, 1.0),
vec2(0.0, 0.0),
vec2(1.0, 1.0),
vec2(1.0, 0.0)
);
void main() {
ge_brightness = vs_brightness[0];
ge_starDistFromSun = vs_starDistFromSun[0];
ge_cameraDistFromSun = vs_cameraDistFromSun[0];
vec4 viewPosition = vec4(view * vs_gPosition[0]);
// Make closer stars look a bit bigger.
ge_observedDist = safeLength(viewPosition / viewScaling);
float closeUpBoost = closeUpBoostDist / ge_observedDist;
float initStarSize = billboardSize;
// Use magnitude for size boost as well.
if ( renderOption != RENDEROPTION_STATIC ) {
// DR1 magnitudes are [4, 20], but could be [-15, 20] according to this chart:
// https://qph.fs.quoracdn.net/main-qimg-317a18e3b228efc7d7f67a1632a55961
// Negative magnitude => Giants
// Big positive magnitude => Dwarfs
float absoluteMagnitude = vs_brightness[0].x;
float normalizedMagnitude = (absoluteMagnitude - 20) / -1; // (-15 - 20);
// TODO: A linear scale is prabably not the best!
initStarSize += normalizedMagnitude * (magnitudeBoost / 50);
}
vec4 position = gl_in[0].gl_Position;
vec2 starSize = vec2(initStarSize + closeUpBoost) * position.w / 1000.0;
float distThreshold = cutOffThreshold - log(ge_observedDist) / log(4.0);
// Discard geometry if star has no position (but wasn't a nullArray).
// Or if observed distance is above threshold set by cutOffThreshold.
// By discarding in gs instead of fs we save computations for when nothing is visible.
if (length(position) < EPS || distThreshold <= 0) {
return;
}
vec4 centerWorldPos = vs_gPosition[0];
dvec3 cameraNormal = normalize(cameraPos - dvec3(centerWorldPos.xyz));
dvec3 newRight = normalize(cross(cameraLookUp, cameraNormal));
dvec3 newUp = cross(cameraNormal, newRight);
vec4 wCameraRight = vec4(newRight, 0.0);
vec4 wCameraUp = vec4(newUp, 0.0);
float multiplier = float(length(cameraPos));
starSize *= float(multiplier/1E1);
for (int i = 0; i < 4; i++) {
// Always turn the billboard towards the camera (needed for warped screen).
vec4 cornerPoint = centerWorldPos
+ wCameraRight * starSize.x * (corners[i].x - 0.5)
+ wCameraUp * starSize.y * (corners[i].y - 0.5);
gl_Position = vec4(projection * view * cornerPoint);
gl_Position.z = 0.0;
texCoord = corners[i];
ge_gPosition = viewPosition;
EmitVertex();
}
EndPrimitive();
}
@@ -0,0 +1,123 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 "fragment.glsl"
#include "floatoperations.glsl"
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float ONE_PARSEC = 3.08567758e16; // 1 Parsec
const float DEFAULT_DEPTH = 3.08567758e19; // 1000 Pc
const float LUM_LOWER_CAP = 0.01;
in vec2 ge_brightness;
in vec4 ge_gPosition;
in vec2 texCoord;
in float ge_starDistFromSun;
in float ge_cameraDistFromSun;
in float ge_observedDist;
uniform sampler2D psfTexture;
uniform sampler1D colorTexture;
uniform float luminosityMultiplier;
uniform float sharpness;
uniform int renderOption;
vec3 color2rgb(float color) {
// BV is [-0.4, 2.0]
float st = (color + 0.4) / (2.0 + 0.4);
// Bp-Rp[-2.0, 6.5], Bp-G[-2.1, 5.0], G-Rp[-1.0, 3.0]
//float st = (color + 1.0) / (5.0 + 1.0);
return texture(colorTexture, st).rgb;
}
Fragment getFragment() {
// Assume all stars has equal luminosity as the Sun when no magnitude is loaded.
float luminosity = 1.0;
vec3 color = vec3(luminosity);
float ratioMultiplier = 0.03;
vec4 textureColor = texture(psfTexture, texCoord);
textureColor.a = pow(textureColor.a, sharpness);
if (textureColor.a < 0.001) {
discard;
}
// Calculate the color and luminosity if we have the magnitude and B-V color.
if ( renderOption != RENDEROPTION_STATIC ) {
color = color2rgb(ge_brightness.y);
ratioMultiplier = 0.5;
// Absolute magnitude is brightness a star would have at 10 pc away.
float absoluteMagnitude = ge_brightness.x;
// From formula: MagSun - MagStar = 2.5*log(LumStar / LumSun), it gives that:
// LumStar = 10^(1.89 - 0.4*Magstar) , if LumSun = 1 and MagSun = 4.72
luminosity = pow(10.0, 1.89 - 0.4 * absoluteMagnitude);
// If luminosity is really really small then set it to a static low number.
if (luminosity < LUM_LOWER_CAP) {
luminosity = LUM_LOWER_CAP;
}
}
// Luminosity decrease by {squared} distance [measured in Pc].
float observedDistance = ge_observedDist / ONE_PARSEC;
luminosity /= pow(observedDistance, 2.0);
// Multiply our color with the luminosity as well as a user-controlled property.
color *= luminosity * pow(luminosityMultiplier, 3.0);
// Decrease contributing brightness for stars in central cluster.
if ( ge_cameraDistFromSun > ge_starDistFromSun ) {
float ratio = ge_starDistFromSun / ge_cameraDistFromSun;
//color *= ratio * ratioMultiplier;
}
// Use truncating tonemapping here so we don't overexposure individual stars.
//color = 1.0 - 1.0 * exp(-5.0 * color.rgb);
float maxVal = max(max(color.r, color.g), color.b);
if (maxVal > 1.0) {
color /= maxVal;
}
if (length(color) < 0.01) {
discard;
}
Fragment frag;
frag.color = vec4(color, textureColor.a);;
// Place stars at back to begin with.
frag.depth = DEFAULT_DEPTH;
frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0);
frag.blend = BLEND_MODE_NORMAL;
return frag;
}
@@ -0,0 +1,98 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
layout (location = 0) out vec4 outColor;
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float ONE_PARSEC = 3.08567758e16; // 1 Parsec
const float LUM_LOWER_CAP = 0.01;
in vec2 ge_brightness;
in vec4 ge_gPosition;
in float ge_starDistFromSun;
in float ge_cameraDistFromSun;
in float ge_observedDist;
uniform sampler1D colorTexture;
uniform float luminosityMultiplier;
uniform int renderOption;
uniform float viewScaling;
vec3 color2rgb(float color) {
// BV is [-0.4, 2.0]
float st = (color + 0.4) / (2.0 + 0.4);
// Bp-Rp[-2.0, 6.5], Bp-G[-2.1, 5.0], G-Rp[-1.0, 3.0]
//float st = (color + 1.0) / (5.0 + 1.0);
return texture(colorTexture, st).rgb;
}
void main() {
// Assume all stars has equal luminosity as the Sun when no magnitude is loaded.
float luminosity = 0.05;
vec3 color = vec3(luminosity);
float ratioMultiplier = 1.0;
// Calculate the color and luminosity if we have the magnitude and B-V color.
if ( renderOption != RENDEROPTION_STATIC ) {
color = color2rgb(ge_brightness.y);
ratioMultiplier = 0.01;
// Absolute magnitude is brightness a star would have at 10 pc away.
float absoluteMagnitude = ge_brightness.x;
// From formula: MagSun - MagStar = 2.5*log(LumStar / LumSun), it gives that:
// LumStar = 10^(1.89 - 0.4*Magstar) , if LumSun = 1 and MagSun = 4.72
luminosity = pow(10.0, 1.89 - 0.4 * absoluteMagnitude);
// If luminosity is really really small then set it to a static low number.
if (luminosity < LUM_LOWER_CAP) {
luminosity = LUM_LOWER_CAP;
}
}
// Luminosity decrease by {squared} distance [measured in Pc].
float observedDistance = ge_observedDist / ONE_PARSEC;
luminosity /= pow(observedDistance, 2.0);
// Multiply our color with the luminosity as well as a user-controlled property.
color *= luminosity * pow(luminosityMultiplier, 3.0);
// Decrease contributing brightness for stars in central cluster.
if ( ge_cameraDistFromSun > ge_starDistFromSun ) {
float ratio = ge_starDistFromSun / ge_cameraDistFromSun;
//color *= ratio * ratioMultiplier;
}
outColor = vec4(color, 1.0f);
}
@@ -24,35 +24,53 @@
#version __CONTEXT__
#include "PowerScaling/powerScaling_vs.hglsl"
#include "floatoperations.glsl"
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec3 in_color;
const float EPS = 1e-5;
out vec4 vs_position;
out vec3 vs_color;
out float vs_screenSpaceDepth;
out float vs_starBrightness;
layout(points) in;
in vec2 vs_brightness[];
in vec4 vs_gPosition[];
in float vs_starDistFromSun[];
in float vs_cameraDistFromSun[];
uniform dmat4 cameraViewProjectionMatrix;
uniform dmat4 modelMatrix;
uniform dvec3 eyePosition;
layout(points, max_vertices = 1) out;
out vec2 ge_brightness;
out vec4 ge_gPosition;
out float ge_starDistFromSun;
out float ge_cameraDistFromSun;
out float ge_observedDist;
const double PARSEC = 3.08567756E16;
uniform dmat4 view;
uniform float viewScaling;
uniform float cutOffThreshold;
void main() {
vs_position = vec4(in_position, 1.0);
dvec4 dpos = dvec4(vs_position);
double distanceToStar = length((dpos.xyz - eyePosition));
vs_starBrightness = clamp(float(8000*PARSEC/distanceToStar), 0.0, 1.0);
ge_brightness = vs_brightness[0];
ge_starDistFromSun = vs_starDistFromSun[0];
ge_cameraDistFromSun = vs_cameraDistFromSun[0];
dpos.xyz *= 8.0;
dpos = modelMatrix * dpos;
dpos /= PARSEC;
vec4 viewPosition = vec4(view * vs_gPosition[0]);
vec4 positionScreenSpace = z_normalization(vec4(cameraViewProjectionMatrix * dpos));
vs_color = in_color;
vs_screenSpaceDepth = positionScreenSpace.w;
gl_Position = positionScreenSpace;
ge_observedDist = safeLength(viewPosition / viewScaling);
float distThreshold = cutOffThreshold - log(ge_observedDist) / log(4.0);
vec4 position = gl_in[0].gl_Position;
// Discard geometry if star has no position (but wasn't a nullArray).
// Or if observed distance is above threshold set by cutOffThreshold.
// By discarding in gs instead of fs we save computations for when nothing is visible.
if (length(position) < EPS || distThreshold <= 0) {
return;
}
//gl_PointSize = 1.0;
gl_Position = position;
gl_Position.z = 0.0;
ge_gPosition = viewPosition;
EmitVertex();
EndPrimitive();
}
@@ -0,0 +1,187 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float EPS = 1e-5;
const float Parsec = 3.0856776e16;
layout (std430) buffer ssbo_idx_data {
int starsPerChunk[];
};
layout (std430) buffer ssbo_comb_data {
float allData[];
};
in int gl_VertexID;
out vec2 vs_brightness;
out vec4 vs_gPosition;
out float vs_starDistFromSun;
out float vs_cameraDistFromSun;
uniform dmat4 model;
uniform dmat4 view;
uniform dmat4 projection;
uniform float time;
uniform int renderOption;
uniform int maxStarsPerNode;
uniform int valuesPerStar;
uniform int nChunksToRender;
uniform vec2 posXThreshold;
uniform vec2 posYThreshold;
uniform vec2 posZThreshold;
uniform vec2 gMagThreshold;
uniform vec2 bpRpThreshold;
uniform vec2 distThreshold;
// Use binary search to find the chunk containing our star ID.
int findChunkId(int left, int right, int id) {
while ( left <= right ) {
int middle = (left + right) / 2;
int firstStarInChunk = starsPerChunk[middle];
if (left == right || (firstStarInChunk <= id && id < starsPerChunk[middle+1])) {
return middle;
}
else if (id < firstStarInChunk) {
// Go smaller
right = middle - 1;
}
else {
// Go bigger
left = middle + 1;
}
}
return -1;
}
void main() {
// Fetch our data.
int chunkId = findChunkId(0, nChunksToRender - 1, gl_VertexID);
// Fail safe - this should never happen!
if (chunkId == -1) {
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
int placeInChunk = gl_VertexID - starsPerChunk[chunkId];
int firstStarInChunk = valuesPerStar * maxStarsPerNode * chunkId; // Chunk offset
int nStarsInChunk = starsPerChunk[chunkId + 1] - starsPerChunk[chunkId]; // Stars in current chunk.
// Remove possible duplicates.
if (nStarsInChunk <= 0) {
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
int startOfPos = firstStarInChunk + placeInChunk * 3;
vec3 in_position = vec3(allData[startOfPos], allData[startOfPos + 1], allData[startOfPos + 2]);
vec2 in_brightness = vec2(0.0);
vec3 in_velocity = vec3(0.0);
// Check if we should filter this star by position.
if ( (abs(posXThreshold.x) > EPS && in_position.x < posXThreshold.x) ||
(abs(posXThreshold.y) > EPS && in_position.x > posXThreshold.y) ||
(abs(posYThreshold.x) > EPS && in_position.y < posYThreshold.x) ||
(abs(posYThreshold.y) > EPS && in_position.y > posYThreshold.y) ||
(abs(posZThreshold.x) > EPS && in_position.z < posZThreshold.x) ||
(abs(posZThreshold.y) > EPS && in_position.z > posZThreshold.y) ||
(abs(distThreshold.x - distThreshold.y) < EPS
&& abs(length(in_position) - distThreshold.y) < EPS) ) {
// Discard star in geometry shader.
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
if ( renderOption != RENDEROPTION_STATIC ) {
int startOfCol = firstStarInChunk + nStarsInChunk * 3 + placeInChunk * 2;
in_brightness = vec2(allData[startOfCol], allData[startOfCol + 1]);
// Check if we should filter this star by magnitude or color.
if ( (abs(gMagThreshold.x - gMagThreshold.y) < EPS && abs(gMagThreshold.x - in_brightness.x) < EPS) ||
(abs(gMagThreshold.x - 20.0f) > EPS && in_brightness.x < gMagThreshold.x) ||
(abs(gMagThreshold.y - 20.0f) > EPS && in_brightness.x > gMagThreshold.y) ||
(abs(bpRpThreshold.x - bpRpThreshold.y) < EPS && abs(bpRpThreshold.x - in_brightness.y) < EPS) ||
(abs(bpRpThreshold.x) > EPS && in_brightness.y < bpRpThreshold.x) ||
(abs(bpRpThreshold.y) > EPS && in_brightness.y > bpRpThreshold.y) ) {
// Discard star in geometry shader.
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
if ( renderOption == RENDEROPTION_MOTION ) {
int startOfVel = firstStarInChunk + nStarsInChunk * 5 + placeInChunk * 3;
in_velocity = vec3(allData[startOfVel], allData[startOfVel + 1], allData[startOfVel + 2]);
}
}
vs_brightness = in_brightness;
// Convert kiloParsec to meter.
vec4 objectPosition = vec4(in_position * 1000 * Parsec, 1.0);
// Add velocity [m/s] if we've read any.
objectPosition.xyz += time * in_velocity;
// Thres moving stars by their new position.
float distPosition = length(objectPosition.xyz / (1000.0 * Parsec) );
if ( (abs(distThreshold.x - distThreshold.y) > EPS &&
((abs(distThreshold.x) > EPS && distPosition < distThreshold.x) ||
(abs(distThreshold.y) > EPS && distPosition > distThreshold.y))) ) {
// Discard star in geometry shader.
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
// Apply camera transforms.
dvec4 viewPosition = view * model * objectPosition;
vec4 sunPosition = vec4(view * model * dvec4(0.0f, 0.0f, 0.0f, 1.0f));
vs_starDistFromSun = safeLength(objectPosition);
vs_cameraDistFromSun = safeLength(sunPosition);
// Remove stars without position, happens when VBO chunk is stuffed with zeros.
// Has to be done in Geometry shader because Vertices cannot be discarded here.
if ( length(in_position) > EPS ){
vs_gPosition = vec4(model * objectPosition);
gl_Position = vec4(projection * viewPosition);
}
else {
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
}
}
@@ -23,28 +23,31 @@
****************************************************************************************/
#include "fragment.glsl"
#include "floatoperations.glsl"
uniform sampler2D psfTexture;
in vec2 uv;
uniform sampler2D renderedTexture;
const float DEFAULT_DEPTH = 3.08567758e19; // 1000 Pc
in vec4 vs_position;
in vec2 psfCoords;
flat in vec3 ge_color;
flat in float ge_screenSpaceDepth;
Fragment getFragment() {
vec4 color = vec4(0.0);
// BILLBOARDS
// Sample color. Tonemapping done in first shader pass.
vec4 textureColor = texture( renderedTexture, uv );
// Use the following to check for any intensity at all.
//color = (length(intensity.rgb) > 0.001) ? vec4(1.0) : vec4(0.0);
Fragment frag;
vec4 textureColor = texture(psfTexture, 0.5*psfCoords + 0.5);
vec4 fullColor = vec4(ge_color*textureColor.a, textureColor.a);
if (fullColor.a == 0) {
discard;
}
frag.color = fullColor;
frag.depth = ge_screenSpaceDepth;
frag.gPosition = vs_position;
frag.color = textureColor;
// Place stars at back to begin with.
frag.depth = DEFAULT_DEPTH;
frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0);
frag.blend = BLEND_MODE_NORMAL;
return frag;
}
@@ -0,0 +1,179 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 "fragment.glsl"
in vec2 uv;
uniform sampler2D renderedTexture;
uniform dmat4 projection;
uniform vec2 screenSize;
uniform int filterSize;
uniform float sigma;
uniform float pixelWeightThreshold;
const float M_PI = 3.141592653589793238462;
const float DEFAULT_DEPTH = 3.08567758e19; // 1000 Pc
Fragment getFragment() {
vec4 color = vec4(0.0);
// GL_POINTS
// Use frustum params to be able to compensate for a skewed frustum (in a dome).
float near = float(projection[3][2] / (projection[2][2] - 1.0));
float left = float(near * (projection[2][0] - 1.0) / projection[0][0]);
float right = float(near * (projection[2][0] + 1.0) / projection[0][0]);
float top = float(near * (projection[2][1] + 1.0) / projection[1][1]);
float bottom = float(near * (projection[2][1] - 1.0) / projection[1][1]);
float xFactor = float(projection[0][0]);
float yFactor = float(projection[1][1]);
float planeAspect = yFactor / xFactor; // Equals: (right - left) / (top - bottom)
float screenAspect = screenSize.x / screenSize.y;
float fullAspect = planeAspect / screenAspect;
// Find screenPos in skewed frustum. uv is [0, 1]
vec2 screenPos = uv * vec2(right - left, top - bottom) + vec2(left, bottom);
// Find our elliptic scale factors by trigonometric approximation.
float beta = atan(length(screenPos) / near);
vec2 sigmaScaleFactor = vec2( 1.0 / cos(beta), 1.0 / pow(cos(beta), 2.0));
float defaultScreen = 1200.0;
float scaling = screenSize.y / defaultScreen * yFactor;
// Scale filter size depending on screen pos.
vec2 filterScaleFactor = vec2(
pow(screenPos.x / near, 2.0) * fullAspect,
pow(screenPos.y / near, 2.0)
);
// Use to ignore scaling.
//filterScaleFactor = vec2(0.0);
//scaling = 1.0;
//sigmaScaleFactor = vec2(1.0);
// Use the following to find the origo in a skewed frustum.
//Fragment origoFrag;
//vec2 screenOrigo = vec2(-left, -bottom) / vec2(right - left, top - bottom);
//if (abs(screenOrigo.x - uv.x) > 0.0005 && abs(screenOrigo.y - uv.y) > 0.0005) {
// origoFrag.color = vec4(0.0);
//}
//else {
// origoFrag.color = vec4(1.0);
//}
//return origoFrag;
// Uncomment to compare to original filterSize (assumes origo in center of screen).
//screenPos = (uv - 0.5) * 2.0; // [-1, 1]
//filterScaleFactor = vec2(
// pow(screenPos.x, 2.0),
// pow(screenPos.y, 2.0)
//);
// Make use of the following flag this to toggle betweeen circular and elliptic distribution.
bool useCircleDist = false;
// Apply scaling on bloom filter.
vec2 newFilterSize = vec2(filterSize) * (1.0 + length(filterScaleFactor)) * scaling;
// Calculate params for a rotated Elliptic Gaussian distribution.
float sigmaMajor;
float sigmaMinor;
float a;
float b;
float c;
if (!useCircleDist) {
float alpha = atan(screenPos.y, screenPos.x);
// Apply scaling on sigma.
sigmaMajor = sigma * sigmaScaleFactor.y * scaling;
sigmaMinor = sigma * sigmaScaleFactor.x * scaling;
a = pow(cos(alpha), 2.0) / (2 * pow(sigmaMajor, 2.0))
+ pow(sin(alpha), 2.0) / (2 * pow(sigmaMinor, 2.0)) ;
b = sin(2 * alpha) / (4 * pow(sigmaMajor, 2.0))
- sin(2 * alpha) / (4 * pow(sigmaMinor, 2.0)) ;
c = pow(sin(alpha), 2.0) / (2 * pow(sigmaMajor, 2.0))
+ pow(cos(alpha), 2.0) / (2 * pow(sigmaMinor, 2.0)) ;
}
// Get a [newFilterSize x newFilterSize] filter around our pixel. UV is [0, 1]
vec3 intensity = vec3(0.0);
vec2 pixelSize = 1.0 / screenSize;
ivec2 halfFilterSize = ivec2((newFilterSize - 1.0) / 2.0);
for (int y = -halfFilterSize.y; y <= halfFilterSize.y; y += 1) {
for (int x = -halfFilterSize.x; x <= halfFilterSize.x; x += 1) {
vec2 sPoint = uv + (pixelSize * ivec2(x, y));
// Calculate the contribution of this pixel (elliptic gaussian distribution).
float pixelWeight = exp(-(
a * pow(x * fullAspect, 2.0)
+ 2 * b * x * y * fullAspect
+ c * pow(y, 2.0)
));
// Only sample inside FBO texture and if the pixel will contribute to final color.
if (all(greaterThan(sPoint, vec2(0.0))) && all(lessThan(sPoint, vec2(1.0)))
&& pixelWeight > pixelWeightThreshold) {
vec4 sIntensity = texture( renderedTexture, sPoint );
// Use normal distribution function for halo/bloom effect.
if (useCircleDist) {
float circleDist = sqrt(pow(x / (1 + length(filterScaleFactor)), 2.0)
+ pow(y / (1 + length(filterScaleFactor)), 2.0));
intensity += sIntensity.rgb * (1.0 / (sigma * sqrt(2.0 * M_PI))) *
exp(-(pow(circleDist, 2.0) / (2.0 * pow(sigma, 2.0)))) / filterSize;
}
else {
// Divide contribution by area of ellipse.
intensity += sIntensity.rgb * pixelWeight * fullAspect;
}
}
}
}
// Tonemap intensity to color!
//intensity = 1.0 - 1.0 * exp(-25.0 * intensity);
intensity = pow(intensity, vec3(0.8));
if (length(intensity) < 0.01) {
discard;
}
color = vec4(intensity, 1.0f);
// Use the following to check for any intensity at all.
//color = (length(intensity.rgb) > 0.001) ? vec4(1.0) : vec4(0.0);
Fragment frag;
frag.color = color;
// Place stars at back to begin with.
frag.depth = DEFAULT_DEPTH;
frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0);
frag.blend = BLEND_MODE_NORMAL;
return frag;
}
@@ -24,13 +24,11 @@
#version __CONTEXT__
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec3 in_color;
in vec3 in_position;
out vec3 vs_color;
out vec2 uv;
void main() {
vs_color = in_color;
gl_Position = vec4(in_position, 1.0);
uv = (in_position.xy + 1.0) / 2.0;
gl_Position = vec4(in_position, 1.0);
}
@@ -0,0 +1,120 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#version __CONTEXT__
#include "floatoperations.glsl"
// Keep in sync with gaiaoptions.h:RenderOption enum
const int RENDEROPTION_STATIC = 0;
const int RENDEROPTION_COLOR = 1;
const int RENDEROPTION_MOTION = 2;
const float EPS = 1e-5;
const float Parsec = 3.0856776e16;
in vec3 in_position;
in vec2 in_brightness;
in vec3 in_velocity;
out vec2 vs_brightness;
out vec4 vs_gPosition;
out float vs_starDistFromSun;
out float vs_cameraDistFromSun;
uniform dmat4 model;
uniform dmat4 view;
uniform dmat4 projection;
uniform float time;
uniform int renderOption;
uniform vec2 posXThreshold;
uniform vec2 posYThreshold;
uniform vec2 posZThreshold;
uniform vec2 gMagThreshold;
uniform vec2 bpRpThreshold;
uniform vec2 distThreshold;
void main() {
vs_brightness = in_brightness;
// Check if we should filter this star by position. Thres depending on original values.
if ( (abs(posXThreshold.x) > EPS && in_position.x < posXThreshold.x) ||
(abs(posXThreshold.y) > EPS && in_position.x > posXThreshold.y) ||
(abs(posYThreshold.x) > EPS && in_position.y < posYThreshold.x) ||
(abs(posYThreshold.y) > EPS && in_position.y > posYThreshold.y) ||
(abs(posZThreshold.x) > EPS && in_position.z < posZThreshold.x) ||
(abs(posZThreshold.y) > EPS && in_position.z > posZThreshold.y) ||
(abs(distThreshold.x - distThreshold.y) < EPS
&& abs(length(in_position) - distThreshold.y) < EPS) ||
( renderOption != RENDEROPTION_STATIC && (
(abs(gMagThreshold.x - gMagThreshold.y) < EPS && abs(gMagThreshold.x - in_brightness.x) < EPS) ||
(abs(gMagThreshold.x - 20.0f) > EPS && in_brightness.x < gMagThreshold.x) ||
(abs(gMagThreshold.y - 20.0f) > EPS && in_brightness.x > gMagThreshold.y) ||
(abs(bpRpThreshold.x - bpRpThreshold.y) < EPS && abs(bpRpThreshold.x - in_brightness.y) < EPS) ||
(abs(bpRpThreshold.x) > EPS && in_brightness.y < bpRpThreshold.x) ||
(abs(bpRpThreshold.y) > EPS && in_brightness.y > bpRpThreshold.y))) ) {
// Discard star in geometry shader.
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
// Convert kiloParsec to meter.
vec4 objectPosition = vec4(in_position * 1000 * Parsec, 1.0);
// Add velocity if we've read any.
if ( renderOption == RENDEROPTION_MOTION ) {
// Velocity is already in [m/s].
objectPosition.xyz += time * in_velocity;
}
// Thres moving stars by their new position.
float distPosition = length(objectPosition.xyz / (1000.0 * Parsec) );
if ( (abs(distThreshold.x - distThreshold.y) > EPS &&
((abs(distThreshold.x) > EPS && distPosition< distThreshold.x) ||
(abs(distThreshold.y) > EPS && distPosition > distThreshold.y))) ) {
// Discard star in geometry shader.
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
return;
}
// Apply camera transforms.
dvec4 viewPosition = view * model * objectPosition;
vec4 sunPosition = vec4(view * model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
vs_starDistFromSun = safeLength(objectPosition);
vs_cameraDistFromSun = safeLength(sunPosition);
// Remove stars without position, happens when VBO chunk is stuffed with zeros.
// Has to be done in Geometry shader because Vertices cannot be discarded here.
if ( length(in_position) > EPS ){
vs_gPosition = vec4(model * objectPosition);
gl_Position = vec4(projection * viewPosition);
}
else {
vs_gPosition = vec4(0.0);
gl_Position = vec4(0.0);
}
}
@@ -1,77 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
uniform float maxStepSize#{id} = 0.1;
uniform vec3 aspect#{id} = vec3(1.0);
uniform float opacityCoefficient#{id} = 1.0;
uniform float absorptionMultiply#{id} = 50.0;
uniform float emissionMultiply#{id} = 1500.0;
uniform sampler3D galaxyTexture#{id};
void sample#{id}(
vec3 samplePos,
vec3 dir,
inout vec3 accumulatedColor,
inout vec3 accumulatedAlpha,
inout float stepSize
) {
vec3 aspect = aspect#{id};
stepSize = maxStepSize#{id} / length(dir / aspect);
//Early ray termination on black parts of the data
vec3 normalizedPos = samplePos * 2.f - 1.f;
if (normalizedPos.x * normalizedPos.x + normalizedPos.y * normalizedPos.y > 0.7) {
return;
}
vec4 sampledColor = texture(galaxyTexture#{id}, samplePos.xyz);
// Source textures currently are square-rooted to avoid dithering in the shadows.
// So square them back
sampledColor = sampledColor*sampledColor;
// Fudge for the dust "spreading"
sampledColor.a = clamp(sampledColor.a, 0.f, 1.f);
sampledColor.a = pow(sampledColor.a, 0.7f);
// Absorption probability
float scaledDensity = sampledColor.a * stepSize * absorptionMultiply#{id};
vec3 alphaTint = vec3(0.3f, 0.54f, 0.85f);
vec3 absorption = alphaTint * scaledDensity;
// Extinction
vec3 extinction = exp(-absorption);
accumulatedColor.rgb *= extinction;
// Emission
accumulatedColor.rgb +=
sampledColor.rgb * stepSize * emissionMultiply#{id} * opacityCoefficient#{id};
vec3 oneMinusFrontAlpha = vec3(1.f) - accumulatedAlpha;
accumulatedAlpha += oneMinusFrontAlpha * sampledColor.rgb * opacityCoefficient#{id};
}
float stepSize#{id}(vec3 samplePos, vec3 dir) {
return maxStepSize#{id} * length(dir * 1.f / aspect#{id});
}
@@ -1,48 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 "fragment.glsl"
#include "floatoperations.glsl"
in vec4 vs_position;
in vec3 vs_color;
in float vs_screenSpaceDepth;
in float vs_starBrightness;
uniform float opacityCoefficient;
Fragment getFragment() {
Fragment frag;
float multipliedOpacityCoefficient = opacityCoefficient*opacityCoefficient;
vec3 extinction = exp(vec3(0.6, 0.2, 0.3) - vs_color);
vec4 fullColor = vec4(vs_color*extinction*vs_starBrightness*multipliedOpacityCoefficient, opacityCoefficient);
frag.color = fullColor;
frag.depth = vs_screenSpaceDepth;
frag.gPosition = vs_position;
frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0);
return frag;
}
@@ -1,46 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 "floatoperations.glsl"
#include "fragment.glsl"
in vec3 modelPosition;
in vec4 viewPosition;
Fragment getFragment() {
Fragment frag;
//Early ray termination on black parts of the data
/*vec3 normalizedPos = (modelPosition*2.0)-1.0;
if (abs(modelPosition.x) > 0.9 || abs(modelPosition.y) > 0.9) {
frag.color = vec4(0.0, 0.0, 0.0, 1.0);
}
else {*/
vec3 pos = modelPosition + 0.5;
//vec3 posClamp = clamp(pos, vec3(0.0), vec3(1.0));
frag.color = vec4(pos, 1.0);
//}
frag.depth = safeLength(viewPosition);
return frag;
}
@@ -22,29 +22,51 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/galaxy/galaxymodule.h>
#include <modules/gaia/gaiamodule.h>
#include <modules/galaxy/rendering/renderablegalaxy.h>
#include <modules/galaxy/tasks/milkywayconversiontask.h>
#include <modules/galaxy/tasks/milkywaypointsconversiontask.h>
#include <modules/gaia/tasks/constructoctreetask.h>
#include <modules/gaia/rendering/renderablegaiastars.h>
#include <modules/gaia/tasks/readfitstask.h>
#include <modules/gaia/tasks/readspecktask.h>
#include <openspace/documentation/documentation.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/util/factorymanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/misc/assert.h>
#include <ghoul/misc/templatefactory.h>
namespace openspace {
GalaxyModule::GalaxyModule() : OpenSpaceModule(Name) {}
GaiaModule::GaiaModule() : OpenSpaceModule(Name) {}
void GalaxyModule::internalInitialize(const ghoul::Dictionary&) {
void GaiaModule::internalInitialize(const ghoul::Dictionary&) {
auto fRenderable = FactoryManager::ref().factory<Renderable>();
ghoul_assert(fRenderable, "No renderable factory existed");
fRenderable->registerClass<RenderableGalaxy>("RenderableGalaxy");
fRenderable->registerClass<RenderableGaiaStars>("RenderableGaiaStars");
auto fTask = FactoryManager::ref().factory<Task>();
ghoul_assert(fRenderable, "No task factory existed");
fTask->registerClass<MilkywayConversionTask>("MilkywayConversionTask");
fTask->registerClass<MilkywayPointsConversionTask>("MilkywayPointsConversionTask");
fTask->registerClass<ReadFitsTask>("ReadFitsTask");
fTask->registerClass<ReadSpeckTask>("ReadSpeckTask");
fTask->registerClass<ConstructOctreeTask>("ConstructOctreeTask");
}
std::vector<documentation::Documentation> GaiaModule::documentations() const {
return {
RenderableGaiaStars::Documentation(),
ReadFitsTask::Documentation(),
ReadSpeckTask::Documentation(),
ConstructOctreeTask::Documentation(),
};
}
scripting::LuaLibrary GaiaModule::luaLibrary() const {
scripting::LuaLibrary res;
res.name = "gaia";
res.scripts = {
absPath("${MODULE_GAIA}/scripts/filtering.lua")
};
return res;
}
} // namespace openspace
@@ -22,18 +22,24 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GALAXY___GALAXYMODULE___H__
#define __OPENSPACE_MODULE_GALAXY___GALAXYMODULE___H__
#ifndef __OPENSPACE_MODULE_GAIA___GAIAMODULE___H__
#define __OPENSPACE_MODULE_GAIA___GAIAMODULE___H__
#include <openspace/util/openspacemodule.h>
#include <openspace/documentation/documentation.h>
namespace openspace {
class GalaxyModule : public OpenSpaceModule {
class GaiaModule : public OpenSpaceModule {
public:
constexpr static const char* Name = "Galaxy";
constexpr static const char* Name = "Gaia";
GalaxyModule();
GaiaModule();
virtual ~GaiaModule() = default;
std::vector<documentation::Documentation> documentations() const override;
scripting::LuaLibrary luaLibrary() const override;
private:
void internalInitialize(const ghoul::Dictionary&) override;
@@ -41,4 +47,4 @@ private:
} // namespace openspace
#endif // __OPENSPACE_MODULE_GALAXY___GALAXYMODULE___H__
#endif // __OPENSPACE_MODULE_GAIA___GAIAMODULE___H__
@@ -0,0 +1,816 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/gaia/tasks/constructoctreetask.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/directory.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <fstream>
#include <thread>
namespace {
constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath";
constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath";
constexpr const char* KeyMaxDist = "MaxDist";
constexpr const char* KeyMaxStarsPerNode = "MaxStarsPerNode";
constexpr const char* KeySingleFileInput = "SingleFileInput";
constexpr const char* KeyFilterPosX = "FilterPosX";
constexpr const char* KeyFilterPosY = "FilterPosY";
constexpr const char* KeyFilterPosZ = "FilterPosZ";
constexpr const char* KeyFilterGMag = "FilterGMag";
constexpr const char* KeyFilterBpRp = "FilterBpRp";
constexpr const char* KeyFilterVelX = "FilterVelX";
constexpr const char* KeyFilterVelY = "FilterVelY";
constexpr const char* KeyFilterVelZ = "FilterVelZ";
constexpr const char* KeyFilterBpMag = "FilterBpMag";
constexpr const char* KeyFilterRpMag = "FilterRpMag";
constexpr const char* KeyFilterBpG = "FilterBpG";
constexpr const char* KeyFilterGRp = "FilterGRp";
constexpr const char* KeyFilterRa = "FilterRa";
constexpr const char* KeyFilterRaError = "FilterRaError";
constexpr const char* KeyFilterDec = "FilterDec";
constexpr const char* KeyFilterDecError = "FilterDecError";
constexpr const char* KeyFilterParallax = "FilterParallax";
constexpr const char* KeyFilterParallaxError = "FilterParallaxError";
constexpr const char* KeyFilterPmra = "FilterPmra";
constexpr const char* KeyFilterPmraError = "FilterPmraError";
constexpr const char* KeyFilterPmdec = "FilterPmdec";
constexpr const char* KeyFilterPmdecError = "FilterPmdecError";
constexpr const char* KeyFilterRv = "FilterRv";
constexpr const char* KeyFilterRvError = "FilterRvError";
constexpr const char* _loggerCat = "ConstructOctreeTask";
} // namespace
namespace openspace {
ConstructOctreeTask::ConstructOctreeTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ConstructOctreeTask"
);
_inFileOrFolderPath = absPath(dictionary.value<std::string>(KeyInFileOrFolderPath));
_outFileOrFolderPath = absPath(dictionary.value<std::string>(KeyOutFileOrFolderPath));
if (dictionary.hasKey(KeyMaxDist)) {
_maxDist = static_cast<int>(dictionary.value<double>(KeyMaxDist));
}
if (dictionary.hasKey(KeyMaxStarsPerNode)) {
_maxStarsPerNode = static_cast<int>(dictionary.value<double>(KeyMaxStarsPerNode));
}
if (dictionary.hasKey(KeySingleFileInput)) {
_singleFileInput = dictionary.value<bool>(KeySingleFileInput);
}
_octreeManager = std::make_shared<OctreeManager>();
_indexOctreeManager = std::make_shared<OctreeManager>();
// Check for filter params.
if (dictionary.hasKey(KeyFilterPosX)) {
_posX = dictionary.value<glm::vec2>(KeyFilterPosX);
_filterPosX = true;
}
if (dictionary.hasKey(KeyFilterPosY)) {
_posY = dictionary.value<glm::vec2>(KeyFilterPosY);
_filterPosY = true;
}
if (dictionary.hasKey(KeyFilterPosZ)) {
_posZ = dictionary.value<glm::vec2>(KeyFilterPosZ);
_filterPosZ = true;
}
if (dictionary.hasKey(KeyFilterGMag)) {
_gMag = dictionary.value<glm::vec2>(KeyFilterGMag);
_filterGMag = true;
}
if (dictionary.hasKey(KeyFilterBpRp)) {
_bpRp = dictionary.value<glm::vec2>(KeyFilterBpRp);
_filterBpRp = true;
}
if (dictionary.hasKey(KeyFilterVelX)) {
_velX = dictionary.value<glm::vec2>(KeyFilterVelX);
_filterVelX = true;
}
if (dictionary.hasKey(KeyFilterVelY)) {
_velY = dictionary.value<glm::vec2>(KeyFilterVelY);
_filterVelY = true;
}
if (dictionary.hasKey(KeyFilterVelZ)) {
_velZ = dictionary.value<glm::vec2>(KeyFilterVelZ);
_filterVelZ = true;
}
if (dictionary.hasKey(KeyFilterBpMag)) {
_bpMag = dictionary.value<glm::vec2>(KeyFilterBpMag);
_filterBpMag = true;
}
if (dictionary.hasKey(KeyFilterRpMag)) {
_rpMag = dictionary.value<glm::vec2>(KeyFilterRpMag);
_filterRpMag = true;
}
if (dictionary.hasKey(KeyFilterBpG)) {
_bpG = dictionary.value<glm::vec2>(KeyFilterBpG);
_filterBpG = true;
}
if (dictionary.hasKey(KeyFilterGRp)) {
_gRp = dictionary.value<glm::vec2>(KeyFilterGRp);
_filterGRp = true;
}
if (dictionary.hasKey(KeyFilterRa)) {
_ra = dictionary.value<glm::vec2>(KeyFilterRa);
_filterRa = true;
}
if (dictionary.hasKey(KeyFilterRaError)) {
_raError = dictionary.value<glm::vec2>(KeyFilterRaError);
_filterRaError = true;
}
if (dictionary.hasKey(KeyFilterDec)) {
_dec = dictionary.value<glm::vec2>(KeyFilterDec);
_filterDec = true;
}
if (dictionary.hasKey(KeyFilterDecError)) {
_decError = dictionary.value<glm::vec2>(KeyFilterDecError);
_filterDecError = true;
}
if (dictionary.hasKey(KeyFilterParallax)) {
_parallax = dictionary.value<glm::vec2>(KeyFilterParallax);
_filterParallax = true;
}
if (dictionary.hasKey(KeyFilterParallaxError)) {
_parallaxError = dictionary.value<glm::vec2>(KeyFilterParallaxError);
_filterParallaxError = true;
}
if (dictionary.hasKey(KeyFilterPmra)) {
_pmra = dictionary.value<glm::vec2>(KeyFilterPmra);
_filterPmra = true;
}
if (dictionary.hasKey(KeyFilterPmraError)) {
_pmraError = dictionary.value<glm::vec2>(KeyFilterPmraError);
_filterPmraError = true;
}
if (dictionary.hasKey(KeyFilterPmdec)) {
_pmdec = dictionary.value<glm::vec2>(KeyFilterPmdec);
_filterPmdec = true;
}
if (dictionary.hasKey(KeyFilterPmdecError)) {
_pmdecError = dictionary.value<glm::vec2>(KeyFilterPmdecError);
_filterPmdecError = true;
}
if (dictionary.hasKey(KeyFilterRv)) {
_rv = dictionary.value<glm::vec2>(KeyFilterRv);
_filterRv = true;
}
if (dictionary.hasKey(KeyFilterRvError)) {
_rvError = dictionary.value<glm::vec2>(KeyFilterRvError);
_filterRvError = true;
}
}
std::string ConstructOctreeTask::description() {
return "Read bin file (or files in folder): " + _inFileOrFolderPath + "\n "
"and write octree data file (or files) into: " + _outFileOrFolderPath + "\n";
}
void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) {
onProgress(0.0f);
if (_singleFileInput) {
constructOctreeFromSingleFile(onProgress);
}
else {
constructOctreeFromFolder(onProgress);
}
onProgress(1.0f);
}
void ConstructOctreeTask::constructOctreeFromSingleFile(
const Task::ProgressCallback& progressCallback)
{
std::vector<float> fullData;
int32_t nValues = 0;
int32_t nValuesPerStar = 0;
size_t nFilteredStars = 0;
int nTotalStars = 0;
_octreeManager->initOctree(0, _maxDist, _maxStarsPerNode);
LINFO("Reading data file: " + _inFileOrFolderPath);
LINFO(fmt::format(
"MAX DIST: {} - MAX STARS PER NODE: {}",
_octreeManager->maxDist(), _octreeManager->maxStarsPerNode()
));
// Use to generate a synthetic dataset
/*for (float z = -1.0; z < 1.0; z += 0.05) {
for (float y = -1.0; y < 1.0; y += 0.05) {
for (float x = -1.0; x < 1.0; x += 0.05) {
float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
std::vector<float> renderValues(8);
renderValues[0] = x; // + x * r;
renderValues[2] = z; // + y * r;
renderValues[1] = y; // + z * r;
renderValues[3] = 5.0; // + 10 * r;
renderValues[4] = 2.0; // + 10 * r;
renderValues[5] = r;
renderValues[6] = r;
renderValues[7] = r;
_octreeManager->insert(renderValues);
nTotalStars++;
nValues+=8;
}
}
}
for (float phi = -180.0; phi < 180.0; phi += 10.0) {
for (float theta = -90.0; theta <= 90.0; theta += 10.0) {
float r = 1.0;
std::vector<float> renderValues(8);
renderValues[0] = r * sin(glm::radians(theta)) * cos(glm::radians(phi));
renderValues[2] = r * sin(glm::radians(theta)) * sin(glm::radians(phi));
renderValues[1] = r * cos(glm::radians(theta));
renderValues[3] = 5.0;
renderValues[4] = 2.0;
renderValues[5] = r;
renderValues[6] = r;
renderValues[7] = r;
_octreeManager->insert(renderValues);
nTotalStars++;
nValues += 8;
}
}*/
std::ifstream inFileStream(_inFileOrFolderPath, std::ifstream::binary);
if (inFileStream.good()) {
inFileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
inFileStream.read(reinterpret_cast<char*>(&nValuesPerStar), sizeof(int32_t));
fullData.resize(nValues);
inFileStream.read(
reinterpret_cast<char*>(fullData.data()),
nValues * sizeof(fullData[0])
);
nTotalStars = nValues / nValuesPerStar;
progressCallback(0.3f);
LINFO("Constructing Octree.");
// Insert star into octree. We assume the data already is in correct order.
for (size_t i = 0; i < fullData.size(); i += nValuesPerStar) {
auto first = fullData.begin() + i;
auto last = fullData.begin() + i + nValuesPerStar;
std::vector<float> filterValues(first, last);
std::vector<float> renderValues(first, first + RENDER_VALUES);
// Filter data by parameters.
if (checkAllFilters(filterValues)) {
nFilteredStars++;
continue;
}
// If all filters passed then insert render values into Octree.
_octreeManager->insert(renderValues);
}
inFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading preprocessed file!",
_inFileOrFolderPath
));
}
LINFO(fmt::format("{} of {} read stars were filtered", nFilteredStars, nTotalStars));
// Slice LOD data before writing to files.
_octreeManager->sliceLodData();
LINFO("Writing octree to: " + _outFileOrFolderPath);
std::ofstream outFileStream(_outFileOrFolderPath, std::ofstream::binary);
if (outFileStream.good()) {
if (nValues == 0) {
LERROR("Error writing file - No values were read from file.");
}
_octreeManager->writeToFile(outFileStream, true);
outFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file: {} as output data file.", _outFileOrFolderPath
));
}
}
void ConstructOctreeTask::constructOctreeFromFolder(
const Task::ProgressCallback& progressCallback)
{
int32_t nStars = 0;
int32_t nValuesPerStar = 0;
size_t nFilteredStars = 0;
//float maxRadius = 0.0;
//int starsOutside10 = 0;
//int starsOutside25 = 0;
//int starsOutside50 = 0;
//int starsOutside75 = 0;
//int starsOutside100 = 0;
//int starsOutside200 = 0;
//int starsOutside300 = 0;
//int starsOutside400 = 0;
//int starsOutside500 = 0;
//int starsOutside750 = 0;
//int starsOutside1000 = 0;
//int starsOutside1500 = 0;
//int starsOutside2000 = 0;
//int starsOutside5000 = 0;
ghoul::filesystem::Directory currentDir(_inFileOrFolderPath);
std::vector<std::string> allInputFiles = currentDir.readFiles();
std::vector<float> filterValues;
auto writeThreads = std::vector<std::thread>(8);
_indexOctreeManager->initOctree(0, _maxDist, _maxStarsPerNode);
float processOneFile = 1.f / allInputFiles.size();
LINFO(fmt::format(
"MAX DIST: {} - MAX STARS PER NODE: {}",
_indexOctreeManager->maxDist(), _indexOctreeManager->maxStarsPerNode()
));
for (size_t idx = 0; idx < allInputFiles.size(); ++idx) {
std::string inFilePath = allInputFiles[idx];
int nStarsInfile = 0;
LINFO("Reading data file: " + inFilePath);
std::ifstream inFileStream(inFilePath, std::ifstream::binary);
if (inFileStream.good()) {
inFileStream.read(reinterpret_cast<char*>(&nValuesPerStar), sizeof(int32_t));
filterValues.resize(nValuesPerStar, 0.f);
while (inFileStream.read(
reinterpret_cast<char*>(filterValues.data()),
nValuesPerStar * sizeof(filterValues[0])
))
{
// Filter data by parameters.
if (checkAllFilters(filterValues)) {
nFilteredStars++;
continue;
}
// Generate a 50/12,5 dataset (gMag <=13/>13).
//if ((filterStar(glm::vec2(20.0), filterValues[3], 20.f)) ||
// (filterStar(glm::vec2(0.0), filterValues[16])) ||
// (filterValues[3] > 13.0 && filterValues[17] > 0.125) ||
// (filterValues[3] <= 13.0 && filterValues[17] > 0.5)) {
// nFilteredStars++;
// continue;
//}
// If all filters passed then insert render values into Octree.
std::vector<float> renderValues(
filterValues.begin(),
filterValues.begin() + RENDER_VALUES
);
_indexOctreeManager->insert(renderValues);
nStarsInfile++;
//float maxVal = fmax(fmax(fabs(renderValues[0]), fabs(renderValues[1])),
// fabs(renderValues[2]));
//if (maxVal > maxRadius) maxRadius = maxVal;
//// Calculate how many stars are outside of different thresholds.
//if (maxVal > 10) starsOutside10++;
//if (maxVal > 25) starsOutside25++;
//if (maxVal > 50) starsOutside50++;
//if (maxVal > 75) starsOutside75++;
//if (maxVal > 100) starsOutside100++;
//if (maxVal > 200) starsOutside200++;
//if (maxVal > 300) starsOutside300++;
//if (maxVal > 400) starsOutside400++;
//if (maxVal > 500) starsOutside500++;
//if (maxVal > 750) starsOutside750++;
//if (maxVal > 1000) starsOutside1000++;
//if (maxVal > 1500) starsOutside1500++;
//if (maxVal > 2000) starsOutside2000++;
//if (maxVal > 5000) starsOutside5000++;
}
inFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading preprocessed file!", inFilePath
));
}
// Slice LOD data.
LINFO("Slicing LOD data!");
_indexOctreeManager->sliceLodData(idx);
progressCallback((idx + 1) * processOneFile);
nStars += nStarsInfile;
LINFO(fmt::format("Writing {} stars to octree files!", nStarsInfile));
LINFO(fmt::format(
"Number leaf nodes: {}\n Number inner nodes: {}\n Total depth of tree: {}",
_indexOctreeManager->numLeafNodes(),
_indexOctreeManager->numInnerNodes(),
_indexOctreeManager->totalDepth()
));
// Write to 8 separate files in a separate thread. Data will be cleared after it
// has been written. Store joinable thread for later sync.
std::thread t(
&OctreeManager::writeToMultipleFiles,
_indexOctreeManager,
_outFileOrFolderPath,
idx
);
writeThreads[idx] = std::move(t);
}
LINFO(fmt::format(
"A total of {} stars were read from files and distributed into {} total nodes",
nStars, _indexOctreeManager->totalNodes()
));
LINFO(std::to_string(nFilteredStars) + " stars were filtered");
//LINFO("Max radius of dataset is: " + std::to_string(maxRadius) +
// "\n Number of stars outside of:" +
// " - 10kPc is " + std::to_string(starsOutside10) + "\n" +
// " - 25kPc is " + std::to_string(starsOutside25) + "\n" +
// " - 50kPc is " + std::to_string(starsOutside50) + "\n" +
// " - 75kPc is " + std::to_string(starsOutside75) + "\n" +
// " - 100kPc is " + std::to_string(starsOutside100) + "\n" +
// " - 200kPc is " + std::to_string(starsOutside200) + "\n" +
// " - 300kPc is " + std::to_string(starsOutside300) + "\n" +
// " - 400kPc is " + std::to_string(starsOutside400) + "\n" +
// " - 500kPc is " + std::to_string(starsOutside500) + "\n" +
// " - 750kPc is " + std::to_string(starsOutside750) + "\n" +
// " - 1000kPc is " + std::to_string(starsOutside1000) + "\n" +
// " - 1500kPc is " + std::to_string(starsOutside1500) + "\n" +
// " - 2000kPc is " + std::to_string(starsOutside2000) + "\n" +
// " - 5000kPc is " + std::to_string(starsOutside5000));
// Write index file of Octree structure.
std::string indexFileOutPath = _outFileOrFolderPath + "index.bin";
std::ofstream outFileStream(indexFileOutPath, std::ofstream::binary);
if (outFileStream.good()) {
LINFO("Writing index file!");
_indexOctreeManager->writeToFile(outFileStream, false);
outFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file: {} as index output file.", indexFileOutPath
));
}
// Make sure all threads are done.
for (int i = 0; i < 8; ++i) {
writeThreads[i].join();
}
}
bool ConstructOctreeTask::checkAllFilters(const std::vector<float>& filterValues) {
// Return true if star is caught in any filter.
return (_filterPosX && filterStar(_posX, filterValues[0])) ||
(_filterPosY && filterStar(_posY, filterValues[1])) ||
(_filterPosZ && filterStar(_posZ, filterValues[2])) ||
(_filterGMag && filterStar(_gMag, filterValues[3], 20.f)) ||
(_filterBpRp && filterStar(_bpRp, filterValues[4])) ||
(_filterVelX && filterStar(_velX, filterValues[5])) ||
(_filterVelY && filterStar(_velY, filterValues[6])) ||
(_filterVelZ && filterStar(_velZ, filterValues[7])) ||
(_filterBpMag && filterStar(_bpMag, filterValues[8], 20.f)) ||
(_filterRpMag && filterStar(_rpMag, filterValues[9], 20.f)) ||
(_filterBpG && filterStar(_bpG, filterValues[10])) ||
(_filterGRp && filterStar(_gRp, filterValues[11])) ||
(_filterRa && filterStar(_ra, filterValues[12])) ||
(_filterRaError && filterStar(_raError, filterValues[13])) ||
(_filterDec && filterStar(_dec, filterValues[14])) ||
(_filterDecError && filterStar(_decError, filterValues[15])) ||
(_filterParallax && filterStar(_parallax, filterValues[16])) ||
(_filterParallaxError && filterStar(_parallaxError, filterValues[17])) ||
(_filterPmra && filterStar(_pmra, filterValues[18])) ||
(_filterPmraError && filterStar(_pmraError, filterValues[19])) ||
(_filterPmdec && filterStar(_pmdec, filterValues[20])) ||
(_filterPmdecError && filterStar(_pmdecError, filterValues[21])) ||
(_filterRv && filterStar(_rv, filterValues[22])) ||
(_filterRvError && filterStar(_rvError, filterValues[23]));
}
bool ConstructOctreeTask::filterStar(const glm::vec2& range, float filterValue,
float normValue)
{
// Return true if star should be filtered away, i.e. if min = max = filterValue or
// if filterValue < min (when min != 0.0) or filterValue > max (when max != 0.0).
return (fabs(range.x - range.y) < FLT_EPSILON &&
fabs(range.x - filterValue) < FLT_EPSILON) ||
(fabs(range.x - normValue) > FLT_EPSILON && filterValue < range.x) ||
(fabs(range.y - normValue) > FLT_EPSILON && filterValue > range.y);
}
documentation::Documentation ConstructOctreeTask::Documentation() {
using namespace documentation;
return {
"ConstructOctreeTask",
"gaiamission_constructoctreefrombin",
{
{
"Type",
new StringEqualVerifier("ConstructOctreeTask"),
Optional::No
},
{
KeyInFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileInput is set to true then this specifies the path to a "
"single BIN file containing a full dataset. Otherwise this specifies the "
"path to a folder with multiple BIN files containing subsets of sorted "
"star data.",
},
{
KeyOutFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileInput is set to true then this specifies the output file "
"name (including full path). Otherwise this specifies the path to the "
"folder which to save all files.",
},
{
KeyMaxDist,
new IntVerifier,
Optional::Yes,
"If set it determines what MAX_DIST to use when creating Octree."
},
{
KeyMaxStarsPerNode,
new IntVerifier,
Optional::Yes,
"If set it determines what MAX_STAR_PER_NODE to use when creating Octree."
},
{
KeySingleFileInput,
new BoolVerifier,
Optional::Yes,
"If true then task will read from a single file and output a single "
"binary file with the full Octree. If false then task will read all "
"files in specified folder and output multiple files for the Octree."
},
{
KeyFilterPosX,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position X values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterPosY,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position Y values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterPosZ,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Position Z values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterGMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with G mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default "
"GMag = 20.0 if no value existed."
},
{
KeyFilterBpRp,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp-Rp color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelX,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity X values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelY,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity Y values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterVelZ,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Velocity Z values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterBpMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default "
"BpMag = 20.0 if no value existed."
},
{
KeyFilterRpMag,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Rp mean magnitude values between "
"[min, max] will be inserted into Octree (if min is set to 20.0 it is "
"read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away. Default RpMag = "
"20.0 if no value existed."
},
{
KeyFilterBpG,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Bp-G color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterGRp,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with G-Rp color values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterRa,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with RA values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterRaError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with RA Error values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterDec,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with DEC values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterDecError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with DEC Error values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterParallax,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Parallax values between [min, max] "
"will be inserted into Octree (if min is set to 0.0 it is read as -Inf, "
"if max is set to 0.0 it is read as +Inf). If min = max then all values "
"equal min|max will be filtered away."
},
{
KeyFilterParallaxError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Parallax Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmra,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion RA values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmraError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion RA Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmdec,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion DEC values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterPmdecError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Proper Motion DEC Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterRv,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Radial Velocity values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
{
KeyFilterRvError,
new Vector2Verifier<double>,
Optional::Yes,
"If defined then only stars with Radial Velocity Error values between "
"[min, max] will be inserted into Octree (if min is set to 0.0 it is "
"read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max "
"then all values equal min|max will be filtered away."
},
}
};
}
} // namespace openspace
@@ -0,0 +1,143 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GAIA___CONSTRUCTOCTREETASK___H__
#define __OPENSPACE_MODULE_GAIA___CONSTRUCTOCTREETASK___H__
#include <openspace/util/task.h>
#include <modules/gaia/rendering/octreeculler.h>
#include <modules/gaia/rendering/octreemanager.h>
namespace openspace {
namespace documentation { struct Documentation; }
class ConstructOctreeTask : public Task {
public:
ConstructOctreeTask(const ghoul::Dictionary& dictionary);
virtual ~ConstructOctreeTask() = default;
std::string description() override;
void perform(const Task::ProgressCallback& onProgress) override;
static documentation::Documentation Documentation();
private:
const int RENDER_VALUES = 8;
/**
* Reads a single binary file with preprocessed star data and insert the render values
* into an octree structure (if star data passed all defined filters).
* Stores the entire octree in one binary file.
*/
void constructOctreeFromSingleFile(const Task::ProgressCallback& progressCallback);
/**
* Reads binary star data from 8 preprocessed files (one per branch) in specified
* folder, prepared by ReadFitsTask, and inserts star render data into an octree
* (if star data passed all defined filters).
* Stores octree structure in a binary index file and stores all render data
* separate files, one file per node in the octree.
*/
void constructOctreeFromFolder(const Task::ProgressCallback& progressCallback);
/**
* Checks all defined filter ranges and \returns true if any of the corresponding
* <code>filterValues</code> are outside of the defined range.
* \returns false if value should be inserted into Octree.
* \param filterValues are all read filter values in binary file.
*/
bool checkAllFilters(const std::vector<float>& filterValues);
/**
* \returns true if star should be filtered away and false if all filters passed.
* \param range contains ]min, max[ and \param filterValue corresponding value in
* star. Star is filtered either if min = max = filterValue or if filterValue < min
* (when min != 0.0) or filterValue > max (when max != 0.0).
*/
bool filterStar(const glm::vec2& range, float filterValue, float normValue = 0.f);
std::string _inFileOrFolderPath;
std::string _outFileOrFolderPath;
int _maxDist = 0;
int _maxStarsPerNode = 0;
bool _singleFileInput = false;
std::shared_ptr<OctreeManager> _octreeManager;
std::shared_ptr<OctreeManager> _indexOctreeManager;
// Filter params
glm::vec2 _posX = glm::vec2(0.f);
bool _filterPosX = false;
glm::vec2 _posY = glm::vec2(0.f);
bool _filterPosY = false;
glm::vec2 _posZ = glm::vec2(0.f);
bool _filterPosZ = false;
glm::vec2 _gMag = glm::vec2(0.f);
bool _filterGMag = false;
glm::vec2 _bpRp = glm::vec2(0.f);
bool _filterBpRp = false;
glm::vec2 _velX = glm::vec2(0.f);
bool _filterVelX = false;
glm::vec2 _velY = glm::vec2(0.f);
bool _filterVelY = false;
glm::vec2 _velZ = glm::vec2(0.f);
bool _filterVelZ = false;
glm::vec2 _bpMag = glm::vec2(0.f);
bool _filterBpMag = false;
glm::vec2 _rpMag = glm::vec2(0.f);
bool _filterRpMag = false;
glm::vec2 _bpG = glm::vec2(0.f);
bool _filterBpG = false;
glm::vec2 _gRp = glm::vec2(0.f);
bool _filterGRp = false;
glm::vec2 _ra = glm::vec2(0.f);
bool _filterRa = false;
glm::vec2 _raError = glm::vec2(0.f);
bool _filterRaError = false;
glm::vec2 _dec = glm::vec2(0.f);
bool _filterDec = false;
glm::vec2 _decError = glm::vec2(0.f);
bool _filterDecError = false;
glm::vec2 _parallax = glm::vec2(0.f);
bool _filterParallax = false;
glm::vec2 _parallaxError = glm::vec2(0.f);
bool _filterParallaxError = false;
glm::vec2 _pmra = glm::vec2(0.f);
bool _filterPmra = false;
glm::vec2 _pmraError = glm::vec2(0.f);
bool _filterPmraError = false;
glm::vec2 _pmdec = glm::vec2(0.f);
bool _filterPmdec = false;
glm::vec2 _pmdecError = glm::vec2(0.f);
bool _filterPmdecError = false;
glm::vec2 _rv = glm::vec2(0.f);
bool _filterRv = false;
glm::vec2 _rvError = glm::vec2(0.f);
bool _filterRvError = false;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___CONSTRUCTOCTREETASK___H__
@@ -1,102 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/galaxy/tasks/milkywayconversiontask.h>
#include <modules/volume/textureslicevolumereader.h>
#include <modules/volume/rawvolumewriter.h>
#include <modules/volume/volumesampler.h>
#include <modules/volume/textureslicevolumereader.h>
#include <modules/volume/rawvolumewriter.h>
#include <openspace/documentation/documentation.h>
#include <ghoul/misc/dictionary.h>
namespace {
constexpr const char* KeyInFilenamePrefix = "InFilenamePrefix";
constexpr const char* KeyInFilenameSuffix = "InFilenameSuffix";
constexpr const char* KeyInFirstIndex = "InFirstIndex";
constexpr const char* KeyInNSlices = "InNSlices";
constexpr const char* KeyOutFilename = "OutFilename";
constexpr const char* KeyOutDimensions = "OutDimensions";
} // namespace
namespace openspace {
MilkywayConversionTask::MilkywayConversionTask(const ghoul::Dictionary& dictionary)
: _inFirstIndex(0)
, _inNSlices(0)
{
dictionary.getValue(KeyInFilenamePrefix, _inFilenamePrefix);
dictionary.getValue(KeyInFilenameSuffix, _inFilenameSuffix);
dictionary.getValue(KeyInFirstIndex, _inFirstIndex);
dictionary.getValue(KeyInNSlices, _inNSlices);
dictionary.getValue(KeyOutFilename, _outFilename);
dictionary.getValue(KeyOutDimensions, _outDimensions);
}
std::string MilkywayConversionTask::description() {
return std::string();
}
void MilkywayConversionTask::perform(const Task::ProgressCallback& onProgress) {
using namespace openspace::volume;
std::vector<std::string> filenames;
for (size_t i = 0; i < _inNSlices; i++) {
filenames.push_back(
_inFilenamePrefix + std::to_string(i + _inFirstIndex) + _inFilenameSuffix
);
}
TextureSliceVolumeReader<glm::tvec4<GLfloat>> sliceReader(filenames, _inNSlices, 10);
sliceReader.initialize();
RawVolumeWriter<glm::tvec4<GLfloat>> rawWriter(_outFilename);
rawWriter.setDimensions(_outDimensions);
const glm::vec3 resolutionRatio = static_cast<glm::vec3>(sliceReader.dimensions()) /
static_cast<glm::vec3>(rawWriter.dimensions());
VolumeSampler<TextureSliceVolumeReader<glm::tvec4<GLfloat>>> sampler(
&sliceReader,
resolutionRatio
);
std::function<glm::tvec4<GLfloat>(glm::ivec3)> sampleFunction =
[&](glm::ivec3 outCoord) {
const glm::vec3 inCoord = ((glm::vec3(outCoord) + glm::vec3(0.5f)) *
resolutionRatio) - glm::vec3(0.5f);
const glm::tvec4<GLfloat> value = sampler.sample(inCoord);
return value;
};
rawWriter.write(sampleFunction, onProgress);
}
documentation::Documentation MilkywayConversionTask::documentation() {
return documentation::Documentation();
}
} // namespace openspace
@@ -0,0 +1,264 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/gaia/tasks/readfilejob.h>
#include <openspace/util/distanceconversion.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/fmt.h>
namespace {
constexpr const char* _loggerCat = "ReadFileJob";
}
namespace openspace::gaia {
ReadFileJob::ReadFileJob(std::string filePath, std::vector<std::string> allColumns,
int firstRow, int lastRow, size_t nDefaultCols,
int nValuesPerStar, std::shared_ptr<FitsFileReader> fitsReader)
: _inFilePath(std::move(filePath))
, _allColumns(std::move(allColumns))
, _firstRow(firstRow)
, _lastRow(lastRow)
, _nDefaultCols(nDefaultCols)
, _nValuesPerStar(nValuesPerStar)
, _fitsFileReader(std::move(fitsReader))
, _octants(8)
{}
void ReadFileJob::execute() {
// Read columns from FITS file. If rows aren't specified then full table will be read.
std::shared_ptr<TableData<float>> table = _fitsFileReader->readTable<float>(
_inFilePath,
_allColumns,
_firstRow,
_lastRow
);
if (!table) {
throw ghoul::RuntimeError(
fmt::format("Failed to open Fits file '{}'", _inFilePath
));
}
int nStars = table->readRows - _firstRow + 1;
int nNullArr = 0;
size_t nColumnsRead = _allColumns.size();
if (nColumnsRead != _nDefaultCols) {
LINFO("Additional columns will be read! Consider add column in code for "
"significant speedup!");
}
// Copy columns to local variables.
std::unordered_map<std::string, std::vector<float>>& tableContent = table->contents;
// Default columns parameters.
//std::vector<float> l_longitude = std::move(tableContent[_allColumns[0]]);
//std::vector<float> b_latitude = std::move(tableContent[_allColumns[1]]);
std::vector<float> ra = std::move(tableContent[_allColumns[0]]);
std::vector<float> ra_err = std::move(tableContent[_allColumns[1]]);
std::vector<float> dec = std::move(tableContent[_allColumns[2]]);
std::vector<float> dec_err = std::move(tableContent[_allColumns[3]]);
std::vector<float> parallax = std::move(tableContent[_allColumns[4]]);
std::vector<float> parallax_err = std::move(tableContent[_allColumns[5]]);
std::vector<float> pmra = std::move(tableContent[_allColumns[6]]);
std::vector<float> pmra_err = std::move(tableContent[_allColumns[7]]);
std::vector<float> pmdec = std::move(tableContent[_allColumns[8]]);
std::vector<float> pmdec_err = std::move(tableContent[_allColumns[9]]);
std::vector<float> meanMagG = std::move(tableContent[_allColumns[10]]);
std::vector<float> meanMagBp = std::move(tableContent[_allColumns[11]]);
std::vector<float> meanMagRp = std::move(tableContent[_allColumns[12]]);
std::vector<float> bp_rp = std::move(tableContent[_allColumns[13]]);
std::vector<float> bp_g = std::move(tableContent[_allColumns[14]]);
std::vector<float> g_rp = std::move(tableContent[_allColumns[15]]);
std::vector<float> radial_vel = std::move(tableContent[_allColumns[16]]);
std::vector<float> radial_vel_err = std::move(tableContent[_allColumns[17]]);
// Construct data array. OBS: ORDERING IS IMPORTANT! This is where slicing happens.
for (int i = 0; i < nStars; ++i) {
std::vector<float> values(_nValuesPerStar);
size_t idx = 0;
// Default order for rendering:
// Position [X, Y, Z]
// Mean G-band Magnitude
// -- Mean Bp-band Magnitude
// -- Mean Rp-band Magnitude
// Bp-Rp Color
// -- Bp-G Color
// -- G-Rp Color
// Velocity [X, Y, Z]
// Return early if star doesn't have a measured position.
if (std::isnan(ra[i]) || std::isnan(dec[i])) {
nNullArr++;
continue;
}
// Store positions. Set to a default distance if parallax doesn't exist.
float radiusInKiloParsec = 9.0;
if (!std::isnan(parallax[i])) {
// Parallax is in milliArcseconds -> distance in kiloParsecs
// https://gea.esac.esa.int/archive/documentation/GDR2/Gaia_archive/
// chap_datamodel/sec_dm_main_tables/ssec_dm_gaia_source.html
//LINFO("Parallax: " + std::to_string(parallax[i]));
radiusInKiloParsec = 1.f / parallax[i];
}
/*// Convert to Galactic Coordinates from Galactic Lon & Lat.
// https://gea.esac.esa.int/archive/documentation/GDR2/Data_processing/
// chap_cu3ast/sec_cu3ast_intro/ssec_cu3ast_intro_tansforms.html#SSS1
values[idx++] = radiusInKiloParsec * cos(glm::radians(b_latitude[i])) *
cos(glm::radians(l_longitude[i])); // Pos X
values[idx++] = radiusInKiloParsec * cos(glm::radians(b_latitude[i])) *
sin(glm::radians(l_longitude[i])); // Pos Y
values[idx++] = radiusInKiloParsec * sin(glm::radians(b_latitude[i])); // Pos Z
*/
// Convert ICRS Equatorial Ra and Dec to Galactic latitude and longitude.
glm::mat3 aPrimG = glm::mat3(
// Col 0
glm::vec3(-0.0548755604162154, 0.4941094278755837, -0.8676661490190047),
// Col 1
glm::vec3(-0.8734370902348850, -0.4448296299600112, -0.1980763734312015),
// Col 2
glm::vec3(-0.4838350155487132, 0.7469822444972189, 0.4559837761750669)
);
glm::vec3 rICRS = glm::vec3(
cos(glm::radians(ra[i])) * cos(glm::radians(dec[i])),
sin(glm::radians(ra[i])) * cos(glm::radians(dec[i])),
sin(glm::radians(dec[i]))
);
glm::vec3 rGal = aPrimG * rICRS;
values[idx++] = radiusInKiloParsec * rGal.x; // Pos X
values[idx++] = radiusInKiloParsec * rGal.y; // Pos Y
values[idx++] = radiusInKiloParsec * rGal.z; // Pos Z
/*if (abs(rGal.x - values[0]) > 1e-5 || abs(rGal.y - values[1]) > 1e-5 ||
abs(rGal.z - values[2]) > 1e-5) {
LINFO("rGal: " + std::to_string(rGal) +
" - LB: [" + std::to_string(values[0]) + ", " + std::to_string(values[1]) +
", " + std::to_string(values[2]) + "]");
}*/
// Store magnitude render value. (Set default to high mag = low brightness)
values[idx++] = std::isnan(meanMagG[i]) ? 20.f : meanMagG[i]; // Mean G-band Mag
// Store color render value. (Default value is bluish stars)
values[idx++] = std::isnan(bp_rp[i]) ? 0.f : bp_rp[i]; // Bp-Rp Color
// Store velocity.
if (std::isnan(pmra[i])) {
pmra[i] = 0.f;
}
if (std::isnan(pmdec[i])) {
pmdec[i] = 0.f;
}
// Convert Proper Motion from ICRS [Ra,Dec] to Galactic Tanget Vector [l,b].
glm::vec3 uICRS = glm::vec3(
-sin(glm::radians(ra[i])) * pmra[i] -
cos(glm::radians(ra[i])) * sin(glm::radians(dec[i])) * pmdec[i],
cos(glm::radians(ra[i])) * pmra[i] -
sin(glm::radians(ra[i])) * sin(glm::radians(dec[i])) * pmdec[i],
cos(glm::radians(dec[i])) * pmdec[i]
);
glm::vec3 pmVecGal = aPrimG * uICRS;
// Convert to Tangential vector [m/s] from Proper Motion vector [mas/yr]
float tanVelX = 1000.f * 4.74f * radiusInKiloParsec * pmVecGal.x;
float tanVelY = 1000.f * 4.74f * radiusInKiloParsec * pmVecGal.y;
float tanVelZ = 1000.f * 4.74f * radiusInKiloParsec * pmVecGal.z;
// Calculate True Space Velocity [m/s] if we have the radial velocity
if (!std::isnan(radial_vel[i])) {
// Calculate Radial Velocity in the direction of the star.
// radial_vel is given in [km/s] -> convert to [m/s].
float radVelX = 1000.f * radial_vel[i] * rGal.x;
float radVelY = 1000.f * radial_vel[i] * rGal.y;
float radVelZ = 1000.f * radial_vel[i] * rGal.z;
// Use Pythagoras theorem for the final Space Velocity [m/s].
values[idx++] = sqrt(pow(radVelX, 2) + pow(tanVelX, 2)); // Vel X [U]
values[idx++] = sqrt(pow(radVelY, 2) + pow(tanVelY, 2)); // Vel Y [V]
values[idx++] = sqrt(pow(radVelZ, 2) + pow(tanVelZ, 2)); // Vel Z [W]
}
// Otherwise use the vector [m/s] we got from proper motion.
else {
radial_vel[i] = 0.f;
values[idx++] = tanVelX; // Vel X [U]
values[idx++] = tanVelY; // Vel Y [V]
values[idx++] = tanVelZ; // Vel Z [W]
}
// Store additional parameters to filter by.
values[idx++] = std::isnan(meanMagBp[i]) ? 20.f : meanMagBp[i];
values[idx++] = std::isnan(meanMagRp[i]) ? 20.f : meanMagRp[i];
values[idx++] = std::isnan(bp_g[i]) ? 0.f : bp_g[i];
values[idx++] = std::isnan(g_rp[i]) ? 0.f : g_rp[i];
values[idx++] = ra[i];
values[idx++] = std::isnan(ra_err[i]) ? 0.f : ra_err[i];
values[idx++] = dec[i];
values[idx++] = std::isnan(dec_err[i]) ? 0.f : dec_err[i];
values[idx++] = std::isnan(parallax[i]) ? 0.f : parallax[i];
values[idx++] = std::isnan(parallax_err[i]) ? 0.f : parallax_err[i];
values[idx++] = pmra[i];
values[idx++] = std::isnan(pmra_err[i]) ? 0.f : pmra_err[i];
values[idx++] = pmdec[i];
values[idx++] = std::isnan(pmdec_err[i]) ? 0.f : pmdec_err[i];
values[idx++] = radial_vel[i];
values[idx++] = std::isnan(radial_vel_err[i]) ? 0.f : radial_vel_err[i];
// Read extra columns, if any. This will slow down the sorting tremendously!
for (size_t col = _nDefaultCols; col < nColumnsRead; ++col) {
std::vector<float> vecData = std::move(tableContent[_allColumns[col]]);
values[idx++] = std::isnan(vecData[col]) ? 0.f : vecData[col];
}
size_t index = 0;
if (values[0] < 0.0) {
index += 1;
}
if (values[1] < 0.0) {
index += 2;
}
if (values[2] < 0.0) {
index += 4;
}
_octants[index].insert(_octants[index].end(), values.begin(), values.end());
}
/*LINFO(std::to_string(nNullArr) + " out of " +
std::to_string(nStars) + " read stars were nullArrays.");*/
}
std::vector<std::vector<float>> ReadFileJob::product() {
return _octants;
}
} // namespace openspace::gaiamission
@@ -0,0 +1,71 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GAIA___READFILEJOB___H__
#define __OPENSPACE_MODULE_GAIA___READFILEJOB___H__
#include <openspace/util/concurrentjobmanager.h>
#include <modules/fitsfilereader/include/fitsfilereader.h>
namespace openspace::gaia {
struct ReadFileJob : public Job<std::vector<std::vector<float>>> {
/**
* Constructs a Job that will read a single FITS file in a concurrent thread and
* divide the star data into 8 octants depending on position.
* \param allColumns define which columns that will be read, it should correspond
* to the pre-defined order in the job. If additional columns are defined they will
* be read but slow down the process.
* Proper conversions of positions and velocities will take place and all values
* will be checked for NaNs.
* If \param firstRow is < 1 then reading will begin at first row in table.
* If \param lastRow < firstRow then entire table will be read.
* \param nValuesPerStar defines how many values that will be stored per star.
*/
ReadFileJob(std::string filePath, std::vector<std::string> allColumns, int firstRow,
int lastRow, size_t nDefaultCols, int nValuesPerStar,
std::shared_ptr<FitsFileReader> fitsReader);
~ReadFileJob() = default;
void execute() override;
std::vector<std::vector<float>> product() override;
private:
std::string _inFilePath;
int _firstRow;
int _lastRow;
size_t _nDefaultCols;
int _nValuesPerStar;
std::vector<std::string> _allColumns;
std::shared_ptr<FitsFileReader> _fitsFileReader;
std::vector<std::vector<float>> _octants;
};
} // namespace openspace::gaiamission
#endif // __OPENSPACE_MODULE_GAIA___READFILEJOB___H__
@@ -0,0 +1,392 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/gaia/tasks/readfitstask.h>
#include <modules/gaia/tasks/readfilejob.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/directory.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/fmt.h>
#include <fstream>
#include <set>
namespace {
constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath";
constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath";
constexpr const char* KeySingleFileProcess = "SingleFileProcess";
constexpr const char* KeyThreadsToUse = "ThreadsToUse";
constexpr const char* KeyFirstRow = "FirstRow";
constexpr const char* KeyLastRow = "LastRow";
constexpr const char* KeyFilterColumnNames = "FilterColumnNames";
constexpr const char* _loggerCat = "ReadFitsTask";
} // namespace
namespace openspace {
ReadFitsTask::ReadFitsTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ReadFitsTask"
);
_inFileOrFolderPath = absPath(dictionary.value<std::string>(KeyInFileOrFolderPath));
_outFileOrFolderPath = absPath(dictionary.value<std::string>(KeyOutFileOrFolderPath));
if (dictionary.hasKey(KeySingleFileProcess)) {
_singleFileProcess = dictionary.value<bool>(KeySingleFileProcess);
}
if (dictionary.hasKey(KeyThreadsToUse)) {
_threadsToUse = static_cast<size_t>(dictionary.value<double>(KeyThreadsToUse));
if (_threadsToUse < 1) {
LINFO(fmt::format(
"User defined ThreadsToUse was: {}. Will be set to 1", _threadsToUse
));
_threadsToUse = 1;
}
}
if (dictionary.hasKey(KeyFirstRow)) {
_firstRow = static_cast<int>(dictionary.value<double>(KeyFirstRow));
}
if (dictionary.hasKey(KeyLastRow)) {
_lastRow = static_cast<int>(dictionary.value<double>(KeyLastRow));
}
if (dictionary.hasKey(KeyFilterColumnNames)) {
ghoul::Dictionary d = dictionary.value<ghoul::Dictionary>(KeyFilterColumnNames);
// Ugly fix for ASCII sorting when there are more columns read than 10.
std::set<int> intKeys;
for (const std::string& key : d.keys()) {
intKeys.insert(std::stoi(key));
}
for (int key : intKeys) {
_filterColumnNames.push_back(d.value<std::string>(std::to_string(key)));
}
}
}
std::string ReadFitsTask::description() {
return fmt::format(
"Read the specified fits file (or all fits files in specified folder): {}\n and "
"write raw star data into: {}\nAll columns required for default rendering and "
"filtering parameters will always be read but user can define additional filter "
"columns to read.", _inFileOrFolderPath, _outFileOrFolderPath
);
}
void ReadFitsTask::perform(const Task::ProgressCallback& onProgress) {
onProgress(0.f);
if (_singleFileProcess) {
readSingleFitsFile(onProgress);
}
else {
readAllFitsFilesFromFolder(onProgress);
}
onProgress(1.f);
}
void ReadFitsTask::readSingleFitsFile(const Task::ProgressCallback& progressCallback) {
int32_t nValuesPerStar = 0;
FitsFileReader fileReader(false);
std::vector<float> fullData = fileReader.readFitsFile(
_inFileOrFolderPath,
nValuesPerStar,
_firstRow,
_lastRow,
_filterColumnNames
);
progressCallback(0.8f);
std::ofstream outFileStream(_outFileOrFolderPath, std::ofstream::binary);
if (outFileStream.good()) {
int32_t nValues = static_cast<int32_t>(fullData.size());
LINFO(fmt::format("Writing {} values to file {}", nValues, _outFileOrFolderPath));
LINFO("Number of values per star: " + std::to_string(nValuesPerStar));
if (nValues == 0) {
LERROR("Error writing file - No values were read from file.");
}
outFileStream.write(
reinterpret_cast<const char*>(&nValues),
sizeof(int32_t)
);
outFileStream.write(
reinterpret_cast<const char*>(&nValuesPerStar),
sizeof(int32_t)
);
size_t nBytes = nValues * sizeof(fullData[0]);
outFileStream.write(reinterpret_cast<const char*>(fullData.data()), nBytes);
outFileStream.close();
}
else {
LERROR(fmt::format(
"Error opening file: {} as output data file.", _outFileOrFolderPath
));
}
}
void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) {
std::vector<std::vector<float>> octants(8);
std::vector<bool> isFirstWrite(8, true);
size_t finishedJobs = 0;
int totalStars = 0;
_firstRow = std::max(_firstRow, 1);
// Create Threadpool and JobManager.
LINFO("Threads in pool: " + std::to_string(_threadsToUse));
ThreadPool threadPool(_threadsToUse);
ConcurrentJobManager<std::vector<std::vector<float>>> jobManager(threadPool);
// Get all files in specified folder.
ghoul::filesystem::Directory currentDir(_inFileOrFolderPath);
std::vector<std::string> allInputFiles = currentDir.readFiles();
size_t nInputFiles = allInputFiles.size();
LINFO("Files to read: " + std::to_string(nInputFiles));
// Define what columns to read.
_allColumnNames.clear();
// Read in the order of table in file.
std::vector<std::string> defaultColumnNames = {
"ra",
"ra_error",
"dec",
"dec_error",
"parallax",
"parallax_error",
"pmra",
"pmra_error",
"pmdec",
"pmdec_error",
"phot_g_mean_mag",
"phot_bp_mean_mag",
"phot_rp_mean_mag",
"bp_rp",
"bp_g",
"g_rp",
"radial_velocity",
"radial_velocity_error",
};
_allColumnNames.insert(
_allColumnNames.end(),
defaultColumnNames.begin(),
defaultColumnNames.end()
);
// Append additional filter parameters to default rendering parameters.
_allColumnNames.insert(
_allColumnNames.end(),
_filterColumnNames.begin(),
_filterColumnNames.end()
);
std::string allNames = "Columns to read: \n";
for (const std::string& colName : _allColumnNames) {
allNames += colName + "\n";
}
LINFO(allNames);
// Declare how many values to save for each star.
int32_t nValuesPerStar = 24;
size_t nDefaultColumns = defaultColumnNames.size();
auto fitsFileReader = std::make_shared<FitsFileReader>(false);
// Divide all files into ReadFilejobs and then delegate them onto several threads!
while (!allInputFiles.empty()) {
std::string fileToRead = allInputFiles.back();
allInputFiles.erase(allInputFiles.end() - 1);
// Add reading of file to jobmanager, which will distribute it to our threadpool.
auto readFileJob = std::make_shared<gaia::ReadFileJob>(
fileToRead,
_allColumnNames,
_firstRow,
_lastRow,
nDefaultColumns,
nValuesPerStar,
fitsFileReader
);
jobManager.enqueueJob(readFileJob);
}
LINFO("All files added to queue!");
// Check for finished jobs.
while (finishedJobs < nInputFiles) {
if (jobManager.numFinishedJobs() > 0) {
std::vector<std::vector<float>> newOctant =
jobManager.popFinishedJob()->product();
finishedJobs++;
for (int i = 0; i < 8; ++i) {
// Add read values to global octant and check if it's time to write!
octants[i].insert(
octants[i].end(),
newOctant[i].begin(),
newOctant[i].end()
);
if ((octants[i].size() > MAX_SIZE_BEFORE_WRITE) ||
(finishedJobs == nInputFiles))
{
// Write to file!
totalStars += writeOctantToFile(
octants[i],
i,
isFirstWrite,
nValuesPerStar
);
octants[i].clear();
octants[i].shrink_to_fit();
}
}
}
}
LINFO(fmt::format("A total of {} stars were written to binary files.", totalStars));
}
int ReadFitsTask::writeOctantToFile(const std::vector<float>& octantData, int index,
std::vector<bool>& isFirstWrite, int nValuesPerStar)
{
std::string outPath = fmt::format("{}octant_{}.bin", _outFileOrFolderPath, index);
std::ofstream fileStream(outPath, std::ofstream::binary | std::ofstream::app);
if (fileStream.good()) {
int32_t nValues = static_cast<int32_t>(octantData.size());
LINFO("Write " + std::to_string(nValues) + " values to " + outPath);
if (nValues == 0) {
LERROR("Error writing file - No values were read from file.");
}
// If this is the first write then write number of values per star!
if (isFirstWrite[index]) {
LINFO("First write for Octant_" + std::to_string(index));
fileStream.write(
reinterpret_cast<const char*>(&nValuesPerStar),
sizeof(int32_t)
);
isFirstWrite[index] = false;
}
size_t nBytes = nValues * sizeof(octantData[0]);
fileStream.write(reinterpret_cast<const char*>(octantData.data()), nBytes);
fileStream.close();
// Return number of stars written.
return nValues / nValuesPerStar;
}
else {
LERROR(fmt::format("Error opening file: {} as output data file.", outPath));
return 0;
}
}
documentation::Documentation ReadFitsTask::Documentation() {
using namespace documentation;
return {
"ReadFitsFile",
"gaiamission_fitsfiletorawdata",
{
{
"Type",
new StringEqualVerifier("ReadFitsTask"),
Optional::No
},
{
KeyInFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileProcess is set to true then this specifies the path to a "
"single FITS file that will be read. Otherwise it specifies the path to "
"a folder with multiple FITS files that are to be read.",
},
{
KeyOutFileOrFolderPath,
new StringVerifier,
Optional::No,
"If SingleFileProcess is set to true then this specifies the name "
"(including entire path) to the output file. Otherwise it specifies the "
"path to the output folder which to export binary star data to.",
},
{
KeySingleFileProcess,
new BoolVerifier,
Optional::Yes,
"If true then task will read from a single FITS file and output a single "
"binary file. If false then task will read all files in specified folder "
"and output multiple files sorted by location."
},
{
KeyThreadsToUse,
new IntVerifier,
Optional::Yes,
"Defines how many threads to use when reading from multiple files."
},
{
KeyFirstRow,
new IntVerifier,
Optional::Yes,
"Defines the first row that will be read from the specified FITS "
"file(s). If not defined then reading will start at first row.",
},
{
KeyLastRow,
new IntVerifier,
Optional::Yes,
"Defines the last row that will be read from the specified FITS file(s). "
"If not defined (or less than FirstRow) then full file(s) will be read.",
},
{
KeyFilterColumnNames,
new StringListVerifier,
Optional::Yes,
"A list of strings with the names of all the additional columns that are "
"to be read from the specified FITS file(s). These columns can be used "
"for filtering while constructing Octree later.",
},
}
};
}
} // namespace openspace
@@ -0,0 +1,82 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GAIA___READFITSTASK___H__
#define __OPENSPACE_MODULE_GAIA___READFITSTASK___H__
#include <openspace/util/task.h>
#include <openspace/util/threadpool.h>
#include <openspace/util/concurrentjobmanager.h>
#include <modules/fitsfilereader/include/fitsfilereader.h>
namespace openspace {
namespace documentation { struct Documentation; }
class ReadFitsTask : public Task {
public:
ReadFitsTask(const ghoul::Dictionary& dictionary);
virtual ~ReadFitsTask() = default;
std::string description() override;
void perform(const Task::ProgressCallback& onProgress) override;
static documentation::Documentation Documentation();
private:
const size_t MAX_SIZE_BEFORE_WRITE = 48000000; // ~183MB -> 2M stars with 24 values
//const size_t MAX_SIZE_BEFORE_WRITE = 9000000; // ~34MB -> 0,5 stars with 18 values
/**
* Reads a single FITS file and stores ordered star data in one binary file.
*/
void readSingleFitsFile(const Task::ProgressCallback& progressCallback);
/**
* Reads all FITS files in a folder with multiple threads and stores ordered star
* data into 8 binary files.
*/
void readAllFitsFilesFromFolder(const Task::ProgressCallback& progressCallback);
/**
* Writes \param data to octant [\param index] file.
* \param isFirstWrite defines if this is the first write to specified octant, if so
* the file is created, otherwise the accumulated data is appended to the end of the
* file.
*/
int writeOctantToFile(const std::vector<float>& data, int index,
std::vector<bool>& isFirstWrite, int nValuesPerStar);
std::string _inFileOrFolderPath;
std::string _outFileOrFolderPath;
bool _singleFileProcess = false;
size_t _threadsToUse = 1;
int _firstRow = 0;
int _lastRow = 0;
std::vector<std::string> _allColumnNames;
std::vector<std::string> _filterColumnNames;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___READFITSTASK___H__
@@ -0,0 +1,122 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <modules/gaia/tasks/readspecktask.h>
#include <modules/fitsfilereader/include/fitsfilereader.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <fstream>
namespace {
constexpr const char* KeyInFilePath = "InFilePath";
constexpr const char* KeyOutFilePath = "OutFilePath";
constexpr const char* _loggerCat = "ReadSpeckTask";
} // namespace
namespace openspace {
ReadSpeckTask::ReadSpeckTask(const ghoul::Dictionary& dictionary) {
openspace::documentation::testSpecificationAndThrow(
documentation(),
dictionary,
"ReadSpeckTask"
);
_inFilePath = absPath(dictionary.value<std::string>(KeyInFilePath));
_outFilePath = absPath(dictionary.value<std::string>(KeyOutFilePath));
}
std::string ReadSpeckTask::description() {
return fmt::format(
"Read speck file {} and write raw star data into {}", _inFilePath, _outFilePath
);
}
void ReadSpeckTask::perform(const Task::ProgressCallback& onProgress) {
onProgress(0.f);
int32_t nRenderValues = 0;
FitsFileReader fileReader(false);
std::vector<float> fullData = fileReader.readSpeckFile(_inFilePath, nRenderValues);
onProgress(0.9f);
std::ofstream fileStream(_outFilePath, std::ofstream::binary);
if (fileStream.good()) {
int32_t nValues = static_cast<int32_t>(fullData.size());
LINFO("nValues: " + std::to_string(nValues));
if (nValues == 0) {
LERROR("Error writing file - No values were read from file.");
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
fileStream.write(reinterpret_cast<const char*>(&nRenderValues), sizeof(int32_t));
size_t nBytes = nValues * sizeof(fullData[0]);
fileStream.write(reinterpret_cast<const char*>(fullData.data()), nBytes);
fileStream.close();
}
else {
LERROR(fmt::format("Error opening file: {} as output data file.", _outFilePath));
}
onProgress(1.f);
}
documentation::Documentation ReadSpeckTask::Documentation() {
using namespace documentation;
return {
"ReadSpeckTask",
"gaiamission_speckfiletorawdata",
{
{
"Type",
new StringEqualVerifier("ReadSpeckTask"),
Optional::No
},
{
KeyInFilePath,
new StringVerifier,
Optional::No,
"The path to the SPECK file that are to be read.",
},
{
KeyOutFilePath,
new StringVerifier,
Optional::No,
"The path to the file to export raw VBO data to.",
},
}
};
}
} // namespace openspace
@@ -22,40 +22,31 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GALAXY___MILKYWAYCONVERSIONTASK___H__
#define __OPENSPACE_MODULE_GALAXY___MILKYWAYCONVERSIONTASK___H__
#ifndef __OPENSPACE_MODULE_GAIA___READSPECKTASK___H__
#define __OPENSPACE_MODULE_GAIA___READSPECKTASK___H__
#include <openspace/util/task.h>
#include <ghoul/glm.h>
#include <string>
namespace openspace {
namespace documentation { struct Documentation; }
/**
* Converts a set of exr image slices to a raw volume
* with floating point RGBA data (32 bit per channel).
*/
class MilkywayConversionTask : public Task {
class ReadSpeckTask : public Task {
public:
MilkywayConversionTask(const ghoul::Dictionary& dictionary);
virtual ~MilkywayConversionTask() = default;
ReadSpeckTask(const ghoul::Dictionary& dictionary);
virtual ~ReadSpeckTask() = default;
std::string description() override;
void perform(const Task::ProgressCallback& onProgress) override;
static documentation::Documentation documentation();
static documentation::Documentation Documentation();
private:
std::string _inFilenamePrefix;
std::string _inFilenameSuffix;
size_t _inFirstIndex = 0;
size_t _inNSlices = 0;
std::string _outFilename;
glm::ivec3 _outDimensions = glm::ivec3(0);
std::string _inFilePath;
std::string _outFilePath;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GALAXY___MILKYWAYCONVERSIONTASK___H__
#endif // __OPENSPACE_MODULE_GAIA___READSPECKTASK___H__