Feature/gaia mission merge (#766)

* Add Adam Aslegård's module that renders Gaia mission stars
 * Add module for Gaia mission
 * Add CCfits and cfitsio submodules and the implemented fitsfilereader module from the OpenSpace-sun-earth-event branch
 * Add a TaskRunner to read from a fits file
This commit is contained in:
Emil Axelsson
2018-11-21 00:19:08 +01:00
committed by Alexander Bock
parent 4b696b2463
commit 0d2935c43b
71 changed files with 10150 additions and 19 deletions

View File

@@ -39,6 +39,7 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/lightsource/scenegraphlightsource.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/multimodelgeometry.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableboxgrid.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecartesianaxes.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.h
@@ -82,6 +83,7 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/lightsource/scenegraphlightsource.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/modelgeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/multimodelgeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableboxgrid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablecartesianaxes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablemodel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderableplane.cpp

View File

@@ -33,6 +33,7 @@
#include <modules/base/dashboard/dashboarditempropertyvalue.h>
#include <modules/base/dashboard/dashboarditemsimulationincrement.h>
#include <modules/base/dashboard/dashboarditemspacing.h>
#include <modules/base/rendering/renderableboxgrid.h>
#include <modules/base/dashboard/dashboarditemvelocity.h>
#include <modules/base/lightsource/cameralightsource.h>
#include <modules/base/lightsource/scenegraphlightsource.h>
@@ -117,6 +118,7 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
auto fRenderable = FactoryManager::ref().factory<Renderable>();
ghoul_assert(fRenderable, "Renderable factory was not created");
fRenderable->registerClass<RenderableBoxGrid>("RenderableBoxGrid");
fRenderable->registerClass<RenderableCartesianAxes>("RenderableCartesianAxes");
fRenderable->registerClass<RenderableModel>("RenderableModel");
fRenderable->registerClass<RenderablePlaneImageLocal>("RenderablePlaneImageLocal");
@@ -181,6 +183,7 @@ std::vector<documentation::Documentation> BaseModule::documentations() const {
DashboardItemSpacing::Documentation(),
DashboardItemVelocity::Documentation(),
RenderableBoxGrid::Documentation(),
RenderableModel::Documentation(),
RenderablePlane::Documentation(),
RenderableSphere::Documentation(),

View File

@@ -0,0 +1,320 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/base/rendering/renderableboxgrid.h>
#include <modules/base/basemodule.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/updatestructures.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/opengl/programobject.h>
namespace {
constexpr const char* ProgramName = "GridProgram";
constexpr openspace::properties::Property::PropertyInfo GridColorInfo = {
"GridColor",
"Grid Color",
"This value determines the color of the grid lines that are rendered."
};
constexpr openspace::properties::Property::PropertyInfo GridMatrixInfo = {
"GridMatrix",
"Grid Matrix",
"This value specifies the local transformation matrix that defines the "
"orientation of this grid relative to the parent's rotation."
};
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
"Segments",
"Number of Segments",
"This value specifies the number of segments that are used to render the "
"surrounding sphere."
};
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
"LineWidth",
"Line Width",
"This value specifies the line width of the spherical grid."
};
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
"Size",
"Grid Size",
"This value species the size of each dimensions of the box"
};
} // namespace
namespace openspace {
documentation::Documentation RenderableBoxGrid::Documentation() {
using namespace documentation;
return {
"RenderableSphericalGrid",
"base_renderable_sphericalgrid",
{
{
GridMatrixInfo.identifier,
new DoubleMatrix4x4Verifier,
Optional::Yes,
GridMatrixInfo.description
},
{
GridColorInfo.identifier,
new DoubleVector4Verifier,
Optional::Yes,
GridColorInfo.description
},
{
SegmentsInfo.identifier,
new IntVerifier,
Optional::Yes,
SegmentsInfo.description
},
{
LineWidthInfo.identifier,
new DoubleVerifier,
Optional::Yes,
LineWidthInfo.description
},
{
SizeInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
SizeInfo.description
}
}
};
}
RenderableBoxGrid::RenderableBoxGrid(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _gridMatrix(GridMatrixInfo, glm::mat4(1.f))
, _gridColor(
GridColorInfo,
glm::vec4(0.5f, 0.5, 0.5f, 1.f),
glm::vec4(0.f),
glm::vec4(1.f)
)
, _segments(SegmentsInfo, 36, 4, 200)
, _lineWidth(LineWidthInfo, 0.5f, 0.f, 20.f)
, _size(SizeInfo, glm::vec3(1e20f), glm::vec3(1.f), glm::vec3(1e35f))
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"RenderableBoxGrid"
);
addProperty(_opacity);
registerUpdateRenderBinFromOpacity();
if (dictionary.hasKey(GridMatrixInfo.identifier)) {
_gridMatrix = dictionary.value<glm::dmat4>(GridMatrixInfo.identifier);
}
addProperty(_gridMatrix);
if (dictionary.hasKey(GridColorInfo.identifier)) {
_gridColor = dictionary.value<glm::vec4>(GridColorInfo.identifier);
}
_gridColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_gridColor);
if (dictionary.hasKey(SegmentsInfo.identifier)) {
_segments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
}
_segments.onChange([&]() { _gridIsDirty = true; });
addProperty(_segments);
if (dictionary.hasKey(LineWidthInfo.identifier)) {
_lineWidth = static_cast<float>(
dictionary.value<double>(LineWidthInfo.identifier)
);
}
addProperty(_lineWidth);
if (dictionary.hasKey(SizeInfo.identifier)) {
_size = dictionary.value<glm::vec3>(SizeInfo.identifier);
}
_size.onChange([&]() { _gridIsDirty = true; });
addProperty(_size);
}
bool RenderableBoxGrid::isReady() const {
return _gridProgram != nullptr;
}
void RenderableBoxGrid::initializeGL() {
_gridProgram = BaseModule::ProgramObjectManager.request(
ProgramName,
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
return global::renderEngine.buildRenderProgram(
ProgramName,
absPath("${MODULE_BASE}/shaders/grid_vs.glsl"),
absPath("${MODULE_BASE}/shaders/grid_fs.glsl")
);
}
);
glGenVertexArrays(1, &_vaoID);
glGenBuffers(1, &_vBufferID);
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
void RenderableBoxGrid::deinitializeGL() {
glDeleteVertexArrays(1, &_vaoID);
_vaoID = 0;
glDeleteBuffers(1, &_vBufferID);
_vBufferID = 0;
BaseModule::ProgramObjectManager.release(
ProgramName,
[](ghoul::opengl::ProgramObject* p) {
global::renderEngine.removeRenderProgram(p);
}
);
_gridProgram = nullptr;
}
void RenderableBoxGrid::render(const RenderData& data, RendererTasks&){
_gridProgram->activate();
_gridProgram->setUniform("opacity", _opacity);
glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
_gridProgram->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
_gridProgram->setUniform("projectionTransform", data.camera.projectionMatrix());
_gridProgram->setUniform("gridColor", _gridColor);
glLineWidth(_lineWidth);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glBindVertexArray(_vaoID);
glDrawArrays(_mode, 0, _varray.size());
glBindVertexArray(0);
_gridProgram->deactivate();
}
void RenderableBoxGrid::update(const UpdateData&) {
if (_gridIsDirty) {
//_vsize = (_segments + 1) * (_segments + 1);
//_varray.resize(_vsize);
const glm::vec3 llf = -_size.value() / 2.f;
const glm::vec3 urb = _size.value() / 2.f;
// 7
// -------------------- 6
// / /
// /| /|
// 4 / | / |
// x-------------------x |
// | | |5 |
// | | | |
// | | | |
// | 3/----------------|--/ 2
// | / | /
// |/ |/
// x-------------------x
// 0 1
//
//
// For Line strip:
// 0 -> 1 -> 2 -> 3 -> 0 -> 4 -> 5 -> 6 -> 7 -> 4 -> 5(d) -> 1 -> 2(d) -> 6
// -> 7(d) -> 3
const glm::vec3 v0 = { llf.x, llf.y, llf.z };
const glm::vec3 v1 = { urb.x, llf.y, llf.z };
const glm::vec3 v2 = { urb.x, urb.y, llf.z };
const glm::vec3 v3 = { llf.x, urb.y, llf.z };
const glm::vec3 v4 = { llf.x, llf.y, urb.z };
const glm::vec3 v5 = { urb.x, llf.y, urb.z };
const glm::vec3 v6 = { urb.x, urb.y, urb.z };
const glm::vec3 v7 = { llf.x, urb.y, urb.z };
// First add the bounds
_varray.push_back({ v0.x, v0.y, v0.z });
_varray.push_back({ v1.x, v1.y, v1.z });
_varray.push_back({ v2.x, v2.y, v2.z });
_varray.push_back({ v3.x, v3.y, v3.z });
_varray.push_back({ v0.x, v0.y, v0.z });
_varray.push_back({ v4.x, v4.y, v4.z });
_varray.push_back({ v5.x, v5.y, v5.z });
_varray.push_back({ v6.x, v6.y, v6.z });
_varray.push_back({ v7.x, v7.y, v7.z });
_varray.push_back({ v4.x, v4.y, v4.z });
_varray.push_back({ v5.x, v5.y, v5.z });
_varray.push_back({ v1.x, v1.y, v1.z });
_varray.push_back({ v2.x, v2.y, v2.z });
_varray.push_back({ v6.x, v6.y, v6.z });
_varray.push_back({ v7.x, v7.y, v7.z });
_varray.push_back({ v3.x, v3.y, v3.z });
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glBufferData(
GL_ARRAY_BUFFER,
_varray.size() * sizeof(Vertex),
_varray.data(),
GL_STATIC_DRAW
);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
nullptr // = reinterpret_cast<const GLvoid*>(offsetof(Vertex, location))
);
glBindVertexArray(0);
_gridIsDirty = false;
}
}
} // namespace openspace

View File

@@ -0,0 +1,83 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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_BASE___RENDERABLEBOXGRID___H__
#define __OPENSPACE_MODULE_BASE___RENDERABLEBOXGRID___H__
#include <openspace/rendering/renderable.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/matrix/dmat4property.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/properties/vector/vec4property.h>
#include <ghoul/opengl/ghoul_gl.h>
namespace ghoul::opengl {
class ProgramObject;
} // namespace ghoul::opengl
namespace openspace::documentation { struct Documentation; }
namespace openspace {
class RenderableBoxGrid : public Renderable {
public:
RenderableBoxGrid(const ghoul::Dictionary& dictionary);
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();
protected:
struct Vertex {
float location[3];
};
ghoul::opengl::ProgramObject* _gridProgram = nullptr;
properties::DMat4Property _gridMatrix;
properties::Vec4Property _gridColor;
properties::IntProperty _segments;
properties::FloatProperty _lineWidth;
properties::Vec3Property _size;
bool _gridIsDirty = true;
GLuint _vaoID = 0;
GLuint _vBufferID = 0;
GLenum _mode = GL_LINE_STRIP;
std::vector<Vertex> _varray;
};
}// namespace openspace
#endif // __OPENSPACE_MODULE_BASE___RENDERABLEBOXGRID___H__

View File

@@ -217,6 +217,12 @@ documentation::Documentation FixedRotation::Documentation() {
Optional::Yes,
XAxisOrthogonalVectorInfo.description
},
{
XAxisInvertObjectInfo.identifier,
new BoolVerifier,
Optional::Yes,
XAxisInvertObjectInfo.description
},
{
KeyYAxis,
new OrVerifier({ new StringVerifier, new DoubleVector3Verifier, }),
@@ -234,6 +240,12 @@ documentation::Documentation FixedRotation::Documentation() {
Optional::Yes,
YAxisOrthogonalVectorInfo.description
},
{
YAxisInvertObjectInfo.identifier,
new BoolVerifier,
Optional::Yes,
YAxisInvertObjectInfo.description
},
{
KeyZAxis,
new OrVerifier({ new StringVerifier, new DoubleVector3Verifier, }),
@@ -251,6 +263,12 @@ documentation::Documentation FixedRotation::Documentation() {
Optional::Yes,
ZAxisOrthogonalVectorInfo.description
},
{
ZAxisInvertObjectInfo.identifier,
new BoolVerifier,
Optional::Yes,
ZAxisInvertObjectInfo.description
},
{
AttachedInfo.identifier,
new StringVerifier,
@@ -454,6 +472,11 @@ bool FixedRotation::initialize() {
if (_constructorDictionary.hasKey(KeyXAxisOrthogonal)) {
_xAxis.isOrthogonal = _constructorDictionary.value<bool>(KeyXAxisOrthogonal);
}
if (_constructorDictionary.hasKey(XAxisInvertObjectInfo.identifier)) {
_xAxis.invertObject = _constructorDictionary.value<bool>(
XAxisInvertObjectInfo.identifier
);
}
if (_xAxis.isOrthogonal) {
_xAxis.type = Axis::Type::OrthogonalVector;
}
@@ -474,6 +497,11 @@ bool FixedRotation::initialize() {
if (_constructorDictionary.hasKey(KeyYAxisOrthogonal)) {
_yAxis.isOrthogonal = _constructorDictionary.value<bool>(KeyYAxisOrthogonal);
}
if (_constructorDictionary.hasKey(YAxisInvertObjectInfo.identifier)) {
_yAxis.invertObject = _constructorDictionary.value<bool>(
YAxisInvertObjectInfo.identifier
);
}
if (_yAxis.isOrthogonal) {
_yAxis.type = Axis::Type::OrthogonalVector;
}
@@ -494,12 +522,16 @@ bool FixedRotation::initialize() {
if (_constructorDictionary.hasKey(KeyZAxisOrthogonal)) {
_zAxis.isOrthogonal = _constructorDictionary.value<bool>(KeyZAxisOrthogonal);
}
if (_constructorDictionary.hasKey(ZAxisInvertObjectInfo.identifier)) {
_yAxis.invertObject = _constructorDictionary.value<bool>(
ZAxisInvertObjectInfo.identifier
);
}
if (_zAxis.isOrthogonal) {
_zAxis.type = Axis::Type::OrthogonalVector;
}
if (!hasXAxis && hasYAxis && hasZAxis) {
_xAxis.type = Axis::Type::CoordinateSystemCompletion;
}
@@ -554,11 +586,14 @@ glm::vec3 FixedRotation::xAxis() const {
return glm::vec3(1.f, 0.f, 0.f);
case Axis::Type::Object:
if (_xAxis.node && _attachedNode) {
glm::vec3 dir = glm::vec3(glm::normalize(
_xAxis.node->worldPosition() -
_attachedNode->worldPosition()
));
return _xAxis.invertObject ? -dir : dir;
glm::dvec3 dir = _xAxis.node->worldPosition() -
_attachedNode->worldPosition();
if (dir == glm::dvec3(0.0)) {
dir = glm::dvec3(1.0, 0.0, 0.0);
}
glm::vec3 dirNorm = glm::vec3(glm::normalize(dir));
return _xAxis.invertObject ? -dirNorm : dirNorm;
}
else {
if (_xAxis.node) {

View File

@@ -0,0 +1,56 @@
##########################################################################################
# #
# OpenSpace #
# #
# Copyright (c) 2014-2018 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
# without restriction, including without limitation the rights to use, copy, modify, #
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to #
# permit persons to whom the Software is furnished to do so, subject to the following #
# conditions: #
# #
# The above copyright notice and this permission notice shall be included in all copies #
# or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A #
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF #
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE #
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/fitsfilereadermodule.h
${CMAKE_CURRENT_SOURCE_DIR}/include/fitsfilereader.h
)
source_group("Header Files" FILES ${HEADER_FILES})
set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/fitsfilereadermodule.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/fitsfilereader.cpp
)
source_group("Source Files" FILES ${SOURCE_FILES})
create_new_module(
"FitsFileReader"
fitsfilereader
${HEADER_FILES}
${SOURCE_FILES})
# Set root directories for external libraries.
set(CFITSIO_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/cfitsio/")
set(CCFITS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/CCfits/")
set(INCLUDES_FOR_TARGET ${CCFITS_ROOT_DIR} "${CCFITS_ROOT_DIR}/../" ${CFITSIO_ROOT_DIR})
set(MODULE_NAME openspace-module-fitsfilereader)
# CCfits is dependent on cfitsio, let it handle the internal linking
add_subdirectory(${CFITSIO_ROOT_DIR})
add_subdirectory(${CCFITS_ROOT_DIR})
TARGET_INCLUDE_DIRECTORIES(${MODULE_NAME} SYSTEM PUBLIC ${INCLUDES_FOR_TARGET})
TARGET_LINK_LIBRARIES(${MODULE_NAME} CCfits)

View File

@@ -0,0 +1,31 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/fitsfilereader/fitsfilereadermodule.h>
namespace openspace {
FitsFileReaderModule::FitsFileReaderModule() : OpenSpaceModule(Name) {}
} // namespace openspace

View File

@@ -0,0 +1,41 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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_FITSFILEREADER___FITSFILEREADERMODULE___H__
#define __OPENSPACE_MODULE_FITSFILEREADER___FITSFILEREADERMODULE___H__
#include <openspace/util/openspacemodule.h>
namespace openspace {
class FitsFileReaderModule : public OpenSpaceModule {
public:
constexpr static const char* Name = "FitsFileReader";
FitsFileReaderModule();
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_FITSFILEREADER___FITSFILEREADERMODULE___H__

View File

View File

@@ -0,0 +1,116 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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_FITSFILEREADER___FITSFILEREADER___H__
#define __OPENSPACE_MODULE_FITSFILEREADER___FITSFILEREADER___H__
#include <string>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <valarray>
#include <vector>
namespace CCfits {
class FITS;
class PHDU;
class ExtHDU;
} // namespace CCfits
namespace ghoul::opengl { class Texture; }
namespace openspace {
template<typename T>
struct ImageData {
std::valarray<T> contents;
long int width;
long int height;
};
template<typename T>
struct TableData {
std::unordered_map<std::string, std::vector<T>> contents;
int readRows;
long int optimalRowsize;
std::string name;
};
class FitsFileReader {
public:
FitsFileReader(bool verboseMode);
~FitsFileReader();
template<typename T>
std::shared_ptr<ImageData<T>> readImage(const std::string& path);
template<typename T>
std::shared_ptr<std::unordered_map<std::string, T>> readHeader(
std::vector<std::string>& keywords);
template<typename T>
std::shared_ptr<T> readHeaderValue(const std::string key);
/**
* Read specified table columns from fits file.
* If <code>readAll</code> is set to true the entire table will be read before the
* selected columns, which makes the function take a lot longer if it's a big file.
* If no HDU index is given the current Extension HDU will be read from.
*/
template<typename T>
std::shared_ptr<TableData<T>> readTable(std::string& path,
const std::vector<std::string>& columnNames, int startRow = 1, int endRow = 10,
int hduIdx = 1, bool readAll = false);
/**
* Reads a single FITS file with pre-defined columns (defined for Viennas TGAS-file).
* Returns a vector with all read stars with <code>nValuesPerStar</code>.
* If additional columns are given by <code>filterColumnNames</code>, they will be
* read but it will slow doen the reading tremendously.
*/
std::vector<float> readFitsFile(std::string filePath, int& nValuesPerStar,
int firstRow, int lastRow, std::vector<std::string> filterColumnNames,
int multiplier = 1);
/**
* Reads a single SPECK file and returns a vector with <code>nRenderValues</code>
* per star. Reads data in pre-defined order based on AMNH's star data files.
*/
std::vector<float> readSpeckFile(std::string filePath, int& nRenderValues);
private:
std::unique_ptr<CCfits::FITS> _infile;
bool _verboseMode;
bool isPrimaryHDU();
template<typename T>
const std::shared_ptr<ImageData<T>> readImageInternal(CCfits::PHDU& image);
template<typename T>
const std::shared_ptr<ImageData<T>> readImageInternal(CCfits::ExtHDU& image);
mutable std::mutex _mutex;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_FITSFILEREADER___FITSFILEREADER___H__

View File

@@ -0,0 +1,668 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/fitsfilereader/include/fitsfilereader.h>
#include <openspace/util/distanceconversion.h>
#include <ghoul/fmt.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <CCfits>
#include <fstream>
using namespace CCfits;
namespace {
constexpr const char* _loggerCat = "FitsFileReader";
} // namespace
namespace openspace {
FitsFileReader::FitsFileReader(bool verboseMode) {
_verboseMode = verboseMode;
FITS::setVerboseMode(_verboseMode);
}
FitsFileReader::~FitsFileReader() {
if (_infile) {
_infile->destroy();
_infile = nullptr;
}
}
bool FitsFileReader::isPrimaryHDU() {
return _infile->extension().size() == 0;
}
template <typename T>
std::shared_ptr<ImageData<T>> FitsFileReader::readImage(const std::string& path) {
try {
_infile = std::make_unique<FITS>(path, Read, true);
// Primary HDU Object
if (isPrimaryHDU()) {
return readImageInternal<T>(_infile->pHDU());
}
// Extension HDU Object
return readImageInternal<T>(_infile->currentExtension());
} catch (const FitsException& e){
LERROR("Could not read FITS image from table. " + e.message() );
}
return nullptr;
}
template <typename T>
std::shared_ptr<std::unordered_map<std::string, T>> FitsFileReader::readHeader(
std::vector<std::string>& keywords)
{
try {
HDU& image = isPrimaryHDU() ?
static_cast<HDU&>(_infile->pHDU()) :
static_cast<HDU&>(_infile->currentExtension());
std::vector<T> values;
image.readKeys(keywords, values);
if (values.size() != keywords.size()) {
LERROR("Number of keywords does not match number of values");
}
std::unordered_map<std::string, T> result;
std::transform(
keywords.begin(),
keywords.end(),
values.begin(),
std::inserter(
result,
result.end()
),
[](std::string key, T value) { return std::make_pair(key, value); }
);
return std::make_shared<std::unordered_map<std::string, T>>(std::move(result));
} catch (const FitsException& e) {
LERROR("Could not read FITS header. " + e.message() );
}
return nullptr;
}
template <typename T>
std::shared_ptr<T> FitsFileReader::readHeaderValue(const std::string key) {
try {
HDU& image = isPrimaryHDU() ?
static_cast<HDU&>(_infile->pHDU()) :
static_cast<HDU&>(_infile->currentExtension());
T value;
image.readKey(key, value);
return std::make_unique<T>(value);
} catch (FitsException& e) {
LERROR("Could not read FITS key. " + e.message() );
}
return nullptr;
}
template<typename T>
std::shared_ptr<TableData<T>> FitsFileReader::readTable(std::string& path,
const std::vector<std::string>& columnNames,
int startRow,
int endRow,
int hduIdx,
bool readAll)
{
// We need to lock reading when using multithreads because CCfits can't handle
// multiple I/O drivers.
std::lock_guard g(_mutex);
try {
_infile = std::make_unique<FITS>(path, Read, readAll);
// Make sure FITS file is not a Primary HDU Object (aka an image).
if (!isPrimaryHDU()) {
ExtHDU& table = _infile->extension(hduIdx);
int numCols = columnNames.size();
int numRowsInTable = table.rows();
std::unordered_map<string, std::vector<T>> contents;
//LINFO("Read file: " + _infile->name());
int firstRow = std::max(startRow, 1);
if (endRow < firstRow) {
endRow = numRowsInTable;
}
for (int i = 0; i < numCols; ++i) {
std::vector<T> columnData;
//LINFO("Read column: " + columnNames[i]);
table.column(columnNames[i]).read(columnData, firstRow, endRow);
contents[columnNames[i]] = columnData;
}
// Create TableData object of table contents.
TableData<T> loadedTable = {
std::move(contents),
static_cast<int>(table.rows()),
table.getRowsize(),
table.name()
};
return std::make_shared<TableData<T>>(loadedTable);
}
}
catch (FitsException& e) {
LERROR(fmt::format(
"Could not read FITS table from file '{}'. Make sure it's not an image file.",
e.message()
));
}
return nullptr;
}
std::vector<float> FitsFileReader::readFitsFile(std::string filePath, int& nValuesPerStar,
int firstRow, int lastRow,
std::vector<std::string> filterColumnNames,
int multiplier)
{
std::vector<float> fullData;
srand(1234567890);
if (firstRow <= 0) {
firstRow = 1;
}
// Define what columns to read.
std::vector<std::string> allColumnNames = {
"Position_X",
"Position_Y",
"Position_Z",
"Velocity_X",
"Velocity_Y",
"Velocity_Z",
"Gaia_Parallax",
"Gaia_G_Mag",
"Tycho_B_Mag",
"Tycho_V_Mag",
"Gaia_Parallax_Err",
"Gaia_Proper_Motion_RA",
"Gaia_Proper_Motion_RA_Err",
"Gaia_Proper_Motion_Dec",
"Gaia_Proper_Motion_Dec_Err",
"Tycho_B_Mag_Err",
"Tycho_V_Mag_Err"
};
// 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);
// Read columns from FITS file. If rows aren't specified then full table will be read.
std::shared_ptr<TableData<float>> table = readTable<float>(
filePath,
allColumnNames,
firstRow,
lastRow
);
if (!table) {
throw ghoul::RuntimeError(fmt::format("Failed to open Fits file '{}'", filePath));
}
int nStars = table->readRows - firstRow + 1;
int nNullArr = 0;
size_t nColumnsRead = allColumnNames.size();
size_t defaultCols = 17; // Number of columns that are copied by predefined code.
if (nColumnsRead != defaultCols) {
LINFO("Additional columns will be read! Consider add column in code for "
"significant speedup!");
}
// Declare how many values to save per star
nValuesPerStar = nColumnsRead + 1; // +1 for B-V color value.
// Copy columns to local variables.
std::unordered_map<std::string, std::vector<float>>& tableContent = table->contents;
// Default render parameters!
std::vector<float> posXcol = std::move(tableContent[allColumnNames[0]]);
std::vector<float> posYcol = std::move(tableContent[allColumnNames[1]]);
std::vector<float> posZcol = std::move(tableContent[allColumnNames[2]]);
std::vector<float> velXcol = std::move(tableContent[allColumnNames[3]]);
std::vector<float> velYcol = std::move(tableContent[allColumnNames[4]]);
std::vector<float> velZcol = std::move(tableContent[allColumnNames[5]]);
std::vector<float> parallax = std::move(tableContent[allColumnNames[6]]);
std::vector<float> magCol = std::move(tableContent[allColumnNames[7]]);
std::vector<float> tycho_b = std::move(tableContent[allColumnNames[8]]);
std::vector<float> tycho_v = std::move(tableContent[allColumnNames[9]]);
// Default filter parameters
// Additional filter parameters are handled as well but slows down reading
std::vector<float> parallax_err = std::move(tableContent[allColumnNames[10]]);
std::vector<float> pr_mot_ra = std::move(tableContent[allColumnNames[11]]);
std::vector<float> pr_mot_ra_err = std::move(tableContent[allColumnNames[12]]);
std::vector<float> pr_mot_dec = std::move(tableContent[allColumnNames[13]]);
std::vector<float> pr_mot_dec_err = std::move(tableContent[allColumnNames[14]]);
std::vector<float> tycho_b_err = std::move(tableContent[allColumnNames[15]]);
std::vector<float> tycho_v_err = std::move(tableContent[allColumnNames[16]]);
// Construct data array. OBS: ORDERING IS IMPORTANT! This is where slicing happens.
for (int i = 0; i < nStars * multiplier; ++i) {
std::vector<float> values(nValuesPerStar);
size_t idx = 0;
// Default order for rendering:
// Position [X, Y, Z]
// Absolute Magnitude
// B-V Color
// Velocity [X, Y, Z]
// Store positions.
values[idx++] = posXcol[i % nStars];
values[idx++] = posYcol[i % nStars];
values[idx++] = posZcol[i % nStars];
// Return early if star doesn't have a measured position.
if (values[0] == -999 && values[1] == -999 && values[2] == -999) {
nNullArr++;
continue;
}
// Store color values.
values[idx++] = magCol[i % nStars] == -999 ? 20.f : magCol[i % nStars];
values[idx++] = tycho_b[i % nStars] - tycho_v[i % nStars];
// Store velocity. Convert it to m/s with help by parallax.
values[idx++] = convertMasPerYearToMeterPerSecond(
velXcol[i % nStars],
parallax[i % nStars]
);
values[idx++] = convertMasPerYearToMeterPerSecond(
velYcol[i % nStars],
parallax[i % nStars]
);
values[idx++] = convertMasPerYearToMeterPerSecond(
velZcol[i % nStars],
parallax[i % nStars]
);
// Store additional parameters to filter by.
values[idx++] = parallax[i % nStars];
values[idx++] = parallax_err[i % nStars];
values[idx++] = pr_mot_ra[i % nStars];
values[idx++] = pr_mot_ra_err[i % nStars];
values[idx++] = pr_mot_dec[i % nStars];
values[idx++] = pr_mot_dec_err[i % nStars];
values[idx++] = tycho_b[i % nStars];
values[idx++] = tycho_b_err[i % nStars];
values[idx++] = tycho_v[i % nStars];
values[idx++] = tycho_v_err[i % nStars];
// Read extra columns, if any. This will slow down the sorting tremendously!
for (size_t col = defaultCols; col < nColumnsRead; ++col) {
std::vector<float> vecData = std::move(tableContent[allColumnNames[col]]);
values[idx++] = vecData[i];
}
for (size_t j = 0; j < nValuesPerStar; ++j) {
// The astronomers in Vienna use -999 as default value. Change it to 0.
if (values[j] == -999) {
values[j] = 0.f;
}
else if (multiplier > 1) {
values[j] *= static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
}
}
fullData.insert(fullData.end(), values.begin(), values.end());
}
// Define what columns to read.
/*auto allColumnNames = std::vector<std::string>({
"ra",
"dec",
"parallax",
"pmra",
"pmdec",
"phot_g_mean_mag",
"phot_bp_mean_mag",
"phot_rp_mean_mag",
"radial_velocity",
});
// Append additional filter parameters to default rendering parameters.
allColumnNames.insert(allColumnNames.end(), filterColumnNames.begin(),
filterColumnNames.end());
std::string allNames = "Columns to read: \n";
for (auto colName : allColumnNames) {
allNames += colName + "\n";
}
LINFO(allNames);
// Read columns from FITS file. If rows aren't specified then full table will be read.
auto table = readTable<float>(filePath, allColumnNames, firstRow, lastRow);
if (!table) {
throw ghoul::RuntimeError(fmt::format("Failed to open Fits file '{}'", filePath));
}
int nStars = table->readRows - firstRow + 1;
int nNullArr = 0;
size_t nColumnsRead = allColumnNames.size();
size_t defaultCols = 9; // Number of columns that are copied by predefined code.
if (nColumnsRead != defaultCols) {
LINFO("Additional columns will be read! Consider add column in code for "
"significant speedup!");
}
// Declare how many values to save per star
nValuesPerStar = 8;
// Copy columns to local variables.
std::unordered_map<std::string, std::vector<float>>& tableContent = table->contents;
std::vector<float> ra = std::move(tableContent[allColumnNames[0]]);
std::vector<float> dec = std::move(tableContent[allColumnNames[1]]);
std::vector<float> parallax = std::move(tableContent[allColumnNames[2]]);
std::vector<float> pmra = std::move(tableContent[allColumnNames[3]]);
std::vector<float> pmdec = std::move(tableContent[allColumnNames[4]]);
std::vector<float> meanMagG = std::move(tableContent[allColumnNames[5]]);
std::vector<float> meanMagBp = std::move(tableContent[allColumnNames[6]]);
std::vector<float> meanMagRp = std::move(tableContent[allColumnNames[7]]);
std::vector<float> radial_vel = std::move(tableContent[allColumnNames[8]]);
// 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
// Bp-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
radiusInKiloParsec = 1.0 / 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
// 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(meanMagBp[i]) && std::isnan(meanMagRp[i]) ? 0.f
: meanMagBp[i] - meanMagRp[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.0 * 4.74 * radiusInKiloParsec * pmVecGal.x;
float tanVelY = 1000.0 * 4.74 * radiusInKiloParsec * pmVecGal.y;
float tanVelZ = 1000.0 * 4.74 * 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.0 * radial_vel[i] * rGal.x;
float radVelY = 1000.0 * radial_vel[i] * rGal.y;
float radVelZ = 1000.0 * 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]
}
fullData.insert(fullData.end(), values.begin(), values.end());
}*/
LINFO(fmt::format("{} out of {} read stars were null arrays", nNullArr, nStars));
LINFO(fmt::format("Multiplier: {}", multiplier));
return fullData;
}
std::vector<float> FitsFileReader::readSpeckFile(std::string filePath, int& nRenderValues)
{
auto fullData = std::vector<float>();
std::ifstream fileStream(filePath);
if (!fileStream.good()) {
LERROR(fmt::format("Failed to open Speck file '{}'", filePath));
return fullData;
}
int nValuesPerStar = 0;
int nNullArr = 0;
size_t nStars = 0;
// The beginning of the speck file has a header that either contains comments
// (signaled by a preceding '#') or information about the structure of the file
// (signaled by the keywords 'datavar', 'texturevar', 'texture' and 'maxcomment')
std::string line = "";
while (true) {
std::streampos position = fileStream.tellg();
std::getline(fileStream, line);
if (line[0] == '#' || line.empty()) {
continue;
}
if (line.substr(0, 7) != "datavar" && line.substr(0, 10) != "texturevar" &&
line.substr(0, 7) != "texture" && line.substr(0, 10) != "maxcomment")
{
// We read a line that doesn't belong to the header, so we have to jump back
// before the beginning of the current line.
fileStream.seekg(position);
break;
}
if (line.substr(0, 7) == "datavar") {
// datavar lines are structured as follows:
// datavar # description
// where # is the index of the data variable; so if we repeatedly overwrite
// the 'nValues' variable with the latest index, we will end up with the total
// number of values (+3 since X Y Z are not counted in the Speck file index)
std::stringstream str(line);
std::string dummy;
str >> dummy;
str >> nValuesPerStar;
nValuesPerStar += 1; // We want the number, but the index is 0 based
}
}
nValuesPerStar += 3; // X Y Z are not counted in the Speck file indices
// Order in DR1 file: DR2 - GaiaGroupMembers:
// 0 BVcolor 0 color
// 1 lum 1 lum
// 2 Vabsmag 2 absmag
// 3 Vappmag 3 Gmag
// 4 distly 4 distpc
// 5 distpcPctErr 5 plx
// 6 U 6 ra
// 7 V 7 dec
// 8 W 8 RadVel
// 9 speed 9 Teff
// 10 sptypeindex 10 vx
// 11 lumclassindex 11 vy
// 12 catsource 12 vz
// 13 texture 13 speed
// 14 texture
do {
std::vector<float> readValues(nValuesPerStar);
nStars++;
std::getline(fileStream, line);
std::stringstream str(line);
// Read values.
for (int i = 0; i < nValuesPerStar; ++i) {
str >> readValues[i];
}
// Check if star is a nullArray.
bool nullArray = true;
for (size_t i = 0; i < readValues.size(); ++i) {
if (readValues[i] != 0.0) {
nullArray = false;
break;
}
}
// Insert to data if we found some values.
if (!nullArray) {
// Re-order data here because Octree expects the data in correct order when
// read.
// Default order for rendering:
// Position [X, Y, Z]
// Absolute Magnitude
// B-V Color
// Velocity [X, Y, Z]
nRenderValues = 8;
std::vector<float> renderValues(nRenderValues);
// Gaia DR1 data from AMNH measures positions in Parsec, but
// RenderableGaiaStars expects kiloParsec (because fits file from Vienna had
// in kPc).
// Thus we need to convert positions twice atm.
renderValues[0] = readValues[0] / 1000.0; // PosX
renderValues[1] = readValues[1] / 1000.0; // PosY
renderValues[2] = readValues[2] / 1000.0; // PosZ
renderValues[3] = readValues[6]; // AbsMag
renderValues[4] = readValues[3]; // color
renderValues[5] = readValues[13] * readValues[16]; // Vel X
renderValues[6] = readValues[14] * readValues[16]; // Vel Y
renderValues[7] = readValues[15] * readValues[16]; // Vel Z
fullData.insert(fullData.end(), renderValues.begin(), renderValues.end());
}
else {
nNullArr++;
}
} while (!fileStream.eof());
LINFO(fmt::format("{} out of {} read stars were null arrays", nNullArr, nStars));
return fullData;
}
// This is pretty annoying, the read method is not derived from the HDU class
// in CCfits - need to explicitly cast to the sub classes to access read
template<typename T>
const std::shared_ptr<ImageData<T>> FitsFileReader::readImageInternal(ExtHDU& image) {
try {
std::valarray<T> contents;
image.read(contents);
ImageData<T> im = { std::move(contents), image.axis(0), image.axis(1) };
return std::make_shared<ImageData<T>>(im);
} catch (const FitsException& e){
LERROR("Could not read FITS image EXTHDU. " + e.message() );
}
return nullptr;
}
template<typename T>
const std::shared_ptr<ImageData<T>> FitsFileReader::readImageInternal(PHDU& image) {
try {
std::valarray<T> contents;
image.read(contents);
ImageData<T> im = { std::move(contents), image.axis(0), image.axis(1) };
return std::make_shared<ImageData<T>>(im);
} catch (const FitsException& e){
LERROR("Could not read FITS image PHDU. " + e.message() );
}
return nullptr;
}
} // namespace openspace

View File

@@ -0,0 +1,71 @@
##########################################################################################
# #
# OpenSpace #
# #
# Copyright (c) 2014-2018 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
# without restriction, including without limitation the rights to use, copy, modify, #
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to #
# permit persons to whom the Software is furnished to do so, subject to the following #
# conditions: #
# #
# The above copyright notice and this permission notice shall be included in all copies #
# or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A #
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF #
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE #
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${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}/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/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(
"Gaia"
gaia
STATIC
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
)

View File

@@ -0,0 +1,72 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/gaiamodule.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>
namespace openspace {
GaiaModule::GaiaModule() : OpenSpaceModule(Name) {}
void GaiaModule::internalInitialize(const ghoul::Dictionary&) {
auto fRenderable = FactoryManager::ref().factory<Renderable>();
ghoul_assert(fRenderable, "No renderable factory existed");
fRenderable->registerClass<RenderableGaiaStars>("RenderableGaiaStars");
auto fTask = FactoryManager::ref().factory<Task>();
ghoul_assert(fRenderable, "No task factory existed");
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_GAIAMISSION}/scripts/filtering.lua")
};
return res;
}
} // namespace openspace

50
modules/gaia/gaiamodule.h Normal file
View File

@@ -0,0 +1,50 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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___GAIAMODULE___H__
#define __OPENSPACE_MODULE_GAIA___GAIAMODULE___H__
#include <openspace/util/openspacemodule.h>
#include <openspace/documentation/documentation.h>
namespace openspace {
class GaiaModule : public OpenSpaceModule {
public:
constexpr static const char* Name = "Gaia";
GaiaModule();
virtual ~GaiaModule() = default;
std::vector<documentation::Documentation> documentations() const override;
scripting::LuaLibrary luaLibrary() const override;
private:
void internalInitialize(const ghoul::Dictionary&) override;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___GAIAMODULE___H__

View File

@@ -0,0 +1,4 @@
set (OPENSPACE_DEPENDENCIES
fitsfilereader
globebrowsing
)

View File

@@ -0,0 +1,54 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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___GAIAOPTIONS___H__
#define __OPENSPACE_MODULE_GAIA___GAIAOPTIONS___H__
namespace openspace::gaia {
enum RenderOption {
Static = 0,
Color = 1,
Motion = 2
};
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
};
} // namespace openspace::gaiamission
#endif // __OPENSPACE_MODULE_GAIA___GAIAOPTIONS___H__

View File

@@ -0,0 +1,86 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/rendering/octreeculler.h>
#include <ghoul/glm.h>
#include <ghoul/logging/logmanager.h>
namespace {
constexpr const char* _loggerCat = "OctreeCuller";
} // namespace
namespace openspace {
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);
}
void expand(globebrowsing::AABB3& bb, const glm::vec3& p) {
bb.min = glm::min(bb.min, p);
bb.max = glm::max(bb.max, p);
}
} // namespace
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);
}
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

View File

@@ -0,0 +1,76 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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___OCTREECULLER___H__
#define __OPENSPACE_MODULE_GAIA___OCTREECULLER___H__
#include <modules/globebrowsing/src/basictypes.h>
#include <vector>
// TODO: Move /geometry/* to libOpenSpace so as not to depend on globebrowsing.
namespace openspace {
/**
* 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 OctreeCuller {
public:
/**
* \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);
~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:
/**
* 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_GAIA___OCTREECULLER___H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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();
/**
* 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(std::shared_ptr<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(std::shared_ptr<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(std::shared_ptr<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(std::shared_ptr<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(
std::shared_ptr<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(
std::shared_ptr<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(std::shared_ptr<OctreeNode> node,
gaia::RenderOption option);
/**
* Clear data from node and its descendants and shrink vectors to deallocate memory.
*/
void clearNodeData(std::shared_ptr<OctreeNode> node);
/**
* Contruct default children nodes for specified node.
*/
void createNodeChildren(std::shared_ptr<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(std::shared_ptr<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(std::shared_ptr<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, std::shared_ptr<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, std::shared_ptr<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,
std::shared_ptr<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(std::shared_ptr<OctreeManager::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(std::shared_ptr<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(std::shared_ptr<OctreeManager::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

View File

@@ -0,0 +1,216 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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();
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;
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__

View File

@@ -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

View File

@@ -0,0 +1,114 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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);
}

View File

@@ -0,0 +1,132 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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();
}

View File

@@ -0,0 +1,123 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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;
}

View File

@@ -0,0 +1,98 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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);
}

View File

@@ -0,0 +1,76 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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"
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(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;
uniform dmat4 view;
uniform float viewScaling;
uniform float cutOffThreshold;
void main() {
ge_brightness = vs_brightness[0];
ge_starDistFromSun = vs_starDistFromSun[0];
ge_cameraDistFromSun = vs_cameraDistFromSun[0];
vec4 viewPosition = vec4(view * vs_gPosition[0]);
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();
}

View File

@@ -0,0 +1,186 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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);
}
}

View File

@@ -0,0 +1,53 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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;
const float DEFAULT_DEPTH = 3.08567758e19; // 1000 Pc
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;
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;
}

View File

@@ -0,0 +1,178 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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;
}

View File

@@ -0,0 +1,34 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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__
in vec3 in_position;
out vec2 uv;
void main() {
uv = (in_position.xy + 1.0) / 2.0;
gl_Position = vec4(in_position, 1.0);
}

View File

@@ -0,0 +1,119 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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);
}
}

View File

@@ -0,0 +1,817 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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;
}
}
ConstructOctreeTask::~ConstructOctreeTask() {}
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& progressCallback) {
progressCallback(0.0f);
if (_singleFileInput) {
constructOctreeFromSingleFile(progressCallback);
}
else {
constructOctreeFromFolder(progressCallback);
}
progressCallback(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

View File

@@ -0,0 +1,143 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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();
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__

View File

@@ -0,0 +1,265 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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(const std::string& filePath,
const std::vector<std::string>& allColumns, int firstRow,
int lastRow, size_t nDefaultCols, int nValuesPerStar,
std::shared_ptr<FitsFileReader> fitsReader)
: _inFilePath(filePath)
, _allColumns(allColumns)
, _firstRow(firstRow)
, _lastRow(lastRow)
, _nDefaultCols(nDefaultCols)
, _nValuesPerStar(nValuesPerStar)
, _fitsFileReader(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.0 / 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.0 * 4.74 * radiusInKiloParsec * pmVecGal.x;
float tanVelY = 1000.0 * 4.74 * radiusInKiloParsec * pmVecGal.y;
float tanVelZ = 1000.0 * 4.74 * 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.0 * radial_vel[i] * rGal.x;
float radVelY = 1000.0 * radial_vel[i] * rGal.y;
float radVelZ = 1000.0 * 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

View File

@@ -0,0 +1,71 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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(const std::string& filePath, const 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__

View File

@@ -0,0 +1,392 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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& progressCallback) {
progressCallback(0.f);
if (_singleFileProcess) {
readSingleFitsFile(progressCallback);
}
else {
readAllFitsFilesFromFolder(progressCallback);
}
progressCallback(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

View File

@@ -0,0 +1,82 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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__

View File

@@ -0,0 +1,122 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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& progressCallback) {
progressCallback(0.f);
int32_t nRenderValues = 0;
FitsFileReader fileReader(false);
std::vector<float> fullData = fileReader.readSpeckFile(_inFilePath, nRenderValues);
progressCallback(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));
}
progressCallback(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

View File

@@ -0,0 +1,50 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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___READSPECKTASK___H__
#define __OPENSPACE_MODULE_GAIA___READSPECKTASK___H__
#include <openspace/util/task.h>
namespace openspace {
namespace documentation { struct Documentation; }
class ReadSpeckTask : public Task {
public:
ReadSpeckTask(const ghoul::Dictionary& dictionary);
virtual ~ReadSpeckTask() = default;
std::string description() override;
void perform(const Task::ProgressCallback& onProgress) override;
static documentation::Documentation Documentation();
private:
std::string _inFilePath;
std::string _outFilePath;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_GAIA___READSPECKTASK___H__

View File

@@ -33,12 +33,6 @@ using json = nlohmann::json;
namespace openspace::properties {
namespace {
} // namespace
void to_json(json& j, const Property& p) {
std::string description = p.generateBaseJsonDescription();
json desc = json::parse(description);

View File

@@ -34,6 +34,7 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/translation/keplertranslation.h
${CMAKE_CURRENT_SOURCE_DIR}/translation/spicetranslation.h
${CMAKE_CURRENT_SOURCE_DIR}/translation/tletranslation.h
${CMAKE_CURRENT_SOURCE_DIR}/translation/horizonstranslation.h
${CMAKE_CURRENT_SOURCE_DIR}/rotation/spicerotation.h
)
source_group("Header Files" FILES ${HEADER_FILES})
@@ -48,6 +49,7 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/translation/keplertranslation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/translation/spicetranslation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/translation/tletranslation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/translation/horizonstranslation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rotation/spicerotation.cpp
)
source_group("Source Files" FILES ${SOURCE_FILES})

View File

@@ -120,4 +120,4 @@ Fragment getFragment() {
}
return frag;
}
}

View File

@@ -105,4 +105,4 @@ void main() {
}
EndPrimitive();
}
}

View File

@@ -56,4 +56,4 @@ void main() {
position = vec4(1E-19, 1E-19, 1E-19, 1.0) * vs_gPosition;
gl_Position = position;
}
}

View File

@@ -32,6 +32,7 @@
#include <modules/space/translation/keplertranslation.h>
#include <modules/space/translation/spicetranslation.h>
#include <modules/space/translation/tletranslation.h>
#include <modules/space/translation/horizonstranslation.h>
#include <modules/space/rotation/spicerotation.h>
#include <openspace/documentation/documentation.h>
#include <openspace/rendering/renderable.h>
@@ -86,6 +87,7 @@ void SpaceModule::internalInitialize(const ghoul::Dictionary&) {
fTranslation->registerClass<KeplerTranslation>("KeplerTranslation");
fTranslation->registerClass<SpiceTranslation>("SpiceTranslation");
fTranslation->registerClass<TLETranslation>("TLETranslation");
fTranslation->registerClass<HorizonsTranslation>("HorizonsTranslation");
auto fRotation = FactoryManager::ref().factory<Rotation>();
ghoul_assert(fRotation, "Rotation factory was not created");
@@ -111,6 +113,7 @@ std::vector<documentation::Documentation> SpaceModule::documentations() const {
SpiceTranslation::Documentation(),
KeplerTranslation::Documentation(),
TLETranslation::Documentation(),
HorizonsTranslation::Documentation(),
planetgeometry::PlanetGeometry::Documentation(),
planetgeometry::SimpleSphereGeometry::Documentation()
};

View File

@@ -0,0 +1,189 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/space/translation/horizonstranslation.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/lua/ghoul_lua.h>
#include <ghoul/lua/lua_helper.h>
#include <fstream>
namespace {
constexpr const char* _loggerCat = "HorizonsTranslation";
} // namespace
namespace {
constexpr openspace::properties::Property::PropertyInfo HorizonsTextFileInfo = {
"HorizonsTextFile",
"Horizons Text File",
"This value is the path to the text file generated by Horizons with observer "
"range and Galactiv longitude and latitude for different timestamps."
};
} // namespace
namespace openspace {
documentation::Documentation HorizonsTranslation::Documentation() {
using namespace documentation;
return {
"Horizons Translation",
"base_transform_translation_horizons",
{
{
"Type",
new StringEqualVerifier("HorizonsTranslation"),
Optional::No
},
{
HorizonsTextFileInfo.identifier,
new StringVerifier,
Optional::No,
HorizonsTextFileInfo.description
}
}
};
}
HorizonsTranslation::HorizonsTranslation()
: _horizonsTextFile(HorizonsTextFileInfo)
{
addProperty(_horizonsTextFile);
_horizonsTextFile.onChange([&](){
requireUpdate();
_fileHandle = std::make_unique<ghoul::filesystem::File>(_horizonsTextFile);
_fileHandle->setCallback([&](const ghoul::filesystem::File&) {
requireUpdate();
notifyObservers();
});
readHorizonsTextFile(_horizonsTextFile);
});
}
HorizonsTranslation::HorizonsTranslation(const ghoul::Dictionary& dictionary)
: HorizonsTranslation()
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"HorizonsTranslation"
);
_horizonsTextFile = absPath(
dictionary.value<std::string>(HorizonsTextFileInfo.identifier)
);
// Read specified file and store it in memory.
readHorizonsTextFile(_horizonsTextFile);
}
glm::dvec3 HorizonsTranslation::position(const UpdateData& data) const {
glm::dvec3 interpolatedPos = glm::dvec3(0.0);
auto lastBefore = _timeline.lastKeyframeBefore(data.time.j2000Seconds(), true);
auto firstAfter = _timeline.firstKeyframeAfter(data.time.j2000Seconds(), false);
if (lastBefore && firstAfter) {
// We're inbetween first and last value.
double timelineDiff = firstAfter->timestamp - lastBefore->timestamp;
double timeDiff = data.time.j2000Seconds() - lastBefore->timestamp;
double diff = (timelineDiff > DBL_EPSILON) ? timeDiff / timelineDiff : 0.0;
glm::dvec3 dir = firstAfter->data - lastBefore->data;
interpolatedPos = lastBefore->data + dir * diff;
}
else if (lastBefore) {
// Requesting a time after last value. Return last known position.
interpolatedPos = lastBefore->data;
}
else if (firstAfter) {
// Requesting a time before first value. Return last known position.
interpolatedPos = firstAfter->data;
}
return interpolatedPos;
}
void HorizonsTranslation::readHorizonsTextFile(const std::string& horizonsTextFilePath) {
std::ifstream fileStream(horizonsTextFilePath);
if (!fileStream.good()) {
LERROR(fmt::format(
"Failed to open Horizons text file '{}'", horizonsTextFilePath
));
return;
}
// The beginning of a Horizons file has a header with a lot of information about the
// query that we do not care about. Ignore everything until data starts, including
// the row marked by $$SOE (i.e. Start Of Ephemerides).
std::string line = "";
while (line[0] != '$') {
std::getline(fileStream, line);
}
// Read data line by line until $$EOE (i.e. End Of Ephemerides).
// Skip the rest of the file.
std::getline(fileStream, line);
while (line[0] != '$') {
std::stringstream str(line);
std::string date;
std::string time;
float range = 0;
float gLon = 0;
float gLat = 0;
// File is structured by:
// YYYY-MM-DD
// HH:MM:SS
// Range-to-observer (km)
// Range-delta (km/s) -- suppressed!
// Galactic Longitude (degrees)
// Galactic Latitude (degrees)
str >> date >> time >> range >> gLon >> gLat;
// Convert date and time to seconds after 2000
// and pos to Galactic positions in meter from Observer.
std::string timeString = date + " " + time;
double timeInJ2000 = Time::convertTime(timeString);
glm::dvec3 gPos = glm::dvec3(
1000 * range * cos(glm::radians(gLat)) * cos(glm::radians(gLon)),
1000 * range * cos(glm::radians(gLat)) * sin(glm::radians(gLon)),
1000 * range * sin(glm::radians(gLat))
);
// Add position to stored timeline.
_timeline.addKeyframe(timeInJ2000, gPos);
std::getline(fileStream, line);
}
fileStream.close();
}
} // namespace openspace

View File

@@ -0,0 +1,72 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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_SPACE___HORIZONSTRANSLATION___H__
#define __OPENSPACE_MODULE_SPACE___HORIZONSTRANSLATION___H__
#include <openspace/scene/translation.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/util/timeline.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/lua/luastate.h>
#include <memory>
namespace openspace {
namespace documentation { struct Documentation; }
/**
* The HorizonsTranslation is based on text files generated from NASA JPL HORIZONS Website
* (https://ssd.jpl.nasa.gov/horizons.cgi). The implementation expects a file with format:
* TIME(YYYY-MM-DD HH:MM:SS) Range(km) GalLon(degrees) GalLat(degrees)
* Range - The distance from target to observer. Chosen as "Observer range & range-rate"
* in "Table Setting". This also generates a delta that can be suppressed under "Optional
* observer-table settings" to limit file size. User must set output settings to
* kilometers.
* GalLon - Galactic Longitude. User must set output to Degrees in "Table Settings".
* GalLat - Galactic Latitude. User must set output to Degrees in "Table Settings".
*/
class HorizonsTranslation : public Translation {
public:
HorizonsTranslation();
HorizonsTranslation(const ghoul::Dictionary& dictionary);
glm::dvec3 position(const UpdateData& time) const override;
static documentation::Documentation Documentation();
private:
void readHorizonsTextFile(const std::string& _horizonsTextFilePath);
properties::StringProperty _horizonsTextFile;
std::unique_ptr<ghoul::filesystem::File> _fileHandle;
ghoul::lua::LuaState _state;
Timeline<glm::dvec3> _timeline;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_SPACE___HORIZONSTRANSLATION___H__

View File

@@ -36,6 +36,12 @@
#include <openspace/util/synchronizationwatcher.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/lua/luastate.h>
#include <string>
#include <fstream>
#include <chrono>
#include <thread>
namespace {

View File

@@ -92,7 +92,7 @@ void WebRenderHandler::OnPaint(CefRefPtr<CefBrowser> browser,
// Add the dirty rect bounds to the GPU texture dirty rect.
_lowerDirtyRectBound = glm::min(lowerUpdatingRectBound, _lowerDirtyRectBound);
_upperDirtyRectBound = glm::max(upperUpdatingRectBound, _upperDirtyRectBound);
_needsRepaint = false;
_needsRepaint = false;
}
void WebRenderHandler::updateTexture() {