Skip to content

Commit

Permalink
Terrain-relative height proof of concept
Browse files Browse the repository at this point in the history
  • Loading branch information
AnyOldName3 committed Jun 19, 2024
1 parent f5da6a7 commit 928884a
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 1 deletion.
82 changes: 82 additions & 0 deletions src/apps/rdemo/Demo_Mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,88 @@ auto Demo_Mesh_Relative = [](Application& app)
}
};

auto Demo_Mesh_TerrainRelative = [](Application& app)
{
static entt::entity entity = entt::null;

if (entity == entt::null)
{
// Create a new entity to host our mesh
entity = app.entities.create();

// Attach the new mesh:
Mesh& mesh = app.entities.emplace<Mesh>(entity);
mesh.name = "Terrain-Relative Mesh";

// Make some geometry that will be relative to a geolocation:
const float s = 250.0;
vsg::vec3 verts[8] = {
{ -s, -s, -s },
{ s, -s, -s },
{ s, s, -s },
{ -s, s, -s },
{ -s, -s, s },
{ s, -s, s },
{ s, s, s },
{ -s, s, s }
};
unsigned indices[48] = {
0,3,2, 0,2,1, 4,5,6, 4,6,7,
1,2,6, 1,6,5, 3,0,4, 3,4,7,
0,1,5, 0,5,4, 2,3,7, 2,7,6
};

vsg::vec4 color{ 1, 0, 1, 0.85 };

for (unsigned i = 0; i < 48; )
{
mesh.add({
{verts[indices[i++]], verts[indices[i++]], verts[indices[i++]]},
{color, color, color} });

if ((i % 6) == 0)
color.r *= 0.8, color.b *= 0.8;
}

// Add a transform component so we can position our mesh relative
// to some geospatial coordinates. We then set the bound on the node
// to control horizon culling a little better
auto& xform = app.entities.emplace<TerrainRelativeTransform>(entity);
xform.setPosition(EntityPosition(GeoPoint(SRS::WGS84, -0.270710, 39.708408, 0), s));
xform.node->bound.radius = s * sqrt(2);
}

if (ImGuiLTable::Begin("Mesh"))
{
auto& mesh = app.entities.get<Mesh>(entity);

ImGuiLTable::Checkbox("Visible", &mesh.active);

auto* style = app.entities.try_get<MeshStyle>(entity);
if (style)
{
if (ImGuiLTable::ColorEdit4("Color", (float*)&style->color))
mesh.dirty();

if (ImGuiLTable::SliderFloat("Wireframe", &style->wireframe, 0.0f, 32.0f, "%.0f"))
mesh.dirty();
}

auto& xform = app.entities.get<TerrainRelativeTransform>(entity).node;

if (ImGuiLTable::SliderDouble("Latitude", &xform->position.basePosition.y, 38.813321, 39.736707, "%.3lf"))
xform->dirty();

if (ImGuiLTable::SliderDouble("Longitude", &xform->position.basePosition.x, -1.271772, -0.083614, "%.3lf"))
xform->dirty();

if (ImGuiLTable::SliderDouble("Relative Altitude", &xform->position.altitude, 0.0, 2500.0, "%.1lf"))
xform->dirty();

ImGuiLTable::End();
}
};


auto Demo_Mesh_Multi = [](Application& app)
{
Expand Down
1 change: 1 addition & 0 deletions src/apps/rdemo/rdemo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ std::vector<Demo> demos =
Demo{ "Line - relative", Demo_Line_Relative },
Demo{ "Mesh - absolute", Demo_Mesh_Absolute },
Demo{ "Mesh - relative", Demo_Mesh_Relative },
Demo{ "Mesh - terrain-relative", Demo_Mesh_TerrainRelative },
Demo{ "Icon", Demo_Icon },
Demo{ "User Model", Demo_Model }
} },
Expand Down
83 changes: 83 additions & 0 deletions src/rocky/EntityPosition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "EntityPosition.h"
#include "Math.h"
#include "Utils.h"

using namespace ROCKY_NAMESPACE;
using namespace ROCKY_NAMESPACE::util;

#undef LC
#define LC "[EntityPosition] "

EntityPosition EntityPosition::INVALID;

EntityPosition::EntityPosition()
{
//nop
}

EntityPosition&
EntityPosition::operator=(EntityPosition&& rhs)
{
basePosition = rhs.basePosition;
altitude = rhs.altitude;
rhs.basePosition = { }; // invalidate rhs
return *this;
}

EntityPosition::EntityPosition(const GeoPoint& in_basePosition, double in_altitude) :
basePosition(in_basePosition),
altitude(in_altitude)
{
//nop
}

bool
EntityPosition::transform(const SRS& outSRS, EntityPosition& output) const
{
if (valid() && outSRS.valid())
{
GeoPoint outBasePosition;
if (basePosition.transform(outSRS, outBasePosition))
{
output = EntityPosition(outBasePosition, altitude);
return true;
}
}
return false;
}

bool
EntityPosition::transformInPlace(const SRS& to_srs)
{
if (valid() && to_srs.valid())
{
if (basePosition.transformInPlace(to_srs))
{
return true;
}
}
return false;
}

#include "json.h"

namespace ROCKY_NAMESPACE
{
void to_json(json& j, const EntityPosition& obj) {
if (obj.valid()) {
j = json::object();
set(j, "basePosition", obj.basePosition);
set(j, "altitude", obj.altitude);
}
}

void from_json(const json& j, EntityPosition& obj) {
GeoPoint basePosition;
double altitude = 0;

get_to(j, "basePosition", basePosition);
get_to(j, "altitude", altitude);

obj = EntityPosition(basePosition, altitude);
}
}
72 changes: 72 additions & 0 deletions src/rocky/EntityPosition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* rocky c++
* Copyright 2024 Pelican Mapping
* MIT License
*/
#pragma once

#include <rocky/GeoPoint.h>

namespace ROCKY_NAMESPACE
{
/**
* A georeferenced 3D point with terrain-relative altitude
*/
class ROCKY_EXPORT EntityPosition
{
public:
GeoPoint basePosition;
double altitude;

//! Constructs an empty (and invalid) EntityPosition.
EntityPosition();

//! Constructs a EntityPosition
EntityPosition(const GeoPoint& srs, double altitude);

//! Destruct
~EntityPosition() { }

//! Transforms this EntityPosition into another SRS and puts the
//! output in the "output"
//! @return true upon success, false upon failure
bool transform(const SRS& outSRS, EntityPosition& output) const;

//! Transforms this point in place to another SRS
bool transformInPlace(const SRS& srs);

bool operator == (const EntityPosition& rhs) const {
return basePosition == rhs.basePosition && altitude == rhs.altitude;
}

bool operator != (const EntityPosition& rhs) const {
return !operator==(rhs);
}

//! Does this object contain a valid geo point?
bool valid() const {
return basePosition.valid();
}

public:
static EntityPosition INVALID;

// copy/move ops
EntityPosition(const EntityPosition& rhs) = default;
EntityPosition& operator=(const EntityPosition& rhs) = default;
EntityPosition(EntityPosition&& rhs) { *this = rhs; }
EntityPosition& operator=(EntityPosition&& rhs);
};


/**
* Base class for any object that has a position on a Map.
*/
class TerrainRelativePositionedObject
{
public:
//! Center position of the object
virtual const EntityPosition& objectPosition() const = 0;
};

}
60 changes: 59 additions & 1 deletion src/rocky/vsg/ECS.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#pragma once
#include <rocky/vsg/Common.h>
#include <rocky/vsg/EntityTransform.h>
#include <rocky/vsg/GeoTransform.h>
#include <rocky/vsg/engine/Runtime.h>
#include <rocky/vsg/engine/Utils.h>
Expand Down Expand Up @@ -321,6 +322,51 @@ namespace ROCKY_NAMESPACE
}
};

/**
* ECS Component that provides an entity with a geotransform.
*/
struct ROCKY_EXPORT TerrainRelativeTransform : public ECS::Component
{
vsg::ref_ptr<EntityTransform> node;
vsg::dmat4 local_matrix = vsg::dmat4(1.0);
TerrainRelativeTransform* parent = nullptr;

//! Sets the transform's geoposition, creating the node on demand
void setPosition(const EntityPosition& p)
{
if (!node)
node = EntityTransform::create();

node->setPosition(p);
}

//! Returns true if the push succeeded (and a pop will be required)
inline bool push(vsg::RecordTraversal& rt, const vsg::dmat4& m)
{
if (node)
{
return node->push(rt, m * local_matrix);
}
else if (parent)
{
return parent->push(rt, m * local_matrix);
}
else return false;
}

inline void pop(vsg::RecordTraversal& rt)
{
if (node)
{
node->pop(rt);
}
else if (parent)
{
parent->pop(rt);
}
}
};

/**
* ECS Component representing a moving entity
*/
Expand Down Expand Up @@ -477,7 +523,19 @@ namespace ROCKY_NAMESPACE
}
else
{
e.component.node->accept(rt);
auto* trxform = registry.try_get<TerrainRelativeTransform>(e.entity);
if (trxform)
{
if (trxform->push(rt, identity_matrix))
{
e.component.node->accept(rt);
trxform->pop(rt);
}
}
else
{
e.component.node->accept(rt);
}
}
}
}
Expand Down
Loading

0 comments on commit 928884a

Please sign in to comment.