src/Viewer/sview.*: Added SviewStepAGL for Tower View AGL.
This commit is contained in:
parent
c3836923cb
commit
0ede0401b7
2 changed files with 212 additions and 9 deletions
|
@ -52,9 +52,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/viewer/Compositor.hxx>
|
||||
|
||||
#include <osgViewer/CompositeViewer>
|
||||
#include <osg/CameraView>
|
||||
#include <osg/GraphicsContext>
|
||||
#include <osgViewer/CompositeViewer>
|
||||
|
||||
static const double pi = 3.141592653589793238463;
|
||||
|
||||
static std::ostream& operator << (std::ostream& out, const osg::Vec3f& vec)
|
||||
{
|
||||
|
@ -117,6 +119,11 @@ struct SviewPosDir
|
|||
SGVec3d position2;
|
||||
SGQuatd direction2;
|
||||
|
||||
/* If a step sets either/both of these to non-zero, the view will alter
|
||||
zoom to accomodate the required field of views (in degreea). */
|
||||
double fov_h = 0;
|
||||
double fov_v = 0;
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& out, const SviewPosDir& posdir)
|
||||
{
|
||||
out << "SviewPosDir {"
|
||||
|
@ -304,7 +311,8 @@ struct SviewStepMove : SviewStep
|
|||
SGVec3d m_offset;
|
||||
};
|
||||
|
||||
/* Modifies heading, pitch and roll by fixed amounts; does not change position.
|
||||
/* Modifies heading, pitch and roll by fixed amounts; does not change
|
||||
position. Optionally implements a mouse drag handler which pans/tilts the view.
|
||||
|
||||
E.g. can be used to preserve direction (relative to aircraft) of Helicopter
|
||||
view at the time it was cloned. */
|
||||
|
@ -583,6 +591,144 @@ struct SviewStepFinal : SviewStep
|
|||
double m_default_roll = 0;
|
||||
};
|
||||
|
||||
/* Change angle and field of view so that we can see an aircraft and the ground
|
||||
immediately below it. */
|
||||
struct SviewStepAGL : SviewStep
|
||||
{
|
||||
SviewStepAGL(const std::string& callsign)
|
||||
:
|
||||
m_callsign(callsign)
|
||||
{
|
||||
}
|
||||
|
||||
void evaluate(SviewPosDir& posdir) override
|
||||
{
|
||||
if (m_callsign.update()) {
|
||||
m_chase_distance = -25;
|
||||
if (m_callsign.m_callsign == "") {
|
||||
m_chase_distance = globals->get_props()->getDoubleValue("/sim/chase-distance-m", m_chase_distance);
|
||||
}
|
||||
else {
|
||||
m_chase_distance = m_callsign.m_root->getDoubleValue("set/sim/chase-distance-m", m_chase_distance);
|
||||
}
|
||||
}
|
||||
double _fov_user_deg = 30;
|
||||
double _configFOV_deg = 30;
|
||||
/* Some aircraft appear to have elevation that is slightly below ground
|
||||
level when on the ground, e.g. SenecaII, which makes get_elevation_m()
|
||||
fail. So we pass a slightly incremented elevation. */
|
||||
double ground_altitude = 0;
|
||||
const simgear::BVHMaterial* material = NULL;
|
||||
SGGeod target0 = posdir.target;
|
||||
SGGeod target_plus = posdir.target;
|
||||
target_plus.setElevationM(target_plus.getElevationM() + 1);
|
||||
bool ok = globals->get_scenery()->get_elevation_m(target_plus, ground_altitude, &material);
|
||||
if (ok) {
|
||||
m_ground_altitude = ground_altitude;
|
||||
}
|
||||
else {
|
||||
/* get_elevation_m() can fail if scenery has been un-cached, which
|
||||
appears to happen quite often with remote multiplayer aircraft, so
|
||||
we preserve the previous ground altitude to give some consistency
|
||||
and avoid confusing zooming when switching between views. */
|
||||
ground_altitude = m_ground_altitude;
|
||||
}
|
||||
|
||||
double h_distance = SGGeodesy::distanceM(posdir.position, posdir.target);
|
||||
if (h_distance == 0) {
|
||||
/* Not sure this should ever happen, but we need to handle this
|
||||
here otherwise we'll get divide-by-zero. */
|
||||
}
|
||||
else {
|
||||
/* Find vertical region we want to be able to see. */
|
||||
double relative_height_target = posdir.target.getElevationM() - posdir.position.getElevationM();
|
||||
double relative_height_ground = ground_altitude - posdir.position.getElevationM();
|
||||
|
||||
/* We expand the field of view so that it hopefully shows the whole
|
||||
aircraft and a little more of the ground.
|
||||
|
||||
We use chase-distance as a crude measure of the aircraft's
|
||||
size. There doesn't seem to be any more definitive information.
|
||||
|
||||
We damp our measure of ground level, to avoid the view jumping around
|
||||
if an aircraft flies over buildings. */
|
||||
|
||||
relative_height_ground -= 2;
|
||||
|
||||
double aircraft_size_vertical = fabs(m_chase_distance) * 0.3;
|
||||
double aircraft_size_horizontal = fabs(m_chase_distance) * 0.9;
|
||||
|
||||
double relative_height_target_plus = relative_height_target + aircraft_size_vertical;
|
||||
double relative_height_ground_ = relative_height_ground;
|
||||
//_lookat_agl_damping.updateTarget(relative_height_ground);
|
||||
if (relative_height_ground > relative_height_target) {
|
||||
/* Damping of relative_height_ground can result in it being
|
||||
temporarily above the aircraft, so we ensure the aircraft is
|
||||
visible. */
|
||||
//_lookat_agl_damping.reset(relative_height_ground_);
|
||||
relative_height_ground = relative_height_ground_;
|
||||
}
|
||||
|
||||
/* Not implemented yet: apply scaling from user field of view
|
||||
setting, altering only relative_height_ground so that the aircraft
|
||||
is always in view. */
|
||||
{
|
||||
double delta = relative_height_target_plus - relative_height_ground;
|
||||
delta *= (_fov_user_deg / _configFOV_deg);
|
||||
relative_height_ground = relative_height_target_plus - delta;
|
||||
}
|
||||
|
||||
double angle_v_target = atan(relative_height_target_plus / h_distance);
|
||||
double angle_v_ground = atan(relative_height_ground / h_distance);
|
||||
|
||||
/* The target we want to use is determined by the midpoint of the two
|
||||
angles we've calculated. */
|
||||
double angle_v_mid = (angle_v_target + angle_v_ground) / 2;
|
||||
double posdir_target_old_elevation = posdir.target.getElevationM();
|
||||
posdir.target.setElevationM(posdir.position.getElevationM() + h_distance * tan(angle_v_mid));
|
||||
|
||||
/* Set required vertical field of view. We use fabs to avoid
|
||||
things being upside down if target is below ground level (e.g. new
|
||||
multiplayer aircraft are briefly at -9999ft). */
|
||||
double fov_v = fabs(angle_v_target - angle_v_ground);
|
||||
|
||||
/* Set required horizontal field of view so that we can see entire
|
||||
horizontal extent of the aircraft (assuming worst case where
|
||||
airplane is horizontal and square-on to viewer). */
|
||||
double fov_h = 2 * atan(aircraft_size_horizontal / 2 / h_distance);
|
||||
|
||||
posdir.fov_v = fov_v * 180 / pi;
|
||||
posdir.fov_h = fov_h * 180 / pi;
|
||||
|
||||
bool verbose = false;
|
||||
if (0) {
|
||||
static time_t t0 = 0;
|
||||
time_t t = time(NULL);
|
||||
if (0 && t - t0 >= 3) {
|
||||
t0 = t;
|
||||
verbose = true;
|
||||
}
|
||||
if (verbose) SG_LOG(SG_VIEW, SG_ALERT, ""
|
||||
<< " target0=" << target0
|
||||
<< " fov_v=" << fov_v * 180/pi
|
||||
<< " ground_altitude=" << ground_altitude
|
||||
<< " relative_height_target_plus=" << relative_height_target_plus
|
||||
<< " h_distance=" << h_distance
|
||||
<< " relative_height_ground=" << relative_height_ground
|
||||
<< " m_chase_distance=" << m_chase_distance
|
||||
<< " angle_v_mid=" << angle_v_mid * 180/pi
|
||||
<< " posdir_target_old_elevation=" << posdir_target_old_elevation
|
||||
<< " posdir.target=" << posdir.target
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double m_chase_distance;
|
||||
Callsign m_callsign;
|
||||
double m_ground_altitude = 0;
|
||||
};
|
||||
|
||||
/* A step that takes the SviewPosDir's eye and target positions and treats
|
||||
them as local and remote points respectively. We choose an eye position and
|
||||
direction such that the local and remote points are both visible, with the
|
||||
|
@ -592,13 +738,13 @@ struct SviewStepDouble : SviewStep
|
|||
SviewStepDouble()
|
||||
{
|
||||
m_local_chase_distance = 25;
|
||||
m_angle_rad = 15 * 3.14159265 / 180;
|
||||
m_angle_rad = 15 * pi / 180;
|
||||
}
|
||||
|
||||
SviewStepDouble(SGPropertyNode* config)
|
||||
{
|
||||
m_local_chase_distance = config->getDoubleValue("chase-distance");
|
||||
m_angle_rad = config->getDoubleValue("angle") * 3.14159265 / 180;
|
||||
m_angle_rad = config->getDoubleValue("angle") * pi / 180;
|
||||
}
|
||||
void evaluate(SviewPosDir& posdir) override
|
||||
{
|
||||
|
@ -668,7 +814,6 @@ struct SviewStepDouble : SviewStep
|
|||
SGVec3d local_pos = SGVec3d::fromGeod(posdir_local.target);
|
||||
SGVec3d remote_pos = SGVec3d::fromGeod(posdir_remote.target);
|
||||
double lr = sqrt(distSqr(local_pos, remote_pos));
|
||||
const double pi = 3.1415926;
|
||||
|
||||
/* Desired angle between local and remote aircraft in final view. */
|
||||
double ler = m_angle_rad;
|
||||
|
@ -836,9 +981,7 @@ struct SviewView
|
|||
|
||||
osg::Camera* camera = m_osg_view->getCamera();
|
||||
osg::Matrix old_m = camera->getViewMatrix();
|
||||
/* This calculation is copied from CameraGroup::update(). As of
|
||||
2020-10-10 we don't yet update the projection matrix so views cannot
|
||||
zoom. */
|
||||
/* This calculation is copied from CameraGroup::update(). */
|
||||
const osg::Matrix new_m(
|
||||
osg::Matrix::translate(-position)
|
||||
* osg::Matrix::rotate(orientation.inverse())
|
||||
|
@ -846,6 +989,51 @@ struct SviewView
|
|||
SG_LOG(SG_VIEW, SG_BULK, "old_m: " << old_m);
|
||||
SG_LOG(SG_VIEW, SG_BULK, "new_m: " << new_m);
|
||||
camera->setViewMatrix(new_m);
|
||||
|
||||
if (posdir.fov_v || posdir.fov_h) {
|
||||
/* Update zoom to accomodate required vertical/horizontal field of
|
||||
views. */
|
||||
double fovy;
|
||||
double aspect_ratio;
|
||||
double zNear;
|
||||
double zFar;
|
||||
camera->getProjectionMatrixAsPerspective(fovy, aspect_ratio, zNear, zFar);
|
||||
if (posdir.fov_v) {
|
||||
camera->setProjectionMatrixAsPerspective(
|
||||
posdir.fov_v,
|
||||
aspect_ratio,
|
||||
zNear,
|
||||
zFar
|
||||
);
|
||||
if (posdir.fov_h) {
|
||||
/* Increase fov if necessary so that we include both
|
||||
fov_h and fov_v. */
|
||||
double aspect_ratio;
|
||||
camera->getProjectionMatrixAsPerspective(
|
||||
fovy,
|
||||
aspect_ratio,
|
||||
zNear,
|
||||
zFar
|
||||
);
|
||||
if (fovy * aspect_ratio < posdir.fov_h) {
|
||||
camera->setProjectionMatrixAsPerspective(
|
||||
posdir.fov_v * posdir.fov_h / (fovy * aspect_ratio),
|
||||
aspect_ratio,
|
||||
zNear,
|
||||
zFar
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
camera->setProjectionMatrixAsPerspective(
|
||||
posdir.fov_h / aspect_ratio,
|
||||
aspect_ratio,
|
||||
zNear,
|
||||
zFar
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osgViewer::View* m_osg_view = nullptr;
|
||||
|
@ -905,6 +1093,11 @@ struct SviewViewEyeTarget : SviewView
|
|||
/* Added steps to set .m_eye up so that it looks from the nearest
|
||||
tower. */
|
||||
m_steps.add_step(new SviewStepNearestTower(callsign));
|
||||
|
||||
if (config->getBoolValue("view/config/lookat-agl")) {
|
||||
SG_LOG(SG_VIEW, SG_ALERT, "lookat-agl");
|
||||
m_steps.add_step(new SviewStepAGL(callsign));
|
||||
}
|
||||
m_steps.add_step(new SviewStepFinalToTarget);
|
||||
|
||||
/* Add a step that moves towards the target a little to avoid eye
|
||||
|
@ -1095,6 +1288,10 @@ struct SviewViewEyeTarget : SviewView
|
|||
finalised = true;
|
||||
m_steps.m_name += " double";
|
||||
}
|
||||
else if (!strcmp(type, "agl")) {
|
||||
const char* callsign = step->getStringValue("callsign");
|
||||
m_steps.add_step(new SviewStepAGL(callsign));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(std::string() + "Unrecognised step name: '" + type + "'");
|
||||
}
|
||||
|
|
|
@ -144,6 +144,12 @@ An sview-step is:
|
|||
Distance to move from eye.
|
||||
angle:
|
||||
Angle to maintain between eye and target.
|
||||
"agl":
|
||||
Tilt and zoom to keep ground immediately below the aircraft
|
||||
visible.
|
||||
callsign:
|
||||
Use chase distance of specified aircraft as a measure of
|
||||
its size, to ensure entire aircraft is visible.
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
Loading…
Reference in a new issue