first commit
This commit is contained in:
28
src/Scenery/CMakeLists.txt
Normal file
28
src/Scenery/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
SceneryPager.cxx
|
||||
redout.cxx
|
||||
scenery.cxx
|
||||
terrain_stg.cxx
|
||||
terrain_pgt.cxx
|
||||
tilecache.cxx
|
||||
tileentry.cxx
|
||||
tilemgr.cxx
|
||||
marker.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
SceneryPager.hxx
|
||||
redout.hxx
|
||||
scenery.hxx
|
||||
terrain.hxx
|
||||
terrain_stg.hxx
|
||||
terrain_pgt.hxx
|
||||
tilecache.hxx
|
||||
tileentry.hxx
|
||||
tilemgr.hxx
|
||||
marker.hxx
|
||||
)
|
||||
|
||||
flightgear_component(Scenery "${SOURCES}" "${HEADERS}")
|
||||
123
src/Scenery/SceneryPager.cxx
Normal file
123
src/Scenery/SceneryPager.cxx
Normal file
@@ -0,0 +1,123 @@
|
||||
// SceneryPager.cxx -- Interface to OSG database pager
|
||||
//
|
||||
// Copyright (C) 2007 Tim Moore timoore@redhat.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "SceneryPager.hxx"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
using namespace osg;
|
||||
using namespace flightgear;
|
||||
|
||||
SceneryPager::SceneryPager()
|
||||
{
|
||||
_pagerRequests.reserve(48);
|
||||
_deleteRequests.reserve(16);
|
||||
}
|
||||
|
||||
SceneryPager::SceneryPager(const SceneryPager& rhs) :
|
||||
DatabasePager(rhs)
|
||||
{
|
||||
}
|
||||
|
||||
SceneryPager::~SceneryPager()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "Destroying scenery pager");
|
||||
}
|
||||
|
||||
void SceneryPager::clearRequests()
|
||||
{
|
||||
_pagerRequests.clear();
|
||||
_deleteRequests.clear();
|
||||
}
|
||||
|
||||
void SceneryPager::queueRequest(const std::string& fileName, Group* group,
|
||||
float priority, FrameStamp* frameStamp,
|
||||
ref_ptr<Referenced>& databaseRequest,
|
||||
osgDB::ReaderWriter::Options* options)
|
||||
{
|
||||
_pagerRequests.push_back(PagerRequest(fileName, group, priority,
|
||||
frameStamp,
|
||||
databaseRequest,
|
||||
options));
|
||||
}
|
||||
|
||||
void SceneryPager::queueDeleteRequest(osg::ref_ptr<osg::Object>& objptr)
|
||||
{
|
||||
_deleteRequests.push_back(objptr);
|
||||
objptr = 0;
|
||||
}
|
||||
|
||||
// Work around interface change in
|
||||
// osgDB::DatabasePager::requestNodeFile
|
||||
namespace
|
||||
{
|
||||
struct NodePathProxy
|
||||
{
|
||||
NodePathProxy(NodePath& nodePath)
|
||||
: _nodePath(nodePath)
|
||||
{
|
||||
}
|
||||
operator Group* () { return static_cast<Group*>(_nodePath.back()); }
|
||||
operator NodePath& () { return _nodePath; }
|
||||
NodePath& _nodePath;
|
||||
};
|
||||
}
|
||||
|
||||
void SceneryPager::PagerRequest::doRequest(SceneryPager* pager)
|
||||
{
|
||||
if (_group->getNumChildren() == 0) {
|
||||
NodePath path;
|
||||
path.push_back(_group.get());
|
||||
pager->requestNodeFile(_fileName, NodePathProxy(path), _priority,
|
||||
_frameStamp.get(),
|
||||
*_databaseRequest,
|
||||
_options.get());
|
||||
}
|
||||
}
|
||||
|
||||
void SceneryPager::signalEndFrame()
|
||||
{
|
||||
using namespace std;
|
||||
bool areDeleteRequests = false;
|
||||
bool arePagerRequests = false;
|
||||
if (!_deleteRequests.empty()) {
|
||||
areDeleteRequests = true;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex>
|
||||
lock(_fileRequestQueue->_childrenToDeleteListMutex);
|
||||
ObjectList& deleteList = _fileRequestQueue->_childrenToDeleteList;
|
||||
deleteList.insert(deleteList.end(),
|
||||
_deleteRequests.begin(),
|
||||
_deleteRequests.end());
|
||||
_deleteRequests.clear();
|
||||
}
|
||||
if (!_pagerRequests.empty()) {
|
||||
arePagerRequests = true;
|
||||
for (auto req : _pagerRequests) {
|
||||
req.doRequest(this);
|
||||
}
|
||||
_pagerRequests.clear();
|
||||
}
|
||||
if (areDeleteRequests && !arePagerRequests) {
|
||||
_fileRequestQueue->updateBlock();
|
||||
}
|
||||
DatabasePager::signalEndFrame();
|
||||
}
|
||||
|
||||
84
src/Scenery/SceneryPager.hxx
Normal file
84
src/Scenery/SceneryPager.hxx
Normal file
@@ -0,0 +1,84 @@
|
||||
// SceneryPager.hxx -- Interface to OSG database pager
|
||||
//
|
||||
// Copyright (C) 2007 Tim Moore timoore@redhat.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef FLIGHTGEAR_SCENERYPAGERHXX
|
||||
#define FLIGHTGEAR_SCENERYPAGERHXX 1
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <osg/FrameStamp>
|
||||
#include <osg/Group>
|
||||
#include <osgDB/DatabasePager>
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
class SceneryPager : public osgDB::DatabasePager
|
||||
{
|
||||
public:
|
||||
SceneryPager();
|
||||
SceneryPager(const SceneryPager& rhs);
|
||||
// Unhide DatabasePager::requestNodeFile
|
||||
using osgDB::DatabasePager::requestNodeFile;
|
||||
void queueRequest(const std::string& fileName, osg::Group* node,
|
||||
float priority, osg::FrameStamp* frameStamp,
|
||||
osg::ref_ptr<osg::Referenced>& databaseRequest,
|
||||
osgDB::ReaderWriter::Options* options);
|
||||
// This is passed a ref_ptr so that it can "take ownership" of the
|
||||
// node to delete and decrement its refcount while holding the
|
||||
// lock on the delete list.
|
||||
void queueDeleteRequest(osg::ref_ptr<osg::Object>& objptr);
|
||||
virtual void signalEndFrame();
|
||||
|
||||
void clearRequests();
|
||||
protected:
|
||||
// Queue up file requests until the end of the frame
|
||||
struct PagerRequest
|
||||
{
|
||||
PagerRequest() : _priority(0.0f), _databaseRequest(0) {}
|
||||
PagerRequest(const PagerRequest& rhs) :
|
||||
_fileName(rhs._fileName), _group(rhs._group),
|
||||
_priority(rhs._priority), _frameStamp(rhs._frameStamp),
|
||||
_options(rhs._options), _databaseRequest(rhs._databaseRequest) {}
|
||||
|
||||
PagerRequest(const std::string& fileName, osg::Group* group,
|
||||
float priority, osg::FrameStamp* frameStamp,
|
||||
osg::ref_ptr<Referenced>& databaseRequest,
|
||||
osgDB::ReaderWriter::Options* options):
|
||||
_fileName(fileName), _group(group), _priority(priority),
|
||||
_frameStamp(frameStamp), _options(options),
|
||||
_databaseRequest(&databaseRequest)
|
||||
{}
|
||||
|
||||
void doRequest(SceneryPager* pager);
|
||||
std::string _fileName;
|
||||
osg::ref_ptr<osg::Group> _group;
|
||||
float _priority;
|
||||
osg::ref_ptr<osg::FrameStamp> _frameStamp;
|
||||
osg::ref_ptr<osgDB::ReaderWriter::Options> _options;
|
||||
osg::ref_ptr<osg::Referenced>* _databaseRequest;
|
||||
};
|
||||
typedef std::vector<PagerRequest> PagerRequestList;
|
||||
PagerRequestList _pagerRequests;
|
||||
typedef std::vector<osg::ref_ptr<osg::Object> > DeleteRequestList;
|
||||
DeleteRequestList _deleteRequests;
|
||||
virtual ~SceneryPager();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
46
src/Scenery/design
Normal file
46
src/Scenery/design
Normal file
@@ -0,0 +1,46 @@
|
||||
(x) class fgOBJECT {
|
||||
// material property pointer
|
||||
int material_ptr;
|
||||
|
||||
// culling data
|
||||
double ref[3];
|
||||
double radius;
|
||||
|
||||
// OpenGL display list for object data
|
||||
GLint display_list_ptr;
|
||||
}
|
||||
|
||||
|
||||
(x) class fgTILE {
|
||||
// culling data
|
||||
double ref[3];
|
||||
double radius;
|
||||
|
||||
list < fgOBJECT > object_list;
|
||||
}
|
||||
|
||||
|
||||
class fgMATERIAL {
|
||||
int list_size;
|
||||
int counter;
|
||||
|
||||
public:
|
||||
|
||||
// material properties
|
||||
GLfloat ambient[4], diffuse[4], specular[4];
|
||||
GLint texture_ptr;
|
||||
|
||||
// transient list of objects with this material type (used for sorting
|
||||
// by material to reduce GL state changes when rendering the scene
|
||||
fgOBJECT *material_object_list[lots];
|
||||
|
||||
init_list();
|
||||
append_list();
|
||||
list_traverse_init();
|
||||
next_obj()
|
||||
}
|
||||
|
||||
|
||||
class fgMATERIAL_MGR {
|
||||
list < fgMATERIAL > material_list;
|
||||
}
|
||||
119
src/Scenery/marker.cxx
Normal file
119
src/Scenery/marker.cxx
Normal file
@@ -0,0 +1,119 @@
|
||||
// marker.cxx - 3D Marker pins in FlightGear
|
||||
// Copyright (C) 2022 Tobias Dammers
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "Scenery/marker.hxx"
|
||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/scene/material/Effect.hxx>
|
||||
#include <simgear/scene/material/EffectGeode.hxx>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Material>
|
||||
#include <osg/Node>
|
||||
#include <osg/Billboard>
|
||||
#include <osgText/Text>
|
||||
#include <osgText/String>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgDB/Registry>
|
||||
|
||||
osg::Node* fgCreateMarkerNode(const osgText::String& label, float font_size, float pin_height, float tip_height, const osg::Vec4f& color)
|
||||
{
|
||||
auto mainNode = new osg::Group;
|
||||
|
||||
auto textNode = new osg::Billboard;
|
||||
mainNode->addChild(textNode);
|
||||
|
||||
textNode->setMode(osg::Billboard::AXIAL_ROT);
|
||||
|
||||
auto text = new osgText::Text;
|
||||
text->setText(label);
|
||||
text->setAlignment(osgText::Text::CENTER_BOTTOM);
|
||||
text->setAxisAlignment(osgText::Text::XZ_PLANE);
|
||||
text->setFont("Fonts/LiberationFonts/LiberationSans-Regular.ttf");
|
||||
// text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT);
|
||||
text->setCharacterSize(font_size, 1.0f);
|
||||
text->setFontResolution(std::max(32.0f, font_size), std::max(32.0f, font_size));
|
||||
text->setColor(color);
|
||||
text->setPosition(osg::Vec3(0, 0, pin_height));
|
||||
text->setBackdropType(osgText::Text::OUTLINE);
|
||||
text->setBackdropColor(osg::Vec4(0, 0, 0, 0.75));
|
||||
textNode->addDrawable(text);
|
||||
|
||||
float top_spacing = font_size * 0.25;
|
||||
|
||||
if (pin_height - top_spacing > tip_height) {
|
||||
osg::Vec4f solid = color;
|
||||
osg::Vec4f transparent = color;
|
||||
osg::Vec3f nvec(0, 1, 0);
|
||||
|
||||
solid[3] = 1.0f;
|
||||
transparent[3] = 0.0f;
|
||||
|
||||
auto geoNode = new simgear::EffectGeode;
|
||||
mainNode->addChild(geoNode);
|
||||
auto pinGeo = new osg::Geometry;
|
||||
osg::Vec3Array* vtx = new osg::Vec3Array;
|
||||
osg::Vec3Array* nor = new osg::Vec3Array;
|
||||
osg::Vec4Array* rgb = new osg::Vec4Array;
|
||||
|
||||
nor->push_back(nvec);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, 0, tip_height));
|
||||
rgb->push_back(solid);
|
||||
|
||||
vtx->push_back(osg::Vec3f(-font_size * 0.125, 0, pin_height - top_spacing));
|
||||
rgb->push_back(transparent);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, 0, tip_height));
|
||||
rgb->push_back(solid);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, font_size * 0.125, pin_height - top_spacing));
|
||||
rgb->push_back(transparent);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, 0, tip_height));
|
||||
rgb->push_back(solid);
|
||||
|
||||
vtx->push_back(osg::Vec3f(font_size * 0.125, 0, pin_height - top_spacing));
|
||||
rgb->push_back(transparent);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, 0, tip_height));
|
||||
rgb->push_back(solid);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, -font_size * 0.125, pin_height - top_spacing));
|
||||
rgb->push_back(transparent);
|
||||
|
||||
vtx->push_back(osg::Vec3f(0, 0, tip_height));
|
||||
rgb->push_back(solid);
|
||||
|
||||
vtx->push_back(osg::Vec3f(-font_size * 0.125, 0, pin_height - top_spacing));
|
||||
rgb->push_back(transparent);
|
||||
|
||||
pinGeo->setVertexArray(vtx);
|
||||
pinGeo->setColorArray(rgb, osg::Array::BIND_PER_VERTEX);
|
||||
pinGeo->setNormalArray(nor, osg::Array::BIND_OVERALL);
|
||||
pinGeo->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP, 0, vtx->size()));
|
||||
geoNode->addDrawable(pinGeo);
|
||||
|
||||
auto stateSet = geoNode->getOrCreateStateSet();
|
||||
|
||||
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
|
||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
|
||||
|
||||
osg::ref_ptr<simgear::SGReaderWriterOptions> opt;
|
||||
opt = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
|
||||
simgear::Effect* effect = simgear::makeEffect("Effects/marker-pin", true, opt);
|
||||
if (effect)
|
||||
geoNode->setEffect(effect);
|
||||
}
|
||||
|
||||
mainNode->setNodeMask(~simgear::CASTSHADOW_BIT);
|
||||
return mainNode;
|
||||
}
|
||||
16
src/Scenery/marker.hxx
Normal file
16
src/Scenery/marker.hxx
Normal file
@@ -0,0 +1,16 @@
|
||||
// marker.hxx - 3D Marker pins in FlightGear
|
||||
// Copyright (C) 2022 Tobias Dammers
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace osgText {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace osg {
|
||||
class Node;
|
||||
class Vec4f;
|
||||
}
|
||||
|
||||
osg::Node* fgCreateMarkerNode(const osgText::String&, float font_size, float pin_height, float tip_height, const osg::Vec4f& color);
|
||||
117
src/Scenery/redout.cxx
Normal file
117
src/Scenery/redout.cxx
Normal file
@@ -0,0 +1,117 @@
|
||||
// redout.hxx
|
||||
//
|
||||
// Written by Mathias Froehlich,
|
||||
//
|
||||
// Copyright (C) 2007 Mathias Froehlich
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "redout.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/Depth>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Switch>
|
||||
#include <osg/StateSet>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
class FGRedoutCallback : public osg::NodeCallback {
|
||||
public:
|
||||
FGRedoutCallback(osg::Vec4Array* colorArray) :
|
||||
_colorArray(colorArray),
|
||||
_redoutNode(fgGetNode("/sim/rendering/redout", true))
|
||||
{
|
||||
fgGetNode("/sim/rendering/redout/alpha", true);
|
||||
}
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
assert(dynamic_cast<osg::Switch*>(node));
|
||||
osg::Switch* sw = static_cast<osg::Switch*>(node);
|
||||
|
||||
// Check if we need to do something further ...
|
||||
float alpha = _redoutNode->getFloatValue("alpha", 0);
|
||||
bool enabled = (0 < alpha);
|
||||
sw->setValue(0, enabled);
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
(*_colorArray)[0][0] = _redoutNode->getFloatValue("red", 1);
|
||||
(*_colorArray)[0][1] = _redoutNode->getFloatValue("green", 0);
|
||||
(*_colorArray)[0][2] = _redoutNode->getFloatValue("blue", 0);
|
||||
(*_colorArray)[0][3] = alpha;
|
||||
_colorArray->dirty();
|
||||
}
|
||||
private:
|
||||
osg::ref_ptr<osg::Vec4Array> _colorArray;
|
||||
SGSharedPtr<SGPropertyNode> _redoutNode;
|
||||
};
|
||||
|
||||
osg::Node* FGCreateRedoutNode()
|
||||
{
|
||||
osg::Geometry* geometry = new osg::Geometry;
|
||||
geometry->setUseDisplayList(false);
|
||||
|
||||
osg::StateSet* stateSet = geometry->getOrCreateStateSet();
|
||||
stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||
stateSet->setAttribute(new osg::BlendFunc);
|
||||
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
stateSet->setAttribute(new osg::Depth(osg::Depth::ALWAYS, 0, 1, false));
|
||||
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateSet->setRenderBinDetails(1000, "RenderBin");
|
||||
|
||||
osg::Vec3Array* vertexArray = new osg::Vec3Array;
|
||||
vertexArray->push_back(osg::Vec3(-1, -1, 0));
|
||||
vertexArray->push_back(osg::Vec3( 1, -1, 0));
|
||||
vertexArray->push_back(osg::Vec3( 1, 1, 0));
|
||||
vertexArray->push_back(osg::Vec3(-1, 1, 0));
|
||||
geometry->setVertexArray(vertexArray);
|
||||
osg::Vec4Array* colorArray = new osg::Vec4Array;
|
||||
colorArray->push_back(osg::Vec4(1, 0, 0, 1));
|
||||
geometry->setColorArray(colorArray);
|
||||
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
|
||||
geometry->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 4));
|
||||
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
geode->addDrawable(geometry);
|
||||
|
||||
osg::Camera* camera = new osg::Camera;
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setProjectionMatrix(osg::Matrix::ortho2D(-1, 1, -1, 1));
|
||||
camera->setViewMatrix(osg::Matrix::identity());
|
||||
camera->setRenderOrder(osg::Camera::NESTED_RENDER);
|
||||
camera->setClearMask(0);
|
||||
camera->setAllowEventFocus(false);
|
||||
camera->setCullingActive(false);
|
||||
camera->addChild(geode);
|
||||
|
||||
osg::Switch* sw = new osg::Switch;
|
||||
sw->setUpdateCallback(new FGRedoutCallback(colorArray));
|
||||
sw->addChild(camera);
|
||||
|
||||
return sw;
|
||||
}
|
||||
|
||||
29
src/Scenery/redout.hxx
Normal file
29
src/Scenery/redout.hxx
Normal file
@@ -0,0 +1,29 @@
|
||||
// redout.hxx
|
||||
//
|
||||
// Written by Mathias Froehlich,
|
||||
//
|
||||
// Copyright (C) 2007 Mathias Froehlich
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef FG_REDOUT_HXX
|
||||
#define FG_REDOUT_HXX
|
||||
|
||||
#include <osg/Node>
|
||||
|
||||
extern osg::Node* FGCreateRedoutNode();
|
||||
|
||||
#endif
|
||||
585
src/Scenery/scenery.cxx
Normal file
585
src/Scenery/scenery.cxx
Normal file
@@ -0,0 +1,585 @@
|
||||
// scenery.cxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include <simgear/simgear_config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Transform>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/CameraView>
|
||||
#include <osg/LOD>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/scene/tgdb/userdata.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/SGSceneUserData.hxx>
|
||||
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
|
||||
#include <simgear/scene/sky/sky.hxx>
|
||||
#include <simgear/scene/util/SGSceneFeatures.hxx>
|
||||
|
||||
#include <simgear/bvh/BVHNode.hxx>
|
||||
#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <Viewer/renderer.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <GUI/MouseCursor.hxx>
|
||||
#include <Main/sentryIntegration.hxx>
|
||||
|
||||
#include "scenery.hxx"
|
||||
#include "terrain_stg.hxx"
|
||||
|
||||
#ifdef ENABLE_GDAL
|
||||
#include "terrain_pgt.hxx"
|
||||
#endif
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear;
|
||||
|
||||
class FGGroundPickCallback : public SGPickCallback {
|
||||
public:
|
||||
FGGroundPickCallback() : SGPickCallback(PriorityScenery)
|
||||
{ }
|
||||
|
||||
virtual bool buttonPressed( int button,
|
||||
const osgGA::GUIEventAdapter&,
|
||||
const Info& info )
|
||||
{
|
||||
// only on left mouse button
|
||||
if (button != 0)
|
||||
return false;
|
||||
|
||||
SGGeod geod = SGGeod::fromCart(info.wgs84);
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod );
|
||||
|
||||
SGPropertyNode *c = fgGetNode("/sim/input/click", true);
|
||||
c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
|
||||
c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
|
||||
c->setDoubleValue("elevation-m", geod.getElevationM());
|
||||
c->setDoubleValue("elevation-ft", geod.getElevationFt());
|
||||
fgSetBool("/sim/signals/click", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class FGSceneryIntersect : public osg::NodeVisitor {
|
||||
public:
|
||||
FGSceneryIntersect(const SGLineSegmentd& lineSegment,
|
||||
const osg::Node* skipNode) :
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
|
||||
_lineSegment(lineSegment),
|
||||
_skipNode(skipNode),
|
||||
_material(0),
|
||||
_haveHit(false)
|
||||
{ }
|
||||
|
||||
bool getHaveHit() const
|
||||
{ return _haveHit; }
|
||||
const SGLineSegmentd& getLineSegment() const
|
||||
{ return _lineSegment; }
|
||||
const simgear::BVHMaterial* getMaterial() const
|
||||
{ return _material; }
|
||||
|
||||
virtual void apply(osg::Node& node)
|
||||
{
|
||||
if (&node == _skipNode)
|
||||
return;
|
||||
if (!testBoundingSphere(node.getBound()))
|
||||
return;
|
||||
|
||||
addBoundingVolume(node);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Group& group)
|
||||
{
|
||||
if (&group == _skipNode)
|
||||
return;
|
||||
if (!testBoundingSphere(group.getBound()))
|
||||
return;
|
||||
|
||||
traverse(group);
|
||||
addBoundingVolume(group);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Transform& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::Camera& camera)
|
||||
{
|
||||
if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
|
||||
return;
|
||||
handleTransform(camera);
|
||||
}
|
||||
virtual void apply(osg::CameraView& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::MatrixTransform& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::PositionAttitudeTransform& transform)
|
||||
{ handleTransform(transform); }
|
||||
|
||||
private:
|
||||
void handleTransform(osg::Transform& transform)
|
||||
{
|
||||
if (&transform == _skipNode)
|
||||
return;
|
||||
// Hmm, may be this needs to be refined somehow ...
|
||||
if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
|
||||
return;
|
||||
|
||||
if (!testBoundingSphere(transform.getBound()))
|
||||
return;
|
||||
|
||||
osg::Matrix inverseMatrix;
|
||||
if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
|
||||
return;
|
||||
osg::Matrix matrix;
|
||||
if (!transform.computeLocalToWorldMatrix(matrix, this))
|
||||
return;
|
||||
|
||||
SGLineSegmentd lineSegment = _lineSegment;
|
||||
bool haveHit = _haveHit;
|
||||
const simgear::BVHMaterial* material = _material;
|
||||
|
||||
_haveHit = false;
|
||||
_lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
|
||||
|
||||
addBoundingVolume(transform);
|
||||
traverse(transform);
|
||||
|
||||
if (_haveHit) {
|
||||
_lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
|
||||
} else {
|
||||
_lineSegment = lineSegment;
|
||||
_material = material;
|
||||
_haveHit = haveHit;
|
||||
}
|
||||
}
|
||||
|
||||
simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
|
||||
{
|
||||
SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
|
||||
if (!userData)
|
||||
return 0;
|
||||
return userData->getBVHNode();
|
||||
}
|
||||
void addBoundingVolume(osg::Node& node)
|
||||
{
|
||||
simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
|
||||
if (!bvNode)
|
||||
return;
|
||||
|
||||
// Find ground intersection on the bvh nodes
|
||||
simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
|
||||
0/*startTime*/);
|
||||
bvNode->accept(lineSegmentVisitor);
|
||||
if (!lineSegmentVisitor.empty()) {
|
||||
_lineSegment = lineSegmentVisitor.getLineSegment();
|
||||
_material = lineSegmentVisitor.getMaterial();
|
||||
_haveHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool testBoundingSphere(const osg::BoundingSphere& bound) const
|
||||
{
|
||||
if (!bound.valid())
|
||||
return false;
|
||||
|
||||
SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
|
||||
return intersects(_lineSegment, sphere);
|
||||
}
|
||||
|
||||
SGLineSegmentd _lineSegment;
|
||||
const osg::Node* _skipNode;
|
||||
|
||||
const simgear::BVHMaterial* _material;
|
||||
bool _haveHit;
|
||||
};
|
||||
class FGScenery::TextureCacheListener : public SGPropertyChangeListener
|
||||
{
|
||||
protected:
|
||||
const char* root_node_path = "/sim/rendering/texture-cache";
|
||||
public:
|
||||
TextureCacheListener()
|
||||
{
|
||||
SGPropertyNode_ptr textureCacheNode = fgGetNode(root_node_path, true);
|
||||
setupPropertyListener(textureCacheNode, "cache-enabled");
|
||||
setupPropertyListener(textureCacheNode, "compress-transparent");
|
||||
setupPropertyListener(textureCacheNode, "compress-solid");
|
||||
setupPropertyListener(textureCacheNode, "compress");
|
||||
}
|
||||
|
||||
~TextureCacheListener()
|
||||
{
|
||||
SGPropertyNode_ptr maskNode = fgGetNode(root_node_path);
|
||||
for (int i = 0; i < maskNode->nChildren(); ++i) {
|
||||
maskNode->getChild(i)->removeChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void setupPropertyListener(SGPropertyNode_ptr textureCacheNode, const char *node)
|
||||
{
|
||||
textureCacheNode->getChild(node, 0, true)->addChangeListener(this, true);
|
||||
}
|
||||
|
||||
virtual void valueChanged(SGPropertyNode * node)
|
||||
{
|
||||
bool b = node->getBoolValue();
|
||||
std::string name(node->getNameString());
|
||||
|
||||
if (name == "cache-enabled") {
|
||||
SGSceneFeatures::instance()->setTextureCacheActive(b);
|
||||
}
|
||||
else if (name == "compress-transparent" || name == "compress") {
|
||||
SGSceneFeatures::instance()->setTextureCacheCompressionActiveTransparent(b);
|
||||
}
|
||||
else if (name == "compress-solid" || name == "compress") {
|
||||
SGSceneFeatures::instance()->setTextureCacheCompressionActive(b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FGScenery::ElevationMeshListener : public SGPropertyChangeListener
|
||||
{
|
||||
protected:
|
||||
const char* root_node_path = "/scenery/elevation-mesh";
|
||||
const char* lod_node_path = "/sim/rendering/static-lod";
|
||||
public:
|
||||
ElevationMeshListener()
|
||||
{
|
||||
SGPropertyNode_ptr elevationMeshNode = fgGetNode(root_node_path, true);
|
||||
setupPropertyListener(elevationMeshNode, "constraint-gap-m");
|
||||
setupPropertyListener(elevationMeshNode, "lod-range-factor");
|
||||
setupPropertyListener(elevationMeshNode, "sample-ratio");
|
||||
setupPropertyListener(elevationMeshNode, "vertical-scale");
|
||||
setupPropertyListener(elevationMeshNode, "separate-water-mesh");
|
||||
|
||||
// We also need to set the maximum range based on the LOD ranges
|
||||
SGPropertyNode_ptr lodNode = fgGetNode(lod_node_path, true);
|
||||
setupPropertyListener(lodNode, "detailed");
|
||||
setupPropertyListener(lodNode, "rough-delta");
|
||||
setupPropertyListener(lodNode, "bare-delta");
|
||||
}
|
||||
|
||||
~ElevationMeshListener()
|
||||
{
|
||||
SGPropertyNode_ptr node = fgGetNode(root_node_path);
|
||||
for (int i = 0; i < node->nChildren(); ++i) {
|
||||
node->getChild(i)->removeChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void setupPropertyListener(SGPropertyNode_ptr elevationMeshNode, const char *node)
|
||||
{
|
||||
elevationMeshNode->getChild(node, 0, true)->addChangeListener(this, true);
|
||||
}
|
||||
|
||||
virtual void valueChanged(SGPropertyNode * node)
|
||||
{
|
||||
float f = node->getFloatValue();
|
||||
bool b = node->getBoolValue();
|
||||
std::string name(node->getNameString());
|
||||
|
||||
if (name == "constraint-gap-m") {
|
||||
SGSceneFeatures::instance()->setVPBConstraintGap(f);
|
||||
} else if (name == "sample-ratio") {
|
||||
SGSceneFeatures::instance()->setVPBSampleRatio(f);
|
||||
} else if (name == "vertical-scale") {
|
||||
SGSceneFeatures::instance()->setVPBVerticalScale(f);
|
||||
} else if (name == "separate-water-mesh") {
|
||||
SGSceneFeatures::instance()->setVPBSeparateWaterMesh(b);
|
||||
} else {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "Unexpected property in listener " << node->getPath());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FGScenery::ScenerySwitchListener : public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
ScenerySwitchListener(FGScenery* scenery) :
|
||||
_scenery(scenery)
|
||||
{
|
||||
SGPropertyNode_ptr maskNode = fgGetNode("/sim/rendering/draw-mask", true);
|
||||
maskNode->getChild("terrain", 0, true)->addChangeListener(this, true);
|
||||
maskNode->getChild("models", 0, true)->addChangeListener(this, true);
|
||||
maskNode->getChild("aircraft", 0, true)->addChangeListener(this, true);
|
||||
maskNode->getChild("clouds", 0, true)->addChangeListener(this, true);
|
||||
|
||||
// legacy compatability option
|
||||
fgGetNode("/sim/rendering/draw-otw")->addChangeListener(this);
|
||||
|
||||
// badly named property, this is what is set by --enable/disable-clouds
|
||||
fgGetNode("/environment/clouds/status")->addChangeListener(this);
|
||||
|
||||
auto vpb_active = fgGetNode("/scenery/use-vpb");
|
||||
if (vpb_active) {
|
||||
vpb_active->addChangeListener(this);
|
||||
SGSceneFeatures::instance()->setVPBActive(vpb_active->getBoolValue());
|
||||
}
|
||||
}
|
||||
|
||||
~ScenerySwitchListener()
|
||||
{
|
||||
SGPropertyNode_ptr maskNode = fgGetNode("/sim/rendering/draw-mask");
|
||||
for (int i=0; i < maskNode->nChildren(); ++i) {
|
||||
maskNode->getChild(i)->removeChangeListener(this);
|
||||
}
|
||||
|
||||
fgGetNode("/sim/rendering/draw-otw")->removeChangeListener(this);
|
||||
fgGetNode("/environment/clouds/status")->removeChangeListener(this);
|
||||
fgGetNode("/scenery/use-vpb")->removeChangeListener(this);
|
||||
}
|
||||
|
||||
virtual void valueChanged (SGPropertyNode * node)
|
||||
{
|
||||
bool b = node->getBoolValue();
|
||||
std::string name(node->getNameString());
|
||||
|
||||
if (name == "use-vpb") {
|
||||
SGSceneFeatures::instance()->setVPBActive(b);
|
||||
} else if (name == "terrain") {
|
||||
_scenery->scene_graph->setChildValue(_scenery->terrain_branch, b);
|
||||
} else if (name == "models") {
|
||||
_scenery->scene_graph->setChildValue(_scenery->models_branch, b);
|
||||
} else if (name == "aircraft") {
|
||||
_scenery->scene_graph->setChildValue(_scenery->aircraft_branch, b);
|
||||
} else if (name == "clouds") {
|
||||
// clouds live elsewhere in the scene, but we handle them here
|
||||
globals->get_renderer()->getSky()->set_clouds_enabled(b);
|
||||
} else if (name == "draw-otw") {
|
||||
// legacy setting but let's keep it working
|
||||
fgGetNode("/sim/rendering/draw-mask")->setBoolValue("terrain", b);
|
||||
fgGetNode("/sim/rendering/draw-mask")->setBoolValue("models", b);
|
||||
} else if (name == "status") {
|
||||
fgGetNode("/sim/rendering/draw-mask")->setBoolValue("clouds", b);
|
||||
}
|
||||
}
|
||||
private:
|
||||
FGScenery* _scenery;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Scenery Management system
|
||||
FGScenery::FGScenery() :
|
||||
_listener(nullptr), _textureCacheListener(nullptr), _elevationMeshListener(nullptr)
|
||||
{
|
||||
// keep reference to pager singleton, so it cannot be destroyed while FGScenery lives
|
||||
_pager = FGScenery::getPagerSingleton();
|
||||
|
||||
// Initialise the state of the scene graph.
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
FGScenery::~FGScenery()
|
||||
{
|
||||
delete _listener;
|
||||
delete _textureCacheListener;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the Scenery Management system
|
||||
void FGScenery::init() {
|
||||
// Already set up.
|
||||
if (_inited)
|
||||
return;
|
||||
|
||||
// Scene graph root
|
||||
scene_graph = new osg::Switch;
|
||||
scene_graph->setName( "FGScenery" );
|
||||
|
||||
// Terrain branch
|
||||
terrain_branch = new osg::Group;
|
||||
terrain_branch->setName( "Terrain" );
|
||||
scene_graph->addChild( terrain_branch.get() );
|
||||
SGSceneUserData* userData;
|
||||
userData = SGSceneUserData::getOrCreateSceneUserData(terrain_branch.get());
|
||||
userData->setPickCallback(new FGGroundPickCallback);
|
||||
|
||||
models_branch = new osg::Group;
|
||||
models_branch->setName( "Models" );
|
||||
scene_graph->addChild( models_branch.get() );
|
||||
|
||||
aircraft_branch = new osg::Group;
|
||||
aircraft_branch->setName( "Aircraft" );
|
||||
scene_graph->addChild( aircraft_branch.get() );
|
||||
|
||||
// choosing to make the interior branch a child of the main
|
||||
// aircraft group, for the moment. This simplifes places which
|
||||
// assume all aircraft elements are within this group - principally
|
||||
// FGODGuage::set_aircraft_texture.
|
||||
interior_branch = new osg::Group;
|
||||
interior_branch->setName( "Interior" );
|
||||
|
||||
osg::LOD* interiorLOD = new osg::LOD;
|
||||
interiorLOD->addChild(interior_branch.get(), 0.0, 50.0);
|
||||
aircraft_branch->addChild( interiorLOD );
|
||||
|
||||
// Set up the particle system as a directly accessible branch of the scene graph.
|
||||
auto paricles = simgear::ParticlesGlobalManager::instance();
|
||||
particles_branch = paricles->getCommonRoot();
|
||||
particles_branch->setName("Particles");
|
||||
scene_graph->addChild(particles_branch.get());
|
||||
paricles->setSwitchNode(fgGetNode("/sim/rendering/particles", true));
|
||||
paricles->initFromMainThread();
|
||||
|
||||
// Set up the precipitation system.
|
||||
precipitation_branch = new osg::Group;
|
||||
precipitation_branch->setName("Precipitation");
|
||||
scene_graph->addChild(precipitation_branch.get());
|
||||
|
||||
// initialize the terrian based on selected engine
|
||||
std::string engine = fgGetString("/sim/scenery/engine", "tilecache" );
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Selected scenery is " << engine );
|
||||
|
||||
if ( engine == "pagedLOD" ) {
|
||||
#ifdef ENABLE_GDAL
|
||||
_terrain.reset(new FGPgtTerrain);
|
||||
#else
|
||||
_terrain.reset(new FGStgTerrain);
|
||||
#endif
|
||||
} else {
|
||||
_terrain.reset(new FGStgTerrain);
|
||||
}
|
||||
_terrain->init( terrain_branch.get() );
|
||||
|
||||
_listener = new ScenerySwitchListener(this);
|
||||
_textureCacheListener = new TextureCacheListener();
|
||||
_elevationMeshListener = new ElevationMeshListener();
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
void FGScenery::reinit()
|
||||
{
|
||||
flightgear::addSentryBreadcrumb("reloading scenery", "info");
|
||||
fgSetBool("/sim/rendering/scenery-reload-required", false);
|
||||
_terrain->reinit();
|
||||
}
|
||||
|
||||
void FGScenery::shutdown()
|
||||
{
|
||||
_terrain->shutdown();
|
||||
|
||||
scene_graph = NULL;
|
||||
terrain_branch = NULL;
|
||||
models_branch = NULL;
|
||||
aircraft_branch = NULL;
|
||||
particles_branch = NULL;
|
||||
precipitation_branch = NULL;
|
||||
|
||||
_terrain.reset();
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = false;
|
||||
|
||||
simgear::ParticlesGlobalManager::clear();
|
||||
}
|
||||
|
||||
|
||||
void FGScenery::update(double dt)
|
||||
{
|
||||
_terrain->update(dt);
|
||||
}
|
||||
|
||||
void FGScenery::bind() {
|
||||
}
|
||||
|
||||
void FGScenery::unbind() {
|
||||
}
|
||||
|
||||
bool
|
||||
FGScenery::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
return _terrain->get_cart_elevation_m(pos, max_altoff, alt,
|
||||
material, butNotFrom);
|
||||
}
|
||||
|
||||
bool
|
||||
FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
return _terrain->get_elevation_m( geod, alt, material,
|
||||
butNotFrom );
|
||||
}
|
||||
|
||||
bool
|
||||
FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
return _terrain->get_cart_ground_intersection( pos, dir, nearestHit, butNotFrom );
|
||||
}
|
||||
|
||||
bool FGScenery::scenery_available(const SGGeod& position, double range_m)
|
||||
{
|
||||
return _terrain->scenery_available( position, range_m );
|
||||
}
|
||||
|
||||
bool FGScenery::schedule_scenery(const SGGeod& position, double range_m, double duration)
|
||||
{
|
||||
return _terrain->schedule_scenery( position, range_m, duration );
|
||||
}
|
||||
|
||||
void FGScenery::materialLibChanged()
|
||||
{
|
||||
_terrain->materialLibChanged();
|
||||
}
|
||||
|
||||
static osg::ref_ptr<SceneryPager> pager;
|
||||
|
||||
SceneryPager* FGScenery::getPagerSingleton()
|
||||
{
|
||||
if (!pager)
|
||||
pager = new SceneryPager;
|
||||
return pager.get();
|
||||
}
|
||||
|
||||
void FGScenery::resetPagerSingleton()
|
||||
{
|
||||
pager = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGScenery> registrantFGScenery(
|
||||
SGSubsystemMgr::DISPLAY,
|
||||
{{"FGRenderer", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD},
|
||||
{"SGSky", SGSubsystemMgr::Dependency::NONSUBSYSTEM_HARD}});
|
||||
160
src/Scenery/scenery.hxx
Normal file
160
src/Scenery/scenery.hxx
Normal file
@@ -0,0 +1,160 @@
|
||||
// scenery.hxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _SCENERY_HXX
|
||||
#define _SCENERY_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Switch>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/scene/model/particles.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include "SceneryPager.hxx"
|
||||
#include "terrain.hxx"
|
||||
|
||||
namespace simgear {
|
||||
class BVHMaterial;
|
||||
}
|
||||
|
||||
class FGTerrain;
|
||||
|
||||
// Define a structure containing global scenery parameters
|
||||
class FGScenery : public SGSubsystem
|
||||
{
|
||||
class ScenerySwitchListener;
|
||||
friend class ScenerySwitchListener;
|
||||
|
||||
// scene graph
|
||||
osg::ref_ptr<osg::Switch> scene_graph;
|
||||
osg::ref_ptr<osg::Group> terrain_branch;
|
||||
osg::ref_ptr<osg::Group> models_branch;
|
||||
osg::ref_ptr<osg::Group> aircraft_branch;
|
||||
osg::ref_ptr<osg::Group> interior_branch;
|
||||
osg::ref_ptr<osg::Group> particles_branch;
|
||||
osg::ref_ptr<osg::Group> precipitation_branch;
|
||||
|
||||
osg::ref_ptr<flightgear::SceneryPager> _pager;
|
||||
ScenerySwitchListener* _listener;
|
||||
|
||||
class TextureCacheListener;
|
||||
friend class TextureCacheListener;
|
||||
TextureCacheListener* _textureCacheListener;
|
||||
|
||||
class ElevationMeshListener;
|
||||
friend class ElevationMeshListener;
|
||||
ElevationMeshListener* _elevationMeshListener;
|
||||
|
||||
public:
|
||||
FGScenery();
|
||||
~FGScenery();
|
||||
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
void reinit() override;
|
||||
void shutdown() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "scenery"; }
|
||||
|
||||
/// Compute the elevation of the scenery at geodetic latitude lat,
|
||||
/// geodetic longitude lon and not higher than max_alt.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters or degrees.
|
||||
bool get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the elevation of the scenery below the cartesian point pos.
|
||||
/// you the returned scenery altitude is not higher than the position
|
||||
/// pos plus an offset given with max_altoff.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters.
|
||||
bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& elevation,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the nearest intersection point of the line starting from
|
||||
/// start going in direction dir with the terrain.
|
||||
/// The input and output values should be in cartesian coordinates in the
|
||||
/// usual earth centered wgs84 coordinate system. Units are meters.
|
||||
/// On success, true is returned.
|
||||
bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
osg::Group *get_scene_graph () const { return scene_graph.get(); }
|
||||
osg::Group *get_terrain_branch () const { return terrain_branch.get(); }
|
||||
osg::Group *get_models_branch () const { return models_branch.get(); }
|
||||
osg::Group *get_aircraft_branch () const { return aircraft_branch.get(); }
|
||||
osg::Group *get_interior_branch () const { return interior_branch.get(); }
|
||||
osg::Group *get_particles_branch () const { return particles_branch.get(); }
|
||||
osg::Group *get_precipitation_branch () const { return precipitation_branch.get(); }
|
||||
|
||||
/// Returns true if scenery is available for the given lat, lon position
|
||||
/// within a range of range_m.
|
||||
/// lat and lon are expected to be in degrees.
|
||||
bool scenery_available(const SGGeod& position, double range_m);
|
||||
|
||||
// Static because access to the pager is needed before the rest of
|
||||
// the scenery is initialized.
|
||||
static flightgear::SceneryPager* getPagerSingleton();
|
||||
static void resetPagerSingleton();
|
||||
|
||||
flightgear::SceneryPager* getPager() { return _pager.get(); }
|
||||
|
||||
// tile mgr api
|
||||
bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
|
||||
void materialLibChanged();
|
||||
private:
|
||||
// the terrain engine
|
||||
std::unique_ptr<FGTerrain> _terrain;
|
||||
|
||||
// The state of the scene graph.
|
||||
bool _inited;
|
||||
};
|
||||
|
||||
#endif // _SCENERY_HXX
|
||||
112
src/Scenery/terrain.hxx
Normal file
112
src/Scenery/terrain.hxx
Normal file
@@ -0,0 +1,112 @@
|
||||
// scenery.hxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _TERRAIN_HXX
|
||||
#define _TERRAIN_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Switch>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/scene/model/particles.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include "scenery.hxx"
|
||||
#include "SceneryPager.hxx"
|
||||
#include "tilemgr.hxx"
|
||||
|
||||
namespace simgear {
|
||||
class BVHMaterial;
|
||||
}
|
||||
|
||||
// Define a structure containing global scenery parameters
|
||||
class FGTerrain
|
||||
{
|
||||
public:
|
||||
FGTerrain() = default;
|
||||
virtual ~FGTerrain() = default;
|
||||
|
||||
// Implementation of SGSubsystem. - called from Scenery
|
||||
virtual void init ( osg::Group* terrain ) = 0;
|
||||
virtual void reinit() = 0;
|
||||
virtual void shutdown () = 0;
|
||||
virtual void bind () = 0;
|
||||
virtual void unbind () = 0;
|
||||
virtual void update (double dt) = 0;
|
||||
|
||||
/// Compute the elevation of the scenery at geodetic latitude lat,
|
||||
/// geodetic longitude lon and not higher than max_alt.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters or degrees.
|
||||
virtual bool get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0) = 0;
|
||||
|
||||
/// Compute the elevation of the scenery below the cartesian point pos.
|
||||
/// you the returned scenery altitude is not higher than the position
|
||||
/// pos plus an offset given with max_altoff.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters.
|
||||
virtual bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& elevation,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0) = 0;
|
||||
|
||||
/// Compute the nearest intersection point of the line starting from
|
||||
/// start going in direction dir with the terrain.
|
||||
/// The input and output values should be in cartesian coordinates in the
|
||||
/// usual earth centered wgs84 coordinate system. Units are meters.
|
||||
/// On success, true is returned.
|
||||
virtual bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom = 0) = 0;
|
||||
|
||||
/// Returns true if scenery is available for the given lat, lon position
|
||||
/// within a range of range_m.
|
||||
/// lat and lon are expected to be in degrees.
|
||||
virtual bool scenery_available(const SGGeod& position, double range_m) = 0;
|
||||
|
||||
// tile mgr api
|
||||
virtual bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0) = 0;
|
||||
virtual void materialLibChanged() = 0;
|
||||
};
|
||||
|
||||
#endif // _TERRAIN_HXX
|
||||
256
src/Scenery/terrain_pgt.cxx
Normal file
256
src/Scenery/terrain_pgt.cxx
Normal file
@@ -0,0 +1,256 @@
|
||||
// terrain_pgt.cxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include <simgear/simgear_config.h>
|
||||
|
||||
#ifdef ENABLE_GDAL
|
||||
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Viewer/splash.hxx>
|
||||
|
||||
#include "terrain_pgt.hxx"
|
||||
#include "scenery.hxx"
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear;
|
||||
|
||||
using flightgear::SceneryPager;
|
||||
|
||||
// Terrain Management system
|
||||
FGPgtTerrain::FGPgtTerrain() :
|
||||
_scenery_loaded(fgGetNode("/sim/sceneryloaded", true)),
|
||||
_scenery_override(fgGetNode("/sim/sceneryloaded-override", true))
|
||||
{
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
FGPgtTerrain::~FGPgtTerrain()
|
||||
{
|
||||
}
|
||||
|
||||
// Initialize the Scenery Management system
|
||||
void FGPgtTerrain::init( osg::Group* terrain ) {
|
||||
// Already set up.
|
||||
if (_inited)
|
||||
return;
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGPgtTerrain::init");
|
||||
|
||||
// remember the scene terrain branch on scenegraph
|
||||
terrain_branch = terrain;
|
||||
|
||||
// load the whole planet tile - database pager handles
|
||||
// the quad tree / loading the highres tiles
|
||||
osg::ref_ptr<simgear::SGReaderWriterOptions> options;
|
||||
|
||||
// drops the previous options reference
|
||||
options = new simgear::SGReaderWriterOptions;
|
||||
options->setPropertyNode(globals->get_props());
|
||||
|
||||
osgDB::FilePathList &fp = options->getDatabasePathList();
|
||||
const PathList &sc = globals->get_fg_scenery();
|
||||
fp.clear();
|
||||
PathList::const_iterator it;
|
||||
for (it = sc.begin(); it != sc.end(); ++it) {
|
||||
fp.push_back(it->local8BitStr());
|
||||
}
|
||||
|
||||
options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str());
|
||||
|
||||
options->setPluginStringData("SimGear::BARE_LOD_RANGE",
|
||||
fgGetString("/sim/rendering/static-lod/bare-delta",
|
||||
std::to_string(SG_OBJECT_RANGE_BARE)));
|
||||
options->setPluginStringData("SimGear::ROUGH_LOD_RANGE",
|
||||
fgGetString("/sim/rendering/static-lod/rough-delta",
|
||||
std::to_string(SG_OBJECT_RANGE_ROUGH)));
|
||||
options->setPluginStringData("SimGear::ROUGH_LOD_DETAILED",
|
||||
fgGetString("/sim/rendering/static-lod/detailed",
|
||||
std::to_string(SG_OBJECT_RANGE_DETAILED)));
|
||||
options->setPluginStringData("SimGear::RENDER_BUILDING_MESH", fgGetBool("/sim/rendering/building-mesh", false) ? "true" : "false");
|
||||
|
||||
options->setPluginStringData("SimGear::FG_EARTH", "ON");
|
||||
|
||||
// tunables
|
||||
options->setPluginStringData("SimGear::SPT_PAGE_LEVELS", fgGetString("/sim/scenery/lod-levels", "1 3 5 7 9" ));
|
||||
options->setPluginStringData("SimGear::SPT_RANGE_MULTIPLIER", fgGetString("/sim/scenery/lod-range-mult", "2" ));
|
||||
options->setPluginStringData("SimGear::SPT_MESH_RESOLUTION", fgGetString("/sim/scenery/lod-res", "1" ));
|
||||
options->setPluginStringData("SimGear::SPT_LOD_TEXTURING", fgGetString("/sim/scenery/lod-texturing", "bluemarble" ));
|
||||
options->setMaterialLib(globals->get_matlib());
|
||||
|
||||
// a DEM can contain multiple levels from multiple locations
|
||||
// priority is based on first found...
|
||||
_dem = new SGDem;
|
||||
if ( _dem ) {
|
||||
for (osgDB::FilePathList::const_iterator i = fp.begin(); i != fp.end(); ++i) {
|
||||
SGPath demPath(*i);
|
||||
demPath.append("DEM");
|
||||
|
||||
int numLevels = _dem->addRoot(demPath);
|
||||
if ( numLevels ) {
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has " << numLevels << " LOD Levels " );
|
||||
} else {
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has NO LOD Levels " );
|
||||
}
|
||||
}
|
||||
|
||||
options->setDem(_dem);
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - Load w180s90-360x180.pgt" );
|
||||
osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFile("w180s90-360x180.pgt", options.get());
|
||||
|
||||
if ( loadedModel ) {
|
||||
terrain_branch->addChild( loadedModel.get() );
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGPgtTerrain::reinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FGPgtTerrain::shutdown()
|
||||
{
|
||||
terrain_branch = NULL;
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
|
||||
void FGPgtTerrain::update(double dt)
|
||||
{
|
||||
// scenery loading check, triggers after each sim (tile manager) reinit
|
||||
if (!_scenery_loaded->getBoolValue())
|
||||
{
|
||||
bool fdmInited = fgGetBool("sim/fdm-initialized");
|
||||
bool positionFinalized = fgGetBool("sim/position-finalized");
|
||||
bool sceneryOverride = _scenery_override->getBoolValue();
|
||||
|
||||
// we are done if final position is set and the scenery & FDM are done.
|
||||
// scenery-override can ignore the last two, but not position finalization.
|
||||
if (positionFinalized && (sceneryOverride || fdmInited))
|
||||
{
|
||||
_scenery_loaded->setBoolValue(true);
|
||||
fgSplashProgress("");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!positionFinalized) {
|
||||
fgSplashProgress("finalize-position");
|
||||
} else {
|
||||
fgSplashProgress("loading-scenery");
|
||||
}
|
||||
|
||||
// be nice to loader threads while waiting for initial scenery, reduce to 20fps
|
||||
SGTimeStamp::sleepForMSec(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGPgtTerrain::bind()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FGPgtTerrain::unbind()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
FGPgtTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
SGGeod geod = SGGeod::fromCart(pos);
|
||||
geod.setElevationM(geod.getElevationM() + max_altoff);
|
||||
|
||||
return get_elevation_m(geod, alt, material, butNotFrom);
|
||||
}
|
||||
|
||||
static simgear::BVHMaterial def_mat;
|
||||
|
||||
bool
|
||||
FGPgtTerrain::get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
alt = 100.0;
|
||||
if (material) {
|
||||
*material = &def_mat;
|
||||
} else {
|
||||
// SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::get_elevation_m: alt " << alt << " no material " );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGPgtTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGPgtTerrain::scenery_available(const SGGeod& position, double range_m)
|
||||
{
|
||||
if( schedule_scenery(position, range_m, 0.0) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FGPgtTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
|
||||
{
|
||||
// sanity check (unfortunately needed!)
|
||||
if (!position.isValid()) {
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGSptTerrain::schedule_scenery - position invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGPgtTerrain::materialLibChanged()
|
||||
{
|
||||
// PSADRO: TODO - passing down new regional textures won't work. these need to be set in the
|
||||
// lod tree at init time, as OSGDBPager generates the load request, not the tile cache.
|
||||
|
||||
// _options->setMaterialLib(globals->get_matlib());
|
||||
}
|
||||
|
||||
#endif
|
||||
127
src/Scenery/terrain_pgt.hxx
Normal file
127
src/Scenery/terrain_pgt.hxx
Normal file
@@ -0,0 +1,127 @@
|
||||
// terrain_pgt.hxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _TERRAIN_PGT_HXX
|
||||
#define _TERRAIN_PGT_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Switch>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/scene/model/particles.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/scene/dem/SGDem.hxx>
|
||||
|
||||
#include "terrain.hxx"
|
||||
//#include "SceneryPager.hxx"
|
||||
//#include "tilemgr.hxx"
|
||||
|
||||
namespace simgear {
|
||||
class BVHMaterial;
|
||||
}
|
||||
|
||||
// Define a structure containing global scenery parameters
|
||||
class FGPgtTerrain : public FGTerrain
|
||||
{
|
||||
public:
|
||||
|
||||
FGPgtTerrain();
|
||||
~FGPgtTerrain();
|
||||
|
||||
// Implementation of SGSubsystem.
|
||||
void init ( osg::Group* terrain );
|
||||
void reinit();
|
||||
void shutdown ();
|
||||
void bind ();
|
||||
void unbind ();
|
||||
void update (double dt);
|
||||
|
||||
/// Compute the elevation of the scenery at geodetic latitude lat,
|
||||
/// geodetic longitude lon and not higher than max_alt.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters or degrees.
|
||||
bool get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the elevation of the scenery below the cartesian point pos.
|
||||
/// you the returned scenery altitude is not higher than the position
|
||||
/// pos plus an offset given with max_altoff.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters.
|
||||
bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& elevation,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the nearest intersection point of the line starting from
|
||||
/// start going in direction dir with the terrain.
|
||||
/// The input and output values should be in cartesian coordinates in the
|
||||
/// usual earth centered wgs84 coordinate system. Units are meters.
|
||||
/// On success, true is returned.
|
||||
bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Returns true if scenery is available for the given lat, lon position
|
||||
/// within a range of range_m.
|
||||
/// lat and lon are expected to be in degrees.
|
||||
bool scenery_available(const SGGeod& position, double range_m);
|
||||
|
||||
// tile mgr api
|
||||
bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
|
||||
void materialLibChanged();
|
||||
|
||||
static const char* staticSubsystemClassId() { return "scenery"; }
|
||||
|
||||
private:
|
||||
// terrain branch of scene graph
|
||||
osg::ref_ptr<osg::Group> terrain_branch;
|
||||
|
||||
SGPropertyNode_ptr _scenery_loaded, _scenery_override;
|
||||
|
||||
bool _inited;
|
||||
|
||||
SGDemPtr _dem;
|
||||
};
|
||||
|
||||
#endif // _TERRAIN_PGT_HXX
|
||||
413
src/Scenery/terrain_stg.cxx
Normal file
413
src/Scenery/terrain_stg.cxx
Normal file
@@ -0,0 +1,413 @@
|
||||
// scenery.cxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Transform>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/CameraView>
|
||||
#include <osg/LOD>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/scene/tgdb/userdata.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/SGSceneUserData.hxx>
|
||||
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
|
||||
#include <simgear/scene/sky/sky.hxx>
|
||||
|
||||
#include <simgear/bvh/BVHNode.hxx>
|
||||
#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <Viewer/renderer.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <GUI/MouseCursor.hxx>
|
||||
|
||||
#include "terrain_stg.hxx"
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear;
|
||||
|
||||
class FGGroundPickCallback : public SGPickCallback {
|
||||
public:
|
||||
FGGroundPickCallback() : SGPickCallback(PriorityScenery)
|
||||
{ }
|
||||
|
||||
virtual bool buttonPressed( int button,
|
||||
const osgGA::GUIEventAdapter&,
|
||||
const Info& info )
|
||||
{
|
||||
// only on left mouse button
|
||||
if (button != 0)
|
||||
return false;
|
||||
|
||||
SGGeod geod = SGGeod::fromCart(info.wgs84);
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod );
|
||||
|
||||
SGPropertyNode *c = fgGetNode("/sim/input/click", true);
|
||||
c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
|
||||
c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
|
||||
c->setDoubleValue("elevation-m", geod.getElevationM());
|
||||
c->setDoubleValue("elevation-ft", geod.getElevationFt());
|
||||
fgSetBool("/sim/signals/click", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class FGSceneryIntersect : public osg::NodeVisitor {
|
||||
public:
|
||||
FGSceneryIntersect(const SGLineSegmentd& lineSegment,
|
||||
const osg::Node* skipNode) :
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
|
||||
_lineSegment(lineSegment),
|
||||
_skipNode(skipNode),
|
||||
_material(0),
|
||||
_haveHit(false)
|
||||
{ }
|
||||
|
||||
bool getHaveHit() const
|
||||
{ return _haveHit; }
|
||||
const SGLineSegmentd& getLineSegment() const
|
||||
{ return _lineSegment; }
|
||||
const simgear::BVHMaterial* getMaterial() const
|
||||
{ return _material; }
|
||||
|
||||
virtual void apply(osg::Node& node)
|
||||
{
|
||||
if (&node == _skipNode)
|
||||
return;
|
||||
if (!testBoundingSphere(node.getBound()))
|
||||
return;
|
||||
|
||||
addBoundingVolume(node);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Group& group)
|
||||
{
|
||||
if (&group == _skipNode)
|
||||
return;
|
||||
if (!testBoundingSphere(group.getBound()))
|
||||
return;
|
||||
|
||||
traverse(group);
|
||||
addBoundingVolume(group);
|
||||
}
|
||||
|
||||
virtual void apply(osg::Transform& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::Camera& camera)
|
||||
{
|
||||
if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
|
||||
return;
|
||||
handleTransform(camera);
|
||||
}
|
||||
virtual void apply(osg::CameraView& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::MatrixTransform& transform)
|
||||
{ handleTransform(transform); }
|
||||
virtual void apply(osg::PositionAttitudeTransform& transform)
|
||||
{ handleTransform(transform); }
|
||||
|
||||
private:
|
||||
void handleTransform(osg::Transform& transform)
|
||||
{
|
||||
if (&transform == _skipNode)
|
||||
return;
|
||||
// Hmm, may be this needs to be refined somehow ...
|
||||
if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
|
||||
return;
|
||||
|
||||
if (!testBoundingSphere(transform.getBound()))
|
||||
return;
|
||||
|
||||
osg::Matrix inverseMatrix;
|
||||
if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
|
||||
return;
|
||||
osg::Matrix matrix;
|
||||
if (!transform.computeLocalToWorldMatrix(matrix, this))
|
||||
return;
|
||||
|
||||
SGLineSegmentd lineSegment = _lineSegment;
|
||||
bool haveHit = _haveHit;
|
||||
const simgear::BVHMaterial* material = _material;
|
||||
|
||||
_haveHit = false;
|
||||
_lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
|
||||
|
||||
addBoundingVolume(transform);
|
||||
traverse(transform);
|
||||
|
||||
if (_haveHit) {
|
||||
_lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
|
||||
} else {
|
||||
_lineSegment = lineSegment;
|
||||
_material = material;
|
||||
_haveHit = haveHit;
|
||||
}
|
||||
}
|
||||
|
||||
simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
|
||||
{
|
||||
SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
|
||||
if (!userData)
|
||||
return 0;
|
||||
return userData->getBVHNode();
|
||||
}
|
||||
void addBoundingVolume(osg::Node& node)
|
||||
{
|
||||
simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
|
||||
if (!bvNode)
|
||||
return;
|
||||
|
||||
// Find ground intersection on the bvh nodes
|
||||
simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
|
||||
0/*startTime*/);
|
||||
bvNode->accept(lineSegmentVisitor);
|
||||
if (!lineSegmentVisitor.empty()) {
|
||||
_lineSegment = lineSegmentVisitor.getLineSegment();
|
||||
_material = lineSegmentVisitor.getMaterial();
|
||||
_haveHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool testBoundingSphere(const osg::BoundingSphere& bound) const
|
||||
{
|
||||
if (!bound.valid())
|
||||
return false;
|
||||
|
||||
SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
|
||||
return intersects(_lineSegment, sphere);
|
||||
}
|
||||
|
||||
SGLineSegmentd _lineSegment;
|
||||
const osg::Node* _skipNode;
|
||||
|
||||
const simgear::BVHMaterial* _material;
|
||||
bool _haveHit;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Terrain Management system
|
||||
FGStgTerrain::FGStgTerrain() :
|
||||
_tilemgr()
|
||||
{
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
FGStgTerrain::~FGStgTerrain()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::dtor");
|
||||
}
|
||||
|
||||
|
||||
// Initialize the Scenery Management system
|
||||
void FGStgTerrain::init( osg::Group* terrain ) {
|
||||
// Already set up.
|
||||
if (_inited)
|
||||
return;
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::init - init tilemgr");
|
||||
|
||||
// remember the scene terrain branch on scenegraph
|
||||
terrain_branch = terrain;
|
||||
|
||||
// initialize the tile manager
|
||||
_tilemgr.init();
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
void FGStgTerrain::reinit()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::reinit - reinit tilemgr");
|
||||
|
||||
_tilemgr.reinit();
|
||||
}
|
||||
|
||||
void FGStgTerrain::shutdown()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::shutdown - shutdown tilemgr");
|
||||
|
||||
_tilemgr.shutdown();
|
||||
|
||||
terrain_branch = NULL;
|
||||
|
||||
// Toggle the setup flag.
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
|
||||
void FGStgTerrain::update(double dt)
|
||||
{
|
||||
_tilemgr.update(dt);
|
||||
}
|
||||
|
||||
void FGStgTerrain::bind()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::bind - noop");
|
||||
}
|
||||
|
||||
void FGStgTerrain::unbind()
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::unbind - noop");
|
||||
}
|
||||
|
||||
bool
|
||||
FGStgTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
SGGeod geod = SGGeod::fromCart(pos);
|
||||
if (!geod.isValid())
|
||||
return false;
|
||||
|
||||
geod.setElevationM(geod.getElevationM() + max_altoff);
|
||||
|
||||
ok = get_elevation_m(geod, alt, material, butNotFrom);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
FGStgTerrain::get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
if (!geod.isValid())
|
||||
return false;
|
||||
|
||||
SGVec3d start = SGVec3d::fromGeod(geod);
|
||||
|
||||
SGGeod geodEnd = geod;
|
||||
geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
|
||||
SGVec3d end = SGVec3d::fromGeod(geodEnd);
|
||||
|
||||
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
|
||||
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
||||
terrain_branch->accept(intersectVisitor);
|
||||
|
||||
if (!intersectVisitor.getHaveHit())
|
||||
return false;
|
||||
|
||||
geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
|
||||
alt = geodEnd.getElevationM();
|
||||
if (material) {
|
||||
*material = intersectVisitor.getMaterial();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FGStgTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom)
|
||||
{
|
||||
// We assume that starting positions in the center of the earth are invalid
|
||||
if ( norm1(pos) < 1 )
|
||||
return false;
|
||||
|
||||
// Make really sure the direction is normalized, is really cheap compared to
|
||||
// computation of ground intersection.
|
||||
SGVec3d start = pos;
|
||||
SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
|
||||
|
||||
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
|
||||
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
|
||||
terrain_branch->accept(intersectVisitor);
|
||||
|
||||
if (!intersectVisitor.getHaveHit())
|
||||
return false;
|
||||
|
||||
nearestHit = intersectVisitor.getLineSegment().getEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGStgTerrain::scenery_available(const SGGeod& position, double range_m)
|
||||
{
|
||||
if( schedule_scenery(position, range_m, 0.0) )
|
||||
{
|
||||
double elev = 0.0;
|
||||
|
||||
bool use_vpb = globals->get_props()->getNode("scenery/use-vpb")->getBoolValue();
|
||||
bool got_elev = get_elevation_m(SGGeod::fromGeodM(position, SG_MAX_ELEVATION_M), elev, 0, 0);
|
||||
|
||||
if (!use_vpb && !got_elev)
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
|
||||
return false;
|
||||
}
|
||||
|
||||
SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev));
|
||||
osg::FrameStamp* framestamp
|
||||
= globals->get_renderer()->getFrameStamp();
|
||||
|
||||
FGScenery* pSceneryManager = globals->get_scenery();
|
||||
simgear::CheckSceneryVisitor csnv(pSceneryManager->getPager(), toOsg(p), range_m, framestamp);
|
||||
// currently the PagedLODs will not be loaded by the DatabasePager
|
||||
// while the splashscreen is there, so CheckSceneryVisitor force-loads
|
||||
// missing objects in the main thread
|
||||
terrain_branch->accept(csnv);
|
||||
if(!csnv.isLoaded()) {
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on CheckSceneryVisitor");
|
||||
return false;
|
||||
}
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - true" );
|
||||
return true;
|
||||
} else {
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on tile manager");
|
||||
}
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FGStgTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
|
||||
{
|
||||
SG_LOG(SG_TERRAIN, SG_BULK, "FGStgTerrain::schedule_scenery");
|
||||
|
||||
return _tilemgr.schedule_scenery( position, range_m, duration );
|
||||
}
|
||||
|
||||
void FGStgTerrain::materialLibChanged()
|
||||
{
|
||||
_tilemgr.materialLibChanged();
|
||||
}
|
||||
126
src/Scenery/terrain_stg.hxx
Normal file
126
src/Scenery/terrain_stg.hxx
Normal file
@@ -0,0 +1,126 @@
|
||||
// scenery.hxx -- data structures and routines for managing scenery.
|
||||
//
|
||||
// Written by Curtis Olson, started May 1997.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _TERRAIN_STG_HXX
|
||||
#define _TERRAIN_STG_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Switch>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/scene/model/particles.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include "terrain.hxx"
|
||||
#include "SceneryPager.hxx"
|
||||
#include "tilemgr.hxx"
|
||||
|
||||
namespace simgear {
|
||||
class BVHMaterial;
|
||||
}
|
||||
|
||||
// Define a structure containing global scenery parameters
|
||||
class FGStgTerrain : public FGTerrain
|
||||
{
|
||||
public:
|
||||
|
||||
FGStgTerrain();
|
||||
virtual ~FGStgTerrain();
|
||||
|
||||
// Implementation of SGSubsystem.
|
||||
void init ( osg::Group* terrain );
|
||||
void reinit();
|
||||
void shutdown ();
|
||||
void bind ();
|
||||
void unbind ();
|
||||
void update (double dt);
|
||||
|
||||
/// Compute the elevation of the scenery at geodetic latitude lat,
|
||||
/// geodetic longitude lon and not higher than max_alt.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters or degrees.
|
||||
bool get_elevation_m(const SGGeod& geod, double& alt,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the elevation of the scenery below the cartesian point pos.
|
||||
/// you the returned scenery altitude is not higher than the position
|
||||
/// pos plus an offset given with max_altoff.
|
||||
/// If the exact flag is set to true, the scenery center is moved to
|
||||
/// gain a higher accuracy of that query. The center is restored past
|
||||
/// that to the original value.
|
||||
/// The altitude hit is returned in the alt argument.
|
||||
/// The method returns true if the scenery is available for the given
|
||||
/// lat/lon pair. If there is no scenery for that point, the altitude
|
||||
/// value is undefined.
|
||||
/// All values are meant to be in meters.
|
||||
bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
|
||||
double& elevation,
|
||||
const simgear::BVHMaterial** material,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Compute the nearest intersection point of the line starting from
|
||||
/// start going in direction dir with the terrain.
|
||||
/// The input and output values should be in cartesian coordinates in the
|
||||
/// usual earth centered wgs84 coordinate system. Units are meters.
|
||||
/// On success, true is returned.
|
||||
bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
|
||||
SGVec3d& nearestHit,
|
||||
const osg::Node* butNotFrom = 0);
|
||||
|
||||
/// Returns true if scenery is available for the given lat, lon position
|
||||
/// within a range of range_m.
|
||||
/// lat and lon are expected to be in degrees.
|
||||
bool scenery_available(const SGGeod& position, double range_m);
|
||||
|
||||
// tile mgr api
|
||||
bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
|
||||
void materialLibChanged();
|
||||
|
||||
static const char* staticSubsystemClassId() { return "scenery"; }
|
||||
|
||||
private:
|
||||
// tile manager
|
||||
FGTileMgr _tilemgr;
|
||||
|
||||
// terrain branch of scene graph
|
||||
osg::ref_ptr<osg::Group> terrain_branch;
|
||||
|
||||
bool _inited;
|
||||
};
|
||||
|
||||
#endif // _TERRAIN_STG_HXX
|
||||
|
||||
|
||||
274
src/Scenery/tilecache.cxx
Normal file
274
src/Scenery/tilecache.cxx
Normal file
@@ -0,0 +1,274 @@
|
||||
// TileCache.cxx -- routines to handle scenery tile caching
|
||||
//
|
||||
// Written by Curtis Olson, started December 2000.
|
||||
//
|
||||
// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "tileentry.hxx"
|
||||
#include "tilecache.hxx"
|
||||
|
||||
TileCache::TileCache( void ) :
|
||||
max_cache_size(100), current_time(0.0)
|
||||
{
|
||||
tile_cache.clear();
|
||||
}
|
||||
|
||||
|
||||
TileCache::~TileCache( void )
|
||||
{
|
||||
tile_map_iterator it = tile_cache.begin();
|
||||
for (; it != tile_cache.end(); ++it) {
|
||||
TileEntry* tile = it->second;
|
||||
tile->removeFromSceneGraph();
|
||||
delete tile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Free a tile cache entry
|
||||
void TileCache::entry_free( long tile_index ) {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << tile_index );
|
||||
TileEntry *tile = tile_cache[tile_index];
|
||||
tile->removeFromSceneGraph();
|
||||
tile_cache.erase( tile_index );
|
||||
delete tile;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the tile cache subsystem
|
||||
void TileCache::init( void ) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Initializing the tile cache." );
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " max cache size = "
|
||||
<< max_cache_size );
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " current cache size = "
|
||||
<< tile_cache.size() );
|
||||
|
||||
clear_cache();
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " done with init()" );
|
||||
}
|
||||
|
||||
|
||||
// Search for the specified "bucket" in the cache
|
||||
bool TileCache::exists_stg( const SGBucket& b ) const {
|
||||
long tile_index = b.gen_index();
|
||||
const_tile_map_iterator it = tile_cache.find( tile_index );
|
||||
|
||||
return ( it != tile_cache.end() );
|
||||
}
|
||||
|
||||
bool TileCache::exists_vpb( const SGBucket& b ) const {
|
||||
// VPB tiles are stored with negative index to avoid clash with STG index
|
||||
long tile_index = - b.gen_vpb_index();
|
||||
const_tile_map_iterator it = tile_cache.find( tile_index );
|
||||
|
||||
return ( it != tile_cache.end() );
|
||||
}
|
||||
|
||||
|
||||
// Return the index of a tile to be dropped from the cache, return -1 if
|
||||
// nothing available to be removed.
|
||||
long TileCache::get_drop_tile() {
|
||||
long min_index = -1;
|
||||
double min_time = DBL_MAX;
|
||||
float priority = FLT_MAX;
|
||||
|
||||
tile_map_iterator current = tile_cache.begin();
|
||||
tile_map_iterator end = tile_cache.end();
|
||||
|
||||
for ( ; current != end; ++current ) {
|
||||
long index = current->first;
|
||||
TileEntry *e = current->second;
|
||||
if (( !e->is_current_view() )&&
|
||||
( e->is_expired(current_time) ))
|
||||
{
|
||||
if (e->is_expired(current_time - 1.0)&&
|
||||
!e->is_loaded())
|
||||
{
|
||||
/* Immediately drop "empty" tiles which are no longer used/requested, and were last requested > 1 second ago...
|
||||
* Allow a 1 second timeout since an empty tiles may just be loaded...
|
||||
*/
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " dropping an unused and empty tile");
|
||||
min_index = index;
|
||||
break;
|
||||
}
|
||||
if (( e->get_time_expired() < min_time )||
|
||||
(( e->get_time_expired() == min_time)&&
|
||||
( priority > e->get_priority())))
|
||||
{
|
||||
// drop oldest tile with lowest priority
|
||||
min_time = e->get_time_expired();
|
||||
priority = e->get_priority();
|
||||
min_index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index );
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time );
|
||||
|
||||
return min_index;
|
||||
}
|
||||
|
||||
long TileCache::get_first_expired_tile() const
|
||||
{
|
||||
const_tile_map_iterator current = tile_cache.begin();
|
||||
const_tile_map_iterator end = tile_cache.end();
|
||||
|
||||
for ( ; current != end; ++current ) {
|
||||
TileEntry *e = current->second;
|
||||
if (!e->is_current_view() && e->is_expired(current_time))
|
||||
{
|
||||
return current->first;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // no expired tile found
|
||||
}
|
||||
|
||||
|
||||
// Clear all flags indicating tiles belonging to the current view
|
||||
void TileCache::clear_current_view()
|
||||
{
|
||||
tile_map_iterator current = tile_cache.begin();
|
||||
tile_map_iterator end = tile_cache.end();
|
||||
|
||||
for ( ; current != end; ++current ) {
|
||||
TileEntry *e = current->second;
|
||||
if (e->is_current_view())
|
||||
{
|
||||
// update expiry time for tiles belonging to most recent position
|
||||
e->update_time_expired( current_time );
|
||||
e->set_current_view( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear a cache entry, note that the cache only holds pointers
|
||||
// and this does not free the object which is pointed to.
|
||||
void TileCache::clear_entry( long tile_index ) {
|
||||
tile_cache.erase( tile_index );
|
||||
}
|
||||
|
||||
|
||||
// Clear all completely loaded tiles (ignores partially loaded tiles)
|
||||
void TileCache::clear_cache() {
|
||||
std::vector<long> indexList;
|
||||
tile_map_iterator current = tile_cache.begin();
|
||||
tile_map_iterator end = tile_cache.end();
|
||||
|
||||
for ( ; current != end; ++current ) {
|
||||
long index = current->first;
|
||||
TileEntry *e = current->second;
|
||||
if ( e->is_loaded() ) {
|
||||
e->tile_bucket.make_bad();
|
||||
// entry_free modifies tile_cache, so store index and call entry_free() later;
|
||||
indexList.push_back( index);
|
||||
}
|
||||
}
|
||||
for (unsigned int it = 0; it < indexList.size(); it++) {
|
||||
entry_free( indexList[ it]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new tile and schedule it for loading.
|
||||
*/
|
||||
bool TileCache::insert_tile( STGTileEntry *e ) {
|
||||
// register tile in the cache
|
||||
long tile_index = e->get_tile_bucket().gen_index();
|
||||
tile_cache[tile_index] = e;
|
||||
e->update_time_expired(current_time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new tile and schedule it for loading. VPB version, with negative index.
|
||||
*/
|
||||
bool TileCache::insert_tile( VPBTileEntry *e ) {
|
||||
// register tile in the cache
|
||||
long tile_index = - e->get_tile_bucket().gen_vpb_index();
|
||||
tile_cache[tile_index] = e;
|
||||
e->update_time_expired(current_time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// update tile's priority and expiry time according to current request
|
||||
void TileCache::request_tile(TileEntry* t,float priority,bool current_view,double request_time)
|
||||
{
|
||||
if ((!current_view)&&(request_time<=0.0))
|
||||
return;
|
||||
|
||||
// update priority when higher - or old request has expired
|
||||
if ((t->is_expired(current_time))||
|
||||
(priority > t->get_priority()))
|
||||
{
|
||||
t->set_priority( priority );
|
||||
}
|
||||
|
||||
if (current_view)
|
||||
{
|
||||
t->update_time_expired( current_time + request_time );
|
||||
t->set_current_view( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
t->update_time_expired( current_time+request_time );
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the specified tile cache entry
|
||||
STGTileEntry* TileCache::get_stg_tile( const SGBucket& b ) const {
|
||||
|
||||
const_tile_map_iterator it = std::find_if(tile_cache.begin(), tile_cache.end(),
|
||||
[b](auto &t) {
|
||||
return ((b.gen_index() == t.first) && (t.second->getExtension() == TileEntry::Extension::STG));
|
||||
});
|
||||
if ( it != tile_cache.end() ) {
|
||||
return dynamic_cast<STGTileEntry*>(it->second);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the specified tile cache entry
|
||||
VPBTileEntry* TileCache::get_vpb_tile( const SGBucket& b ) const {
|
||||
const_tile_map_iterator it = std::find_if(tile_cache.begin(), tile_cache.end(),
|
||||
[b](auto &t) {
|
||||
// Negative indices are used for the VPB tiles.
|
||||
return (( - b.gen_vpb_index() == t.first) && (t.second->getExtension() == TileEntry::Extension::VPB));
|
||||
});
|
||||
if ( it != tile_cache.end() ) {
|
||||
return dynamic_cast<VPBTileEntry*>(it->second);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
129
src/Scenery/tilecache.hxx
Normal file
129
src/Scenery/tilecache.hxx
Normal file
@@ -0,0 +1,129 @@
|
||||
// TileCache.hxx -- routines to handle scenery tile caching
|
||||
//
|
||||
// Written by Curtis Olson, started December 2000.
|
||||
//
|
||||
// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include "tileentry.hxx"
|
||||
|
||||
|
||||
// A class to store and manage a pile of tiles
|
||||
class TileCache {
|
||||
public:
|
||||
typedef std::map < long, TileEntry * > tile_map;
|
||||
typedef tile_map::iterator tile_map_iterator;
|
||||
typedef tile_map::const_iterator const_tile_map_iterator;
|
||||
private:
|
||||
// cache storage space
|
||||
tile_map tile_cache;
|
||||
|
||||
// maximum cache size
|
||||
int max_cache_size;
|
||||
|
||||
// pointers to allow an external linear traversal of cache entries
|
||||
tile_map_iterator current;
|
||||
|
||||
double current_time;
|
||||
|
||||
// Free a tile cache entry
|
||||
void entry_free( long cache_index );
|
||||
|
||||
public:
|
||||
tile_map_iterator begin() { return tile_cache.begin(); }
|
||||
tile_map_iterator end() { return tile_cache.end(); }
|
||||
const_tile_map_iterator begin() const { return tile_cache.begin(); }
|
||||
const_tile_map_iterator end() const { return tile_cache.end(); }
|
||||
|
||||
// Constructor
|
||||
TileCache();
|
||||
|
||||
// Destructor
|
||||
~TileCache();
|
||||
|
||||
// Initialize the tile cache subsystem
|
||||
void init( void );
|
||||
|
||||
// Check if the specified "bucket" exists in the cache
|
||||
bool exists_stg( const SGBucket& b ) const;
|
||||
bool exists_vpb( const SGBucket& b ) const;
|
||||
|
||||
// Return the index of a tile to be dropped from the cache, return -1 if
|
||||
// nothing available to be removed.
|
||||
long get_drop_tile();
|
||||
|
||||
long get_first_expired_tile() const;
|
||||
|
||||
// Clear all flags indicating tiles belonging to the current view
|
||||
void clear_current_view();
|
||||
|
||||
// Clear a cache entry, note that the cache only holds pointers
|
||||
// and this does not free the object which is pointed to.
|
||||
void clear_entry( long cache_entry );
|
||||
|
||||
// Clear all completely loaded tiles (ignores partially loaded tiles)
|
||||
void clear_cache();
|
||||
|
||||
// Return a pointer to the specified tile cache entry
|
||||
inline TileEntry *get_tile( const long tile_index ) const {
|
||||
const_tile_map_iterator it = tile_cache.find( tile_index );
|
||||
if ( it != tile_cache.end() ) {
|
||||
return it->second;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
STGTileEntry* get_stg_tile( const SGBucket& b ) const;
|
||||
VPBTileEntry* get_vpb_tile( const SGBucket& b ) const;
|
||||
|
||||
// Return the cache size
|
||||
inline size_t get_size() const { return tile_cache.size(); }
|
||||
|
||||
// External linear traversal of cache
|
||||
inline void reset_traversal() { current = tile_cache.begin(); }
|
||||
inline bool at_end() { return current == tile_cache.end(); }
|
||||
inline TileEntry *get_current() const {
|
||||
// cout << "index = " << current->first << endl;
|
||||
return current->second;
|
||||
}
|
||||
inline void next() { ++current; }
|
||||
|
||||
inline int get_max_cache_size() const { return max_cache_size; }
|
||||
inline void set_max_cache_size( int m ) { max_cache_size = m; }
|
||||
|
||||
/**
|
||||
* Create a new tile and enqueue it for loading.
|
||||
* @param b
|
||||
* @return success/failure
|
||||
*/
|
||||
bool insert_tile( STGTileEntry* e );
|
||||
bool insert_tile( VPBTileEntry* e );
|
||||
|
||||
void set_current_time(double val) { current_time = val; }
|
||||
double get_current_time() const { return current_time; }
|
||||
|
||||
// update tile's priority and expiry time according to current request
|
||||
void request_tile(TileEntry* t,float priority,bool current_view,double requesttime);
|
||||
};
|
||||
158
src/Scenery/tileentry.cxx
Normal file
158
src/Scenery/tileentry.cxx
Normal file
@@ -0,0 +1,158 @@
|
||||
// tileentry.cxx -- routines to handle a scenery tile
|
||||
//
|
||||
// Written by Curtis Olson, started May 1998.
|
||||
//
|
||||
// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <istream>
|
||||
|
||||
#include <osg/LOD>
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include "tileentry.hxx"
|
||||
|
||||
using std::string;
|
||||
|
||||
// Base constructor
|
||||
TileEntry::TileEntry ( const SGBucket& b )
|
||||
: tile_bucket( b ),
|
||||
_node( new osg::LOD ),
|
||||
_priority(-FLT_MAX),
|
||||
_current_view(false),
|
||||
_time_expired(-1.0)
|
||||
{
|
||||
_create_orthophoto();
|
||||
|
||||
// Give a default LOD range so that traversals that traverse
|
||||
// active children (like the groundcache lookup) will work before
|
||||
// tile manager has had a chance to update this node.
|
||||
_node->setRange(0, 0.0, 10000.0);
|
||||
}
|
||||
|
||||
TileEntry::TileEntry( const TileEntry& t )
|
||||
: tile_bucket( t.tile_bucket ),
|
||||
tileFileName(t.tileFileName),
|
||||
_node( new osg::LOD ),
|
||||
_priority(t._priority),
|
||||
_current_view(t._current_view),
|
||||
_time_expired(t._time_expired)
|
||||
{
|
||||
_create_orthophoto();
|
||||
|
||||
_node->setName(tileFileName);
|
||||
// Give a default LOD range so that traversals that traverse
|
||||
// active children (like the groundcache lookup) will work before
|
||||
// tile manager has had a chance to update this node.
|
||||
_node->setRange(0, 0.0, 10000.0);
|
||||
}
|
||||
|
||||
void TileEntry::_create_orthophoto() {
|
||||
bool use_photoscenery = fgGetBool("/sim/rendering/photoscenery/enabled");
|
||||
if (use_photoscenery) {
|
||||
_orthophoto = simgear::Orthophoto::fromBucket(tile_bucket, globals->get_fg_scenery());
|
||||
if (_orthophoto) {
|
||||
simgear::OrthophotoManager::instance()->registerOrthophoto(tile_bucket.gen_index(), _orthophoto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor
|
||||
TileEntry::~TileEntry ()
|
||||
{
|
||||
}
|
||||
|
||||
// Update the ssg transform node for this tile so it can be
|
||||
// properly drawn relative to our (0,0,0) point
|
||||
void TileEntry::prep_ssg_node(float vis) {
|
||||
if (!is_loaded())
|
||||
return;
|
||||
// visibility can change from frame to frame so we update the
|
||||
// range selector cutoff's each time.
|
||||
float bounding_radius = _node->getChild(0)->getBound().radius();
|
||||
_node->setRange( 0, 0, vis + bounding_radius );
|
||||
}
|
||||
|
||||
void
|
||||
TileEntry::addToSceneGraph(osg::Group *terrain_branch)
|
||||
{
|
||||
terrain_branch->addChild( _node.get() );
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG,
|
||||
"connected a tile into scene graph. _node = "
|
||||
<< _node.get() );
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
|
||||
<< _node->getNumParents() );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TileEntry::removeFromSceneGraph()
|
||||
{
|
||||
if (! is_loaded()) {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() );
|
||||
}
|
||||
|
||||
// find the nodes branch parent
|
||||
if ( _node->getNumParents() > 0 ) {
|
||||
// find the first parent (should only be one)
|
||||
osg::Group *parent = _node->getParent( 0 ) ;
|
||||
if( parent ) {
|
||||
parent->removeChild( _node.get() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor - STG Variant
|
||||
STGTileEntry::STGTileEntry ( const SGBucket& b ) : TileEntry(b)
|
||||
{
|
||||
tileFileName = b.gen_index_str() + ".stg";
|
||||
_node->setName(tileFileName);
|
||||
}
|
||||
|
||||
// Destructor - STG Variant
|
||||
STGTileEntry::~STGTileEntry ()
|
||||
{
|
||||
}
|
||||
|
||||
// Constructur - VPB version
|
||||
VPBTileEntry::VPBTileEntry ( const SGBucket& b ) : TileEntry(b)
|
||||
{
|
||||
tileFileName = "vpb/" + b.gen_vpb_base() + ".osgb";
|
||||
_node->setName(tileFileName);
|
||||
// Give a default LOD range so that traversals that traverse
|
||||
// active children (like the groundcache lookup) will work before
|
||||
// tile manager has had a chance to update this node.
|
||||
_node->setRange(0, 0.0, 160000.0);
|
||||
}
|
||||
|
||||
// Destructor - VPB Variant
|
||||
VPBTileEntry::~VPBTileEntry ()
|
||||
{
|
||||
}
|
||||
|
||||
175
src/Scenery/tileentry.hxx
Normal file
175
src/Scenery/tileentry.hxx
Normal file
@@ -0,0 +1,175 @@
|
||||
// tileentry.hxx -- routines to handle an individual scenery tile
|
||||
//
|
||||
// Written by Curtis Olson, started May 1998.
|
||||
//
|
||||
// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _TILEENTRY_HXX
|
||||
#define _TILEENTRY_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/scene/util/OrthophotoManager.hxx>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osg/Group>
|
||||
#include <osg/LOD>
|
||||
|
||||
/**
|
||||
* A class to encapsulate everything we need to know about a scenery tile.
|
||||
*/
|
||||
class TileEntry {
|
||||
|
||||
public:
|
||||
// this tile's official location in the world
|
||||
SGBucket tile_bucket;
|
||||
std::string tileFileName;
|
||||
|
||||
protected:
|
||||
// pointer to ssg range selector for this tile
|
||||
osg::ref_ptr<osg::LOD> _node;
|
||||
|
||||
private:
|
||||
// Reference to DatabaseRequest object set and used by the
|
||||
// osgDB::DatabasePager.
|
||||
osg::ref_ptr<osg::Referenced> _databaseRequest;
|
||||
// Overlay image/orthophoto for this tile
|
||||
simgear::OrthophotoRef _orthophoto;
|
||||
|
||||
/**
|
||||
* This value is used by the tile scheduler/loader to load tiles
|
||||
* in a useful sequence. The priority is set to reflect the tiles
|
||||
* distance from the center, so all tiles are loaded in an innermost
|
||||
* to outermost sequence.
|
||||
*/
|
||||
float _priority;
|
||||
/** Flag indicating if tile belongs to current view. */
|
||||
bool _current_view;
|
||||
/** Time when tile expires. */
|
||||
double _time_expired;
|
||||
|
||||
void _create_orthophoto();
|
||||
|
||||
public:
|
||||
|
||||
// Constructor.
|
||||
TileEntry( const SGBucket& b );
|
||||
TileEntry( const TileEntry& t );
|
||||
|
||||
// Destructor
|
||||
virtual ~TileEntry() = 0;
|
||||
|
||||
// Update the ssg transform node for this tile so it can be
|
||||
// properly drawn relative to our (0,0,0) point
|
||||
void prep_ssg_node(float vis);
|
||||
|
||||
/**
|
||||
* Transition to OSG database pager
|
||||
*/
|
||||
static osg::Node* loadTileByFileName(const std::string& index_str,
|
||||
const osgDB::Options*);
|
||||
/**
|
||||
* Return true if the tile entry is loaded, otherwise return false
|
||||
* indicating that the loading thread is still working on this.
|
||||
*/
|
||||
inline bool is_loaded() const
|
||||
{
|
||||
return _node->getNumChildren() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "bucket" for this tile
|
||||
*/
|
||||
inline const SGBucket& get_tile_bucket() const { return tile_bucket; }
|
||||
|
||||
/**
|
||||
* Add terrain mesh and ground lighting to scene graph.
|
||||
*/
|
||||
void addToSceneGraph( osg::Group *terrain_branch);
|
||||
|
||||
/**
|
||||
* disconnect terrain mesh and ground lighting nodes from scene
|
||||
* graph for this tile.
|
||||
*/
|
||||
void removeFromSceneGraph();
|
||||
|
||||
/**
|
||||
* return the scenegraph node for the terrain
|
||||
*/
|
||||
osg::LOD *getNode() const { return _node.get(); }
|
||||
|
||||
inline double get_time_expired() const { return _time_expired; }
|
||||
inline void update_time_expired( double time_expired ) { if (_time_expired<time_expired) _time_expired = time_expired; }
|
||||
|
||||
inline void set_priority(float priority) { _priority=priority; }
|
||||
inline float get_priority() const { return _priority; }
|
||||
inline void set_current_view(bool current_view) { _current_view = current_view; }
|
||||
inline bool is_current_view() const { return _current_view; }
|
||||
|
||||
/**
|
||||
* Return false if the tile entry is still needed, otherwise return true
|
||||
* indicating that the tile is no longer in active use.
|
||||
*/
|
||||
inline bool is_expired(double current_time) const { return (_current_view) ? false : (current_time > _time_expired); }
|
||||
|
||||
// Get the ref_ptr to the DatabaseRequest object, in order to pass
|
||||
// this to the pager.
|
||||
osg::ref_ptr<osg::Referenced>& getDatabaseRequest()
|
||||
{
|
||||
return _databaseRequest;
|
||||
}
|
||||
|
||||
enum Extension {
|
||||
STG, VPB
|
||||
};
|
||||
|
||||
virtual TileEntry::Extension getExtension() = 0;
|
||||
};
|
||||
|
||||
class STGTileEntry : public TileEntry {
|
||||
public:
|
||||
STGTileEntry ( const SGBucket& b );
|
||||
~STGTileEntry();
|
||||
inline TileEntry::Extension getExtension() { return TileEntry::Extension::STG; };
|
||||
};
|
||||
|
||||
class VPBTileEntry : public TileEntry {
|
||||
public:
|
||||
VPBTileEntry ( const SGBucket& b );
|
||||
~VPBTileEntry();
|
||||
inline TileEntry::Extension getExtension() { return TileEntry::Extension::VPB; };
|
||||
};
|
||||
|
||||
|
||||
#endif // _TILEENTRY_HXX
|
||||
674
src/Scenery/tilemgr.cxx
Normal file
674
src/Scenery/tilemgr.cxx
Normal file
@@ -0,0 +1,674 @@
|
||||
// tilemgr.cxx -- routines to handle dynamic management of scenery tiles
|
||||
//
|
||||
// Written by Curtis Olson, started January 1998.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
#include <osgDB/Registry>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/scene/model/modellib.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/scene/tsync/terrasync.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Viewer/renderer.hxx>
|
||||
#include <Viewer/splash.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <Scripting/NasalModelData.hxx>
|
||||
|
||||
#include "scenery.hxx"
|
||||
#include "SceneryPager.hxx"
|
||||
#include "tilemgr.hxx"
|
||||
|
||||
using flightgear::SceneryPager;
|
||||
|
||||
class FGTileMgr::TileManagerListener : public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
TileManagerListener(FGTileMgr* manager) :
|
||||
_manager(manager),
|
||||
_useVBOsProp(fgGetNode("/sim/rendering/use-vbos", true)),
|
||||
_enableCacheProp(fgGetNode("/sim/tile-cache/enable", true)),
|
||||
_pagedLODMaximumProp(fgGetNode("/sim/rendering/max-paged-lod", true)),
|
||||
_lodDetailed(fgGetNode("/sim/rendering/static-lod/detailed", true)),
|
||||
_lodRoughDelta(fgGetNode("/sim/rendering/static-lod/rough-delta", true)),
|
||||
_lodBareDelta(fgGetNode("/sim/rendering/static-lod/bare-delta", true)),
|
||||
_lodRough(fgGetNode("/sim/rendering/static-lod/rough", true)),
|
||||
_lodBare(fgGetNode("/sim/rendering/static-lod/bare", true))
|
||||
{
|
||||
_useVBOsProp->addChangeListener(this, true);
|
||||
|
||||
_enableCacheProp->addChangeListener(this, true);
|
||||
if (_enableCacheProp->getType() == simgear::props::NONE) {
|
||||
_enableCacheProp->setBoolValue(true);
|
||||
}
|
||||
|
||||
if (_pagedLODMaximumProp->getType() == simgear::props::NONE) {
|
||||
// not set, use OSG default / environment value variable
|
||||
osg::ref_ptr<osgViewer::View> view(globals->get_renderer()->getView());
|
||||
int current = view->getDatabasePager()->getTargetMaximumNumberOfPageLOD();
|
||||
_pagedLODMaximumProp->setIntValue(current);
|
||||
}
|
||||
_pagedLODMaximumProp->addChangeListener(this, true);
|
||||
_lodDetailed->addChangeListener(this, true);
|
||||
_lodBareDelta->addChangeListener(this, true);
|
||||
_lodRoughDelta->addChangeListener(this, true);
|
||||
}
|
||||
|
||||
~TileManagerListener()
|
||||
{
|
||||
_useVBOsProp->removeChangeListener(this);
|
||||
_enableCacheProp->removeChangeListener(this);
|
||||
_pagedLODMaximumProp->removeChangeListener(this);
|
||||
_lodDetailed->removeChangeListener(this);
|
||||
_lodBareDelta->removeChangeListener(this);
|
||||
_lodRoughDelta->removeChangeListener(this);
|
||||
}
|
||||
|
||||
virtual void valueChanged(SGPropertyNode* prop)
|
||||
{
|
||||
if (prop == _useVBOsProp) {
|
||||
bool useVBOs = prop->getBoolValue();
|
||||
_manager->_options->setPluginStringData("SimGear::USE_VBOS",
|
||||
useVBOs ? "ON" : "OFF");
|
||||
} else if (prop == _enableCacheProp) {
|
||||
_manager->_enableCache = prop->getBoolValue();
|
||||
} else if (prop == _pagedLODMaximumProp) {
|
||||
int v = prop->getIntValue();
|
||||
osg::ref_ptr<osgViewer::View> view(globals->get_renderer()->getView());
|
||||
if (view) {
|
||||
osgDB::DatabasePager* pager = view->getDatabasePager();
|
||||
if (pager) pager->setTargetMaximumNumberOfPageLOD(v);
|
||||
}
|
||||
} else if (prop == _lodDetailed || prop == _lodBareDelta || prop == _lodRoughDelta) {
|
||||
// compatibility with earlier versions; set the static lod ranges appropriately as otherwise (bad) self managed
|
||||
// LOD on scenery with range animations doesn't work.
|
||||
// see also /sim/rendering/enable-range-lod-animations - which is false by default in > 2019.2 which also fixes
|
||||
// the scenery but in a more efficient way.
|
||||
_lodRough->setDoubleValue(_lodDetailed->getDoubleValue() + _lodRoughDelta->getDoubleValue());
|
||||
_lodBare->setDoubleValue(_lodRough->getDoubleValue() + _lodBareDelta->getDoubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FGTileMgr* _manager;
|
||||
SGPropertyNode_ptr _useVBOsProp,
|
||||
_enableCacheProp,
|
||||
_pagedLODMaximumProp,
|
||||
_lodDetailed,
|
||||
_lodRoughDelta,
|
||||
_lodBareDelta,
|
||||
_lodRough,
|
||||
_lodBare
|
||||
;
|
||||
};
|
||||
|
||||
FGTileMgr::FGTileMgr():
|
||||
state( Start ),
|
||||
last_state( Running ),
|
||||
scheduled_visibility(100.0),
|
||||
_visibilityMeters(fgGetNode("/environment/visibility-m", true)),
|
||||
_lodDetailed(fgGetNode("/sim/rendering/static-lod/detailed", true)),
|
||||
_lodRoughDelta(fgGetNode("/sim/rendering/static-lod/rough-delta", true)),
|
||||
_lodBareDelta(fgGetNode("/sim/rendering/static-lod/bare-delta", true)),
|
||||
_disableNasalHooks(fgGetNode("/sim/temp/disable-scenery-nasal", true)),
|
||||
_scenery_loaded(fgGetNode("/sim/sceneryloaded", true)),
|
||||
_scenery_override(fgGetNode("/sim/sceneryloaded-override", true)),
|
||||
_pager(FGScenery::getPagerSingleton()),
|
||||
_enableCache(true),
|
||||
_use_vpb(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FGTileMgr::~FGTileMgr()
|
||||
{
|
||||
}
|
||||
|
||||
// Initialize the Tile Manager subsystem
|
||||
void FGTileMgr::init()
|
||||
{
|
||||
reinit();
|
||||
}
|
||||
|
||||
void FGTileMgr::shutdown()
|
||||
{
|
||||
_listener.reset();
|
||||
|
||||
FGScenery* scenery = globals->get_scenery();
|
||||
if (scenery && scenery->get_terrain_branch()) {
|
||||
osg::Group* group = scenery->get_terrain_branch();
|
||||
group->removeChildren(0, group->getNumChildren());
|
||||
}
|
||||
// clear OSG cache
|
||||
osgDB::Registry::instance()->clearObjectCache();
|
||||
state = Start; // need to init again
|
||||
}
|
||||
|
||||
void FGTileMgr::reinit()
|
||||
{
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Initializing Tile Manager subsystem." );
|
||||
auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
|
||||
|
||||
// drops the previous options reference
|
||||
_options = new simgear::SGReaderWriterOptions;
|
||||
_listener.reset(new TileManagerListener(this));
|
||||
|
||||
materialLibChanged();
|
||||
_options->setPropertyNode(globals->get_props());
|
||||
|
||||
osgDB::FilePathList &fp = _options->getDatabasePathList();
|
||||
const PathList &sc = globals->get_fg_scenery();
|
||||
fp.clear();
|
||||
for (auto it = sc.begin(); it != sc.end(); ++it) {
|
||||
fp.push_back(it->utf8Str());
|
||||
}
|
||||
_options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str());
|
||||
|
||||
if (terraSync) {
|
||||
_options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().utf8Str());
|
||||
}
|
||||
|
||||
if (!_disableNasalHooks->getBoolValue())
|
||||
_options->setModelData(new FGNasalModelDataProxy);
|
||||
|
||||
double detailed = fgGetDouble("/sim/rendering/static-lod/detailed", SG_OBJECT_RANGE_DETAILED);
|
||||
double rough = fgGetDouble("/sim/rendering/static-lod/rough-delta", SG_OBJECT_RANGE_ROUGH) + detailed;
|
||||
double bare = fgGetDouble("/sim/rendering/static-lod/bare", SG_OBJECT_RANGE_BARE) + rough;
|
||||
double tile_min_expiry = fgGetDouble("/sim/rendering/plod-minimum-expiry-time-secs", SG_TILE_MIN_EXPIRY);
|
||||
_use_vpb = fgGetBool("/scenery/use-vpb");
|
||||
|
||||
_options->setPluginStringData("SimGear::LOD_RANGE_BARE", std::to_string(bare));
|
||||
_options->setPluginStringData("SimGear::LOD_RANGE_ROUGH", std::to_string(rough));
|
||||
_options->setPluginStringData("SimGear::LOD_RANGE_DETAILED", std::to_string(detailed));
|
||||
_options->setPluginStringData("SimGear::PAGED_LOD_EXPIRY", std::to_string(tile_min_expiry));
|
||||
|
||||
string_list scenerySuffixes;
|
||||
for (auto node : fgGetNode("/sim/rendering/", true)->getChildren("scenery-path-suffix")) {
|
||||
if (node->getBoolValue("enabled", true)) {
|
||||
scenerySuffixes.push_back(node->getStringValue("name"));
|
||||
}
|
||||
}
|
||||
|
||||
if (scenerySuffixes.empty()) {
|
||||
// if preferences didn't load, use some default
|
||||
scenerySuffixes = {"Objects", "Terrain"}; // defaut values
|
||||
}
|
||||
|
||||
if (terraSync) {
|
||||
terraSync->setSceneryPathSuffixes(scenerySuffixes);
|
||||
}
|
||||
_options->setSceneryPathSuffixes(scenerySuffixes);
|
||||
|
||||
if (state != Start)
|
||||
{
|
||||
// protect against multiple scenery reloads and properly reset flags,
|
||||
// otherwise aircraft fall through the ground while reloading scenery
|
||||
if (_scenery_loaded->getBoolValue() == false) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "/sim/sceneryloaded already false, avoiding duplicate re-init of tile manager" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_scenery_loaded->setBoolValue(false);
|
||||
fgSetDouble("/sim/startup/splash-alpha", 1.0);
|
||||
|
||||
materialLibChanged();
|
||||
|
||||
// remove all old scenery nodes from scenegraph and clear cache
|
||||
osg::Group* group = globals->get_scenery()->get_terrain_branch();
|
||||
group->removeChildren(0, group->getNumChildren());
|
||||
tile_cache.init();
|
||||
|
||||
// clear OSG cache, except on initial start-up
|
||||
if (state != Start)
|
||||
{
|
||||
osgDB::Registry::instance()->clearObjectCache();
|
||||
}
|
||||
|
||||
state = Inited;
|
||||
|
||||
previous_bucket.make_bad();
|
||||
current_bucket.make_bad();
|
||||
scheduled_visibility = 100.0;
|
||||
|
||||
// force an update now
|
||||
update(0.0);
|
||||
}
|
||||
|
||||
void FGTileMgr::materialLibChanged()
|
||||
{
|
||||
_options->setMaterialLib(globals->get_matlib());
|
||||
}
|
||||
|
||||
/* schedule a tile for loading, keep request for given amount of time.
|
||||
* Returns true if tile is already loaded. */
|
||||
bool FGTileMgr::sched_tile( const SGBucket& b, double priority, bool current_view, double duration)
|
||||
{
|
||||
// see if tile already exists in the cache
|
||||
STGTileEntry *t = tile_cache.get_stg_tile( b );
|
||||
if (!t)
|
||||
{
|
||||
// create a new entry
|
||||
t = new STGTileEntry( b );
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "sched_tile: new STG tile entry for:" << b );
|
||||
|
||||
// insert the tile into the cache, update will generate load request
|
||||
if ( tile_cache.insert_tile( t ) )
|
||||
{
|
||||
// Attach to scene graph
|
||||
|
||||
t->addToSceneGraph(globals->get_scenery()->get_terrain_branch());
|
||||
} else
|
||||
{
|
||||
// insert failed (cache full with no available entries to
|
||||
// delete.) Try again later
|
||||
delete t;
|
||||
return false;
|
||||
}
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " New tile cache size " << (int)tile_cache.get_size() );
|
||||
}
|
||||
|
||||
// update tile's properties
|
||||
tile_cache.request_tile(t,priority,current_view,duration);
|
||||
|
||||
if (_use_vpb) {
|
||||
VPBTileEntry *v = tile_cache.get_vpb_tile( b );
|
||||
|
||||
if (!v)
|
||||
{
|
||||
// create a new entry
|
||||
v = new VPBTileEntry( b );
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "sched_tile: new VPB tile entry for:" << b );
|
||||
|
||||
// insert the tile into the cache, update will generate load request
|
||||
if ( tile_cache.insert_tile( v ) )
|
||||
{
|
||||
// Attach to scene graph
|
||||
v->addToSceneGraph(globals->get_scenery()->get_terrain_branch());
|
||||
} else {
|
||||
// insert failed (cache full with no available entries to
|
||||
// delete.) Try again later
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " New tile cache size " << (int)tile_cache.get_size() );
|
||||
}
|
||||
|
||||
// update tile's properties. We ensure VPB tiles have maximum priority - priority is calcualated as
|
||||
// _negative_ the square of the distance from the viewer to the tile.
|
||||
// so by multiplying by 0.1 we increase the number towards 0.
|
||||
tile_cache.request_tile(v,priority * 0.1,current_view,duration);
|
||||
}
|
||||
|
||||
return t->is_loaded();
|
||||
}
|
||||
|
||||
/* schedule needed buckets for the current view position for loading,
|
||||
* keep request for given amount of time */
|
||||
void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
|
||||
{
|
||||
// sanity check (unfortunately needed!)
|
||||
if (!curr_bucket.isValid() )
|
||||
{
|
||||
SG_LOG( SG_TERRAIN, SG_ALERT,
|
||||
"Attempting to schedule tiles for invalid bucket" );
|
||||
return;
|
||||
}
|
||||
|
||||
double tile_width = curr_bucket.get_width_m();
|
||||
double tile_height = curr_bucket.get_height_m();
|
||||
SG_LOG( SG_TERRAIN, SG_INFO,
|
||||
"scheduling needed tiles for " << curr_bucket
|
||||
<< ", tile-width-m:" << tile_width << ", tile-height-m:" << tile_height);
|
||||
|
||||
|
||||
// cout << "tile width = " << tile_width << " tile_height = "
|
||||
// << tile_height << endl;
|
||||
// starting with 2018.3 we will use deltas rather than absolutes as it is more intuitive for the user
|
||||
// and somewhat easier to visualise
|
||||
double maxTileRange = _lodDetailed->getDoubleValue() + _lodRoughDelta->getDoubleValue() + _lodBareDelta->getDoubleValue();
|
||||
|
||||
double tileRangeM = std::min(vis, maxTileRange);
|
||||
int xrange = (int)(tileRangeM / tile_width) + 1;
|
||||
int yrange = (int)(tileRangeM / tile_height) + 1;
|
||||
if ( xrange < 1 ) { xrange = 1; }
|
||||
if ( yrange < 1 ) { yrange = 1; }
|
||||
|
||||
// make the cache twice as large to avoid losing terrain when switching
|
||||
// between aircraft and tower views
|
||||
tile_cache.set_max_cache_size( (2*xrange + 2) * (2*yrange + 2) * 2 );
|
||||
// cout << "xrange = " << xrange << " yrange = " << yrange << endl;
|
||||
// cout << "max cache size = " << tile_cache.get_max_cache_size()
|
||||
// << " current cache size = " << tile_cache.get_size() << endl;
|
||||
|
||||
// clear flags of all tiles belonging to the previous view set
|
||||
tile_cache.clear_current_view();
|
||||
|
||||
// update timestamps, so all tiles scheduled now are *newer* than any tile previously loaded
|
||||
osg::FrameStamp* framestamp
|
||||
= globals->get_renderer()->getFrameStamp();
|
||||
tile_cache.set_current_time(framestamp->getReferenceTime());
|
||||
|
||||
SGBucket b;
|
||||
|
||||
int x, y;
|
||||
auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
|
||||
|
||||
/* schedule all tiles, use distance-based loading priority,
|
||||
* so tiles are loaded in innermost-to-outermost sequence. */
|
||||
for ( x = -xrange; x <= xrange; ++x )
|
||||
{
|
||||
for ( y = -yrange; y <= yrange; ++y )
|
||||
{
|
||||
SGBucket b = curr_bucket.sibling(x, y);
|
||||
if (!b.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float priority = (-1.0) * (x*x+y*y);
|
||||
sched_tile( b, priority, true, 0.0 );
|
||||
|
||||
if (terraSync) {
|
||||
terraSync->scheduleTile(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the various queues maintained by the tilemgr (private
|
||||
* internal function, do not call directly.)
|
||||
*/
|
||||
void FGTileMgr::update_queues(bool& isDownloadingScenery)
|
||||
{
|
||||
osg::FrameStamp* framestamp = globals->get_renderer()->getFrameStamp();
|
||||
double current_time = framestamp->getReferenceTime();
|
||||
double vis = _visibilityMeters->getDoubleValue();
|
||||
TileEntry *e;
|
||||
int loading=0;
|
||||
int sz=0;
|
||||
|
||||
tile_cache.set_current_time( current_time );
|
||||
tile_cache.reset_traversal();
|
||||
|
||||
while ( ! tile_cache.at_end() )
|
||||
{
|
||||
e = tile_cache.get_current();
|
||||
if ( e )
|
||||
{
|
||||
// Prepare the ssg nodes corresponding to each tile.
|
||||
// Set the ssg transform and update it's range selector
|
||||
// based on current visibilty
|
||||
e->prep_ssg_node(vis);
|
||||
|
||||
if (!e->is_loaded()) {
|
||||
bool nonExpiredOrCurrent = !e->is_expired(current_time) || e->is_current_view();
|
||||
bool downloading = isTileDirSyncing(e->tileFileName);
|
||||
isDownloadingScenery |= downloading;
|
||||
if ( !downloading && nonExpiredOrCurrent) {
|
||||
// schedule tile for loading with osg pager
|
||||
_pager->queueRequest(e->tileFileName,
|
||||
e->getNode(),
|
||||
e->get_priority(),
|
||||
framestamp,
|
||||
e->getDatabaseRequest(),
|
||||
_options.get());
|
||||
loading++;
|
||||
}
|
||||
} // of tile not loaded case
|
||||
} else {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "Warning: empty tile in cache!");
|
||||
}
|
||||
tile_cache.next();
|
||||
sz++;
|
||||
}
|
||||
|
||||
int drop_count = sz - tile_cache.get_max_cache_size();
|
||||
bool dropTiles = false;
|
||||
if (_enableCache) {
|
||||
dropTiles = ( drop_count > 0 ) && ((loading==0)||(drop_count > 10));
|
||||
} else {
|
||||
dropTiles = true;
|
||||
drop_count = sz; // no limit on tiles to drop
|
||||
}
|
||||
|
||||
if (dropTiles)
|
||||
{
|
||||
long drop_index = _enableCache ? tile_cache.get_drop_tile() :
|
||||
tile_cache.get_first_expired_tile();
|
||||
while ( drop_index > -1 )
|
||||
{
|
||||
// schedule tile for deletion with osg pager
|
||||
TileEntry* old = tile_cache.get_tile(drop_index);
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Dropping:" << old->get_tile_bucket());
|
||||
|
||||
tile_cache.clear_entry(drop_index);
|
||||
|
||||
osg::ref_ptr<osg::Object> subgraph = old->getNode();
|
||||
old->removeFromSceneGraph();
|
||||
delete old;
|
||||
// zeros out subgraph ref_ptr, so subgraph is owned by
|
||||
// the pager and will be deleted in the pager thread.
|
||||
_pager->queueDeleteRequest(subgraph);
|
||||
|
||||
if (!_enableCache)
|
||||
drop_index = tile_cache.get_first_expired_tile();
|
||||
// limit tiles dropped to drop_count
|
||||
else if (--drop_count > 0)
|
||||
drop_index = tile_cache.get_drop_tile();
|
||||
else
|
||||
drop_index = -1;
|
||||
}
|
||||
} // of dropping tiles loop
|
||||
}
|
||||
|
||||
// given the current lon/lat (in degrees), fill in the array of local
|
||||
// chunks. If the chunk isn't already in the cache, then read it from
|
||||
// disk.
|
||||
void FGTileMgr::update(double)
|
||||
{
|
||||
double vis = _visibilityMeters->getDoubleValue();
|
||||
schedule_tiles_at(globals->get_view_position(), vis);
|
||||
|
||||
bool waitingOnTerrasync = false;
|
||||
update_queues(waitingOnTerrasync);
|
||||
|
||||
// scenery loading check, triggers after each sim (tile manager) reinit
|
||||
if (!_scenery_loaded->getBoolValue())
|
||||
{
|
||||
bool fdmInited = fgGetBool("sim/fdm-initialized");
|
||||
bool positionFinalized = fgGetBool("sim/position-finalized");
|
||||
bool sceneryOverride = _scenery_override->getBoolValue();
|
||||
|
||||
|
||||
// we are done if final position is set and the scenery & FDM are done.
|
||||
// scenery-override can ignore the last two, but not position finalization.
|
||||
if (positionFinalized && (sceneryOverride || (isSceneryLoaded() && fdmInited)))
|
||||
{
|
||||
_scenery_loaded->setBoolValue(true);
|
||||
fgSplashProgress("");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!positionFinalized) {
|
||||
fgSplashProgress("finalize-position");
|
||||
} else if (waitingOnTerrasync) {
|
||||
fgSplashProgress("downloading-scenery");
|
||||
} else {
|
||||
fgSplashProgress("loading-scenery");
|
||||
}
|
||||
|
||||
// be nice to loader threads while waiting for initial scenery, reduce to 20fps
|
||||
SGTimeStamp::sleepForMSec(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// schedule tiles for the viewer bucket
|
||||
// (FDM/AI/groundcache/... should use "schedule_scenery" instead)
|
||||
void FGTileMgr::schedule_tiles_at(const SGGeod& location, double range_m)
|
||||
{
|
||||
// SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update() for "
|
||||
// << longitude << " " << latitude );
|
||||
|
||||
current_bucket = SGBucket( location );
|
||||
|
||||
// schedule more tiles when visibility increased considerably
|
||||
// TODO Calculate tile size - instead of using fixed value (5000m)
|
||||
if (range_m - scheduled_visibility > 5000.0)
|
||||
previous_bucket.make_bad();
|
||||
|
||||
// SG_LOG( SG_TERRAIN, SG_DEBUG, "Updating tile list for "
|
||||
// << current_bucket );
|
||||
fgSetInt( "/environment/current-tile-id", current_bucket.gen_index() );
|
||||
|
||||
// do tile load scheduling.
|
||||
// Note that we need keep track of both viewer buckets and fdm buckets.
|
||||
if ( state == Running ) {
|
||||
if (last_state != state)
|
||||
{
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Running" );
|
||||
}
|
||||
if (current_bucket != previous_bucket) {
|
||||
// We've moved to a new bucket, we need to schedule any
|
||||
// needed tiles for loading.
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "FGTileMgr: at " << location << ", scheduling needed for:" << current_bucket
|
||||
<< ", visibility=" << range_m);
|
||||
scheduled_visibility = range_m;
|
||||
schedule_needed(current_bucket, range_m);
|
||||
}
|
||||
|
||||
// save bucket
|
||||
previous_bucket = current_bucket;
|
||||
} else if ( state == Start || state == Inited ) {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Start || Inited" );
|
||||
// do not update bucket yet (position not valid in initial loop)
|
||||
state = Running;
|
||||
previous_bucket.make_bad();
|
||||
}
|
||||
last_state = state;
|
||||
}
|
||||
|
||||
/** Schedules scenery for given position. Load request remains valid for given duration
|
||||
* (duration=0.0 => nothing is loaded).
|
||||
* Used for FDM/AI/groundcache/... requests. Viewer uses "schedule_tiles_at" instead.
|
||||
* Returns true when all tiles for the given position are already loaded, false otherwise.
|
||||
*/
|
||||
bool FGTileMgr::schedule_scenery(const SGGeod& position, double range_m, double duration)
|
||||
{
|
||||
// sanity check (unfortunately needed!)
|
||||
if (!position.isValid())
|
||||
return false;
|
||||
const float priority = 0.0;
|
||||
bool available = true;
|
||||
|
||||
SGBucket bucket(position);
|
||||
available = sched_tile( bucket, priority, false, duration );
|
||||
|
||||
if ((!available)&&(duration==0.0)) {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "schedule_scenery: Scheduling tile at bucket:" << bucket << " return false" );
|
||||
return false;
|
||||
}
|
||||
|
||||
SGVec3d cartPos = SGVec3d::fromGeod(position);
|
||||
|
||||
// Traverse all tiles required to be there for the given visibility.
|
||||
double tile_width = bucket.get_width_m();
|
||||
double tile_height = bucket.get_height_m();
|
||||
double tile_r = 0.5*sqrt(tile_width*tile_width + tile_height*tile_height);
|
||||
double max_dist = tile_r + range_m;
|
||||
double max_dist2 = max_dist*max_dist;
|
||||
|
||||
int xrange = (int)fabs(range_m / tile_width) + 1;
|
||||
int yrange = (int)fabs(range_m / tile_height) + 1;
|
||||
|
||||
for ( int x = -xrange; x <= xrange; ++x )
|
||||
{
|
||||
for ( int y = -yrange; y <= yrange; ++y )
|
||||
{
|
||||
// We have already checked for the center tile.
|
||||
if ( x != 0 || y != 0 )
|
||||
{
|
||||
SGBucket b = bucket.sibling(x, y );
|
||||
if (!b.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double distance2 = distSqr(cartPos, SGVec3d::fromGeod(b.get_center()));
|
||||
// Do not ask if it is just the next tile but way out of range.
|
||||
if (distance2 <= max_dist2)
|
||||
{
|
||||
available &= sched_tile( b, priority, false, duration );
|
||||
if ((!available)&&(duration==0.0))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
// Returns true if tiles around current view position have been loaded
|
||||
bool FGTileMgr::isSceneryLoaded()
|
||||
{
|
||||
double range_m = 100.0;
|
||||
if (scheduled_visibility < range_m)
|
||||
range_m = scheduled_visibility;
|
||||
|
||||
return schedule_scenery(globals->get_view_position(), range_m, 0.0);
|
||||
}
|
||||
|
||||
bool FGTileMgr::isTileDirSyncing(const std::string& tileFileName) const
|
||||
{
|
||||
auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
|
||||
if (!terraSync) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if Models is syncing, also wait for it, since otherwise
|
||||
// we get load errors
|
||||
if (terraSync->isDataDirPending("Models")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string nameWithoutExtension = tileFileName.substr(0, tileFileName.size() - 4);
|
||||
long int bucketIndex = simgear::strutils::to_int(nameWithoutExtension);
|
||||
SGBucket bucket(bucketIndex);
|
||||
|
||||
return terraSync->isTileDirPending(bucket.gen_base_path());
|
||||
}
|
||||
120
src/Scenery/tilemgr.hxx
Normal file
120
src/Scenery/tilemgr.hxx
Normal file
@@ -0,0 +1,120 @@
|
||||
// tilemgr.hxx -- routines to handle dynamic management of scenery tiles
|
||||
//
|
||||
// Written by Curtis Olson, started January 1998.
|
||||
//
|
||||
// Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _TILEMGR_HXX
|
||||
#define _TILEMGR_HXX
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include "SceneryPager.hxx"
|
||||
#include "tilecache.hxx"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Node;
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
class SGReaderWriterOptions;
|
||||
}
|
||||
|
||||
class FGTileMgr {
|
||||
|
||||
private:
|
||||
|
||||
// Tile loading state
|
||||
enum load_state {
|
||||
Start = 0,
|
||||
Inited = 1,
|
||||
Running = 2
|
||||
};
|
||||
|
||||
load_state state, last_state;
|
||||
|
||||
// schedule a tile for loading, returns true when tile is already loaded
|
||||
bool sched_tile( const SGBucket& b, double priority,bool current_view, double request_time);
|
||||
|
||||
// schedule a needed buckets for loading
|
||||
void schedule_needed(const SGBucket& curr_bucket, double rangeM);
|
||||
|
||||
bool isTileDirSyncing(const std::string& tileFileName) const;
|
||||
|
||||
SGBucket previous_bucket;
|
||||
SGBucket current_bucket;
|
||||
SGBucket pending;
|
||||
osg::ref_ptr<simgear::SGReaderWriterOptions> _options;
|
||||
|
||||
double scheduled_visibility;
|
||||
|
||||
/**
|
||||
* tile cache
|
||||
*/
|
||||
TileCache tile_cache;
|
||||
|
||||
class TileManagerListener;
|
||||
friend class TileManagerListener;
|
||||
std::unique_ptr<TileManagerListener> _listener;
|
||||
|
||||
// update various queues internal queues
|
||||
void update_queues(bool& isDownloadingScenery);
|
||||
|
||||
// schedule tiles for the viewer bucket
|
||||
void schedule_tiles_at(const SGGeod& location, double rangeM);
|
||||
|
||||
SGPropertyNode_ptr _visibilityMeters;
|
||||
SGPropertyNode_ptr _lodDetailed, _lodRoughDelta, _lodBareDelta, _disableNasalHooks;
|
||||
SGPropertyNode_ptr _scenery_loaded, _scenery_override;
|
||||
|
||||
osg::ref_ptr<flightgear::SceneryPager> _pager;
|
||||
|
||||
/// is caching of expired tiles enabled or not?
|
||||
bool _enableCache;
|
||||
bool _use_vpb;
|
||||
public:
|
||||
FGTileMgr();
|
||||
~FGTileMgr();
|
||||
|
||||
// Initialize the Tile Manager
|
||||
void init();
|
||||
void reinit();
|
||||
void shutdown();
|
||||
void update(double dt);
|
||||
|
||||
const SGBucket& get_current_bucket () const { return current_bucket; }
|
||||
|
||||
// Returns true if scenery is available for the given lat, lon position
|
||||
// within a range of range_m.
|
||||
// lat and lon are expected to be in degrees.
|
||||
bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
|
||||
|
||||
// Returns true if tiles around current view position have been loaded
|
||||
bool isSceneryLoaded();
|
||||
|
||||
// notify the tile manahger the material library was reloaded,
|
||||
// so it can pass this through to its options object
|
||||
void materialLibChanged();
|
||||
};
|
||||
|
||||
#endif // _TILEMGR_HXX
|
||||
Reference in New Issue
Block a user