예제 #1
0
ScreenSpaceImage::ScreenSpaceImage(const ghoul::Dictionary& dictionary)
    : ScreenSpaceRenderable(dictionary)
    , _texturePath("texturePath", "Texture path", "")
    , _downloadImage(false)
{
    std::string name;
    if (dictionary.getValue(KeyName, name)) {
        setName(name);
    } else {
        static int id = 0;
        setName("ScreenSpaceImage " + std::to_string(id));
        ++id;
    }

    addProperty(_texturePath);

    std::string texturePath;
    if (dictionary.getValue(KeyTexturePath, texturePath)) {
        _texturePath = texturePath;
        _texturePath.onChange([this](){ loadTexture(); });
    }

    if (dictionary.getValue(KeyUrl, _url)) {
        _downloadImage = true;
    }
}
예제 #2
0
void InteractionHandler::setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict) {
    bool readSuccessful = true;

    std::string focus;
    glm::dvec3 cameraPosition;
    glm::dvec4 cameraRotation; // Need to read the quaternion as a vector first.

    readSuccessful &= cameraDict.getValue(KeyFocus, focus);
    readSuccessful &= cameraDict.getValue(KeyPosition, cameraPosition);
    readSuccessful &= cameraDict.getValue(KeyRotation, cameraRotation);

    if (!readSuccessful) {
        throw ghoul::RuntimeError(
            "Position, Rotation and Focus need to be defined for camera dictionary.");
    }

    SceneGraphNode* node = sceneGraphNode(focus);
    if (!node) {
        throw ghoul::RuntimeError(
            "Could not find a node in scenegraph called '" + focus + "'");
    }

    // Set state
    setFocusNode(node);
    _camera->setPositionVec3(cameraPosition);
    _camera->setRotation(glm::dquat(
        cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));
}
예제 #3
0
InstrumentDecoder::InstrumentDecoder(const ghoul::Dictionary& dictionary)
{
	bool success = dictionary.getValue(keyDetector, _type);
	ghoul_assert(success, "Instrument has not provided detector type");
	for_each(_type.begin(), _type.end(), [](char& in){ in = ::toupper(in); });

	if (!dictionary.hasKeyAndValue<std::string>(keyStopCommand) && _type == "SCANNER"){
		LWARNING("Scanner must provide stop command, please check mod file.");
	}else{
		dictionary.getValue(keyStopCommand, _stopCommand);
	}

	std::vector<std::string> spice;
	ghoul::Dictionary spiceDictionary;
	success = dictionary.getValue(keySpice, spiceDictionary);
	ghoul_assert(success, "Instrument did not provide spice ids");


	_spiceIDs.resize(spiceDictionary.size());
	for (int i = 0; i < _spiceIDs.size(); ++i) {
		std::string id;
		spiceDictionary.getValue(std::to_string(i + 1), id);
		_spiceIDs[i] = id;
	}
}
예제 #4
0
ModelGeometry::ModelGeometry(const ghoul::Dictionary& dictionary)
    : _parent(nullptr)
    , _magnification("magnification", "Magnification", 1.f, 0.f, 10.f)
    , _mode(GL_TRIANGLES)
{
    setName("ModelGeometry");

    std::string name;
    bool success = dictionary.getValue(keyName, name);
    ghoul_assert(success, "Name tag was not present");

    if (dictionary.hasKeyAndValue<double>(keySize))
        _magnification = static_cast<float>(dictionary.value<double>(keySize));

    success = dictionary.getValue(keyGeomModelFile, _file);
    if (!success) {
        LERROR("Geometric Model file of '" << name << "' did not provide a key '"
            << keyGeomModelFile << "'");
    }
    _file = FileSys.absolutePath(_file);

    if (!FileSys.fileExists(_file, ghoul::filesystem::FileSystem::RawPath::Yes))
        LERROR("Could not load the geometric model file '" << _file << "': File not found");
    

    addProperty(_magnification);
}
RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& dictionary)
	: Renderable(dictionary)
    , _texturePath("")
    , _planeIsDirty(false)
	, _shader(nullptr)
    , _textureIsDirty(false)
	, _texture(nullptr)
	, _quad(0)
	, _vertexPositionBuffer(0)
	, _name("ImagePlane")
	, _previousTime(0)
	, _moving(false)
	, _hasImage(false)
{
	dictionary.getValue(KeySpacecraft, _spacecraft);
	dictionary.getValue(KeyInstrument, _instrument);
	dictionary.getValue(KeyMoving, _moving);
	dictionary.getValue(KeyName, _name);
	dictionary.getValue(KeyTarget, _defaultTarget);

	std::string texturePath = "";
	bool success = dictionary.getValue(KeyTexture, _texturePath);
	if (success) {
		_texturePath = absPath(_texturePath);
		_textureFile = new ghoul::filesystem::File(_texturePath);
	}

	loadTexture();
}
예제 #6
0
MissionPhase::MissionPhase(const ghoul::Dictionary& dict) {
    _name = dict.value<std::string>(KeyName);
    dict.getValue(KeyDescription, _description);
    
    ghoul::Dictionary childDicts;
    if (dict.getValue(KeyPhases, childDicts)) {
        // This is a nested mission phase
        _subphases.reserve(childDicts.size());
        for (size_t i = 0; i < childDicts.size(); ++i) {
            std::string key = std::to_string(i + 1);
            _subphases.emplace_back(childDicts.value<ghoul::Dictionary>(key));
        }

        // Ensure subphases are sorted
        std::stable_sort(
            _subphases.begin(),
            _subphases.end(),
            [](const MissionPhase& a, const MissionPhase& b) {
                return a.timeRange().start < b.timeRange().start;
            }
        );

        // Calculate the total time range of all subphases
        TimeRange timeRangeSubPhases;
        timeRangeSubPhases.start = _subphases[0].timeRange().start;
        timeRangeSubPhases.end = _subphases.back().timeRange().end;

        // user may specify an overall time range. In that case expand this timerange.
        ghoul::Dictionary timeRangeDict;
        if (dict.getValue(KeyTimeRange, timeRangeDict)) {
            TimeRange overallTimeRange(timeRangeDict);
            if (!overallTimeRange.includes(timeRangeSubPhases)) {
                throw ghoul::RuntimeError(
                    "User specified time range must at least include its subphases'",
                    "Mission (" + _name + ")"
                );
            }

            _timeRange.include(overallTimeRange);
        }
        else {
            // Its OK to not specify an overall time range, the time range for the 
            // subphases will simply be used. 
            _timeRange.include(timeRangeSubPhases);
        }
    }
    else {
        ghoul::Dictionary timeRangeDict;
        if (dict.getValue(KeyTimeRange, timeRangeDict)) {
            _timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse
        }
        else {
            throw ghoul::RuntimeError(
                "If there are no subphases specified, the time range has to be specified",
                "Mission (" + _name + ")"
            );
        }
    }
}
예제 #7
0
TargetDecoder::TargetDecoder(const ghoul::Dictionary& dictionary) :_type("TARGET")
{
    _names.resize(dictionary.size());
    for (int i = 0; i < _names.size(); ++i) {
        std::string readMe;
        dictionary.getValue(std::to_string(i + 1), readMe);
        _names[i] = readMe;
    }
}
예제 #8
0
RenderablePath::RenderablePath(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _lineWidth("lineWidth", "Line Width", 2.f, 1.f, 20.f)
    , _drawLine("drawline", "Draw Line", false)
    , _programObject(nullptr)
    , _successfullDictionaryFetch(true)
    , _vaoID(0)
    , _vBufferID(0)
    , _needsSweep(true)
    , _start(0.0)
    , _stop(0.0)
{
    _successfullDictionaryFetch &= dictionary.getValue(keyBody, _target);
    _successfullDictionaryFetch &= dictionary.getValue(keyObserver, _observer);
    _successfullDictionaryFetch &= dictionary.getValue(keyFrame, _frame);
    _successfullDictionaryFetch &= dictionary.getValue(keyTimeSteps, _increment);

    float fPointSteps; // Dictionary can not pick out ints...
    if (!dictionary.getValue(keyPointSteps, fPointSteps))
        fPointSteps = 4;
    _pointSteps = fPointSteps;

    glm::vec3 color(0.f);
    if (dictionary.hasKeyAndValue<glm::vec3>(keyColor))
        dictionary.getValue(keyColor, color);
    _lineColor = color;

    bool drawLine = false;
    if (dictionary.hasKeyAndValue<bool>(keyDrawLine))
        dictionary.getValue(keyDrawLine, drawLine);
    _drawLine = drawLine;
    addProperty(_drawLine);
    addProperty(_lineWidth);
    _distanceFade = 1.0;
}
RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _program(nullptr)
    , _imageSequenceTime(-1.f)
    , _vao(0)
    , _vbo(0)
{
    dictionary.getValue(KeySource, _source);
    dictionary.getValue(KeyTarget, _target);
    dictionary.getValue(KeyInstrument, _instrumentName);
    dictionary.getValue(KeyReferenceFrame, _referenceFrame);


    if (dictionary.hasKeyAndValue<glm::vec3>(keyColor))
        dictionary.getValue(keyColor, _lineColor);
    else
        _lineColor = glm::vec3(1);
}
예제 #10
0
SimpleSphereGeometry::SimpleSphereGeometry(const ghoul::Dictionary& dictionary)
    : PlanetGeometry()
    , _realRadius("radius", "Radius", glm::vec4(1.f, 1.f, 1.f, 0.f), glm::vec4(-10.f, -10.f, -10.f, -20.f),
                glm::vec4(10.f, 10.f, 10.f, 20.f))
    , _segments("segments", "Segments", 20, 1, 5000)
    , _sphere(nullptr)
{
    using constants::simplespheregeometry::keyRadius;
    using constants::simplespheregeometry::keySegments;

    // The name is passed down from the SceneGraphNode
    bool success = dictionary.getValue(SceneGraphNode::KeyName, _name);
    assert(success);
    
    glm::vec4 radius;
    success = dictionary.getValue(keyRadius, _modRadius);
    if (!success) {
        LERROR("SimpleSphereGeometry of '" << _name << "' did not provide a key '"
                                           << keyRadius << "'");
    }
    else {
        radius[0] = _modRadius[0];
        radius[1] = _modRadius[0];
        radius[2] = _modRadius[0];
        radius[3] = _modRadius[1];
        _realRadius = radius; // In case the kernels does not supply a real
    }

    double segments;
    success = dictionary.getValue(keySegments, segments);
    if (!success) {
        LERROR("SimpleSphereGeometry of '" << _name << "' did not provide a key '"
                                           << keySegments << "'");
    }
    else
        _segments = static_cast<int>(segments);
    // The shader need the radii values but they are not changeable runtime
    // TODO: Possibly add a scaling property @AA
    addProperty(_realRadius);
    // Changing the radius/scaling should affect the shader but not the geometry? @AA
    //_radius.onChange(std::bind(&SimpleSphereGeometry::createSphere, this));
    addProperty(_segments);
    _segments.onChange(std::bind(&SimpleSphereGeometry::createSphere, this));
}
예제 #11
0
DynamicEphemeris::DynamicEphemeris(const ghoul::Dictionary& dictionary)
    : _position(0.f, 0.f, 0.f, 0.f)
{
    const bool hasPosition = dictionary.hasKeyAndValue<glm::vec4>(KeyPosition);
    if (hasPosition) {
        glm::vec4 tmp;
        dictionary.getValue(KeyPosition, tmp);
        _position = tmp;
    }
}
예제 #12
0
StaticRotation::StaticRotation(const ghoul::Dictionary& dictionary)
    : _rotationMatrix(1.0)
{
    const bool hasEulerAngles = dictionary.hasKeyAndValue<glm::dvec3>(KeyEulerAngles);
    if (hasEulerAngles) {
        glm::dvec3 tmp;
        dictionary.getValue(KeyEulerAngles, tmp);
        _rotationMatrix = glm::mat3_cast(glm::dquat(tmp));
    }
}
예제 #13
0
void ScriptScheduler::loadScripts(const ghoul::Dictionary& dictionary) {
    // Check if all of the scheduled scripts are formed correctly
    documentation::testSpecificationAndThrow(
        Documentation(),
        dictionary,
        "ScriptScheduler"
    );
    
    // Create all the scheduled script first
    std::vector<ScheduledScript> scheduledScripts;
    for (size_t i = 1; i <= dictionary.size(); ++i) {
        const ghoul::Dictionary& timedScriptDict = dictionary.value<ghoul::Dictionary>(
            std::to_string(i)
        );
        scheduledScripts.emplace_back(timedScriptDict);
    }

    // Sort scripts by time; use a stable_sort as the user might have had an intention
    // specifying multiple scripts for the same time in a specific order
    std::stable_sort(
        scheduledScripts.begin(),
        scheduledScripts.end(),
        [](const ScheduledScript& lhs, const ScheduledScript& rhs) {
            return lhs.time < rhs.time;
        }
    );
    
    // Move the scheduled scripts into their SOA alignment
    // For the forward scripts, this is the forwards direction
    // For the backward scripts, we insert them in the opposite order so that we can still
    // return forward iterators to them in the progressTo method
    for (ScheduledScript& script : scheduledScripts) {
        _timings.push_back(script.time);

        _forwardScripts.push_back(std::move(script.forwardScript));

        _backwardScripts.insert(
            _backwardScripts.begin(),
            std::move(script.backwardScript)
        );
    }

    // Ensure _currentIndex and _currentTime is accurate after new scripts was added
    double lastTime = _currentTime;
    rewind();
    progressTo(lastTime);
    
    ghoul_assert(
        (_timings.size() == _forwardScripts.size()) &&
        (_timings.size() == _backwardScripts.size()),
        "The SOA data structure has been mistreated and has different number of values"
    );
}
예제 #14
0
    SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary) {
        _fontSize = 50;
        ghoul::fontrendering::FontManager& fm = OsEng.fontManager();
        _font = fm.font("Mono", _fontSize);

        glm::dvec3 radii(1,1,1);
        if (!dictionary.getValue(KeyRadii, radii)) {
            throw std::runtime_error("Must define key '" + KeyRadii + "'");
        }
        _ellipsoid = Ellipsoid(radii);

        _backgroundTile.status = Tile::Status::Unavailable;
        std::string backgroundImagePath;
        if (dictionary.getValue(KeyBackgroundImagePath, backgroundImagePath)) {
            using namespace ghoul::io;
            std::string imgAbsPath = absPath(backgroundImagePath);
            _backgroundTile.texture = TextureReader::ref().loadTexture(imgAbsPath);
            _backgroundTile.texture->uploadTexture();
            _backgroundTile.texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
            _backgroundTile.status = Tile::Status::OK;
        }

    }
예제 #15
0
    TileProviderManager::TileProviderManager(
        const ghoul::Dictionary& textureCategoriesDictionary,
        const ghoul::Dictionary& textureInitDictionary){
        // Create all the categories of tile providers
        for (size_t i = 0; i < textureCategoriesDictionary.size(); i++) {
            ghoul::Dictionary texturesDict = textureCategoriesDictionary.value<ghoul::Dictionary>(
                LayeredTextures::TEXTURE_CATEGORY_NAMES[i]);

            ghoul_assert(texturesDict.size() <=
                LayeredTextures::MAX_NUM_TEXTURES_PER_CATEGORY,
                "Too many textures! Number of textures per category must be less than or equal to "
                << LayeredTextures::MAX_NUM_TEXTURES_PER_CATEGORY);

            TileProviderInitData initData;

            if (i == LayeredTextures::ColorTextures ||
                i == LayeredTextures::NightTextures ||
                i == LayeredTextures::WaterMasks    ||
                i == LayeredTextures::GrayScaleOverlays) {
                initData.minimumPixelSize = textureInitDictionary.value<double>("ColorTextureMinimumSize");
            }
            else if (i == LayeredTextures::Overlays) {
                initData.minimumPixelSize = textureInitDictionary.value<double>("OverlayMinimumSize");
            }
            else if (i == LayeredTextures::HeightMaps) {
                initData.minimumPixelSize = textureInitDictionary.value<double>("HeightMapMinimumSize");
            }
            else {
                initData.minimumPixelSize = 512;
            }

            initData.threads = 1;
            initData.cacheSize = 5000;
            initData.framesUntilRequestQueueFlush = 60;
            // Only preprocess height maps.
            initData.preprocessTiles = i == LayeredTextures::HeightMaps;

            initTexures(
                _layerCategories[i].tileProviders,
                texturesDict,
                initData);

            // init level blending to be true
            _layerCategories[i].levelBlendingEnabled = true;
        }
    }
예제 #16
0
    void TileProviderManager::initTexures(std::vector<NamedTileProvider>& dest,
        const ghoul::Dictionary& texturesDict, const TileProviderInitData& initData)
    {
        // Create TileProviders for all textures within this category
        for (size_t i = 0; i < texturesDict.size(); i++) {
            std::string name, path;
            
            std::string dictKey = std::to_string(i + 1);
            ghoul::Dictionary texDict = texturesDict.value<ghoul::Dictionary>(dictKey);
            texDict.getValue("Name", name);
            texDict.getValue("FilePath", path);

            std::string type = "LRUCaching"; // if type is unspecified
            texDict.getValue("Type", type);

            TileProvider* tileProvider;
            auto tileProviderFactory = FactoryManager::ref().factory<TileProvider>();
            try {
                tileProvider = tileProviderFactory->create(type, texDict);
            }
            catch (const ghoul::RuntimeError& e) {
                LERROR(e.what());
                continue;
            }

            // Something else went wrong and no exception was thrown
            if (tileProvider == nullptr) {
                LERROR("Unable to create TileProvider '" << name << "' of type '"
                    << type << "'");
                continue;
            }

            bool enabled = false; // defaults to false if unspecified
            texDict.getValue("Enabled", enabled);

            dest.push_back({ name, std::shared_ptr<TileProvider>(tileProvider), enabled });
        }
    }
예제 #17
0
ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dictionary)
    : time(-std::numeric_limits<double>::max())
{
    std::string timeStr = dictionary.value<std::string>(KeyTime);
    time = Time::ref().convertTime(timeStr);
    
    // If a universal script is specified, retrieve it and add a ; as a separator so that
    // it can be added to the other scripts
    std::string universal;
    dictionary.getValue(KeyUniversalScript, universal);
    if (!universal.empty()) {
        universal += ";";
    }
    
    if (dictionary.hasKeyAndValue<std::string>(KeyForwardScript)) {
        forwardScript =
            universal + dictionary.value<std::string>(KeyForwardScript);
    }
    
    if (dictionary.hasKeyAndValue<std::string>(KeyBackwardScript)) {
        backwardScript =
            universal + dictionary.value<std::string>(KeyBackwardScript);
    }
}
예제 #18
0
    RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _stepSize("stepSize", "Step Size", 0.012, 0.0005, 0.05)
    , _pointStepSize("pointStepSize", "Point Step Size", 0.01, 0.01, 0.1)
    , _translation("translation", "Translation", glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0), glm::vec3(10.0))
    , _rotation("rotation", "Euler rotation", glm::vec3(0.0, 0.0, 0.0), glm::vec3(0), glm::vec3(6.28))
    , _enabledPointsRatio("nEnabledPointsRatio", "Enabled points", 0.2, 0, 1) {

    float stepSize;
    glm::vec3 scaling, translation, rotation;
    glm::vec4 color;
    ghoul::Dictionary volumeDictionary, pointsDictionary;

    if (dictionary.getValue("Translation", translation)) {
        _translation = translation;
    }
    if (dictionary.getValue("Rotation", rotation)) {
        _rotation = rotation;
    }
    if (dictionary.getValue("StepSize", stepSize)) {
        _stepSize = stepSize;
    }
    if (dictionary.getValue("Volume", volumeDictionary)) {
        std::string volumeFilename;
        if (volumeDictionary.getValue("Filename", volumeFilename)) {
            _volumeFilename = absPath(volumeFilename);
        } else {
            LERROR("No volume filename specified.");
        }
        glm::vec3 volumeDimensions;
        if (volumeDictionary.getValue("Dimensions", volumeDimensions)) {
            _volumeDimensions = static_cast<glm::ivec3>(volumeDimensions);
        } else {
            LERROR("No volume dimensions specified.");
        }
        glm::vec3 volumeSize;
        if (volumeDictionary.getValue("Size", volumeSize)) {
            _volumeSize = static_cast<glm::vec3>(volumeSize);
        }
        else {
            LERROR("No volume dimensions specified.");
        }

    } else {
        LERROR("No volume dictionary specified.");
    }
    if (dictionary.getValue("Points", pointsDictionary)) {
        std::string pointsFilename;
        if (pointsDictionary.getValue("Filename", pointsFilename)) {
            _pointsFilename = absPath(pointsFilename);
        } else {
            LERROR("No points filename specified.");
        }
        glm::vec3 pointsScaling;
        if (pointsDictionary.getValue("Scaling", pointsScaling)) {
            _pointScaling = static_cast<glm::vec3>(pointsScaling);
        }
        else {
            LERROR("No volume dimensions specified.");
        }
    } else {
        LERROR("No points dictionary specified.");
    }

}
예제 #19
0
RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _texture("texture", "Texture", -1, -1, 255)
    , _billboard("billboard", "Billboard", false)
    , _size("size", "Size", glm::vec2(1,1), glm::vec2(0.f), glm::vec2(1.f, 25.f))
    , _origin(Origin::Center)
    , _shader(nullptr)
    , _quad(0)
    , _vertexPositionBuffer(0)
{
    glm::vec2 size;
    dictionary.getValue("Size", size);
    _size = size;

    if (dictionary.hasKey("Name")){
        dictionary.getValue("Name", _nodeName);
    }

    if (dictionary.hasKey("Texture")) {
        int t;
        dictionary.getValue("Texture", t);
        _texture = t;
    }

    std::string origin;
    if (dictionary.getValue("Origin", origin)) {
        if (origin == "LowerLeft") {
            _origin = Origin::LowerLeft;
        }
        else if (origin == "LowerRight") {
            _origin = Origin::LowerRight;
        }
        else if (origin == "UpperLeft") {
            _origin = Origin::UpperLeft;
        }
        else if (origin == "UpperRight") {
            _origin = Origin::UpperRight;
        }
        else if (origin == "Center") {
            _origin = Origin::Center;
        }
    }

    // Attempt to get the billboard value
    bool billboard = false;
    if (dictionary.getValue("Billboard", billboard)) {
        _billboard = billboard;
    }

    int texture;
    if (dictionary.getValue("Texture", texture))
        _texture = texture;
    addProperty(_texture);

    addProperty(_billboard);

    addProperty(_size);
    _size.onChange([this](){ _planeIsDirty = true; });

    setBoundingSphere(_size.value());
}
예제 #20
0
RenderableTrailNew::RenderableTrailNew(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    // Properties
    , _lineColor("lineColor", "Line Color", DEFAULT_COLOR, glm::vec3(0), glm::vec3(1))
    , _pointColor("pointColor", "Point Color", DEFAULT_COLOR, glm::vec3(0), glm::vec3(1))
    , _lineFade("lineFade", "Line Fade", DEFAULT_LINE_FADE, 0, 1)
    , _lineWidth("lineWidth", "Line Width", DEFAULT_LINE_WIDTH, 1, 10)
    , _renderPart("renderPart", "Render Part", DEFAULT_RENDER_PART, 0, DEFAULT_RENDER_PART)
    , _showTimeStamps("showTimeStamps", "Show TimeStamps", DEFAULT_SHOW_TIME_STAMPS)
    , _renderFullTrail("renderFullTrail", "Render Full Trail", DEFAULT_RENDER_FULL_TRAIL)
    // OpenGL
    , _vaoGlobalID(0)
    , _vBufferGlobalID(0)
    , _vaoLocalID(0)
    , _vBufferLocalID(0)
    // Other
    , _programObject(nullptr)
    , _successfullDictionaryFetch(true)
    , _currentTimeClamped(0)
    , _subSamples(0)
{
    ghoul::Dictionary timeRangeDict;

    // Values that needs to be set
    _successfullDictionaryFetch &= dictionary.getValue(keyBody, _body);
    _successfullDictionaryFetch &= dictionary.getValue(keyObserver, _observer);
    _successfullDictionaryFetch &= dictionary.getValue(keyFrame, _frame);
    _successfullDictionaryFetch &= dictionary.getValue(keySampleDeltaTime, _sampleDeltaTime);
    _successfullDictionaryFetch &= dictionary.getValue(keyTimeRange, timeRangeDict);
    _successfullDictionaryFetch &= TimeRange::initializeFromDictionary(
        timeRangeDict, _timeRange);

    // Validate
    _successfullDictionaryFetch &= _sampleDeltaTime > 0;

    // Initialize optional values
    glm::vec3 lineColor = glm::vec3(DEFAULT_COLOR);
    glm::vec3 pointColor = glm::vec3(DEFAULT_COLOR);
    float lineFade = DEFAULT_LINE_FADE;
    float lineWidth = DEFAULT_LINE_WIDTH;
    float pointSteps = DEFAULT_POINT_STEPS;
    double renderPart = DEFAULT_RENDER_PART;
    bool showTimeStamps = DEFAULT_SHOW_TIME_STAMPS;
    bool renderFullTrail = DEFAULT_RENDER_FULL_TRAIL;

    // Fetch from dictionary
    dictionary.getValue(keyLineColor, lineColor);
    dictionary.getValue(keyPointColor, pointColor);
    dictionary.getValue(keyLineFade, lineFade);
    dictionary.getValue(keyLineWidth, lineWidth);
    dictionary.getValue(keyRenderPart, renderPart);
    dictionary.getValue(keyShowTimeStamps, showTimeStamps);
    dictionary.getValue(keyRenderFullTrail, renderFullTrail);
    float fSubSamples; // ghoul can not read ints from dictionaries...
    if (dictionary.getValue(keySubSamples, fSubSamples))
        _subSamples = fSubSamples;

    // Set property values
    _lineColor = lineColor;
    _pointColor = pointColor;
    _lineFade = lineFade;
    _lineWidth = lineWidth;
    _renderPart = renderPart;
    _showTimeStamps = showTimeStamps;
    _renderFullTrail = renderFullTrail;

    // Add all properties and set view options
    addProperty(_lineColor);
    addProperty(_pointColor);
    addProperty(_lineFade);
    addProperty(_lineWidth);
    addProperty(_renderPart);
    addProperty(_showTimeStamps);
    addProperty(_renderFullTrail);

    _lineColor.setViewOption(properties::Property::ViewOptions::Color);
    _pointColor.setViewOption(properties::Property::ViewOptions::Color);
}
RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictionary)  
    : Renderable(dictionary)
    , _gridProgram(nullptr)
    , _vaoID(0)
    , _vBufferID(0)
    , _iBufferID(0)
    , _mode(GL_LINES)
{
    _gridMatrix = glm::mat4(1);
    dictionary.getValue(KeyGridType, _gridType);
    dictionary.getValue(KeyGridColor, _gridColor);

    staticGrid = dictionary.getValue(KeyGridMatrix, _gridMatrix);
    if (!staticGrid){
        staticGrid = dictionary.getValue(KeyGridParentsRotation, _parentsRotation);
    }
    
    dictionary.getValue(KeyGridSegments, _segments);


    /*glm::vec2 radius;
    dictionary.getValue(constants::renderablesphericalgrid::gridRadius, radius);
    */

    _isize = int(6 * _segments * _segments);
    _vsize = int((_segments + 1) * (_segments + 1));
    _varray = new Vertex[_vsize];
    _iarray = new int[_isize];

    static_assert(sizeof(Vertex) == 64, "The size of the Vertex needs to be 64 for performance");

    int nr = 0;
    const float fsegments = static_cast<float>(_segments);
    const float r = static_cast<float>(radius[0]);

    //int nr2 = 0;

    for (int i = 0; i <= _segments; i++) {
        // define an extra vertex around the y-axis due to texture mapping
        for (int j = 0; j <= _segments; j++) {
            const float fi = static_cast<float>(i);
            const float fj = static_cast<float>(j);

            // inclination angle (north to south)
            const float theta = fi * float(M_PI) / fsegments*2.f;  // 0 -> PI

            // azimuth angle (east to west)
            const float phi = fj * float(M_PI) * 2.0f / fsegments;  // 0 -> 2*PI

            const float x = r * sin(phi) * sin(theta);  //
            const float y = r * cos(theta);             // up
            const float z = r * cos(phi) * sin(theta);  //
            
            glm::vec3 normal = glm::vec3(x, y, z);
            if (!(x == 0.f && y == 0.f && z == 0.f))
                normal = glm::normalize(normal);

            //const float t1 = fj / fsegments;
            const float t2 = fi / fsegments;

            // tex coord. not used, use to manip color 
            if (round(y) == 0.0f) _varray[nr].tex[0] = -2;
            _varray[nr].tex[1] = t2;

            glm::vec4 tmp(x, y, z, 1);
            glm::mat4 rot = glm::rotate(glm::mat4(1), static_cast<float>(M_PI_2), glm::vec3(1, 0, 0));
            tmp = _gridMatrix*rot*tmp;
            
            for (int i = 0; i < 3; i++){
                _varray[nr].location[i]  = tmp[i];
                _varray[nr].normal[i] = normal[i];
            }
            _varray[nr].location[3] = static_cast<GLfloat>(radius[1]);            
            ++nr;
        }
    }
    nr = 0;
    // define indices for all triangles
    for (int i = 1; i <= _segments; ++i) {
        for (int j = 0; j < _segments; ++j) {
            const int t = _segments + 1;
            _iarray[nr] = t * (i - 1) + j + 0; ++nr;
            _iarray[nr] = t * (i + 0) + j + 0; ++nr;
            _iarray[nr] = t * (i + 0) + j + 1; ++nr;
            _iarray[nr] = t * (i - 1) + j + 1; ++nr;
            _iarray[nr] = t * (i - 1) + j + 0; ++nr;
        }
    }
}