Feature/speck loader (#1585)

* Implement a shared speckfile loader
* Apply new speck loader to RenderableBillboardsCloud, RenderablePlanesCloud, RenderablePoints
This commit is contained in:
Alexander Bock
2021-05-17 13:59:54 +02:00
committed by GitHub
parent 92432caed2
commit 205d4c9db1
16 changed files with 1416 additions and 2032 deletions
@@ -55,7 +55,6 @@ namespace {
"spriteTexture", "hasColorMap"
};
constexpr int8_t CurrentCacheVersion = 1;
constexpr double PARSEC = 0.308567756E17;
constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = {
@@ -197,26 +196,22 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary)
}
bool RenderablePoints::isReady() const {
return (_program != nullptr) && (!_fullData.empty());
return _program && (!_dataset.entries.empty());
}
void RenderablePoints::initialize() {
ZoneScoped
bool success = loadData();
if (!success) {
throw ghoul::RuntimeError("Error loading data");
_dataset = speck::data::loadFileWithCache(_speckFile);
if (_hasColorMapFile) {
readColorMapFile();
}
}
void RenderablePoints::initializeGL() {
ZoneScoped
// OBS: The ProgramObject name is later used to release the program as well, so the
// name parameter to requestProgramObject and the first parameter to
// buildRenderProgram has to be the same or an assertion will be thrown at the
// end of the program.
if (_hasSpriteTexture) {
_program = DigitalUniverseModule::ProgramObjectManager.request(
"RenderablePoints Sprite",
@@ -288,10 +283,7 @@ void RenderablePoints::render(const RenderData& data, RendererTasks&) {
glEnable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(_vao);
const GLsizei nAstronomicalObjects = static_cast<GLsizei>(
_fullData.size() / _nValuesPerAstronomicalObject
);
glDrawArrays(GL_POINTS, 0, nAstronomicalObjects);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_dataset.entries.size()));
glDisable(GL_PROGRAM_POINT_SIZE);
glBindVertexArray(0);
@@ -304,7 +296,7 @@ void RenderablePoints::update(const UpdateData&) {
if (_dataIsDirty) {
LDEBUG("Regenerating data");
createDataSlice();
std::vector<double> slice = createDataSlice();
if (_vao == 0) {
glGenVertexArrays(1, &_vao);
@@ -317,19 +309,13 @@ void RenderablePoints::update(const UpdateData&) {
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(
GL_ARRAY_BUFFER,
_slicedData.size() * sizeof(double),
&_slicedData[0],
slice.size() * sizeof(double),
slice.data(),
GL_STATIC_DRAW
);
GLint positionAttrib = _program->attributeLocation("in_position");
if (_hasColorMapFile) {
// const size_t nAstronomicalObjects = _fullData.size() /
// _nValuesPerAstronomicalObject;
// const size_t nValues = _slicedData.size() / nAstronomicalObjects;
// GLsizei stride = static_cast<GLsizei>(sizeof(double) * nValues);
glEnableVertexAttribArray(positionAttrib);
glVertexAttribLPointer(
positionAttrib, 4, GL_DOUBLE, sizeof(double) * 8, nullptr
@@ -341,8 +327,8 @@ void RenderablePoints::update(const UpdateData&) {
colorMapAttrib,
4,
GL_DOUBLE,
sizeof(double) * 8,
reinterpret_cast<void*>(sizeof(double) * 4)
8 * sizeof(double),
reinterpret_cast<void*>(4 * sizeof(double))
);
}
else {
@@ -381,120 +367,12 @@ void RenderablePoints::update(const UpdateData&) {
}
}
bool RenderablePoints::loadData() {
std::string cachedFile = FileSys.cacheManager()->cachedFilename(_speckFile);
bool hasCachedFile = std::filesystem::is_regular_file(cachedFile);
if (hasCachedFile) {
LINFO(fmt::format(
"Cached file '{}' used for Speck file '{}'",
cachedFile, _speckFile
));
bool success = loadCachedFile(cachedFile);
if (success) {
if (_hasColorMapFile) {
success &= readColorMapFile();
}
return success;
}
else {
FileSys.cacheManager()->removeCacheFile(_speckFile);
// Intentional fall-through to the 'else' to generate the cache file for
// the next run
}
}
else {
LINFO(fmt::format("Cache for Speck file '{}' not found", _speckFile));
}
LINFO(fmt::format("Loading Speck file '{}'", _speckFile));
bool success = readSpeckFile();
if (!success) {
return false;
}
LINFO("Saving cache");
success = saveCachedFile(cachedFile);
if (_hasColorMapFile) {
success &= readColorMapFile();
}
return success;
}
bool RenderablePoints::readSpeckFile() {
std::ifstream file(_speckFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile));
return false;
}
_nValuesPerAstronomicalObject = 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', and 'texture')
std::string line;
while (true) {
std::streampos position = file.tellg();
std::getline(file, line);
if (line[0] == '#' || line.empty()) {
continue;
}
if (line.substr(0, 7) != "datavar" &&
line.substr(0, 10) != "texturevar" &&
line.substr(0, 7) != "texture")
{
// we read a line that doesn't belong to the header, so we have to jump
// back before the beginning of the current line
file.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 >> _nValuesPerAstronomicalObject;
// We want the number, but the index is 0 based
_nValuesPerAstronomicalObject += 1;
}
}
// X Y Z are not counted in the Speck file indices
_nValuesPerAstronomicalObject += 3;
do {
std::vector<float> values(_nValuesPerAstronomicalObject);
std::getline(file, line);
std::stringstream str(line);
for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) {
str >> values[i];
}
_fullData.insert(_fullData.end(), values.begin(), values.end());
} while (!file.eof());
return true;
}
bool RenderablePoints::readColorMapFile() {
void RenderablePoints::readColorMapFile() {
std::ifstream file(_colorMapFile);
if (!file.good()) {
LERROR(fmt::format("Failed to open Color Map file '{}'", _colorMapFile));
return false;
throw ghoul::RuntimeError(fmt::format(
"Failed to open Color Map file '{}'", _colorMapFile
));
}
std::size_t numberOfColors = 0;
@@ -519,7 +397,9 @@ bool RenderablePoints::readColorMapFile() {
break;
}
else if (file.eof()) {
return false;
throw ghoul::RuntimeError(fmt::format(
"Failed to load colors from Color Map file '{}'", _colorMapFile
));
}
}
@@ -528,108 +408,26 @@ bool RenderablePoints::readColorMapFile() {
std::stringstream str(line);
glm::vec4 color;
for (int j = 0; j < 4; ++j) {
str >> color[j];
}
str >> color.r >> color.g >> color.b >> color.a;
_colorMapData.push_back(color);
}
return true;
}
bool RenderablePoints::loadCachedFile(const std::string& file) {
std::ifstream fileStream(file, std::ifstream::binary);
if (fileStream.good()) {
int8_t version = 0;
fileStream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed: deleting old cache");
fileStream.close();
if (std::filesystem::is_regular_file(file)) {
std::filesystem::remove(file);
}
return false;
}
int32_t nValues = 0;
fileStream.read(reinterpret_cast<char*>(&nValues), sizeof(int32_t));
fileStream.read(
reinterpret_cast<char*>(&_nValuesPerAstronomicalObject),
sizeof(int32_t)
);
_fullData.resize(nValues);
fileStream.read(reinterpret_cast<char*>(
&_fullData[0]),
nValues * sizeof(_fullData[0])
);
const bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format(
"Error opening file '{}' for loading cache file",
file
));
return false;
}
}
bool RenderablePoints::saveCachedFile(const std::string& file) const {
std::ofstream fileStream(file, std::ofstream::binary);
if (fileStream.good()) {
fileStream.write(
reinterpret_cast<const char*>(&CurrentCacheVersion),
sizeof(int8_t)
);
const int32_t nValues = static_cast<int32_t>(_fullData.size());
if (nValues == 0) {
LERROR("Error writing cache: No values were loaded");
return false;
}
fileStream.write(reinterpret_cast<const char*>(&nValues), sizeof(int32_t));
const int32_t nValuesPerAstronomicalObject = static_cast<int32_t>(
_nValuesPerAstronomicalObject
);
fileStream.write(
reinterpret_cast<const char*>(&nValuesPerAstronomicalObject),
sizeof(int32_t)
);
const size_t nBytes = nValues * sizeof(_fullData[0]);
fileStream.write(reinterpret_cast<const char*>(&_fullData[0]), nBytes);
const bool success = fileStream.good();
return success;
}
else {
LERROR(fmt::format("Error opening file '{}' for save cache file", file));
return false;
}
}
void RenderablePoints::createDataSlice() {
_slicedData.clear();
std::vector<double> RenderablePoints::createDataSlice() {
std::vector<double> slice;
if (_hasColorMapFile) {
_slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject));
slice.reserve(8 * _dataset.entries.size());
}
else {
_slicedData.reserve(4 * (_fullData.size()/_nValuesPerAstronomicalObject));
slice.reserve(4 * _dataset.entries.size());
}
int colorIndex = 0;
for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) {
glm::dvec3 p = glm::dvec3(
_fullData[i + 0],
_fullData[i + 1],
_fullData[i + 2]
);
for (const speck::Dataset::Entry& e : _dataset.entries) {
glm::dvec3 p = e.position;
// Converting untis
// Converting units
if (_unit == Kilometer) {
p *= 1E3;
}
@@ -653,15 +451,15 @@ void RenderablePoints::createDataSlice() {
if (_hasColorMapFile) {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(position[j]);
slice.push_back(position[j]);
}
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(_colorMapData[colorIndex][j]);
slice.push_back(_colorMapData[colorIndex][j]);
}
}
else {
for (int j = 0; j < 4; ++j) {
_slicedData.push_back(position[j]);
slice.push_back(position[j]);
}
}
@@ -669,6 +467,8 @@ void RenderablePoints::createDataSlice() {
0 :
colorIndex + 1;
}
return slice;
}
} // namespace openspace